diff --git a/.gitignore b/.gitignore index 2adcb2b..4dbf738 100644 --- a/.gitignore +++ b/.gitignore @@ -1,53 +1,48 @@ -###################################################################### -# Build Tools - -.gradle -/build/ -!gradle/wrapper/gradle-wrapper.jar - -target/ -!.mvn/wrapper/maven-wrapper.jar - -###################################################################### -# IDE - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### JRebel ### -rebel.xml - -### NetBeans ### -nbproject/private/ -build/* -nbbuild/ -dist/ -nbdist/ -.nb-gradle/ - +###################################################################### +# Build Tools + +.gradle +/build/ +!gradle/wrapper/gradle-wrapper.jar + +target/ +!.mvn/wrapper/maven-wrapper.jar + +###################################################################### +# IDE + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### JRebel ### +rebel.xml + +### NetBeans ### +nbproject/private/ +build/* +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ + ###################################################################### # Others +.DS_Store *.log *.xml.versionsBackup *.swp - -!*/build/*.java -!*/build/*.html -!*/build/*.xml - - -logs/ -ruoyi-ui/dist.zip - -.playwright-cli \ No newline at end of file + +!*/build/*.java +!*/build/*.html +!*/build/*.xml diff --git a/.playwright-cli/page-2026-04-11T07-16-36-054Z.yml b/.playwright-cli/page-2026-04-11T07-16-36-054Z.yml new file mode 100644 index 0000000..711875f --- /dev/null +++ b/.playwright-cli/page-2026-04-11T07-16-36-054Z.yml @@ -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:  \ No newline at end of file diff --git a/.playwright-cli/page-2026-04-11T07-17-14-786Z.yml b/.playwright-cli/page-2026-04-11T07-17-14-786Z.yml new file mode 100644 index 0000000..429f91e --- /dev/null +++ b/.playwright-cli/page-2026-04-11T07-17-14-786Z.yml @@ -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:  \ No newline at end of file diff --git a/.playwright-cli/page-2026-04-11T07-18-09-957Z.yml b/.playwright-cli/page-2026-04-11T07-18-09-957Z.yml new file mode 100644 index 0000000..7de5200 --- /dev/null +++ b/.playwright-cli/page-2026-04-11T07-18-09-957Z.yml @@ -0,0 +1 @@ +- generic [ref=e2]:  \ No newline at end of file diff --git a/.playwright-cli/page-2026-04-11T07-18-43-284Z.yml b/.playwright-cli/page-2026-04-11T07-18-43-284Z.yml new file mode 100644 index 0000000..7a036c1 --- /dev/null +++ b/.playwright-cli/page-2026-04-11T07-18-43-284Z.yml @@ -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:  \ No newline at end of file diff --git a/.playwright-cli/page-2026-04-11T07-25-40-868Z.yml b/.playwright-cli/page-2026-04-11T07-25-40-868Z.yml new file mode 100644 index 0000000..711875f --- /dev/null +++ b/.playwright-cli/page-2026-04-11T07-25-40-868Z.yml @@ -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:  \ No newline at end of file diff --git a/.playwright-cli/page-2026-04-11T07-25-55-827Z.yml b/.playwright-cli/page-2026-04-11T07-25-55-827Z.yml new file mode 100644 index 0000000..7de5200 --- /dev/null +++ b/.playwright-cli/page-2026-04-11T07-25-55-827Z.yml @@ -0,0 +1 @@ +- generic [ref=e2]:  \ No newline at end of file diff --git a/.playwright-cli/page-2026-04-11T07-26-20-300Z.yml b/.playwright-cli/page-2026-04-11T07-26-20-300Z.yml new file mode 100644 index 0000000..7de5200 --- /dev/null +++ b/.playwright-cli/page-2026-04-11T07-26-20-300Z.yml @@ -0,0 +1 @@ +- generic [ref=e2]:  \ No newline at end of file diff --git a/.playwright-cli/page-2026-04-11T07-26-34-973Z.yml b/.playwright-cli/page-2026-04-11T07-26-34-973Z.yml new file mode 100644 index 0000000..7de5200 --- /dev/null +++ b/.playwright-cli/page-2026-04-11T07-26-34-973Z.yml @@ -0,0 +1 @@ +- generic [ref=e2]:  \ No newline at end of file diff --git a/.playwright-cli/page-2026-04-14T05-22-21-308Z.yml b/.playwright-cli/page-2026-04-14T05-22-21-308Z.yml new file mode 100644 index 0000000..19944d6 --- /dev/null +++ b/.playwright-cli/page-2026-04-14T05-22-21-308Z.yml @@ -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异常 \ No newline at end of file diff --git a/.playwright-cli/page-2026-04-14T05-39-30-657Z.yml b/.playwright-cli/page-2026-04-14T05-39-30-657Z.yml new file mode 100644 index 0000000..cd1ffad --- /dev/null +++ b/.playwright-cli/page-2026-04-14T05-39-30-657Z.yml @@ -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:  \ No newline at end of file diff --git a/README.md b/README.md index 34ffad8..7c4bcbf 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@

logo

-

RuoYi v3.9.1

+

RuoYi v3.9.2

基于SpringBoot+Vue前后端分离的Java快速开发框架

- +

@@ -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. 技术栈经典稳定
2. 社区资料丰富
3. 当前维护重心已转移 | 1. 现代前端技术栈
2. 开发体验与性能更优
3. 官方主推的活跃版本 | 1. 类型加持,减少沟通成本
2. 开发时有提示,效率更高
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群: [![加入QQ群](https://img.shields.io/badge/已满-937441-blue.svg)](https://jq.qq.com/?_wv=1027&k=5bVB1og) [![加入QQ群](https://img.shields.io/badge/已满-887144332-blue.svg)](https://jq.qq.com/?_wv=1027&k=5eiA4DH) [![加入QQ群](https://img.shields.io/badge/已满-180251782-blue.svg)](https://jq.qq.com/?_wv=1027&k=5AxMKlC) [![加入QQ群](https://img.shields.io/badge/已满-104180207-blue.svg)](https://jq.qq.com/?_wv=1027&k=51G72yr) [![加入QQ群](https://img.shields.io/badge/已满-186866453-blue.svg)](https://jq.qq.com/?_wv=1027&k=VvjN2nvu) [![加入QQ群](https://img.shields.io/badge/已满-201396349-blue.svg)](https://jq.qq.com/?_wv=1027&k=5vYAqA05) [![加入QQ群](https://img.shields.io/badge/已满-101456076-blue.svg)](https://jq.qq.com/?_wv=1027&k=kOIINEb5) [![加入QQ群](https://img.shields.io/badge/已满-101539465-blue.svg)](https://jq.qq.com/?_wv=1027&k=UKtX5jhs) [![加入QQ群](https://img.shields.io/badge/已满-264312783-blue.svg)](https://jq.qq.com/?_wv=1027&k=EI9an8lJ) [![加入QQ群](https://img.shields.io/badge/已满-167385320-blue.svg)](https://jq.qq.com/?_wv=1027&k=SWCtLnMz) [![加入QQ群](https://img.shields.io/badge/已满-104748341-blue.svg)](https://jq.qq.com/?_wv=1027&k=96Dkdq0k) [![加入QQ群](https://img.shields.io/badge/已满-160110482-blue.svg)](https://jq.qq.com/?_wv=1027&k=0fsNiYZt) [![加入QQ群](https://img.shields.io/badge/已满-170801498-blue.svg)](https://jq.qq.com/?_wv=1027&k=7xw4xUG1) [![加入QQ群](https://img.shields.io/badge/已满-108482800-blue.svg)](https://jq.qq.com/?_wv=1027&k=eCx8eyoJ) [![加入QQ群](https://img.shields.io/badge/已满-101046199-blue.svg)](https://jq.qq.com/?_wv=1027&k=SpyH2875) [![加入QQ群](https://img.shields.io/badge/已满-136919097-blue.svg)](https://jq.qq.com/?_wv=1027&k=tKEt51dz) [![加入QQ群](https://img.shields.io/badge/已满-143961921-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=0vBbSb0ztbBgVtn3kJS-Q4HUNYwip89G&authKey=8irq5PhutrZmWIvsUsklBxhj57l%2F1nOZqjzigkXZVoZE451GG4JHPOqW7AW6cf0T&noverify=0&group_code=143961921) [![加入QQ群](https://img.shields.io/badge/已满-174951577-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=ZFAPAbp09S2ltvwrJzp7wGlbopsc0rwi&authKey=HB2cxpxP2yspk%2Bo3WKTBfktRCccVkU26cgi5B16u0KcAYrVu7sBaE7XSEqmMdFQp&noverify=0&group_code=174951577) [![加入QQ群](https://img.shields.io/badge/已满-161281055-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Fn2aF5IHpwsy8j6VlalNJK6qbwFLFHat&authKey=uyIT%2B97x2AXj3odyXpsSpVaPMC%2Bidw0LxG5MAtEqlrcBcWJUA%2FeS43rsF1Tg7IRJ&noverify=0&group_code=161281055) [![加入QQ群](https://img.shields.io/badge/已满-138988063-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=XIzkm_mV2xTsUtFxo63bmicYoDBA6Ifm&authKey=dDW%2F4qsmw3x9govoZY9w%2FoWAoC4wbHqGal%2BbqLzoS6VBarU8EBptIgPKN%2FviyC8j&noverify=0&group_code=138988063) [![加入QQ群](https://img.shields.io/badge/已满-151450850-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=DkugnCg68PevlycJSKSwjhFqfIgrWWwR&authKey=pR1Pa5lPIeGF%2FFtIk6d%2FGB5qFi0EdvyErtpQXULzo03zbhopBHLWcuqdpwY241R%2F&noverify=0&group_code=151450850) [![加入QQ群](https://img.shields.io/badge/已满-224622315-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=F58bgRa-Dp-rsQJThiJqIYv8t4-lWfXh&authKey=UmUs4CVG5OPA1whvsa4uSespOvyd8%2FAr9olEGaWAfdLmfKQk%2FVBp2YU3u2xXXt76&noverify=0&group_code=224622315) [![加入QQ群](https://img.shields.io/badge/已满-287842588-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Nxb2EQ5qozWa218Wbs7zgBnjLSNk_tVT&authKey=obBKXj6SBKgrFTJZx0AqQnIYbNOvBB2kmgwWvGhzxR67RoRr84%2Bus5OadzMcdJl5&noverify=0&group_code=287842588) [![加入QQ群](https://img.shields.io/badge/已满-187944233-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=numtK1M_I4eVd2Gvg8qtbuL8JgX42qNh&authKey=giV9XWMaFZTY%2FqPlmWbkB9g3fi0Ev5CwEtT9Tgei0oUlFFCQLDp4ozWRiVIzubIm&noverify=0&group_code=187944233) [![加入QQ群](https://img.shields.io/badge/已满-228578329-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=G6r5KGCaa3pqdbUSXNIgYloyb8e0_L0D&authKey=4w8tF1eGW7%2FedWn%2FHAypQksdrML%2BDHolQSx7094Agm7Luakj9EbfPnSTxSi2T1LQ&noverify=0&group_code=228578329) [![加入QQ群](https://img.shields.io/badge/已满-191164766-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=GsOo-OLz53J8y_9TPoO6XXSGNRTgbFxA&authKey=R7Uy%2Feq%2BZsoKNqHvRKhiXpypW7DAogoWapOawUGHokJSBIBIre2%2FoiAZeZBSLuBc&noverify=0&group_code=191164766) [![加入QQ群](https://img.shields.io/badge/174569686-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=PmYavuzsOthVqfdAPbo4uAeIbu7Ttjgc&authKey=p52l8%2FXa4PS1JcEmS3VccKSwOPJUZ1ZfQ69MEKzbrooNUljRtlKjvsXf04bxNp3G&noverify=0&group_code=174569686) 点击按钮入群。 \ No newline at end of file +QQ群: [![加入QQ群](https://img.shields.io/badge/已满-937441-blue.svg)](https://jq.qq.com/?_wv=1027&k=5bVB1og) [![加入QQ群](https://img.shields.io/badge/已满-887144332-blue.svg)](https://jq.qq.com/?_wv=1027&k=5eiA4DH) [![加入QQ群](https://img.shields.io/badge/已满-180251782-blue.svg)](https://jq.qq.com/?_wv=1027&k=5AxMKlC) [![加入QQ群](https://img.shields.io/badge/已满-104180207-blue.svg)](https://jq.qq.com/?_wv=1027&k=51G72yr) [![加入QQ群](https://img.shields.io/badge/已满-186866453-blue.svg)](https://jq.qq.com/?_wv=1027&k=VvjN2nvu) [![加入QQ群](https://img.shields.io/badge/已满-201396349-blue.svg)](https://jq.qq.com/?_wv=1027&k=5vYAqA05) [![加入QQ群](https://img.shields.io/badge/已满-101456076-blue.svg)](https://jq.qq.com/?_wv=1027&k=kOIINEb5) [![加入QQ群](https://img.shields.io/badge/已满-101539465-blue.svg)](https://jq.qq.com/?_wv=1027&k=UKtX5jhs) [![加入QQ群](https://img.shields.io/badge/已满-264312783-blue.svg)](https://jq.qq.com/?_wv=1027&k=EI9an8lJ) [![加入QQ群](https://img.shields.io/badge/已满-167385320-blue.svg)](https://jq.qq.com/?_wv=1027&k=SWCtLnMz) [![加入QQ群](https://img.shields.io/badge/已满-104748341-blue.svg)](https://jq.qq.com/?_wv=1027&k=96Dkdq0k) [![加入QQ群](https://img.shields.io/badge/已满-160110482-blue.svg)](https://jq.qq.com/?_wv=1027&k=0fsNiYZt) [![加入QQ群](https://img.shields.io/badge/已满-170801498-blue.svg)](https://jq.qq.com/?_wv=1027&k=7xw4xUG1) [![加入QQ群](https://img.shields.io/badge/已满-108482800-blue.svg)](https://jq.qq.com/?_wv=1027&k=eCx8eyoJ) [![加入QQ群](https://img.shields.io/badge/已满-101046199-blue.svg)](https://jq.qq.com/?_wv=1027&k=SpyH2875) [![加入QQ群](https://img.shields.io/badge/已满-136919097-blue.svg)](https://jq.qq.com/?_wv=1027&k=tKEt51dz) [![加入QQ群](https://img.shields.io/badge/已满-143961921-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=0vBbSb0ztbBgVtn3kJS-Q4HUNYwip89G&authKey=8irq5PhutrZmWIvsUsklBxhj57l%2F1nOZqjzigkXZVoZE451GG4JHPOqW7AW6cf0T&noverify=0&group_code=143961921) [![加入QQ群](https://img.shields.io/badge/已满-174951577-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=ZFAPAbp09S2ltvwrJzp7wGlbopsc0rwi&authKey=HB2cxpxP2yspk%2Bo3WKTBfktRCccVkU26cgi5B16u0KcAYrVu7sBaE7XSEqmMdFQp&noverify=0&group_code=174951577) [![加入QQ群](https://img.shields.io/badge/已满-161281055-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Fn2aF5IHpwsy8j6VlalNJK6qbwFLFHat&authKey=uyIT%2B97x2AXj3odyXpsSpVaPMC%2Bidw0LxG5MAtEqlrcBcWJUA%2FeS43rsF1Tg7IRJ&noverify=0&group_code=161281055) [![加入QQ群](https://img.shields.io/badge/已满-138988063-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=XIzkm_mV2xTsUtFxo63bmicYoDBA6Ifm&authKey=dDW%2F4qsmw3x9govoZY9w%2FoWAoC4wbHqGal%2BbqLzoS6VBarU8EBptIgPKN%2FviyC8j&noverify=0&group_code=138988063) [![加入QQ群](https://img.shields.io/badge/已满-151450850-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=DkugnCg68PevlycJSKSwjhFqfIgrWWwR&authKey=pR1Pa5lPIeGF%2FFtIk6d%2FGB5qFi0EdvyErtpQXULzo03zbhopBHLWcuqdpwY241R%2F&noverify=0&group_code=151450850) [![加入QQ群](https://img.shields.io/badge/已满-224622315-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=F58bgRa-Dp-rsQJThiJqIYv8t4-lWfXh&authKey=UmUs4CVG5OPA1whvsa4uSespOvyd8%2FAr9olEGaWAfdLmfKQk%2FVBp2YU3u2xXXt76&noverify=0&group_code=224622315) [![加入QQ群](https://img.shields.io/badge/已满-287842588-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Nxb2EQ5qozWa218Wbs7zgBnjLSNk_tVT&authKey=obBKXj6SBKgrFTJZx0AqQnIYbNOvBB2kmgwWvGhzxR67RoRr84%2Bus5OadzMcdJl5&noverify=0&group_code=287842588) [![加入QQ群](https://img.shields.io/badge/已满-187944233-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=numtK1M_I4eVd2Gvg8qtbuL8JgX42qNh&authKey=giV9XWMaFZTY%2FqPlmWbkB9g3fi0Ev5CwEtT9Tgei0oUlFFCQLDp4ozWRiVIzubIm&noverify=0&group_code=187944233) [![加入QQ群](https://img.shields.io/badge/已满-228578329-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=G6r5KGCaa3pqdbUSXNIgYloyb8e0_L0D&authKey=4w8tF1eGW7%2FedWn%2FHAypQksdrML%2BDHolQSx7094Agm7Luakj9EbfPnSTxSi2T1LQ&noverify=0&group_code=228578329) [![加入QQ群](https://img.shields.io/badge/已满-191164766-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=GsOo-OLz53J8y_9TPoO6XXSGNRTgbFxA&authKey=R7Uy%2Feq%2BZsoKNqHvRKhiXpypW7DAogoWapOawUGHokJSBIBIre2%2FoiAZeZBSLuBc&noverify=0&group_code=191164766) [![加入QQ群](https://img.shields.io/badge/已满-174569686-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=PmYavuzsOthVqfdAPbo4uAeIbu7Ttjgc&authKey=p52l8%2FXa4PS1JcEmS3VccKSwOPJUZ1ZfQ69MEKzbrooNUljRtlKjvsXf04bxNp3G&noverify=0&group_code=174569686) [![加入QQ群](https://img.shields.io/badge/127358632-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=M9y5NjAl44lAL_Vh2crmEehZU_PMU6KS&authKey=ZSDz8hEREWSaPuxQV3gEwqGIaGjfRNnkB4rJjf0IvXhrSUGSGwQFmBA%2Boe8HFxyl&noverify=0&group_code=127358632) 点击按钮入群。 \ No newline at end of file diff --git a/bin/.DS_Store b/bin/.DS_Store deleted file mode 100644 index f0c5f42..0000000 Binary files a/bin/.DS_Store and /dev/null differ diff --git a/bin/clean.bat b/bin/clean.bat index 24c0974..af841d8 100644 --- a/bin/clean.bat +++ b/bin/clean.bat @@ -1,12 +1,12 @@ -@echo off -echo. -echo [Ϣ] target· -echo. - -%~d0 -cd %~dp0 - -cd .. -call mvn clean - +@echo off +echo. +echo [Ϣ] target· +echo. + +%~d0 +cd %~dp0 + +cd .. +call mvn clean + pause \ No newline at end of file diff --git a/bin/package.bat b/bin/package.bat index c693ec0..af8af8e 100644 --- a/bin/package.bat +++ b/bin/package.bat @@ -1,12 +1,12 @@ -@echo off -echo. -echo [Ϣ] Weḅwar/jarļ -echo. - -%~d0 -cd %~dp0 - -cd .. -call mvn clean package -Dmaven.test.skip=true - +@echo off +echo. +echo [Ϣ] Weḅwar/jarļ +echo. + +%~d0 +cd %~dp0 + +cd .. +call mvn clean package -Dmaven.test.skip=true + pause \ No newline at end of file diff --git a/bin/prod/restart_java_test.sh b/bin/prod/restart_java_test.sh old mode 100644 new mode 100755 diff --git a/bin/restart_java_backend_test.sh b/bin/restart_java_backend_test.sh old mode 100644 new mode 100755 diff --git a/bin/run.bat b/bin/run.bat index 41efbd0..99c4992 100644 --- a/bin/run.bat +++ b/bin/run.bat @@ -1,14 +1,14 @@ -@echo off -echo. -echo [Ϣ] ʹJarWeb̡ -echo. - -cd %~dp0 -cd ../ruoyi-admin/target - -set JAVA_OPTS=-Xms256m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m - -java -jar %JAVA_OPTS% ruoyi-admin.jar - -cd bin +@echo off +echo. +echo [Ϣ] ʹJarWeb̡ +echo. + +cd %~dp0 +cd ../ruoyi-admin/target + +set JAVA_OPTS=-Xms256m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m + +java -jar %JAVA_OPTS% ruoyi-admin.jar + +cd bin pause \ No newline at end of file diff --git a/doc/2026-04-13-local-tomcat-remote-tongweb-backend-plan.md b/doc/2026-04-13-local-tomcat-remote-tongweb-backend-plan.md new file mode 100644 index 0000000..6444612 --- /dev/null +++ b/doc/2026-04-13-local-tomcat-remote-tongweb-backend-plan.md @@ -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` diff --git a/doc/2026-04-13-local-tomcat-remote-tongweb-design.md b/doc/2026-04-13-local-tomcat-remote-tongweb-design.md new file mode 100644 index 0000000..7822649 --- /dev/null +++ b/doc/2026-04-13-local-tomcat-remote-tongweb-design.md @@ -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` + +在不新增模块、不扩散复杂度的前提下,满足开发与部署两端的实际需要。 diff --git a/doc/2026-04-13-tongweb-replace-tomcat-backend-plan.md b/doc/2026-04-13-tongweb-replace-tomcat-backend-plan.md new file mode 100644 index 0000000..9618c34 --- /dev/null +++ b/doc/2026-04-13-tongweb-replace-tomcat-backend-plan.md @@ -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` diff --git a/doc/2026-04-14-ruoyi-vue-springboot2-backend-plan.md b/doc/2026-04-14-ruoyi-vue-springboot2-backend-plan.md new file mode 100644 index 0000000..6ab0d24 --- /dev/null +++ b/doc/2026-04-14-ruoyi-vue-springboot2-backend-plan.md @@ -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 可运行实现 diff --git a/doc/2026-04-14-ruoyi-vue-springboot2-frontend-plan.md b/doc/2026-04-14-ruoyi-vue-springboot2-frontend-plan.md new file mode 100644 index 0000000..597707c --- /dev/null +++ b/doc/2026-04-14-ruoyi-vue-springboot2-frontend-plan.md @@ -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` 控制 +- 测试完成后要关闭前端调试进程 diff --git a/doc/2026-04-14-ruoyi-vue-springboot2-migration-design.md b/doc/2026-04-14-ruoyi-vue-springboot2-migration-design.md new file mode 100644 index 0000000..d74a560 --- /dev/null +++ b/doc/2026-04-14-ruoyi-vue-springboot2-migration-design.md @@ -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. 产出物 + +本次任务最终需产出: + +- 本设计文档 +- 后端实施计划文档 +- 前端实施计划文档 +- 迁移实施记录文档 diff --git a/doc/implementation-report-2026-04-13-local-tomcat-remote-tongweb.md b/doc/implementation-report-2026-04-13-local-tomcat-remote-tongweb.md new file mode 100644 index 0000000..4815378 --- /dev/null +++ b/doc/implementation-report-2026-04-13-local-tomcat-remote-tongweb.md @@ -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 部署产物 diff --git a/doc/implementation-report-2026-04-13-tongweb-replace-tomcat.md b/doc/implementation-report-2026-04-13-tongweb-replace-tomcat.md new file mode 100644 index 0000000..18982bc --- /dev/null +++ b/doc/implementation-report-2026-04-13-tongweb-replace-tomcat.md @@ -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` 转发到后端容器 diff --git a/doc/implementation-report-2026-04-14-ruoyi-vue-springboot2-migration.md b/doc/implementation-report-2026-04-14-ruoyi-vue-springboot2-migration.md new file mode 100644 index 0000000..9d156ce --- /dev/null +++ b/doc/implementation-report-2026-04-14-ruoyi-vue-springboot2-migration.md @@ -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` 运行态查询仍需继续收口 diff --git a/pom.xml b/pom.xml index 1e76ac2..75ffadd 100644 --- a/pom.xml +++ b/pom.xml @@ -1,191 +1,218 @@ - - - 4.0.0 - - com.ruoyi - ruoyi - 3.9.1 - - ruoyi - http://www.ruoyi.vip - 若依管理系统 - - - 3.9.1 - UTF-8 - UTF-8 - 17 - 3.1.1 - 3.0.5 - 1.2.27 - 7.32.0 - 3.0.0 - 2.3.3 - 2.1.1 - 2.0.60 - 6.9.1 - 2.21.0 - 4.1.2 + + + 4.0.0 + + com.ruoyi + ruoyi + 3.9.2 + + ruoyi + http://www.ruoyi.vip + 若依管理系统 + + + 3.9.2 + UTF-8 + UTF-8 + 1.8 + 3.1.1 + 2.5.15 + 1.2.28 + 7.32.0 + 3.0.0 + 2.3.3 + 1.4.7 + 2.0.61 + 6.10.0 + 2.21.0 + 4.1.2 2.3 0.9.1 - 2.5.2 - 8.2.0 - 2.3.1 - 6.0.0 - 2.8.14 - - - - - - - - - org.springframework.boot - spring-boot-dependencies - 3.5.8 - pom - import - - - - - com.alibaba - druid-spring-boot-3-starter - ${druid.version} - - - - - nl.basjes.parse.useragent - yauaa - ${yauaa.version} - - - - - com.github.pagehelper - pagehelper-spring-boot-starter - ${pagehelper.boot.version} - - - - org.mybatis.spring.boot - mybatis-spring-boot-starter - ${mybatis-spring-boot.version} - - - - com.mysql - mysql-connector-j - ${mysql.version} - - - - javax.xml.bind - jaxb-api - ${jaxb-api.version} - - - - jakarta.servlet - jakarta.servlet-api - ${jakarta.version} - - - - - com.github.oshi - oshi-core - ${oshi.version} - - - - - org.springdoc - springdoc-openapi-starter-webmvc-ui - ${springdoc.version} - - - - - commons-io - commons-io - ${commons.io.version} - - - - - org.apache.poi - poi-ooxml - ${poi.version} - - - - - org.apache.velocity - velocity-engine-core - ${velocity.version} - - - - - org.quartz-scheduler - quartz - ${quartz.version} - - - - - com.alibaba.fastjson2 - fastjson2 - ${fastjson.version} - - - - - io.jsonwebtoken - jjwt - ${jwt.version} - - - - - pro.fessional - kaptcha - ${kaptcha.version} - - - - - com.ruoyi - ruoyi-quartz - ${ruoyi.version} - - - - - com.ruoyi - ruoyi-generator - ${ruoyi.version} - - - - - com.ruoyi - ruoyi-framework - ${ruoyi.version} - - - - - com.ruoyi - ruoyi-system - ${ruoyi.version} - - + 3.5.7 + 4.5 + + 9.0.112 + 1.2.13 + 5.7.14 + 5.3.39 + + + + + + + + + org.springframework + spring-framework-bom + ${spring-framework.version} + pom + import + + + + + org.springframework.security + spring-security-bom + ${spring-security.version} + pom + import + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + ch.qos.logback + logback-core + ${logback.version} + + + + ch.qos.logback + logback-classic + ${logback.version} + + + + + org.apache.tomcat.embed + tomcat-embed-core + ${tomcat.version} + + + + org.apache.tomcat.embed + tomcat-embed-el + ${tomcat.version} + + + + org.apache.tomcat.embed + tomcat-embed-websocket + ${tomcat.version} + + + + + com.alibaba + druid-spring-boot-starter + ${druid.version} + + + + + nl.basjes.parse.useragent + yauaa + ${yauaa.version} + + + + + com.github.pagehelper + pagehelper-spring-boot-starter + ${pagehelper.boot.version} + + + + + com.github.oshi + oshi-core + ${oshi.version} + + + + + io.springfox + springfox-boot-starter + ${swagger.version} + + + io.swagger + swagger-models + + + + + + + commons-io + commons-io + ${commons.io.version} + + + + + org.apache.poi + poi-ooxml + ${poi.version} + + + + + org.apache.velocity + velocity-engine-core + ${velocity.version} + + + + + com.alibaba.fastjson2 + fastjson2 + ${fastjson.version} + + + + + io.jsonwebtoken + jjwt + ${jwt.version} + + + + + pro.fessional + kaptcha + ${kaptcha.version} + + + + + com.ruoyi + ruoyi-quartz + ${ruoyi.version} + + + + + com.ruoyi + ruoyi-generator + ${ruoyi.version} + + + + + com.ruoyi + ruoyi-framework + ${ruoyi.version} + + + + + com.ruoyi + ruoyi-system + ${ruoyi.version} + + com.ruoyi @@ -200,64 +227,70 @@ ${ruoyi.version} + + com.baomidou + mybatis-plus-boot-starter + ${mybatis-plus.version} + + + + com.github.jsqlparser + jsqlparser + ${jsqlparser.version} + + - - - ruoyi-admin - ruoyi-framework - ruoyi-system + + + ruoyi-admin + ruoyi-framework + ruoyi-system ruoyi-quartz ruoyi-generator ruoyi-common ruoyi-loan-pricing - pom - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.13.0 - - true - ${java.version} - ${java.version} - ${project.build.sourceEncoding} - - - - org.springframework.boot - spring-boot-maven-plugin - 3.3.0 - - - - - - - public - aliyun nexus - https://maven.aliyun.com/repository/public - - true - - - - - - - public - aliyun nexus - https://maven.aliyun.com/repository/public - - true - - - false - - - - - \ No newline at end of file + pom + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + + + + + + public + aliyun nexus + https://maven.aliyun.com/repository/public + + true + + + + + + + public + aliyun nexus + https://maven.aliyun.com/repository/public + + true + + + false + + + + + diff --git a/ruoyi-admin/.DS_Store b/ruoyi-admin/.DS_Store deleted file mode 100644 index 5f70785..0000000 Binary files a/ruoyi-admin/.DS_Store and /dev/null differ diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml index 84c510f..9c6e921 100644 --- a/ruoyi-admin/pom.xml +++ b/ruoyi-admin/pom.xml @@ -1,53 +1,60 @@ - - - - ruoyi - com.ruoyi - 3.9.1 - - 4.0.0 - jar - ruoyi-admin - - - web服务入口 - - - - - - - org.springframework.boot - spring-boot-devtools - true - - - - - org.springdoc - springdoc-openapi-starter-webmvc-ui - - - - - com.mysql - mysql-connector-j - - - - - com.ruoyi - ruoyi-framework - - - - - com.ruoyi - ruoyi-quartz - - + + + + ruoyi + com.ruoyi + 3.9.2 + + 4.0.0 + jar + ruoyi-admin + + + web服务入口 + + + + + + + org.springframework.boot + spring-boot-devtools + true + + + + + io.springfox + springfox-boot-starter + + + + + io.swagger + swagger-models + 1.6.2 + + + + + mysql + mysql-connector-java + + + + + com.ruoyi + ruoyi-framework + + + + + com.ruoyi + ruoyi-quartz + + com.ruoyi @@ -60,42 +67,36 @@ ruoyi-loan-pricing - - org.springframework.boot - spring-boot-starter-test - test - - - - - - - org.springframework.boot - spring-boot-maven-plugin - 3.5.4 - - true - - - - - repackage - - - - - - org.apache.maven.plugins - maven-war-plugin - 3.1.0 - - false - ${project.artifactId} - - - - ${project.artifactId} - - + + + + + org.springframework.boot + spring-boot-maven-plugin + 2.5.15 + + true + + + + + repackage + + + + + + org.apache.maven.plugins + maven-war-plugin + 3.1.0 + + false + ${project.artifactId} + + + + ${project.artifactId} + + diff --git a/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java index 32eb6f1..e3c56ee 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java @@ -1,30 +1,30 @@ -package com.ruoyi; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; - -/** - * 启动程序 - * - * @author ruoyi - */ -@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }) -public class RuoYiApplication -{ - public static void main(String[] args) - { - // System.setProperty("spring.devtools.restart.enabled", "false"); - SpringApplication.run(RuoYiApplication.class, args); - System.out.println("(♥◠‿◠)ノ゙ 若依启动成功 ლ(´ڡ`ლ)゙ \n" + - " .-------. ____ __ \n" + - " | _ _ \\ \\ \\ / / \n" + - " | ( ' ) | \\ _. / ' \n" + - " |(_ o _) / _( )_ .' \n" + - " | (_,_).' __ ___(_ o _)' \n" + - " | |\\ \\ | || |(_,_)' \n" + - " | | \\ `' /| `-' / \n" + - " | | \\ / \\ / \n" + - " ''-' `'-' `-..-' "); - } -} +package com.ruoyi; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; + +/** + * 启动程序 + * + * @author ruoyi + */ +@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }) +public class RuoYiApplication +{ + public static void main(String[] args) + { + // System.setProperty("spring.devtools.restart.enabled", "false"); + SpringApplication.run(RuoYiApplication.class, args); + System.out.println("(♥◠‿◠)ノ゙ 若依启动成功 ლ(´ڡ`ლ)゙ \n" + + " .-------. ____ __ \n" + + " | _ _ \\ \\ \\ / / \n" + + " | ( ' ) | \\ _. / ' \n" + + " |(_ o _) / _( )_ .' \n" + + " | (_,_).' __ ___(_ o _)' \n" + + " | |\\ \\ | || |(_,_)' \n" + + " | | \\ `' /| `-' / \n" + + " | | \\ / \\ / \n" + + " ''-' `'-' `-..-' "); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/RuoYiServletInitializer.java b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiServletInitializer.java index 6de67dc..9d82a23 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/RuoYiServletInitializer.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiServletInitializer.java @@ -1,18 +1,18 @@ -package com.ruoyi; - -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; - -/** - * web容器中进行部署 - * - * @author ruoyi - */ -public class RuoYiServletInitializer extends SpringBootServletInitializer -{ - @Override - protected SpringApplicationBuilder configure(SpringApplicationBuilder application) - { - return application.sources(RuoYiApplication.class); - } -} +package com.ruoyi; + +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +/** + * web容器中进行部署 + * + * @author ruoyi + */ +public class RuoYiServletInitializer extends SpringBootServletInitializer +{ + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) + { + return application.sources(RuoYiApplication.class); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java index d6dcf06..e1a397e 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java @@ -1,94 +1,94 @@ -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.imageio.ImageIO; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.util.FastByteArrayOutputStream; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; -import com.google.code.kaptcha.Producer; -import com.ruoyi.common.config.RuoYiConfig; -import com.ruoyi.common.constant.CacheConstants; -import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.core.domain.AjaxResult; -import com.ruoyi.common.core.redis.RedisCache; -import com.ruoyi.common.utils.sign.Base64; -import com.ruoyi.common.utils.uuid.IdUtils; -import com.ruoyi.system.service.ISysConfigService; - -/** - * 验证码操作处理 - * - * @author ruoyi - */ -@RestController -public class CaptchaController -{ - @Resource(name = "captchaProducer") - private Producer captchaProducer; - - @Resource(name = "captchaProducerMath") - private Producer captchaProducerMath; - - @Autowired - private RedisCache redisCache; - - @Autowired - private ISysConfigService configService; - /** - * 生成验证码 - */ - @GetMapping("/captchaImage") - public AjaxResult getCode(HttpServletResponse response) throws IOException - { - AjaxResult ajax = AjaxResult.success(); - boolean captchaEnabled = configService.selectCaptchaEnabled(); - ajax.put("captchaEnabled", captchaEnabled); - if (!captchaEnabled) - { - return ajax; - } - - // 保存验证码信息 - String uuid = IdUtils.simpleUUID(); - String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid; - - String capStr = null, code = null; - BufferedImage image = null; - - // 生成验证码 - String captchaType = RuoYiConfig.getCaptchaType(); - if ("math".equals(captchaType)) - { - String capText = captchaProducerMath.createText(); - capStr = capText.substring(0, capText.lastIndexOf("@")); - code = capText.substring(capText.lastIndexOf("@") + 1); - image = captchaProducerMath.createImage(capStr); - } - else if ("char".equals(captchaType)) - { - capStr = code = captchaProducer.createText(); - image = captchaProducer.createImage(capStr); - } - - redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES); - // 转换流信息写出 - FastByteArrayOutputStream os = new FastByteArrayOutputStream(); - try - { - ImageIO.write(image, "jpg", os); - } - catch (IOException e) - { - return AjaxResult.error(e.getMessage()); - } - - ajax.put("uuid", uuid); - ajax.put("img", Base64.encode(os.toByteArray())); - return ajax; - } -} +package com.ruoyi.web.controller.common; + +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import javax.annotation.Resource; +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.FastByteArrayOutputStream; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import com.google.code.kaptcha.Producer; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.sign.Base64; +import com.ruoyi.common.utils.uuid.IdUtils; +import com.ruoyi.system.service.ISysConfigService; + +/** + * 验证码操作处理 + * + * @author ruoyi + */ +@RestController +public class CaptchaController +{ + @Resource(name = "captchaProducer") + private Producer captchaProducer; + + @Resource(name = "captchaProducerMath") + private Producer captchaProducerMath; + + @Autowired + private RedisCache redisCache; + + @Autowired + private ISysConfigService configService; + /** + * 生成验证码 + */ + @GetMapping("/captchaImage") + public AjaxResult getCode(HttpServletResponse response) throws IOException + { + AjaxResult ajax = AjaxResult.success(); + boolean captchaEnabled = configService.selectCaptchaEnabled(); + ajax.put("captchaEnabled", captchaEnabled); + if (!captchaEnabled) + { + return ajax; + } + + // 保存验证码信息 + String uuid = IdUtils.simpleUUID(); + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid; + + String capStr = null, code = null; + BufferedImage image = null; + + // 生成验证码 + String captchaType = RuoYiConfig.getCaptchaType(); + if ("math".equals(captchaType)) + { + String capText = captchaProducerMath.createText(); + capStr = capText.substring(0, capText.lastIndexOf("@")); + code = capText.substring(capText.lastIndexOf("@") + 1); + image = captchaProducerMath.createImage(capStr); + } + else if ("char".equals(captchaType)) + { + capStr = code = captchaProducer.createText(); + image = captchaProducer.createImage(capStr); + } + + redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES); + // 转换流信息写出 + FastByteArrayOutputStream os = new FastByteArrayOutputStream(); + try + { + ImageIO.write(image, "jpg", os); + } + catch (IOException e) + { + return AjaxResult.error(e.getMessage()); + } + + ajax.put("uuid", uuid); + ajax.put("img", Base64.encode(os.toByteArray())); + return ajax; + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java index a2333d5..cda80bd 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java @@ -1,162 +1,162 @@ -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 org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.multipart.MultipartFile; -import com.ruoyi.common.config.RuoYiConfig; -import com.ruoyi.common.core.domain.AjaxResult; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.common.utils.file.FileUploadUtils; -import com.ruoyi.common.utils.file.FileUtils; -import com.ruoyi.framework.config.ServerConfig; - -/** - * 通用请求处理 - * - * @author ruoyi - */ -@RestController -@RequestMapping("/common") -public class CommonController -{ - private static final Logger log = LoggerFactory.getLogger(CommonController.class); - - @Autowired - private ServerConfig serverConfig; - - private static final String FILE_DELIMITER = ","; - - /** - * 通用下载请求 - * - * @param fileName 文件名称 - * @param delete 是否删除 - */ - @GetMapping("/download") - public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request) - { - try - { - if (!FileUtils.checkAllowDownload(fileName)) - { - throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName)); - } - String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1); - String filePath = RuoYiConfig.getDownloadPath() + fileName; - - response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); - FileUtils.setAttachmentResponseHeader(response, realFileName); - FileUtils.writeBytes(filePath, response.getOutputStream()); - if (delete) - { - FileUtils.deleteFile(filePath); - } - } - catch (Exception e) - { - log.error("下载文件失败", e); - } - } - - /** - * 通用上传请求(单个) - */ - @PostMapping("/upload") - public AjaxResult uploadFile(MultipartFile file) throws Exception - { - try - { - // 上传文件路径 - String filePath = RuoYiConfig.getUploadPath(); - // 上传并返回新文件名称 - String fileName = FileUploadUtils.upload(filePath, file); - String url = serverConfig.getUrl() + fileName; - AjaxResult ajax = AjaxResult.success(); - ajax.put("url", url); - ajax.put("fileName", fileName); - ajax.put("newFileName", FileUtils.getName(fileName)); - ajax.put("originalFilename", file.getOriginalFilename()); - return ajax; - } - catch (Exception e) - { - return AjaxResult.error(e.getMessage()); - } - } - - /** - * 通用上传请求(多个) - */ - @PostMapping("/uploads") - public AjaxResult uploadFiles(List files) throws Exception - { - try - { - // 上传文件路径 - String filePath = RuoYiConfig.getUploadPath(); - List urls = new ArrayList(); - List fileNames = new ArrayList(); - List newFileNames = new ArrayList(); - List originalFilenames = new ArrayList(); - for (MultipartFile file : files) - { - // 上传并返回新文件名称 - String fileName = FileUploadUtils.upload(filePath, file); - String url = serverConfig.getUrl() + fileName; - urls.add(url); - fileNames.add(fileName); - newFileNames.add(FileUtils.getName(fileName)); - originalFilenames.add(file.getOriginalFilename()); - } - AjaxResult ajax = AjaxResult.success(); - ajax.put("urls", StringUtils.join(urls, FILE_DELIMITER)); - ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMITER)); - ajax.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMITER)); - ajax.put("originalFilenames", StringUtils.join(originalFilenames, FILE_DELIMITER)); - return ajax; - } - catch (Exception e) - { - return AjaxResult.error(e.getMessage()); - } - } - - /** - * 本地资源通用下载 - */ - @GetMapping("/download/resource") - public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response) - throws Exception - { - try - { - if (!FileUtils.checkAllowDownload(resource)) - { - throw new Exception(StringUtils.format("资源文件({})非法,不允许下载。 ", resource)); - } - // 本地资源路径 - String localPath = RuoYiConfig.getProfile(); - // 数据库资源地址 - String downloadPath = localPath + FileUtils.stripPrefix(resource); - // 下载名称 - String downloadName = StringUtils.substringAfterLast(downloadPath, "/"); - response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); - FileUtils.setAttachmentResponseHeader(response, downloadName); - FileUtils.writeBytes(downloadPath, response.getOutputStream()); - } - catch (Exception e) - { - log.error("下载文件失败", e); - } - } -} +package com.ruoyi.web.controller.common; + +import java.util.ArrayList; +import java.util.List; +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; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.file.FileUploadUtils; +import com.ruoyi.common.utils.file.FileUtils; +import com.ruoyi.framework.config.ServerConfig; + +/** + * 通用请求处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/common") +public class CommonController +{ + private static final Logger log = LoggerFactory.getLogger(CommonController.class); + + @Autowired + private ServerConfig serverConfig; + + private static final String FILE_DELIMITER = ","; + + /** + * 通用下载请求 + * + * @param fileName 文件名称 + * @param delete 是否删除 + */ + @GetMapping("/download") + public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request) + { + try + { + if (!FileUtils.checkAllowDownload(fileName)) + { + throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName)); + } + String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1); + String filePath = RuoYiConfig.getDownloadPath() + fileName; + + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + FileUtils.setAttachmentResponseHeader(response, realFileName); + FileUtils.writeBytes(filePath, response.getOutputStream()); + if (delete) + { + FileUtils.deleteFile(filePath); + } + } + catch (Exception e) + { + log.error("下载文件失败", e); + } + } + + /** + * 通用上传请求(单个) + */ + @PostMapping("/upload") + public AjaxResult uploadFile(MultipartFile file) throws Exception + { + try + { + // 上传文件路径 + String filePath = RuoYiConfig.getUploadPath(); + // 上传并返回新文件名称 + String fileName = FileUploadUtils.upload(filePath, file); + String url = serverConfig.getUrl() + fileName; + AjaxResult ajax = AjaxResult.success(); + ajax.put("url", url); + ajax.put("fileName", fileName); + ajax.put("newFileName", FileUtils.getName(fileName)); + ajax.put("originalFilename", file.getOriginalFilename()); + return ajax; + } + catch (Exception e) + { + return AjaxResult.error(e.getMessage()); + } + } + + /** + * 通用上传请求(多个) + */ + @PostMapping("/uploads") + public AjaxResult uploadFiles(List files) throws Exception + { + try + { + // 上传文件路径 + String filePath = RuoYiConfig.getUploadPath(); + List urls = new ArrayList(); + List fileNames = new ArrayList(); + List newFileNames = new ArrayList(); + List originalFilenames = new ArrayList(); + for (MultipartFile file : files) + { + // 上传并返回新文件名称 + String fileName = FileUploadUtils.upload(filePath, file); + String url = serverConfig.getUrl() + fileName; + urls.add(url); + fileNames.add(fileName); + newFileNames.add(FileUtils.getName(fileName)); + originalFilenames.add(file.getOriginalFilename()); + } + AjaxResult ajax = AjaxResult.success(); + ajax.put("urls", StringUtils.join(urls, FILE_DELIMITER)); + ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMITER)); + ajax.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMITER)); + ajax.put("originalFilenames", StringUtils.join(originalFilenames, FILE_DELIMITER)); + return ajax; + } + catch (Exception e) + { + return AjaxResult.error(e.getMessage()); + } + } + + /** + * 本地资源通用下载 + */ + @GetMapping("/download/resource") + public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response) + throws Exception + { + try + { + if (!FileUtils.checkAllowDownload(resource)) + { + throw new Exception(StringUtils.format("资源文件({})非法,不允许下载。 ", resource)); + } + // 本地资源路径 + String localPath = RuoYiConfig.getProfile(); + // 数据库资源地址 + String downloadPath = localPath + FileUtils.stripPrefix(resource); + // 下载名称 + String downloadName = StringUtils.substringAfterLast(downloadPath, "/"); + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + FileUtils.setAttachmentResponseHeader(response, downloadName); + FileUtils.writeBytes(downloadPath, response.getOutputStream()); + } + catch (Exception e) + { + log.error("下载文件失败", e); + } + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java index 854bdcc..44b3f91 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java @@ -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 caches = new ArrayList(); + private static final List CACHES = new ArrayList(); + + 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 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 result = new HashMap<>(3); result.put("info", info); - result.put("dbSize", stats.keySize()); + result.put("dbSize", stats.getKeySize()); List> 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); } diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java index cc805ad..082027b 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java @@ -1,27 +1,27 @@ -package com.ruoyi.web.controller.monitor; - -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import com.ruoyi.common.core.domain.AjaxResult; -import com.ruoyi.framework.web.domain.Server; - -/** - * 服务器监控 - * - * @author ruoyi - */ -@RestController -@RequestMapping("/monitor/server") -public class ServerController -{ - @PreAuthorize("@ss.hasPermi('monitor:server:list')") - @GetMapping() - public AjaxResult getInfo() throws Exception - { - Server server = new Server(); - server.copyTo(); - return AjaxResult.success(server); - } -} +package com.ruoyi.web.controller.monitor; + +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.framework.web.domain.Server; + +/** + * 服务器监控 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/server") +public class ServerController +{ + @PreAuthorize("@ss.hasPermi('monitor:server:list')") + @GetMapping() + public AjaxResult getInfo() throws Exception + { + Server server = new Server(); + server.copyTo(); + return AjaxResult.success(server); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java index f6fac71..f9f262e 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java @@ -1,82 +1,82 @@ -package com.ruoyi.web.controller.monitor; - -import java.util.List; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.beans.factory.annotation.Autowired; -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.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -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.enums.BusinessType; -import com.ruoyi.common.utils.poi.ExcelUtil; -import com.ruoyi.framework.web.service.SysPasswordService; -import com.ruoyi.system.domain.SysLogininfor; -import com.ruoyi.system.service.ISysLogininforService; - -/** - * 系统访问记录 - * - * @author ruoyi - */ -@RestController -@RequestMapping("/monitor/logininfor") -public class SysLogininforController extends BaseController -{ - @Autowired - private ISysLogininforService logininforService; - - @Autowired - private SysPasswordService passwordService; - - @PreAuthorize("@ss.hasPermi('monitor:logininfor:list')") - @GetMapping("/list") - public TableDataInfo list(SysLogininfor logininfor) - { - startPage(); - List list = logininforService.selectLogininforList(logininfor); - return getDataTable(list); - } - - @Log(title = "登录日志", businessType = BusinessType.EXPORT) - @PreAuthorize("@ss.hasPermi('monitor:logininfor:export')") - @PostMapping("/export") - public void export(HttpServletResponse response, SysLogininfor logininfor) - { - List list = logininforService.selectLogininforList(logininfor); - ExcelUtil util = new ExcelUtil(SysLogininfor.class); - util.exportExcel(response, list, "登录日志"); - } - - @PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')") - @Log(title = "登录日志", businessType = BusinessType.DELETE) - @DeleteMapping("/{infoIds}") - public AjaxResult remove(@PathVariable Long[] infoIds) - { - return toAjax(logininforService.deleteLogininforByIds(infoIds)); - } - - @PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')") - @Log(title = "登录日志", businessType = BusinessType.CLEAN) - @DeleteMapping("/clean") - public AjaxResult clean() - { - logininforService.cleanLogininfor(); - return success(); - } - - @PreAuthorize("@ss.hasPermi('monitor:logininfor:unlock')") - @Log(title = "账户解锁", businessType = BusinessType.OTHER) - @GetMapping("/unlock/{userName}") - public AjaxResult unlock(@PathVariable("userName") String userName) - { - passwordService.clearLoginRecordCache(userName); - return success(); - } -} +package com.ruoyi.web.controller.monitor; + +import java.util.List; +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; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +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.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.framework.web.service.SysPasswordService; +import com.ruoyi.system.domain.SysLogininfor; +import com.ruoyi.system.service.ISysLogininforService; + +/** + * 系统访问记录 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/logininfor") +public class SysLogininforController extends BaseController +{ + @Autowired + private ISysLogininforService logininforService; + + @Autowired + private SysPasswordService passwordService; + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:list')") + @GetMapping("/list") + public TableDataInfo list(SysLogininfor logininfor) + { + startPage(); + List list = logininforService.selectLogininforList(logininfor); + return getDataTable(list); + } + + @Log(title = "登录日志", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('monitor:logininfor:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysLogininfor logininfor) + { + List list = logininforService.selectLogininforList(logininfor); + ExcelUtil util = new ExcelUtil(SysLogininfor.class); + util.exportExcel(response, list, "登录日志"); + } + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')") + @Log(title = "登录日志", businessType = BusinessType.DELETE) + @DeleteMapping("/{infoIds}") + public AjaxResult remove(@PathVariable Long[] infoIds) + { + return toAjax(logininforService.deleteLogininforByIds(infoIds)); + } + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')") + @Log(title = "登录日志", businessType = BusinessType.CLEAN) + @DeleteMapping("/clean") + public AjaxResult clean() + { + logininforService.cleanLogininfor(); + return success(); + } + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:unlock')") + @Log(title = "账户解锁", businessType = BusinessType.OTHER) + @GetMapping("/unlock/{userName}") + public AjaxResult unlock(@PathVariable("userName") String userName) + { + passwordService.clearLoginRecordCache(userName); + return success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java index f056d65..5378424 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java @@ -1,69 +1,69 @@ -package com.ruoyi.web.controller.monitor; - -import java.util.List; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.beans.factory.annotation.Autowired; -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.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -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.enums.BusinessType; -import com.ruoyi.common.utils.poi.ExcelUtil; -import com.ruoyi.system.domain.SysOperLog; -import com.ruoyi.system.service.ISysOperLogService; - -/** - * 操作日志记录 - * - * @author ruoyi - */ -@RestController -@RequestMapping("/monitor/operlog") -public class SysOperlogController extends BaseController -{ - @Autowired - private ISysOperLogService operLogService; - - @PreAuthorize("@ss.hasPermi('monitor:operlog:list')") - @GetMapping("/list") - public TableDataInfo list(SysOperLog operLog) - { - startPage(); - List list = operLogService.selectOperLogList(operLog); - return getDataTable(list); - } - - @Log(title = "操作日志", businessType = BusinessType.EXPORT) - @PreAuthorize("@ss.hasPermi('monitor:operlog:export')") - @PostMapping("/export") - public void export(HttpServletResponse response, SysOperLog operLog) - { - List list = operLogService.selectOperLogList(operLog); - ExcelUtil util = new ExcelUtil(SysOperLog.class); - util.exportExcel(response, list, "操作日志"); - } - - @Log(title = "操作日志", businessType = BusinessType.DELETE) - @PreAuthorize("@ss.hasPermi('monitor:operlog:remove')") - @DeleteMapping("/{operIds}") - public AjaxResult remove(@PathVariable Long[] operIds) - { - return toAjax(operLogService.deleteOperLogByIds(operIds)); - } - - @Log(title = "操作日志", businessType = BusinessType.CLEAN) - @PreAuthorize("@ss.hasPermi('monitor:operlog:remove')") - @DeleteMapping("/clean") - public AjaxResult clean() - { - operLogService.cleanOperLog(); - return success(); - } -} +package com.ruoyi.web.controller.monitor; + +import java.util.List; +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; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +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.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.domain.SysOperLog; +import com.ruoyi.system.service.ISysOperLogService; + +/** + * 操作日志记录 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/operlog") +public class SysOperlogController extends BaseController +{ + @Autowired + private ISysOperLogService operLogService; + + @PreAuthorize("@ss.hasPermi('monitor:operlog:list')") + @GetMapping("/list") + public TableDataInfo list(SysOperLog operLog) + { + startPage(); + List list = operLogService.selectOperLogList(operLog); + return getDataTable(list); + } + + @Log(title = "操作日志", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('monitor:operlog:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysOperLog operLog) + { + List list = operLogService.selectOperLogList(operLog); + ExcelUtil util = new ExcelUtil(SysOperLog.class); + util.exportExcel(response, list, "操作日志"); + } + + @Log(title = "操作日志", businessType = BusinessType.DELETE) + @PreAuthorize("@ss.hasPermi('monitor:operlog:remove')") + @DeleteMapping("/{operIds}") + public AjaxResult remove(@PathVariable Long[] operIds) + { + return toAjax(operLogService.deleteOperLogByIds(operIds)); + } + + @Log(title = "操作日志", businessType = BusinessType.CLEAN) + @PreAuthorize("@ss.hasPermi('monitor:operlog:remove')") + @DeleteMapping("/clean") + public AjaxResult clean() + { + operLogService.cleanOperLog(); + return success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java index a442863..7c343e2 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java @@ -1,83 +1,83 @@ -package com.ruoyi.web.controller.monitor; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import org.springframework.beans.factory.annotation.Autowired; -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 com.ruoyi.common.annotation.Log; -import com.ruoyi.common.constant.CacheConstants; -import com.ruoyi.common.core.controller.BaseController; -import com.ruoyi.common.core.domain.AjaxResult; -import com.ruoyi.common.core.domain.model.LoginUser; -import com.ruoyi.common.core.page.TableDataInfo; -import com.ruoyi.common.core.redis.RedisCache; -import com.ruoyi.common.enums.BusinessType; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.system.domain.SysUserOnline; -import com.ruoyi.system.service.ISysUserOnlineService; - -/** - * 在线用户监控 - * - * @author ruoyi - */ -@RestController -@RequestMapping("/monitor/online") -public class SysUserOnlineController extends BaseController -{ - @Autowired - private ISysUserOnlineService userOnlineService; - - @Autowired - private RedisCache redisCache; - - @PreAuthorize("@ss.hasPermi('monitor:online:list')") - @GetMapping("/list") - public TableDataInfo list(String ipaddr, String userName) - { - Collection keys = redisCache.keys(CacheConstants.LOGIN_TOKEN_KEY + "*"); - List userOnlineList = new ArrayList(); - for (String key : keys) - { - LoginUser user = redisCache.getCacheObject(key); - if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) - { - userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user)); - } - else if (StringUtils.isNotEmpty(ipaddr)) - { - userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user)); - } - else if (StringUtils.isNotEmpty(userName) && StringUtils.isNotNull(user.getUser())) - { - userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user)); - } - else - { - userOnlineList.add(userOnlineService.loginUserToUserOnline(user)); - } - } - Collections.reverse(userOnlineList); - userOnlineList.removeAll(Collections.singleton(null)); - return getDataTable(userOnlineList); - } - - /** - * 强退用户 - */ - @PreAuthorize("@ss.hasPermi('monitor:online:forceLogout')") - @Log(title = "在线用户", businessType = BusinessType.FORCE) - @DeleteMapping("/{tokenId}") - public AjaxResult forceLogout(@PathVariable String tokenId) - { - redisCache.deleteObject(CacheConstants.LOGIN_TOKEN_KEY + tokenId); - return success(); - } -} +package com.ruoyi.web.controller.monitor; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +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 com.ruoyi.common.annotation.Log; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.domain.SysUserOnline; +import com.ruoyi.system.service.ISysUserOnlineService; + +/** + * 在线用户监控 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/online") +public class SysUserOnlineController extends BaseController +{ + @Autowired + private ISysUserOnlineService userOnlineService; + + @Autowired + private RedisCache redisCache; + + @PreAuthorize("@ss.hasPermi('monitor:online:list')") + @GetMapping("/list") + public TableDataInfo list(String ipaddr, String userName) + { + Collection keys = redisCache.keys(CacheConstants.LOGIN_TOKEN_KEY + "*"); + List userOnlineList = new ArrayList(); + for (String key : keys) + { + LoginUser user = redisCache.getCacheObject(key); + if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) + { + userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user)); + } + else if (StringUtils.isNotEmpty(ipaddr)) + { + userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user)); + } + else if (StringUtils.isNotEmpty(userName) && StringUtils.isNotNull(user.getUser())) + { + userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user)); + } + else + { + userOnlineList.add(userOnlineService.loginUserToUserOnline(user)); + } + } + Collections.reverse(userOnlineList); + userOnlineList.removeAll(Collections.singleton(null)); + return getDataTable(userOnlineList); + } + + /** + * 强退用户 + */ + @PreAuthorize("@ss.hasPermi('monitor:online:forceLogout')") + @Log(title = "在线用户", businessType = BusinessType.FORCE) + @DeleteMapping("/{tokenId}") + public AjaxResult forceLogout(@PathVariable String tokenId) + { + redisCache.deleteObject(CacheConstants.LOGIN_TOKEN_KEY + tokenId); + return success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java index 91a854f..95bcaec 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java @@ -1,133 +1,133 @@ -package com.ruoyi.web.controller.system; - -import java.util.List; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -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.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.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.enums.BusinessType; -import com.ruoyi.common.utils.poi.ExcelUtil; -import com.ruoyi.system.domain.SysConfig; -import com.ruoyi.system.service.ISysConfigService; - -/** - * 参数配置 信息操作处理 - * - * @author ruoyi - */ -@RestController -@RequestMapping("/system/config") -public class SysConfigController extends BaseController -{ - @Autowired - private ISysConfigService configService; - - /** - * 获取参数配置列表 - */ - @PreAuthorize("@ss.hasPermi('system:config:list')") - @GetMapping("/list") - public TableDataInfo list(SysConfig config) - { - startPage(); - List list = configService.selectConfigList(config); - return getDataTable(list); - } - - @Log(title = "参数管理", businessType = BusinessType.EXPORT) - @PreAuthorize("@ss.hasPermi('system:config:export')") - @PostMapping("/export") - public void export(HttpServletResponse response, SysConfig config) - { - List list = configService.selectConfigList(config); - ExcelUtil util = new ExcelUtil(SysConfig.class); - util.exportExcel(response, list, "参数数据"); - } - - /** - * 根据参数编号获取详细信息 - */ - @PreAuthorize("@ss.hasPermi('system:config:query')") - @GetMapping(value = "/{configId}") - public AjaxResult getInfo(@PathVariable Long configId) - { - return success(configService.selectConfigById(configId)); - } - - /** - * 根据参数键名查询参数值 - */ - @GetMapping(value = "/configKey/{configKey}") - public AjaxResult getConfigKey(@PathVariable String configKey) - { - return success(configService.selectConfigByKey(configKey)); - } - - /** - * 新增参数配置 - */ - @PreAuthorize("@ss.hasPermi('system:config:add')") - @Log(title = "参数管理", businessType = BusinessType.INSERT) - @PostMapping - public AjaxResult add(@Validated @RequestBody SysConfig config) - { - if (!configService.checkConfigKeyUnique(config)) - { - return error("新增参数'" + config.getConfigName() + "'失败,参数键名已存在"); - } - config.setCreateBy(getUsername()); - return toAjax(configService.insertConfig(config)); - } - - /** - * 修改参数配置 - */ - @PreAuthorize("@ss.hasPermi('system:config:edit')") - @Log(title = "参数管理", businessType = BusinessType.UPDATE) - @PutMapping - public AjaxResult edit(@Validated @RequestBody SysConfig config) - { - if (!configService.checkConfigKeyUnique(config)) - { - return error("修改参数'" + config.getConfigName() + "'失败,参数键名已存在"); - } - config.setUpdateBy(getUsername()); - return toAjax(configService.updateConfig(config)); - } - - /** - * 删除参数配置 - */ - @PreAuthorize("@ss.hasPermi('system:config:remove')") - @Log(title = "参数管理", businessType = BusinessType.DELETE) - @DeleteMapping("/{configIds}") - public AjaxResult remove(@PathVariable Long[] configIds) - { - configService.deleteConfigByIds(configIds); - return success(); - } - - /** - * 刷新参数缓存 - */ - @PreAuthorize("@ss.hasPermi('system:config:remove')") - @Log(title = "参数管理", businessType = BusinessType.CLEAN) - @DeleteMapping("/refreshCache") - public AjaxResult refreshCache() - { - configService.resetConfigCache(); - return success(); - } -} +package com.ruoyi.web.controller.system; + +import java.util.List; +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; +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.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.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.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.domain.SysConfig; +import com.ruoyi.system.service.ISysConfigService; + +/** + * 参数配置 信息操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/config") +public class SysConfigController extends BaseController +{ + @Autowired + private ISysConfigService configService; + + /** + * 获取参数配置列表 + */ + @PreAuthorize("@ss.hasPermi('system:config:list')") + @GetMapping("/list") + public TableDataInfo list(SysConfig config) + { + startPage(); + List list = configService.selectConfigList(config); + return getDataTable(list); + } + + @Log(title = "参数管理", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:config:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysConfig config) + { + List list = configService.selectConfigList(config); + ExcelUtil util = new ExcelUtil(SysConfig.class); + util.exportExcel(response, list, "参数数据"); + } + + /** + * 根据参数编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:config:query')") + @GetMapping(value = "/{configId}") + public AjaxResult getInfo(@PathVariable Long configId) + { + return success(configService.selectConfigById(configId)); + } + + /** + * 根据参数键名查询参数值 + */ + @GetMapping(value = "/configKey/{configKey}") + public AjaxResult getConfigKey(@PathVariable String configKey) + { + return success(configService.selectConfigByKey(configKey)); + } + + /** + * 新增参数配置 + */ + @PreAuthorize("@ss.hasPermi('system:config:add')") + @Log(title = "参数管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysConfig config) + { + if (!configService.checkConfigKeyUnique(config)) + { + return error("新增参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + config.setCreateBy(getUsername()); + return toAjax(configService.insertConfig(config)); + } + + /** + * 修改参数配置 + */ + @PreAuthorize("@ss.hasPermi('system:config:edit')") + @Log(title = "参数管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysConfig config) + { + if (!configService.checkConfigKeyUnique(config)) + { + return error("修改参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + config.setUpdateBy(getUsername()); + return toAjax(configService.updateConfig(config)); + } + + /** + * 删除参数配置 + */ + @PreAuthorize("@ss.hasPermi('system:config:remove')") + @Log(title = "参数管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{configIds}") + public AjaxResult remove(@PathVariable Long[] configIds) + { + configService.deleteConfigByIds(configIds); + return success(); + } + + /** + * 刷新参数缓存 + */ + @PreAuthorize("@ss.hasPermi('system:config:remove')") + @Log(title = "参数管理", businessType = BusinessType.CLEAN) + @DeleteMapping("/refreshCache") + public AjaxResult refreshCache() + { + configService.resetConfigCache(); + return success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java index 59e7588..abd4793 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java @@ -1,132 +1,147 @@ -package com.ruoyi.web.controller.system; - -import java.util.List; -import org.apache.commons.lang3.ArrayUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -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.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.RestController; -import com.ruoyi.common.annotation.Log; -import com.ruoyi.common.constant.UserConstants; -import com.ruoyi.common.core.controller.BaseController; -import com.ruoyi.common.core.domain.AjaxResult; -import com.ruoyi.common.core.domain.entity.SysDept; -import com.ruoyi.common.enums.BusinessType; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.system.service.ISysDeptService; - -/** - * 部门信息 - * - * @author ruoyi - */ -@RestController -@RequestMapping("/system/dept") -public class SysDeptController extends BaseController -{ - @Autowired - private ISysDeptService deptService; - - /** - * 获取部门列表 - */ - @PreAuthorize("@ss.hasPermi('system:dept:list')") - @GetMapping("/list") - public AjaxResult list(SysDept dept) - { - List depts = deptService.selectDeptList(dept); - return success(depts); - } - - /** - * 查询部门列表(排除节点) - */ - @PreAuthorize("@ss.hasPermi('system:dept:list')") - @GetMapping("/list/exclude/{deptId}") - public AjaxResult excludeChild(@PathVariable(value = "deptId", required = false) Long deptId) - { - List depts = deptService.selectDeptList(new SysDept()); - depts.removeIf(d -> d.getDeptId().intValue() == deptId || ArrayUtils.contains(StringUtils.split(d.getAncestors(), ","), deptId + "")); - return success(depts); - } - - /** - * 根据部门编号获取详细信息 - */ - @PreAuthorize("@ss.hasPermi('system:dept:query')") - @GetMapping(value = "/{deptId}") - public AjaxResult getInfo(@PathVariable Long deptId) - { - deptService.checkDeptDataScope(deptId); - return success(deptService.selectDeptById(deptId)); - } - - /** - * 新增部门 - */ - @PreAuthorize("@ss.hasPermi('system:dept:add')") - @Log(title = "部门管理", businessType = BusinessType.INSERT) - @PostMapping - public AjaxResult add(@Validated @RequestBody SysDept dept) - { - if (!deptService.checkDeptNameUnique(dept)) - { - return error("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在"); - } - dept.setCreateBy(getUsername()); - return toAjax(deptService.insertDept(dept)); - } - - /** - * 修改部门 - */ - @PreAuthorize("@ss.hasPermi('system:dept:edit')") - @Log(title = "部门管理", businessType = BusinessType.UPDATE) - @PutMapping - public AjaxResult edit(@Validated @RequestBody SysDept dept) - { - Long deptId = dept.getDeptId(); - deptService.checkDeptDataScope(deptId); - if (!deptService.checkDeptNameUnique(dept)) - { - return error("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在"); - } - else if (dept.getParentId().equals(deptId)) - { - return error("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己"); - } - else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus()) && deptService.selectNormalChildrenDeptById(deptId) > 0) - { - return error("该部门包含未停用的子部门!"); - } - dept.setUpdateBy(getUsername()); - return toAjax(deptService.updateDept(dept)); - } - - /** - * 删除部门 - */ - @PreAuthorize("@ss.hasPermi('system:dept:remove')") - @Log(title = "部门管理", businessType = BusinessType.DELETE) - @DeleteMapping("/{deptId}") - public AjaxResult remove(@PathVariable Long deptId) - { - if (deptService.hasChildByDeptId(deptId)) - { - return warn("存在下级部门,不允许删除"); - } - if (deptService.checkDeptExistUser(deptId)) - { - return warn("部门存在用户,不允许删除"); - } - deptService.checkDeptDataScope(deptId); - return toAjax(deptService.deleteDeptById(deptId)); - } -} +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; +import org.springframework.validation.annotation.Validated; +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.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.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.service.ISysDeptService; + +/** + * 部门信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/dept") +public class SysDeptController extends BaseController +{ + @Autowired + private ISysDeptService deptService; + + /** + * 获取部门列表 + */ + @PreAuthorize("@ss.hasPermi('system:dept:list')") + @GetMapping("/list") + public AjaxResult list(SysDept dept) + { + List depts = deptService.selectDeptList(dept); + return success(depts); + } + + /** + * 查询部门列表(排除节点) + */ + @PreAuthorize("@ss.hasPermi('system:dept:list')") + @GetMapping("/list/exclude/{deptId}") + public AjaxResult excludeChild(@PathVariable(value = "deptId", required = false) Long deptId) + { + List depts = deptService.selectDeptList(new SysDept()); + depts.removeIf(d -> d.getDeptId().intValue() == deptId || ArrayUtils.contains(StringUtils.split(d.getAncestors(), ","), deptId + "")); + return success(depts); + } + + /** + * 根据部门编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:dept:query')") + @GetMapping(value = "/{deptId}") + public AjaxResult getInfo(@PathVariable Long deptId) + { + deptService.checkDeptDataScope(deptId); + return success(deptService.selectDeptById(deptId)); + } + + /** + * 新增部门 + */ + @PreAuthorize("@ss.hasPermi('system:dept:add')") + @Log(title = "部门管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysDept dept) + { + if (!deptService.checkDeptNameUnique(dept)) + { + return error("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在"); + } + dept.setCreateBy(getUsername()); + return toAjax(deptService.insertDept(dept)); + } + + /** + * 修改部门 + */ + @PreAuthorize("@ss.hasPermi('system:dept:edit')") + @Log(title = "部门管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysDept dept) + { + Long deptId = dept.getDeptId(); + deptService.checkDeptDataScope(deptId); + if (!deptService.checkDeptNameUnique(dept)) + { + return error("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在"); + } + else if (dept.getParentId().equals(deptId)) + { + return error("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己"); + } + else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus()) && deptService.selectNormalChildrenDeptById(deptId) > 0) + { + return error("该部门包含未停用的子部门!"); + } + dept.setUpdateBy(getUsername()); + return toAjax(deptService.updateDept(dept)); + } + + /** + * 保存部门排序 + */ + @PreAuthorize("@ss.hasPermi('system:dept:edit')") + @Log(title = "保存部门排序", businessType = BusinessType.UPDATE) + @PutMapping("/updateSort") + public AjaxResult updateSort(@RequestBody Map params) + { + String[] deptIds = params.get("deptIds").split(","); + String[] orderNums = params.get("orderNums").split(","); + deptService.updateDeptSort(deptIds, orderNums); + return success(); + } + + /** + * 删除部门 + */ + @PreAuthorize("@ss.hasPermi('system:dept:remove')") + @Log(title = "部门管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{deptId}") + public AjaxResult remove(@PathVariable Long deptId) + { + if (deptService.hasChildByDeptId(deptId)) + { + return warn("存在下级部门,不允许删除"); + } + if (deptService.checkDeptExistUser(deptId)) + { + return warn("部门存在用户,不允许删除"); + } + deptService.checkDeptDataScope(deptId); + return toAjax(deptService.deleteDeptById(deptId)); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java index f65492b..1914b99 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java @@ -1,121 +1,121 @@ -package com.ruoyi.web.controller.system; - -import java.util.ArrayList; -import java.util.List; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -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.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.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.domain.entity.SysDictData; -import com.ruoyi.common.core.page.TableDataInfo; -import com.ruoyi.common.enums.BusinessType; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.common.utils.poi.ExcelUtil; -import com.ruoyi.system.service.ISysDictDataService; -import com.ruoyi.system.service.ISysDictTypeService; - -/** - * 数据字典信息 - * - * @author ruoyi - */ -@RestController -@RequestMapping("/system/dict/data") -public class SysDictDataController extends BaseController -{ - @Autowired - private ISysDictDataService dictDataService; - - @Autowired - private ISysDictTypeService dictTypeService; - - @PreAuthorize("@ss.hasPermi('system:dict:list')") - @GetMapping("/list") - public TableDataInfo list(SysDictData dictData) - { - startPage(); - List list = dictDataService.selectDictDataList(dictData); - return getDataTable(list); - } - - @Log(title = "字典数据", businessType = BusinessType.EXPORT) - @PreAuthorize("@ss.hasPermi('system:dict:export')") - @PostMapping("/export") - public void export(HttpServletResponse response, SysDictData dictData) - { - List list = dictDataService.selectDictDataList(dictData); - ExcelUtil util = new ExcelUtil(SysDictData.class); - util.exportExcel(response, list, "字典数据"); - } - - /** - * 查询字典数据详细 - */ - @PreAuthorize("@ss.hasPermi('system:dict:query')") - @GetMapping(value = "/{dictCode}") - public AjaxResult getInfo(@PathVariable Long dictCode) - { - return success(dictDataService.selectDictDataById(dictCode)); - } - - /** - * 根据字典类型查询字典数据信息 - */ - @GetMapping(value = "/type/{dictType}") - public AjaxResult dictType(@PathVariable String dictType) - { - List data = dictTypeService.selectDictDataByType(dictType); - if (StringUtils.isNull(data)) - { - data = new ArrayList(); - } - return success(data); - } - - /** - * 新增字典类型 - */ - @PreAuthorize("@ss.hasPermi('system:dict:add')") - @Log(title = "字典数据", businessType = BusinessType.INSERT) - @PostMapping - public AjaxResult add(@Validated @RequestBody SysDictData dict) - { - dict.setCreateBy(getUsername()); - return toAjax(dictDataService.insertDictData(dict)); - } - - /** - * 修改保存字典类型 - */ - @PreAuthorize("@ss.hasPermi('system:dict:edit')") - @Log(title = "字典数据", businessType = BusinessType.UPDATE) - @PutMapping - public AjaxResult edit(@Validated @RequestBody SysDictData dict) - { - dict.setUpdateBy(getUsername()); - return toAjax(dictDataService.updateDictData(dict)); - } - - /** - * 删除字典类型 - */ - @PreAuthorize("@ss.hasPermi('system:dict:remove')") - @Log(title = "字典类型", businessType = BusinessType.DELETE) - @DeleteMapping("/{dictCodes}") - public AjaxResult remove(@PathVariable Long[] dictCodes) - { - dictDataService.deleteDictDataByIds(dictCodes); - return success(); - } -} +package com.ruoyi.web.controller.system; + +import java.util.ArrayList; +import java.util.List; +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; +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.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.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.domain.entity.SysDictData; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.service.ISysDictDataService; +import com.ruoyi.system.service.ISysDictTypeService; + +/** + * 数据字典信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/dict/data") +public class SysDictDataController extends BaseController +{ + @Autowired + private ISysDictDataService dictDataService; + + @Autowired + private ISysDictTypeService dictTypeService; + + @PreAuthorize("@ss.hasPermi('system:dict:list')") + @GetMapping("/list") + public TableDataInfo list(SysDictData dictData) + { + startPage(); + List list = dictDataService.selectDictDataList(dictData); + return getDataTable(list); + } + + @Log(title = "字典数据", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:dict:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysDictData dictData) + { + List list = dictDataService.selectDictDataList(dictData); + ExcelUtil util = new ExcelUtil(SysDictData.class); + util.exportExcel(response, list, "字典数据"); + } + + /** + * 查询字典数据详细 + */ + @PreAuthorize("@ss.hasPermi('system:dict:query')") + @GetMapping(value = "/{dictCode}") + public AjaxResult getInfo(@PathVariable Long dictCode) + { + return success(dictDataService.selectDictDataById(dictCode)); + } + + /** + * 根据字典类型查询字典数据信息 + */ + @GetMapping(value = "/type/{dictType}") + public AjaxResult dictType(@PathVariable String dictType) + { + List data = dictTypeService.selectDictDataByType(dictType); + if (StringUtils.isNull(data)) + { + data = new ArrayList(); + } + return success(data); + } + + /** + * 新增字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:add')") + @Log(title = "字典数据", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysDictData dict) + { + dict.setCreateBy(getUsername()); + return toAjax(dictDataService.insertDictData(dict)); + } + + /** + * 修改保存字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:edit')") + @Log(title = "字典数据", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysDictData dict) + { + dict.setUpdateBy(getUsername()); + return toAjax(dictDataService.updateDictData(dict)); + } + + /** + * 删除字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:remove')") + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{dictCodes}") + public AjaxResult remove(@PathVariable Long[] dictCodes) + { + dictDataService.deleteDictDataByIds(dictCodes); + return success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java index 3ff314c..9808842 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java @@ -1,131 +1,131 @@ -package com.ruoyi.web.controller.system; - -import java.util.List; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -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.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.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.domain.entity.SysDictType; -import com.ruoyi.common.core.page.TableDataInfo; -import com.ruoyi.common.enums.BusinessType; -import com.ruoyi.common.utils.poi.ExcelUtil; -import com.ruoyi.system.service.ISysDictTypeService; - -/** - * 数据字典信息 - * - * @author ruoyi - */ -@RestController -@RequestMapping("/system/dict/type") -public class SysDictTypeController extends BaseController -{ - @Autowired - private ISysDictTypeService dictTypeService; - - @PreAuthorize("@ss.hasPermi('system:dict:list')") - @GetMapping("/list") - public TableDataInfo list(SysDictType dictType) - { - startPage(); - List list = dictTypeService.selectDictTypeList(dictType); - return getDataTable(list); - } - - @Log(title = "字典类型", businessType = BusinessType.EXPORT) - @PreAuthorize("@ss.hasPermi('system:dict:export')") - @PostMapping("/export") - public void export(HttpServletResponse response, SysDictType dictType) - { - List list = dictTypeService.selectDictTypeList(dictType); - ExcelUtil util = new ExcelUtil(SysDictType.class); - util.exportExcel(response, list, "字典类型"); - } - - /** - * 查询字典类型详细 - */ - @PreAuthorize("@ss.hasPermi('system:dict:query')") - @GetMapping(value = "/{dictId}") - public AjaxResult getInfo(@PathVariable Long dictId) - { - return success(dictTypeService.selectDictTypeById(dictId)); - } - - /** - * 新增字典类型 - */ - @PreAuthorize("@ss.hasPermi('system:dict:add')") - @Log(title = "字典类型", businessType = BusinessType.INSERT) - @PostMapping - public AjaxResult add(@Validated @RequestBody SysDictType dict) - { - if (!dictTypeService.checkDictTypeUnique(dict)) - { - return error("新增字典'" + dict.getDictName() + "'失败,字典类型已存在"); - } - dict.setCreateBy(getUsername()); - return toAjax(dictTypeService.insertDictType(dict)); - } - - /** - * 修改字典类型 - */ - @PreAuthorize("@ss.hasPermi('system:dict:edit')") - @Log(title = "字典类型", businessType = BusinessType.UPDATE) - @PutMapping - public AjaxResult edit(@Validated @RequestBody SysDictType dict) - { - if (!dictTypeService.checkDictTypeUnique(dict)) - { - return error("修改字典'" + dict.getDictName() + "'失败,字典类型已存在"); - } - dict.setUpdateBy(getUsername()); - return toAjax(dictTypeService.updateDictType(dict)); - } - - /** - * 删除字典类型 - */ - @PreAuthorize("@ss.hasPermi('system:dict:remove')") - @Log(title = "字典类型", businessType = BusinessType.DELETE) - @DeleteMapping("/{dictIds}") - public AjaxResult remove(@PathVariable Long[] dictIds) - { - dictTypeService.deleteDictTypeByIds(dictIds); - return success(); - } - - /** - * 刷新字典缓存 - */ - @PreAuthorize("@ss.hasPermi('system:dict:remove')") - @Log(title = "字典类型", businessType = BusinessType.CLEAN) - @DeleteMapping("/refreshCache") - public AjaxResult refreshCache() - { - dictTypeService.resetDictCache(); - return success(); - } - - /** - * 获取字典选择框列表 - */ - @GetMapping("/optionselect") - public AjaxResult optionselect() - { - List dictTypes = dictTypeService.selectDictTypeAll(); - return success(dictTypes); - } -} +package com.ruoyi.web.controller.system; + +import java.util.List; +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; +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.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.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.domain.entity.SysDictType; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.service.ISysDictTypeService; + +/** + * 数据字典信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/dict/type") +public class SysDictTypeController extends BaseController +{ + @Autowired + private ISysDictTypeService dictTypeService; + + @PreAuthorize("@ss.hasPermi('system:dict:list')") + @GetMapping("/list") + public TableDataInfo list(SysDictType dictType) + { + startPage(); + List list = dictTypeService.selectDictTypeList(dictType); + return getDataTable(list); + } + + @Log(title = "字典类型", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:dict:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysDictType dictType) + { + List list = dictTypeService.selectDictTypeList(dictType); + ExcelUtil util = new ExcelUtil(SysDictType.class); + util.exportExcel(response, list, "字典类型"); + } + + /** + * 查询字典类型详细 + */ + @PreAuthorize("@ss.hasPermi('system:dict:query')") + @GetMapping(value = "/{dictId}") + public AjaxResult getInfo(@PathVariable Long dictId) + { + return success(dictTypeService.selectDictTypeById(dictId)); + } + + /** + * 新增字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:add')") + @Log(title = "字典类型", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysDictType dict) + { + if (!dictTypeService.checkDictTypeUnique(dict)) + { + return error("新增字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dict.setCreateBy(getUsername()); + return toAjax(dictTypeService.insertDictType(dict)); + } + + /** + * 修改字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:edit')") + @Log(title = "字典类型", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysDictType dict) + { + if (!dictTypeService.checkDictTypeUnique(dict)) + { + return error("修改字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dict.setUpdateBy(getUsername()); + return toAjax(dictTypeService.updateDictType(dict)); + } + + /** + * 删除字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:remove')") + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{dictIds}") + public AjaxResult remove(@PathVariable Long[] dictIds) + { + dictTypeService.deleteDictTypeByIds(dictIds); + return success(); + } + + /** + * 刷新字典缓存 + */ + @PreAuthorize("@ss.hasPermi('system:dict:remove')") + @Log(title = "字典类型", businessType = BusinessType.CLEAN) + @DeleteMapping("/refreshCache") + public AjaxResult refreshCache() + { + dictTypeService.resetDictCache(); + return success(); + } + + /** + * 获取字典选择框列表 + */ + @GetMapping("/optionselect") + public AjaxResult optionselect() + { + List dictTypes = dictTypeService.selectDictTypeAll(); + return success(dictTypes); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java index 13007eb..e0ecc01 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java @@ -1,29 +1,64 @@ -package com.ruoyi.web.controller.system; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import com.ruoyi.common.config.RuoYiConfig; -import com.ruoyi.common.utils.StringUtils; - -/** - * 首页 - * - * @author ruoyi - */ -@RestController -public class SysIndexController -{ - /** 系统基础配置 */ - @Autowired - private RuoYiConfig ruoyiConfig; - - /** - * 访问首页,提示语 - */ - @RequestMapping("/") - public String index() - { - return StringUtils.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion()); - } -} +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; + +/** + * 首页 + * + * @author ruoyi + */ +@RestController +public class SysIndexController +{ + /** 系统基础配置 */ + @Autowired + private RuoYiConfig ruoyiConfig; + + @Autowired + private ISysUserService userService; + + /** + * 访问首页,提示语 + */ + @RequestMapping("/") + public String index() + { + return StringUtils.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion()); + } + + /** + * 解锁屏幕 + */ + @PostMapping("/unlockscreen") + public AjaxResult unlockScreen(@RequestBody Map 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("解锁成功"); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java index 6a2a0b0..caebb39 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java @@ -1,152 +1,131 @@ -package com.ruoyi.web.controller.system; - -import java.util.Date; -import java.util.List; -import java.util.Set; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; -import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.core.domain.AjaxResult; -import com.ruoyi.common.core.domain.entity.SysMenu; -import com.ruoyi.common.core.domain.entity.SysUser; -import com.ruoyi.common.core.domain.model.LoginBody; -import com.ruoyi.common.core.domain.model.LoginUser; -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; -import com.ruoyi.system.service.ISysConfigService; -import com.ruoyi.system.service.ISysMenuService; - -/** - * 登录验证 - * - * @author ruoyi - */ -@RestController -public class SysLoginController -{ - @Autowired - private SysLoginService loginService; - - @Autowired - private ISysMenuService menuService; - - @Autowired - private SysPermissionService permissionService; - - @Autowired - private TokenService tokenService; - - @Autowired - private ISysConfigService configService; - - @Autowired - private PasswordTransferCryptoService passwordTransferCryptoService; - - /** - * 登录方法 - * - * @param loginBody 登录信息 - * @return 结果 - */ - @PostMapping("/login") - 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()); - ajax.put(Constants.TOKEN, token); - 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; - } - - /** - * 获取用户信息 - * - * @return 用户信息 - */ - @GetMapping("getInfo") - public AjaxResult getInfo() - { - LoginUser loginUser = SecurityUtils.getLoginUser(); - SysUser user = loginUser.getUser(); - // 角色集合 - Set roles = permissionService.getRolePermission(user); - // 权限集合 - Set permissions = permissionService.getMenuPermission(user); - if (!loginUser.getPermissions().equals(permissions)) - { - loginUser.setPermissions(permissions); - tokenService.refreshToken(loginUser); - } - AjaxResult ajax = AjaxResult.success(); - ajax.put("user", user); - ajax.put("roles", roles); - ajax.put("permissions", permissions); - ajax.put("isDefaultModifyPwd", initPasswordIsModify(user.getPwdUpdateDate())); - ajax.put("isPasswordExpired", passwordIsExpiration(user.getPwdUpdateDate())); - return ajax; - } - - /** - * 获取路由信息 - * - * @return 路由信息 - */ - @GetMapping("getRouters") - public AjaxResult getRouters() - { - Long userId = SecurityUtils.getUserId(); - List menus = menuService.selectMenuTreeByUserId(userId); - return AjaxResult.success(menuService.buildMenus(menus)); - } - - // 检查初始密码是否提醒修改 - public boolean initPasswordIsModify(Date pwdUpdateDate) - { - Integer initPasswordModify = Convert.toInt(configService.selectConfigByKey("sys.account.initPasswordModify")); - return initPasswordModify != null && initPasswordModify == 1 && pwdUpdateDate == null; - } - - // 检查密码是否过期 - public boolean passwordIsExpiration(Date pwdUpdateDate) - { - Integer passwordValidateDays = Convert.toInt(configService.selectConfigByKey("sys.account.passwordValidateDays")); - if (passwordValidateDays != null && passwordValidateDays > 0) - { - if (StringUtils.isNull(pwdUpdateDate)) - { - // 如果从未修改过初始密码,直接提醒过期 - return true; - } - Date nowDate = DateUtils.getNowDate(); - return DateUtils.differentDaysByMillisecond(nowDate, pwdUpdateDate) > passwordValidateDays; - } - return false; - } -} +package com.ruoyi.web.controller.system; + +import java.util.Date; +import java.util.List; +import java.util.Set; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysMenu; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginBody; +import com.ruoyi.common.core.domain.model.LoginUser; +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.SysLoginService; +import com.ruoyi.framework.web.service.SysPermissionService; +import com.ruoyi.framework.web.service.TokenService; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.system.service.ISysMenuService; + +/** + * 登录验证 + * + * @author ruoyi + */ +@RestController +public class SysLoginController +{ + @Autowired + private SysLoginService loginService; + + @Autowired + private ISysMenuService menuService; + + @Autowired + private SysPermissionService permissionService; + + @Autowired + private TokenService tokenService; + + @Autowired + private ISysConfigService configService; + + /** + * 登录方法 + * + * @param loginBody 登录信息 + * @return 结果 + */ + @PostMapping("/login") + public AjaxResult login(@RequestBody LoginBody loginBody) + { + AjaxResult ajax = AjaxResult.success(); + // 生成令牌 + String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(), + loginBody.getUuid()); + ajax.put(Constants.TOKEN, token); + return ajax; + } + + /** + * 获取用户信息 + * + * @return 用户信息 + */ + @GetMapping("getInfo") + public AjaxResult getInfo() + { + LoginUser loginUser = SecurityUtils.getLoginUser(); + SysUser user = loginUser.getUser(); + // 角色集合 + Set roles = permissionService.getRolePermission(user); + // 权限集合 + Set permissions = permissionService.getMenuPermission(user); + if (!loginUser.getPermissions().equals(permissions)) + { + loginUser.setPermissions(permissions); + tokenService.refreshToken(loginUser); + } + AjaxResult ajax = AjaxResult.success(); + ajax.put("user", user); + ajax.put("roles", roles); + ajax.put("permissions", permissions); + ajax.put("isDefaultModifyPwd", initPasswordIsModify(user.getPwdUpdateDate())); + ajax.put("isPasswordExpired", passwordIsExpiration(user.getPwdUpdateDate())); + return ajax; + } + + /** + * 获取路由信息 + * + * @return 路由信息 + */ + @GetMapping("getRouters") + public AjaxResult getRouters() + { + Long userId = SecurityUtils.getUserId(); + List menus = menuService.selectMenuTreeByUserId(userId); + return AjaxResult.success(menuService.buildMenus(menus)); + } + + // 检查初始密码是否提醒修改 + public boolean initPasswordIsModify(Date pwdUpdateDate) + { + Integer initPasswordModify = Convert.toInt(configService.selectConfigByKey("sys.account.initPasswordModify")); + return initPasswordModify != null && initPasswordModify == 1 && pwdUpdateDate == null; + } + + // 检查密码是否过期 + public boolean passwordIsExpiration(Date pwdUpdateDate) + { + Integer passwordValidateDays = Convert.toInt(configService.selectConfigByKey("sys.account.passwordValidateDays")); + if (passwordValidateDays != null && passwordValidateDays > 0) + { + if (StringUtils.isNull(pwdUpdateDate)) + { + // 如果从未修改过初始密码,直接提醒过期 + return true; + } + Date nowDate = DateUtils.getNowDate(); + return DateUtils.differentDaysByMillisecond(nowDate, pwdUpdateDate) > passwordValidateDays; + } + return false; + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java index 72abc00..545d6a8 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java @@ -1,150 +1,165 @@ -package com.ruoyi.web.controller.system; - -import java.util.List; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -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.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.RestController; -import com.ruoyi.common.annotation.Log; -import com.ruoyi.common.constant.UserConstants; -import com.ruoyi.common.core.controller.BaseController; -import com.ruoyi.common.core.domain.AjaxResult; -import com.ruoyi.common.core.domain.entity.SysMenu; -import com.ruoyi.common.enums.BusinessType; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.system.service.ISysMenuService; - -/** - * 菜单信息 - * - * @author ruoyi - */ -@RestController -@RequestMapping("/system/menu") -public class SysMenuController extends BaseController -{ - @Autowired - private ISysMenuService menuService; - - /** - * 获取菜单列表 - */ - @PreAuthorize("@ss.hasPermi('system:menu:list')") - @GetMapping("/list") - public AjaxResult list(SysMenu menu) - { - List menus = menuService.selectMenuList(menu, getUserId()); - return success(menus); - } - - /** - * 根据菜单编号获取详细信息 - */ - @PreAuthorize("@ss.hasPermi('system:menu:query')") - @GetMapping(value = "/{menuId}") - public AjaxResult getInfo(@PathVariable Long menuId) - { - return success(menuService.selectMenuById(menuId)); - } - - /** - * 获取菜单下拉树列表 - */ - @GetMapping("/treeselect") - public AjaxResult treeselect(SysMenu menu) - { - List menus = menuService.selectMenuList(menu, getUserId()); - return success(menuService.buildMenuTreeSelect(menus)); - } - - /** - * 加载对应角色菜单列表树 - */ - @GetMapping(value = "/roleMenuTreeselect/{roleId}") - public AjaxResult roleMenuTreeselect(@PathVariable("roleId") Long roleId) - { - List menus = menuService.selectMenuList(getUserId()); - AjaxResult ajax = AjaxResult.success(); - ajax.put("checkedKeys", menuService.selectMenuListByRoleId(roleId)); - ajax.put("menus", menuService.buildMenuTreeSelect(menus)); - return ajax; - } - - /** - * 新增菜单 - */ - @PreAuthorize("@ss.hasPermi('system:menu:add')") - @Log(title = "菜单管理", businessType = BusinessType.INSERT) - @PostMapping - public AjaxResult add(@Validated @RequestBody SysMenu menu) - { - if (!menuService.checkMenuNameUnique(menu)) - { - return error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); - } - else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) - { - return error("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头"); - } - else if (!menuService.checkRouteConfigUnique(menu)) - { - return error("新增菜单'" + menu.getMenuName() + "'失败,路由名称或地址已存在"); - } - menu.setCreateBy(getUsername()); - return toAjax(menuService.insertMenu(menu)); - } - - /** - * 修改菜单 - */ - @PreAuthorize("@ss.hasPermi('system:menu:edit')") - @Log(title = "菜单管理", businessType = BusinessType.UPDATE) - @PutMapping - public AjaxResult edit(@Validated @RequestBody SysMenu menu) - { - if (!menuService.checkMenuNameUnique(menu)) - { - return error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); - } - else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) - { - return error("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头"); - } - else if (menu.getMenuId().equals(menu.getParentId())) - { - return error("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己"); - } - else if (!menuService.checkRouteConfigUnique(menu)) - { - return error("修改菜单'" + menu.getMenuName() + "'失败,路由名称或地址已存在"); - } - menu.setUpdateBy(getUsername()); - return toAjax(menuService.updateMenu(menu)); - } - - /** - * 删除菜单 - */ - @PreAuthorize("@ss.hasPermi('system:menu:remove')") - @Log(title = "菜单管理", businessType = BusinessType.DELETE) - @DeleteMapping("/{menuId}") - public AjaxResult remove(@PathVariable("menuId") Long menuId) - { - if (menuService.hasChildByMenuId(menuId)) - { - return warn("存在子菜单,不允许删除"); - } - if (menuService.checkMenuExistRole(menuId)) - { - return warn("菜单已分配,不允许删除"); - } - return toAjax(menuService.deleteMenuById(menuId)); - } +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; +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.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.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysMenu; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.service.ISysMenuService; + +/** + * 菜单信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/menu") +public class SysMenuController extends BaseController +{ + @Autowired + private ISysMenuService menuService; + + /** + * 获取菜单列表 + */ + @PreAuthorize("@ss.hasPermi('system:menu:list')") + @GetMapping("/list") + public AjaxResult list(SysMenu menu) + { + List menus = menuService.selectMenuList(menu, getUserId()); + return success(menus); + } + + /** + * 根据菜单编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:menu:query')") + @GetMapping(value = "/{menuId}") + public AjaxResult getInfo(@PathVariable Long menuId) + { + return success(menuService.selectMenuById(menuId)); + } + + /** + * 获取菜单下拉树列表 + */ + @GetMapping("/treeselect") + public AjaxResult treeselect(SysMenu menu) + { + List menus = menuService.selectMenuList(menu, getUserId()); + return success(menuService.buildMenuTreeSelect(menus)); + } + + /** + * 加载对应角色菜单列表树 + */ + @GetMapping(value = "/roleMenuTreeselect/{roleId}") + public AjaxResult roleMenuTreeselect(@PathVariable("roleId") Long roleId) + { + List menus = menuService.selectMenuList(getUserId()); + AjaxResult ajax = AjaxResult.success(); + ajax.put("checkedKeys", menuService.selectMenuListByRoleId(roleId)); + ajax.put("menus", menuService.buildMenuTreeSelect(menus)); + return ajax; + } + + /** + * 新增菜单 + */ + @PreAuthorize("@ss.hasPermi('system:menu:add')") + @Log(title = "菜单管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysMenu menu) + { + if (!menuService.checkMenuNameUnique(menu)) + { + return error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } + else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) + { + return error("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头"); + } + else if (!menuService.checkRouteConfigUnique(menu)) + { + return error("新增菜单'" + menu.getMenuName() + "'失败,路由名称或地址已存在"); + } + menu.setCreateBy(getUsername()); + return toAjax(menuService.insertMenu(menu)); + } + + /** + * 修改菜单 + */ + @PreAuthorize("@ss.hasPermi('system:menu:edit')") + @Log(title = "菜单管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysMenu menu) + { + if (!menuService.checkMenuNameUnique(menu)) + { + return error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } + else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) + { + return error("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头"); + } + else if (menu.getMenuId().equals(menu.getParentId())) + { + return error("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己"); + } + else if (!menuService.checkRouteConfigUnique(menu)) + { + return error("修改菜单'" + menu.getMenuName() + "'失败,路由名称或地址已存在"); + } + menu.setUpdateBy(getUsername()); + return toAjax(menuService.updateMenu(menu)); + } + + /** + * 保存菜单排序 + */ + @PreAuthorize("@ss.hasPermi('system:menu:edit')") + @Log(title = "保存菜单排序", businessType = BusinessType.UPDATE) + @PutMapping("/updateSort") + public AjaxResult updateSort(@RequestBody Map params) + { + String[] menuIds = params.get("menuIds").split(","); + String[] orderNums = params.get("orderNums").split(","); + menuService.updateMenuSort(menuIds, orderNums); + return success(); + } + + /** + * 删除菜单 + */ + @PreAuthorize("@ss.hasPermi('system:menu:remove')") + @Log(title = "菜单管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{menuId}") + public AjaxResult remove(@PathVariable("menuId") Long menuId) + { + if (menuService.hasChildByMenuId(menuId)) + { + return warn("存在子菜单,不允许删除"); + } + if (menuService.checkMenuExistRole(menuId)) + { + return warn("菜单已分配,不允许删除"); + } + return toAjax(menuService.deleteMenuById(menuId)); + } } \ No newline at end of file diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java index 8622828..b6080c6 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java @@ -1,91 +1,138 @@ -package com.ruoyi.web.controller.system; - -import java.util.List; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -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.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.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.enums.BusinessType; -import com.ruoyi.system.domain.SysNotice; -import com.ruoyi.system.service.ISysNoticeService; - -/** - * 公告 信息操作处理 - * - * @author ruoyi - */ -@RestController -@RequestMapping("/system/notice") -public class SysNoticeController extends BaseController -{ - @Autowired - private ISysNoticeService noticeService; - - /** - * 获取通知公告列表 - */ - @PreAuthorize("@ss.hasPermi('system:notice:list')") - @GetMapping("/list") - public TableDataInfo list(SysNotice notice) - { - startPage(); - List list = noticeService.selectNoticeList(notice); - return getDataTable(list); - } - - /** - * 根据通知公告编号获取详细信息 - */ - @PreAuthorize("@ss.hasPermi('system:notice:query')") - @GetMapping(value = "/{noticeId}") - public AjaxResult getInfo(@PathVariable Long noticeId) - { - return success(noticeService.selectNoticeById(noticeId)); - } - - /** - * 新增通知公告 - */ - @PreAuthorize("@ss.hasPermi('system:notice:add')") - @Log(title = "通知公告", businessType = BusinessType.INSERT) - @PostMapping - public AjaxResult add(@Validated @RequestBody SysNotice notice) - { - notice.setCreateBy(getUsername()); - return toAjax(noticeService.insertNotice(notice)); - } - - /** - * 修改通知公告 - */ - @PreAuthorize("@ss.hasPermi('system:notice:edit')") - @Log(title = "通知公告", businessType = BusinessType.UPDATE) - @PutMapping - public AjaxResult edit(@Validated @RequestBody SysNotice notice) - { - notice.setUpdateBy(getUsername()); - return toAjax(noticeService.updateNotice(notice)); - } - - /** - * 删除通知公告 - */ - @PreAuthorize("@ss.hasPermi('system:notice:remove')") - @Log(title = "通知公告", businessType = BusinessType.DELETE) - @DeleteMapping("/{noticeIds}") - public AjaxResult remove(@PathVariable Long[] noticeIds) - { - return toAjax(noticeService.deleteNoticeByIds(noticeIds)); - } -} +package com.ruoyi.web.controller.system; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +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.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; + +/** + * 公告 信息操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/notice") +public class SysNoticeController extends BaseController +{ + @Autowired + private ISysNoticeService noticeService; + + @Autowired + private ISysNoticeReadService noticeReadService; + + /** + * 获取通知公告列表 + */ + @PreAuthorize("@ss.hasPermi('system:notice:list')") + @GetMapping("/list") + public TableDataInfo list(SysNotice notice) + { + startPage(); + List list = noticeService.selectNoticeList(notice); + return getDataTable(list); + } + + /** + * 根据通知公告编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:notice:query')") + @GetMapping(value = "/{noticeId}") + public AjaxResult getInfo(@PathVariable Long noticeId) + { + return success(noticeService.selectNoticeById(noticeId)); + } + + /** + * 新增通知公告 + */ + @PreAuthorize("@ss.hasPermi('system:notice:add')") + @Log(title = "通知公告", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysNotice notice) + { + notice.setCreateBy(getUsername()); + return toAjax(noticeService.insertNotice(notice)); + } + + /** + * 修改通知公告 + */ + @PreAuthorize("@ss.hasPermi('system:notice:edit')") + @Log(title = "通知公告", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysNotice notice) + { + notice.setUpdateBy(getUsername()); + return toAjax(noticeService.updateNotice(notice)); + } + + /** + * 首页顶部公告列表(返回全部正常公告,带当前用户已读标记,最多5条) + */ + @GetMapping("/listTop") + @ResponseBody + public AjaxResult listTop() + { + Long userId = getUserId(); + List 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(); + } + + /** + * 删除通知公告 + */ + @PreAuthorize("@ss.hasPermi('system:notice:remove')") + @Log(title = "通知公告", businessType = BusinessType.DELETE) + @DeleteMapping("/{noticeIds}") + public AjaxResult remove(@PathVariable Long[] noticeIds) + { + noticeReadService.deleteByNoticeIds(noticeIds); + return toAjax(noticeService.deleteNoticeByIds(noticeIds)); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java index 5b604cf..a5b96c8 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java @@ -1,129 +1,129 @@ -package com.ruoyi.web.controller.system; - -import java.util.List; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -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.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.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.enums.BusinessType; -import com.ruoyi.common.utils.poi.ExcelUtil; -import com.ruoyi.system.domain.SysPost; -import com.ruoyi.system.service.ISysPostService; - -/** - * 岗位信息操作处理 - * - * @author ruoyi - */ -@RestController -@RequestMapping("/system/post") -public class SysPostController extends BaseController -{ - @Autowired - private ISysPostService postService; - - /** - * 获取岗位列表 - */ - @PreAuthorize("@ss.hasPermi('system:post:list')") - @GetMapping("/list") - public TableDataInfo list(SysPost post) - { - startPage(); - List list = postService.selectPostList(post); - return getDataTable(list); - } - - @Log(title = "岗位管理", businessType = BusinessType.EXPORT) - @PreAuthorize("@ss.hasPermi('system:post:export')") - @PostMapping("/export") - public void export(HttpServletResponse response, SysPost post) - { - List list = postService.selectPostList(post); - ExcelUtil util = new ExcelUtil(SysPost.class); - util.exportExcel(response, list, "岗位数据"); - } - - /** - * 根据岗位编号获取详细信息 - */ - @PreAuthorize("@ss.hasPermi('system:post:query')") - @GetMapping(value = "/{postId}") - public AjaxResult getInfo(@PathVariable Long postId) - { - return success(postService.selectPostById(postId)); - } - - /** - * 新增岗位 - */ - @PreAuthorize("@ss.hasPermi('system:post:add')") - @Log(title = "岗位管理", businessType = BusinessType.INSERT) - @PostMapping - public AjaxResult add(@Validated @RequestBody SysPost post) - { - if (!postService.checkPostNameUnique(post)) - { - return error("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在"); - } - else if (!postService.checkPostCodeUnique(post)) - { - return error("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在"); - } - post.setCreateBy(getUsername()); - return toAjax(postService.insertPost(post)); - } - - /** - * 修改岗位 - */ - @PreAuthorize("@ss.hasPermi('system:post:edit')") - @Log(title = "岗位管理", businessType = BusinessType.UPDATE) - @PutMapping - public AjaxResult edit(@Validated @RequestBody SysPost post) - { - if (!postService.checkPostNameUnique(post)) - { - return error("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在"); - } - else if (!postService.checkPostCodeUnique(post)) - { - return error("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在"); - } - post.setUpdateBy(getUsername()); - return toAjax(postService.updatePost(post)); - } - - /** - * 删除岗位 - */ - @PreAuthorize("@ss.hasPermi('system:post:remove')") - @Log(title = "岗位管理", businessType = BusinessType.DELETE) - @DeleteMapping("/{postIds}") - public AjaxResult remove(@PathVariable Long[] postIds) - { - return toAjax(postService.deletePostByIds(postIds)); - } - - /** - * 获取岗位选择框列表 - */ - @GetMapping("/optionselect") - public AjaxResult optionselect() - { - List posts = postService.selectPostAll(); - return success(posts); - } -} +package com.ruoyi.web.controller.system; + +import java.util.List; +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; +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.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.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.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.domain.SysPost; +import com.ruoyi.system.service.ISysPostService; + +/** + * 岗位信息操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/post") +public class SysPostController extends BaseController +{ + @Autowired + private ISysPostService postService; + + /** + * 获取岗位列表 + */ + @PreAuthorize("@ss.hasPermi('system:post:list')") + @GetMapping("/list") + public TableDataInfo list(SysPost post) + { + startPage(); + List list = postService.selectPostList(post); + return getDataTable(list); + } + + @Log(title = "岗位管理", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:post:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysPost post) + { + List list = postService.selectPostList(post); + ExcelUtil util = new ExcelUtil(SysPost.class); + util.exportExcel(response, list, "岗位数据"); + } + + /** + * 根据岗位编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:post:query')") + @GetMapping(value = "/{postId}") + public AjaxResult getInfo(@PathVariable Long postId) + { + return success(postService.selectPostById(postId)); + } + + /** + * 新增岗位 + */ + @PreAuthorize("@ss.hasPermi('system:post:add')") + @Log(title = "岗位管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysPost post) + { + if (!postService.checkPostNameUnique(post)) + { + return error("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在"); + } + else if (!postService.checkPostCodeUnique(post)) + { + return error("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在"); + } + post.setCreateBy(getUsername()); + return toAjax(postService.insertPost(post)); + } + + /** + * 修改岗位 + */ + @PreAuthorize("@ss.hasPermi('system:post:edit')") + @Log(title = "岗位管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysPost post) + { + if (!postService.checkPostNameUnique(post)) + { + return error("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在"); + } + else if (!postService.checkPostCodeUnique(post)) + { + return error("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在"); + } + post.setUpdateBy(getUsername()); + return toAjax(postService.updatePost(post)); + } + + /** + * 删除岗位 + */ + @PreAuthorize("@ss.hasPermi('system:post:remove')") + @Log(title = "岗位管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{postIds}") + public AjaxResult remove(@PathVariable Long[] postIds) + { + return toAjax(postService.deletePostByIds(postIds)); + } + + /** + * 获取岗位选择框列表 + */ + @GetMapping("/optionselect") + public AjaxResult optionselect() + { + List posts = postService.selectPostAll(); + return success(posts); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java index fd101d5..b325045 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java @@ -1,153 +1,149 @@ -package com.ruoyi.web.controller.system; - -import java.util.Map; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.GetMapping; -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.RequestParam; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.multipart.MultipartFile; -import com.ruoyi.common.annotation.Log; -import com.ruoyi.common.config.RuoYiConfig; -import com.ruoyi.common.core.controller.BaseController; -import com.ruoyi.common.core.domain.AjaxResult; -import com.ruoyi.common.core.domain.entity.SysUser; -import com.ruoyi.common.core.domain.model.LoginUser; -import com.ruoyi.common.enums.BusinessType; -import com.ruoyi.common.utils.DateUtils; -import com.ruoyi.common.utils.SecurityUtils; -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; - -/** - * 个人信息 业务处理 - * - * @author ruoyi - */ -@RestController -@RequestMapping("/system/user/profile") -public class SysProfileController extends BaseController -{ - @Autowired - private ISysUserService userService; - - @Autowired - private TokenService tokenService; - - @Autowired - private PasswordTransferCryptoService passwordTransferCryptoService; - - /** - * 个人信息 - */ - @GetMapping - public AjaxResult profile() - { - LoginUser loginUser = getLoginUser(); - SysUser user = loginUser.getUser(); - AjaxResult ajax = AjaxResult.success(user); - ajax.put("roleGroup", userService.selectUserRoleGroup(loginUser.getUsername())); - ajax.put("postGroup", userService.selectUserPostGroup(loginUser.getUsername())); - return ajax; - } - - /** - * 修改用户 - */ - @Log(title = "个人信息", businessType = BusinessType.UPDATE) - @PutMapping - public AjaxResult updateProfile(@RequestBody SysUser user) - { - LoginUser loginUser = getLoginUser(); - SysUser currentUser = loginUser.getUser(); - currentUser.setNickName(user.getNickName()); - currentUser.setEmail(user.getEmail()); - currentUser.setPhonenumber(user.getPhonenumber()); - currentUser.setSex(user.getSex()); - if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(currentUser)) - { - return error("修改用户'" + loginUser.getUsername() + "'失败,手机号码已存在"); - } - if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(currentUser)) - { - return error("修改用户'" + loginUser.getUsername() + "'失败,邮箱账号已存在"); - } - if (userService.updateUserProfile(currentUser) > 0) - { - // 更新缓存用户信息 - tokenService.setLoginUser(loginUser); - return success(); - } - return error("修改个人信息异常,请联系管理员"); - } - - /** - * 重置密码 - */ - @Log(title = "个人信息", businessType = BusinessType.UPDATE) - @PutMapping("/updatePwd") - public AjaxResult updatePwd(@RequestBody Map params) - { - String oldPassword = passwordTransferCryptoService.decrypt(params.get("oldPassword")); - String newPassword = passwordTransferCryptoService.decrypt(params.get("newPassword")); - LoginUser loginUser = getLoginUser(); - Long userId = loginUser.getUserId(); - SysUser user = userService.selectUserById(userId); - String password = user.getPassword(); - if (!SecurityUtils.matchesPassword(oldPassword, password)) - { - return error("修改密码失败,旧密码错误"); - } - if (SecurityUtils.matchesPassword(newPassword, password)) - { - return error("新密码不能与旧密码相同"); - } - newPassword = SecurityUtils.encryptPassword(newPassword); - if (userService.resetUserPwd(userId, newPassword) > 0) - { - // 更新缓存用户密码&密码最后更新时间 - loginUser.getUser().setPwdUpdateDate(DateUtils.getNowDate()); - loginUser.getUser().setPassword(newPassword); - tokenService.setLoginUser(loginUser); - return success(); - } - return error("修改密码异常,请联系管理员"); - } - - /** - * 头像上传 - */ - @Log(title = "用户头像", businessType = BusinessType.UPDATE) - @PostMapping("/avatar") - public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) throws Exception - { - if (!file.isEmpty()) - { - LoginUser loginUser = getLoginUser(); - String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION, true); - if (userService.updateUserAvatar(loginUser.getUserId(), avatar)) - { - String oldAvatar = loginUser.getUser().getAvatar(); - if (StringUtils.isNotEmpty(oldAvatar)) - { - FileUtils.deleteFile(RuoYiConfig.getProfile() + FileUtils.stripPrefix(oldAvatar)); - } - AjaxResult ajax = AjaxResult.success(); - ajax.put("imgUrl", avatar); - // 更新缓存用户头像 - loginUser.getUser().setAvatar(avatar); - tokenService.setLoginUser(loginUser); - return ajax; - } - } - return error("上传图片异常,请联系管理员"); - } -} +package com.ruoyi.web.controller.system; + +import java.util.Map; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +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.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.SecurityUtils; +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.TokenService; +import com.ruoyi.system.service.ISysUserService; + +/** + * 个人信息 业务处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/user/profile") +public class SysProfileController extends BaseController +{ + @Autowired + private ISysUserService userService; + + @Autowired + private TokenService tokenService; + + /** + * 个人信息 + */ + @GetMapping + public AjaxResult profile() + { + LoginUser loginUser = getLoginUser(); + SysUser user = loginUser.getUser(); + AjaxResult ajax = AjaxResult.success(user); + ajax.put("roleGroup", userService.selectUserRoleGroup(loginUser.getUsername())); + ajax.put("postGroup", userService.selectUserPostGroup(loginUser.getUsername())); + return ajax; + } + + /** + * 修改用户 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult updateProfile(@RequestBody SysUser user) + { + LoginUser loginUser = getLoginUser(); + SysUser currentUser = loginUser.getUser(); + currentUser.setNickName(user.getNickName()); + currentUser.setEmail(user.getEmail()); + currentUser.setPhonenumber(user.getPhonenumber()); + currentUser.setSex(user.getSex()); + if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(currentUser)) + { + return error("修改用户'" + loginUser.getUsername() + "'失败,手机号码已存在"); + } + if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(currentUser)) + { + return error("修改用户'" + loginUser.getUsername() + "'失败,邮箱账号已存在"); + } + if (userService.updateUserProfile(currentUser) > 0) + { + // 更新缓存用户信息 + tokenService.setLoginUser(loginUser); + return success(); + } + return error("修改个人信息异常,请联系管理员"); + } + + /** + * 重置密码 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping("/updatePwd") + public AjaxResult updatePwd(@RequestBody Map params) + { + String oldPassword = params.get("oldPassword"); + String newPassword = params.get("newPassword"); + LoginUser loginUser = getLoginUser(); + Long userId = loginUser.getUserId(); + SysUser user = userService.selectUserById(userId); + String password = user.getPassword(); + if (!SecurityUtils.matchesPassword(oldPassword, password)) + { + return error("修改密码失败,旧密码错误"); + } + if (SecurityUtils.matchesPassword(newPassword, password)) + { + return error("新密码不能与旧密码相同"); + } + newPassword = SecurityUtils.encryptPassword(newPassword); + if (userService.resetUserPwd(userId, newPassword) > 0) + { + // 更新缓存用户密码&密码最后更新时间 + loginUser.getUser().setPwdUpdateDate(DateUtils.getNowDate()); + loginUser.getUser().setPassword(newPassword); + tokenService.setLoginUser(loginUser); + return success(); + } + return error("修改密码异常,请联系管理员"); + } + + /** + * 头像上传 + */ + @Log(title = "用户头像", businessType = BusinessType.UPDATE) + @PostMapping("/avatar") + public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) throws Exception + { + if (!file.isEmpty()) + { + LoginUser loginUser = getLoginUser(); + String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION, true); + if (userService.updateUserAvatar(loginUser.getUserId(), avatar)) + { + String oldAvatar = loginUser.getUser().getAvatar(); + if (StringUtils.isNotEmpty(oldAvatar)) + { + FileUtils.deleteFile(RuoYiConfig.getProfile() + FileUtils.stripPrefix(oldAvatar)); + } + AjaxResult ajax = AjaxResult.success(); + ajax.put("imgUrl", avatar); + // 更新缓存用户头像 + loginUser.getUser().setAvatar(avatar); + tokenService.setLoginUser(loginUser); + return ajax; + } + } + return error("上传图片异常,请联系管理员"); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java index 80f1ad9..f1552e0 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java @@ -1,43 +1,38 @@ -package com.ruoyi.web.controller.system; - -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.RestController; -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; - -/** - * 注册验证 - * - * @author ruoyi - */ -@RestController -public class SysRegisterController extends BaseController -{ - @Autowired - private SysRegisterService registerService; - - @Autowired - private ISysConfigService configService; - - @Autowired - private PasswordTransferCryptoService passwordTransferCryptoService; - - @PostMapping("/register") - public AjaxResult register(@RequestBody RegisterBody user) - { - if (!("true".equals(configService.selectConfigByKey("sys.account.registerUser")))) - { - return error("当前系统没有开启注册功能!"); - } - user.setPassword(passwordTransferCryptoService.decrypt(user.getPassword())); - String msg = registerService.register(user); - return StringUtils.isEmpty(msg) ? success() : error(msg); - } -} +package com.ruoyi.web.controller.system; + +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.RestController; +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.SysRegisterService; +import com.ruoyi.system.service.ISysConfigService; + +/** + * 注册验证 + * + * @author ruoyi + */ +@RestController +public class SysRegisterController extends BaseController +{ + @Autowired + private SysRegisterService registerService; + + @Autowired + private ISysConfigService configService; + + @PostMapping("/register") + public AjaxResult register(@RequestBody RegisterBody user) + { + if (!("true".equals(configService.selectConfigByKey("sys.account.registerUser")))) + { + return error("当前系统没有开启注册功能!"); + } + String msg = registerService.register(user); + return StringUtils.isEmpty(msg) ? success() : error(msg); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java index c8e7ab6..dfbf256 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java @@ -1,262 +1,262 @@ -package com.ruoyi.web.controller.system; - -import java.util.List; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -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.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.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.domain.entity.SysDept; -import com.ruoyi.common.core.domain.entity.SysRole; -import com.ruoyi.common.core.domain.entity.SysUser; -import com.ruoyi.common.core.domain.model.LoginUser; -import com.ruoyi.common.core.page.TableDataInfo; -import com.ruoyi.common.enums.BusinessType; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.common.utils.poi.ExcelUtil; -import com.ruoyi.framework.web.service.SysPermissionService; -import com.ruoyi.framework.web.service.TokenService; -import com.ruoyi.system.domain.SysUserRole; -import com.ruoyi.system.service.ISysDeptService; -import com.ruoyi.system.service.ISysRoleService; -import com.ruoyi.system.service.ISysUserService; - -/** - * 角色信息 - * - * @author ruoyi - */ -@RestController -@RequestMapping("/system/role") -public class SysRoleController extends BaseController -{ - @Autowired - private ISysRoleService roleService; - - @Autowired - private TokenService tokenService; - - @Autowired - private SysPermissionService permissionService; - - @Autowired - private ISysUserService userService; - - @Autowired - private ISysDeptService deptService; - - @PreAuthorize("@ss.hasPermi('system:role:list')") - @GetMapping("/list") - public TableDataInfo list(SysRole role) - { - startPage(); - List list = roleService.selectRoleList(role); - return getDataTable(list); - } - - @Log(title = "角色管理", businessType = BusinessType.EXPORT) - @PreAuthorize("@ss.hasPermi('system:role:export')") - @PostMapping("/export") - public void export(HttpServletResponse response, SysRole role) - { - List list = roleService.selectRoleList(role); - ExcelUtil util = new ExcelUtil(SysRole.class); - util.exportExcel(response, list, "角色数据"); - } - - /** - * 根据角色编号获取详细信息 - */ - @PreAuthorize("@ss.hasPermi('system:role:query')") - @GetMapping(value = "/{roleId}") - public AjaxResult getInfo(@PathVariable Long roleId) - { - roleService.checkRoleDataScope(roleId); - return success(roleService.selectRoleById(roleId)); - } - - /** - * 新增角色 - */ - @PreAuthorize("@ss.hasPermi('system:role:add')") - @Log(title = "角色管理", businessType = BusinessType.INSERT) - @PostMapping - public AjaxResult add(@Validated @RequestBody SysRole role) - { - if (!roleService.checkRoleNameUnique(role)) - { - return error("新增角色'" + role.getRoleName() + "'失败,角色名称已存在"); - } - else if (!roleService.checkRoleKeyUnique(role)) - { - return error("新增角色'" + role.getRoleName() + "'失败,角色权限已存在"); - } - role.setCreateBy(getUsername()); - return toAjax(roleService.insertRole(role)); - - } - - /** - * 修改保存角色 - */ - @PreAuthorize("@ss.hasPermi('system:role:edit')") - @Log(title = "角色管理", businessType = BusinessType.UPDATE) - @PutMapping - public AjaxResult edit(@Validated @RequestBody SysRole role) - { - roleService.checkRoleAllowed(role); - roleService.checkRoleDataScope(role.getRoleId()); - if (!roleService.checkRoleNameUnique(role)) - { - return error("修改角色'" + role.getRoleName() + "'失败,角色名称已存在"); - } - else if (!roleService.checkRoleKeyUnique(role)) - { - return error("修改角色'" + role.getRoleName() + "'失败,角色权限已存在"); - } - role.setUpdateBy(getUsername()); - - if (roleService.updateRole(role) > 0) - { - // 更新缓存用户权限 - LoginUser loginUser = getLoginUser(); - if (StringUtils.isNotNull(loginUser.getUser()) && !loginUser.getUser().isAdmin()) - { - loginUser.setUser(userService.selectUserByUserName(loginUser.getUser().getUserName())); - loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser())); - tokenService.setLoginUser(loginUser); - } - return success(); - } - return error("修改角色'" + role.getRoleName() + "'失败,请联系管理员"); - } - - /** - * 修改保存数据权限 - */ - @PreAuthorize("@ss.hasPermi('system:role:edit')") - @Log(title = "角色管理", businessType = BusinessType.UPDATE) - @PutMapping("/dataScope") - public AjaxResult dataScope(@RequestBody SysRole role) - { - roleService.checkRoleAllowed(role); - roleService.checkRoleDataScope(role.getRoleId()); - return toAjax(roleService.authDataScope(role)); - } - - /** - * 状态修改 - */ - @PreAuthorize("@ss.hasPermi('system:role:edit')") - @Log(title = "角色管理", businessType = BusinessType.UPDATE) - @PutMapping("/changeStatus") - public AjaxResult changeStatus(@RequestBody SysRole role) - { - roleService.checkRoleAllowed(role); - roleService.checkRoleDataScope(role.getRoleId()); - role.setUpdateBy(getUsername()); - return toAjax(roleService.updateRoleStatus(role)); - } - - /** - * 删除角色 - */ - @PreAuthorize("@ss.hasPermi('system:role:remove')") - @Log(title = "角色管理", businessType = BusinessType.DELETE) - @DeleteMapping("/{roleIds}") - public AjaxResult remove(@PathVariable Long[] roleIds) - { - return toAjax(roleService.deleteRoleByIds(roleIds)); - } - - /** - * 获取角色选择框列表 - */ - @PreAuthorize("@ss.hasPermi('system:role:query')") - @GetMapping("/optionselect") - public AjaxResult optionselect() - { - return success(roleService.selectRoleAll()); - } - - /** - * 查询已分配用户角色列表 - */ - @PreAuthorize("@ss.hasPermi('system:role:list')") - @GetMapping("/authUser/allocatedList") - public TableDataInfo allocatedList(SysUser user) - { - startPage(); - List list = userService.selectAllocatedList(user); - return getDataTable(list); - } - - /** - * 查询未分配用户角色列表 - */ - @PreAuthorize("@ss.hasPermi('system:role:list')") - @GetMapping("/authUser/unallocatedList") - public TableDataInfo unallocatedList(SysUser user) - { - startPage(); - List list = userService.selectUnallocatedList(user); - return getDataTable(list); - } - - /** - * 取消授权用户 - */ - @PreAuthorize("@ss.hasPermi('system:role:edit')") - @Log(title = "角色管理", businessType = BusinessType.GRANT) - @PutMapping("/authUser/cancel") - public AjaxResult cancelAuthUser(@RequestBody SysUserRole userRole) - { - return toAjax(roleService.deleteAuthUser(userRole)); - } - - /** - * 批量取消授权用户 - */ - @PreAuthorize("@ss.hasPermi('system:role:edit')") - @Log(title = "角色管理", businessType = BusinessType.GRANT) - @PutMapping("/authUser/cancelAll") - public AjaxResult cancelAuthUserAll(Long roleId, Long[] userIds) - { - return toAjax(roleService.deleteAuthUsers(roleId, userIds)); - } - - /** - * 批量选择用户授权 - */ - @PreAuthorize("@ss.hasPermi('system:role:edit')") - @Log(title = "角色管理", businessType = BusinessType.GRANT) - @PutMapping("/authUser/selectAll") - public AjaxResult selectAuthUserAll(Long roleId, Long[] userIds) - { - roleService.checkRoleDataScope(roleId); - return toAjax(roleService.insertAuthUsers(roleId, userIds)); - } - - /** - * 获取对应角色部门树列表 - */ - @PreAuthorize("@ss.hasPermi('system:role:query')") - @GetMapping(value = "/deptTree/{roleId}") - public AjaxResult deptTree(@PathVariable("roleId") Long roleId) - { - AjaxResult ajax = AjaxResult.success(); - ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId)); - ajax.put("depts", deptService.selectDeptTreeList(new SysDept())); - return ajax; - } -} +package com.ruoyi.web.controller.system; + +import java.util.List; +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; +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.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.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.domain.entity.SysDept; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.framework.web.service.SysPermissionService; +import com.ruoyi.framework.web.service.TokenService; +import com.ruoyi.system.domain.SysUserRole; +import com.ruoyi.system.service.ISysDeptService; +import com.ruoyi.system.service.ISysRoleService; +import com.ruoyi.system.service.ISysUserService; + +/** + * 角色信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/role") +public class SysRoleController extends BaseController +{ + @Autowired + private ISysRoleService roleService; + + @Autowired + private TokenService tokenService; + + @Autowired + private SysPermissionService permissionService; + + @Autowired + private ISysUserService userService; + + @Autowired + private ISysDeptService deptService; + + @PreAuthorize("@ss.hasPermi('system:role:list')") + @GetMapping("/list") + public TableDataInfo list(SysRole role) + { + startPage(); + List list = roleService.selectRoleList(role); + return getDataTable(list); + } + + @Log(title = "角色管理", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:role:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysRole role) + { + List list = roleService.selectRoleList(role); + ExcelUtil util = new ExcelUtil(SysRole.class); + util.exportExcel(response, list, "角色数据"); + } + + /** + * 根据角色编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:role:query')") + @GetMapping(value = "/{roleId}") + public AjaxResult getInfo(@PathVariable Long roleId) + { + roleService.checkRoleDataScope(roleId); + return success(roleService.selectRoleById(roleId)); + } + + /** + * 新增角色 + */ + @PreAuthorize("@ss.hasPermi('system:role:add')") + @Log(title = "角色管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysRole role) + { + if (!roleService.checkRoleNameUnique(role)) + { + return error("新增角色'" + role.getRoleName() + "'失败,角色名称已存在"); + } + else if (!roleService.checkRoleKeyUnique(role)) + { + return error("新增角色'" + role.getRoleName() + "'失败,角色权限已存在"); + } + role.setCreateBy(getUsername()); + return toAjax(roleService.insertRole(role)); + + } + + /** + * 修改保存角色 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysRole role) + { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + if (!roleService.checkRoleNameUnique(role)) + { + return error("修改角色'" + role.getRoleName() + "'失败,角色名称已存在"); + } + else if (!roleService.checkRoleKeyUnique(role)) + { + return error("修改角色'" + role.getRoleName() + "'失败,角色权限已存在"); + } + role.setUpdateBy(getUsername()); + + if (roleService.updateRole(role) > 0) + { + // 更新缓存用户权限 + LoginUser loginUser = getLoginUser(); + if (StringUtils.isNotNull(loginUser.getUser()) && !loginUser.getUser().isAdmin()) + { + loginUser.setUser(userService.selectUserByUserName(loginUser.getUser().getUserName())); + loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser())); + tokenService.setLoginUser(loginUser); + } + return success(); + } + return error("修改角色'" + role.getRoleName() + "'失败,请联系管理员"); + } + + /** + * 修改保存数据权限 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping("/dataScope") + public AjaxResult dataScope(@RequestBody SysRole role) + { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + return toAjax(roleService.authDataScope(role)); + } + + /** + * 状态修改 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public AjaxResult changeStatus(@RequestBody SysRole role) + { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + role.setUpdateBy(getUsername()); + return toAjax(roleService.updateRoleStatus(role)); + } + + /** + * 删除角色 + */ + @PreAuthorize("@ss.hasPermi('system:role:remove')") + @Log(title = "角色管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{roleIds}") + public AjaxResult remove(@PathVariable Long[] roleIds) + { + return toAjax(roleService.deleteRoleByIds(roleIds)); + } + + /** + * 获取角色选择框列表 + */ + @PreAuthorize("@ss.hasPermi('system:role:query')") + @GetMapping("/optionselect") + public AjaxResult optionselect() + { + return success(roleService.selectRoleAll()); + } + + /** + * 查询已分配用户角色列表 + */ + @PreAuthorize("@ss.hasPermi('system:role:list')") + @GetMapping("/authUser/allocatedList") + public TableDataInfo allocatedList(SysUser user) + { + startPage(); + List list = userService.selectAllocatedList(user); + return getDataTable(list); + } + + /** + * 查询未分配用户角色列表 + */ + @PreAuthorize("@ss.hasPermi('system:role:list')") + @GetMapping("/authUser/unallocatedList") + public TableDataInfo unallocatedList(SysUser user) + { + startPage(); + List list = userService.selectUnallocatedList(user); + return getDataTable(list); + } + + /** + * 取消授权用户 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/cancel") + public AjaxResult cancelAuthUser(@RequestBody SysUserRole userRole) + { + return toAjax(roleService.deleteAuthUser(userRole)); + } + + /** + * 批量取消授权用户 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/cancelAll") + public AjaxResult cancelAuthUserAll(Long roleId, Long[] userIds) + { + return toAjax(roleService.deleteAuthUsers(roleId, userIds)); + } + + /** + * 批量选择用户授权 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/selectAll") + public AjaxResult selectAuthUserAll(Long roleId, Long[] userIds) + { + roleService.checkRoleDataScope(roleId); + return toAjax(roleService.insertAuthUsers(roleId, userIds)); + } + + /** + * 获取对应角色部门树列表 + */ + @PreAuthorize("@ss.hasPermi('system:role:query')") + @GetMapping(value = "/deptTree/{roleId}") + public AjaxResult deptTree(@PathVariable("roleId") Long roleId) + { + AjaxResult ajax = AjaxResult.success(); + ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId)); + ajax.put("depts", deptService.selectDeptTreeList(new SysDept())); + return ajax; + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java index 95c7899..209161c 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java @@ -1,262 +1,256 @@ -package com.ruoyi.web.controller.system; - -import java.util.List; -import java.util.stream.Collectors; -import jakarta.servlet.http.HttpServletResponse; -import org.apache.commons.lang3.ArrayUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -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.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.RestController; -import org.springframework.web.multipart.MultipartFile; -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.domain.entity.SysDept; -import com.ruoyi.common.core.domain.entity.SysRole; -import com.ruoyi.common.core.domain.entity.SysUser; -import com.ruoyi.common.core.page.TableDataInfo; -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; -import com.ruoyi.system.service.ISysUserService; - -/** - * 用户信息 - * - * @author ruoyi - */ -@RestController -@RequestMapping("/system/user") -public class SysUserController extends BaseController -{ - @Autowired - private ISysUserService userService; - - @Autowired - private ISysRoleService roleService; - - @Autowired - private ISysDeptService deptService; - - @Autowired - private ISysPostService postService; - - @Autowired - private PasswordTransferCryptoService passwordTransferCryptoService; - - /** - * 获取用户列表 - */ - @PreAuthorize("@ss.hasPermi('system:user:list')") - @GetMapping("/list") - public TableDataInfo list(SysUser user) - { - startPage(); - List list = userService.selectUserList(user); - return getDataTable(list); - } - - @Log(title = "用户管理", businessType = BusinessType.EXPORT) - @PreAuthorize("@ss.hasPermi('system:user:export')") - @PostMapping("/export") - public void export(HttpServletResponse response, SysUser user) - { - List list = userService.selectUserList(user); - ExcelUtil util = new ExcelUtil(SysUser.class); - util.exportExcel(response, list, "用户数据"); - } - - @Log(title = "用户管理", businessType = BusinessType.IMPORT) - @PreAuthorize("@ss.hasPermi('system:user:import')") - @PostMapping("/importData") - public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception - { - ExcelUtil util = new ExcelUtil(SysUser.class); - List userList = util.importExcel(file.getInputStream()); - String operName = getUsername(); - String message = userService.importUser(userList, updateSupport, operName); - return success(message); - } - - @PostMapping("/importTemplate") - public void importTemplate(HttpServletResponse response) - { - ExcelUtil util = new ExcelUtil(SysUser.class); - util.importTemplateExcel(response, "用户数据"); - } - - /** - * 根据用户编号获取详细信息 - */ - @PreAuthorize("@ss.hasPermi('system:user:query')") - @GetMapping(value = { "/", "/{userId}" }) - public AjaxResult getInfo(@PathVariable(value = "userId", required = false) Long userId) - { - AjaxResult ajax = AjaxResult.success(); - if (StringUtils.isNotNull(userId)) - { - userService.checkUserDataScope(userId); - SysUser sysUser = userService.selectUserById(userId); - ajax.put(AjaxResult.DATA_TAG, sysUser); - ajax.put("postIds", postService.selectPostListByUserId(userId)); - ajax.put("roleIds", sysUser.getRoles().stream().map(SysRole::getRoleId).collect(Collectors.toList())); - } - List roles = roleService.selectRoleAll(); - ajax.put("roles", SecurityUtils.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList())); - ajax.put("posts", postService.selectPostAll()); - return ajax; - } - - /** - * 新增用户 - */ - @PreAuthorize("@ss.hasPermi('system:user:add')") - @Log(title = "用户管理", businessType = BusinessType.INSERT) - @PostMapping - public AjaxResult add(@Validated @RequestBody SysUser user) - { - deptService.checkDeptDataScope(user.getDeptId()); - roleService.checkRoleDataScope(user.getRoleIds()); - if (!userService.checkUserNameUnique(user)) - { - return error("新增用户'" + user.getUserName() + "'失败,登录账号已存在"); - } - else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) - { - return error("新增用户'" + user.getUserName() + "'失败,手机号码已存在"); - } - else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) - { - return error("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在"); - } - user.setCreateBy(getUsername()); - user.setPassword(passwordTransferCryptoService.decrypt(user.getPassword())); - user.setPassword(SecurityUtils.encryptPassword(user.getPassword())); - return toAjax(userService.insertUser(user)); - } - - /** - * 修改用户 - */ - @PreAuthorize("@ss.hasPermi('system:user:edit')") - @Log(title = "用户管理", businessType = BusinessType.UPDATE) - @PutMapping - public AjaxResult edit(@Validated @RequestBody SysUser user) - { - userService.checkUserAllowed(user); - userService.checkUserDataScope(user.getUserId()); - deptService.checkDeptDataScope(user.getDeptId()); - roleService.checkRoleDataScope(user.getRoleIds()); - if (!userService.checkUserNameUnique(user)) - { - return error("修改用户'" + user.getUserName() + "'失败,登录账号已存在"); - } - else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) - { - return error("修改用户'" + user.getUserName() + "'失败,手机号码已存在"); - } - else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) - { - return error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在"); - } - user.setUpdateBy(getUsername()); - return toAjax(userService.updateUser(user)); - } - - /** - * 删除用户 - */ - @PreAuthorize("@ss.hasPermi('system:user:remove')") - @Log(title = "用户管理", businessType = BusinessType.DELETE) - @DeleteMapping("/{userIds}") - public AjaxResult remove(@PathVariable Long[] userIds) - { - if (ArrayUtils.contains(userIds, getUserId())) - { - return error("当前用户不能删除"); - } - return toAjax(userService.deleteUserByIds(userIds)); - } - - /** - * 重置密码 - */ - @PreAuthorize("@ss.hasPermi('system:user:resetPwd')") - @Log(title = "用户管理", businessType = BusinessType.UPDATE) - @PutMapping("/resetPwd") - public AjaxResult resetPwd(@RequestBody SysUser user) - { - 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)); - } - - /** - * 状态修改 - */ - @PreAuthorize("@ss.hasPermi('system:user:edit')") - @Log(title = "用户管理", businessType = BusinessType.UPDATE) - @PutMapping("/changeStatus") - public AjaxResult changeStatus(@RequestBody SysUser user) - { - userService.checkUserAllowed(user); - userService.checkUserDataScope(user.getUserId()); - user.setUpdateBy(getUsername()); - return toAjax(userService.updateUserStatus(user)); - } - - /** - * 根据用户编号获取授权角色 - */ - @PreAuthorize("@ss.hasPermi('system:user:query')") - @GetMapping("/authRole/{userId}") - public AjaxResult authRole(@PathVariable("userId") Long userId) - { - AjaxResult ajax = AjaxResult.success(); - SysUser user = userService.selectUserById(userId); - List roles = roleService.selectRolesByUserId(userId); - ajax.put("user", user); - ajax.put("roles", SecurityUtils.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList())); - return ajax; - } - - /** - * 用户授权角色 - */ - @PreAuthorize("@ss.hasPermi('system:user:edit')") - @Log(title = "用户管理", businessType = BusinessType.GRANT) - @PutMapping("/authRole") - public AjaxResult insertAuthRole(Long userId, Long[] roleIds) - { - userService.checkUserDataScope(userId); - roleService.checkRoleDataScope(roleIds); - userService.insertUserAuth(userId, roleIds); - return success(); - } - - /** - * 获取部门树列表 - */ - @PreAuthorize("@ss.hasPermi('system:user:list')") - @GetMapping("/deptTree") - public AjaxResult deptTree(SysDept dept) - { - return success(deptService.selectDeptTreeList(dept)); - } -} +package com.ruoyi.web.controller.system; + +import java.util.List; +import java.util.stream.Collectors; +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; +import org.springframework.validation.annotation.Validated; +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.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.RestController; +import org.springframework.web.multipart.MultipartFile; +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.domain.entity.SysDept; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.page.TableDataInfo; +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.system.service.ISysDeptService; +import com.ruoyi.system.service.ISysPostService; +import com.ruoyi.system.service.ISysRoleService; +import com.ruoyi.system.service.ISysUserService; + +/** + * 用户信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/user") +public class SysUserController extends BaseController +{ + @Autowired + private ISysUserService userService; + + @Autowired + private ISysRoleService roleService; + + @Autowired + private ISysDeptService deptService; + + @Autowired + private ISysPostService postService; + + /** + * 获取用户列表 + */ + @PreAuthorize("@ss.hasPermi('system:user:list')") + @GetMapping("/list") + public TableDataInfo list(SysUser user) + { + startPage(); + List list = userService.selectUserList(user); + return getDataTable(list); + } + + @Log(title = "用户管理", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:user:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysUser user) + { + List list = userService.selectUserList(user); + ExcelUtil util = new ExcelUtil(SysUser.class); + util.exportExcel(response, list, "用户数据"); + } + + @Log(title = "用户管理", businessType = BusinessType.IMPORT) + @PreAuthorize("@ss.hasPermi('system:user:import')") + @PostMapping("/importData") + public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception + { + ExcelUtil util = new ExcelUtil(SysUser.class); + List userList = util.importExcel(file.getInputStream()); + String operName = getUsername(); + String message = userService.importUser(userList, updateSupport, operName); + return success(message); + } + + @PostMapping("/importTemplate") + public void importTemplate(HttpServletResponse response) + { + ExcelUtil util = new ExcelUtil(SysUser.class); + util.importTemplateExcel(response, "用户数据"); + } + + /** + * 根据用户编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:user:query')") + @GetMapping(value = { "/", "/{userId}" }) + public AjaxResult getInfo(@PathVariable(value = "userId", required = false) Long userId) + { + AjaxResult ajax = AjaxResult.success(); + if (StringUtils.isNotNull(userId)) + { + userService.checkUserDataScope(userId); + SysUser sysUser = userService.selectUserById(userId); + ajax.put(AjaxResult.DATA_TAG, sysUser); + ajax.put("postIds", postService.selectPostListByUserId(userId)); + ajax.put("roleIds", sysUser.getRoles().stream().map(SysRole::getRoleId).collect(Collectors.toList())); + } + List roles = roleService.selectRoleAll(); + ajax.put("roles", SecurityUtils.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList())); + ajax.put("posts", postService.selectPostAll()); + return ajax; + } + + /** + * 新增用户 + */ + @PreAuthorize("@ss.hasPermi('system:user:add')") + @Log(title = "用户管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysUser user) + { + deptService.checkDeptDataScope(user.getDeptId()); + roleService.checkRoleDataScope(user.getRoleIds()); + if (!userService.checkUserNameUnique(user)) + { + return error("新增用户'" + user.getUserName() + "'失败,登录账号已存在"); + } + else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) + { + return error("新增用户'" + user.getUserName() + "'失败,手机号码已存在"); + } + else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) + { + return error("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + user.setCreateBy(getUsername()); + user.setPassword(SecurityUtils.encryptPassword(user.getPassword())); + return toAjax(userService.insertUser(user)); + } + + /** + * 修改用户 + */ + @PreAuthorize("@ss.hasPermi('system:user:edit')") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysUser user) + { + userService.checkUserAllowed(user); + userService.checkUserDataScope(user.getUserId()); + deptService.checkDeptDataScope(user.getDeptId()); + roleService.checkRoleDataScope(user.getRoleIds()); + if (!userService.checkUserNameUnique(user)) + { + return error("修改用户'" + user.getUserName() + "'失败,登录账号已存在"); + } + else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) + { + return error("修改用户'" + user.getUserName() + "'失败,手机号码已存在"); + } + else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) + { + return error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + user.setUpdateBy(getUsername()); + return toAjax(userService.updateUser(user)); + } + + /** + * 删除用户 + */ + @PreAuthorize("@ss.hasPermi('system:user:remove')") + @Log(title = "用户管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{userIds}") + public AjaxResult remove(@PathVariable Long[] userIds) + { + if (ArrayUtils.contains(userIds, getUserId())) + { + return error("当前用户不能删除"); + } + return toAjax(userService.deleteUserByIds(userIds)); + } + + /** + * 重置密码 + */ + @PreAuthorize("@ss.hasPermi('system:user:resetPwd')") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping("/resetPwd") + public AjaxResult resetPwd(@RequestBody SysUser user) + { + userService.checkUserAllowed(user); + userService.checkUserDataScope(user.getUserId()); + user.setPassword(SecurityUtils.encryptPassword(user.getPassword())); + user.setUpdateBy(getUsername()); + return toAjax(userService.resetPwd(user)); + } + + /** + * 状态修改 + */ + @PreAuthorize("@ss.hasPermi('system:user:edit')") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public AjaxResult changeStatus(@RequestBody SysUser user) + { + userService.checkUserAllowed(user); + userService.checkUserDataScope(user.getUserId()); + user.setUpdateBy(getUsername()); + return toAjax(userService.updateUserStatus(user)); + } + + /** + * 根据用户编号获取授权角色 + */ + @PreAuthorize("@ss.hasPermi('system:user:query')") + @GetMapping("/authRole/{userId}") + public AjaxResult authRole(@PathVariable("userId") Long userId) + { + AjaxResult ajax = AjaxResult.success(); + SysUser user = userService.selectUserById(userId); + List roles = roleService.selectRolesByUserId(userId); + ajax.put("user", user); + ajax.put("roles", SecurityUtils.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList())); + return ajax; + } + + /** + * 用户授权角色 + */ + @PreAuthorize("@ss.hasPermi('system:user:edit')") + @Log(title = "用户管理", businessType = BusinessType.GRANT) + @PutMapping("/authRole") + public AjaxResult insertAuthRole(Long userId, Long[] roleIds) + { + userService.checkUserDataScope(userId); + roleService.checkRoleDataScope(roleIds); + userService.insertUserAuth(userId, roleIds); + return success(); + } + + /** + * 获取部门树列表 + */ + @PreAuthorize("@ss.hasPermi('system:user:list')") + @GetMapping("/deptTree") + public AjaxResult deptTree(SysDept dept) + { + return success(deptService.selectDeptTreeList(dept)); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/TestController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/TestController.java index 586e1b8..c55f974 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/TestController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/TestController.java @@ -1,175 +1,183 @@ -package com.ruoyi.web.controller.tool; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -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.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.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; - -/** - * swagger 用户测试方法 - * - * @author ruoyi - */ -@Tag(name = "用户信息管理") -@RestController -@RequestMapping("/test/user") -public class TestController extends BaseController -{ - private final static Map users = new LinkedHashMap(); - { - users.put(1, new UserEntity(1, "admin", "admin123", "15888888888")); - users.put(2, new UserEntity(2, "ry", "admin123", "15666666666")); - } - - @Operation(summary = "获取用户列表") - @GetMapping("/list") - public R> userList() - { - List userList = new ArrayList(users.values()); - return R.ok(userList); - } - - @Operation(summary = "获取用户详细") - @GetMapping("/{userId}") - public R getUser(@PathVariable(name = "userId") - Integer userId) - { - if (!users.isEmpty() && users.containsKey(userId)) - { - return R.ok(users.get(userId)); - } - else - { - return R.fail("用户不存在"); - } - } - - @Operation(summary = "新增用户") - @PostMapping("/save") - public R save(UserEntity user) - { - if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId())) - { - return R.fail("用户ID不能为空"); - } - users.put(user.getUserId(), user); - return R.ok(); - } - - @Operation(summary = "更新用户") - @PutMapping("/update") - public R update(@RequestBody - UserEntity user) - { - if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId())) - { - return R.fail("用户ID不能为空"); - } - if (users.isEmpty() || !users.containsKey(user.getUserId())) - { - return R.fail("用户不存在"); - } - users.remove(user.getUserId()); - users.put(user.getUserId(), user); - return R.ok(); - } - - @Operation(summary = "删除用户信息") - @DeleteMapping("/{userId}") - public R delete(@PathVariable(name = "userId") - Integer userId) - { - if (!users.isEmpty() && users.containsKey(userId)) - { - users.remove(userId); - return R.ok(); - } - else - { - return R.fail("用户不存在"); - } - } -} - -@Schema(description = "用户实体") -class UserEntity -{ - @Schema(title = "用户ID") - private Integer userId; - - @Schema(title = "用户名称") - private String username; - - @Schema(title = "用户密码") - private String password; - - @Schema(title = "用户手机") - private String mobile; - - public UserEntity() - { - - } - - public UserEntity(Integer userId, String username, String password, String mobile) - { - this.userId = userId; - this.username = username; - this.password = password; - this.mobile = mobile; - } - - public Integer getUserId() - { - return userId; - } - - public void setUserId(Integer userId) - { - this.userId = userId; - } - - public String getUsername() - { - return username; - } - - public void setUsername(String username) - { - this.username = username; - } - - public String getPassword() - { - return password; - } - - public void setPassword(String password) - { - this.password = password; - } - - public String getMobile() - { - return mobile; - } - - public void setMobile(String mobile) - { - this.mobile = mobile; - } -} +package com.ruoyi.web.controller.tool; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +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.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.RestController; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.StringUtils; +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 + */ +@Api("用户信息管理") +@RestController +@RequestMapping("/test/user") +public class TestController extends BaseController +{ + private final static Map users = new LinkedHashMap(); + { + users.put(1, new UserEntity(1, "admin", "admin123", "15888888888")); + users.put(2, new UserEntity(2, "ry", "admin123", "15666666666")); + } + + @ApiOperation("获取用户列表") + @GetMapping("/list") + public R> userList() + { + List userList = new ArrayList(users.values()); + return R.ok(userList); + } + + @ApiOperation("获取用户详细") + @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class) + @GetMapping("/{userId}") + public R getUser(@PathVariable Integer userId) + { + if (!users.isEmpty() && users.containsKey(userId)) + { + return R.ok(users.get(userId)); + } + else + { + return R.fail("用户不存在"); + } + } + + @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 save(UserEntity user) + { + if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId())) + { + return R.fail("用户ID不能为空"); + } + users.put(user.getUserId(), user); + return R.ok(); + } + + @ApiOperation("更新用户") + @PutMapping("/update") + public R update(@RequestBody UserEntity user) + { + if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId())) + { + return R.fail("用户ID不能为空"); + } + if (users.isEmpty() || !users.containsKey(user.getUserId())) + { + return R.fail("用户不存在"); + } + users.remove(user.getUserId()); + users.put(user.getUserId(), user); + return R.ok(); + } + + @ApiOperation("删除用户信息") + @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class) + @DeleteMapping("/{userId}") + public R delete(@PathVariable Integer userId) + { + if (!users.isEmpty() && users.containsKey(userId)) + { + users.remove(userId); + return R.ok(); + } + else + { + return R.fail("用户不存在"); + } + } +} + +@ApiModel(value = "UserEntity", description = "用户实体") +class UserEntity +{ + @ApiModelProperty("用户ID") + private Integer userId; + + @ApiModelProperty("用户名称") + private String username; + + @ApiModelProperty("用户密码") + private String password; + + @ApiModelProperty("用户手机") + private String mobile; + + public UserEntity() + { + + } + + public UserEntity(Integer userId, String username, String password, String mobile) + { + this.userId = userId; + this.username = username; + this.password = password; + this.mobile = mobile; + } + + public Integer getUserId() + { + return userId; + } + + public void setUserId(Integer userId) + { + this.userId = userId; + } + + public String getUsername() + { + return username; + } + + public void setUsername(String username) + { + this.username = username; + } + + public String getPassword() + { + return password; + } + + public void setPassword(String password) + { + this.password = password; + } + + public String getMobile() + { + return mobile; + } + + public void setMobile(String mobile) + { + this.mobile = mobile; + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java b/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java index 9ab7c24..0b94245 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java @@ -1,64 +1,125 @@ -package com.ruoyi.web.core.config; - -import org.springframework.beans.factory.annotation.Autowired; -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; - -/** - * Swagger2的接口配置 - * - * @author ruoyi - */ -@Configuration -public class SwaggerConfig -{ - /** 系统基础配置 */ - @Autowired - private RuoYiConfig ruoyiConfig; - - /** - * 自定义的 OpenAPI 对象 - */ - @Bean - public OpenAPI customOpenApi() - { - return new OpenAPI().components(new Components() - // 设置认证的请求头 - .addSecuritySchemes("apikey", securityScheme())) - .addSecurityItem(new SecurityRequirement().addList("apikey")) - .info(getApiInfo()); - } - - @Bean - public SecurityScheme securityScheme() - { - return new SecurityScheme() - .type(SecurityScheme.Type.APIKEY) - .name("Authorization") - .in(SecurityScheme.In.HEADER) - .scheme("Bearer"); - } - - /** - * 添加摘要信息 - */ - public Info getApiInfo() - { - return new Info() - // 设置标题 - .title("标题:若依管理系统_接口文档") - // 描述 - .description("描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...") - // 作者信息 - .contact(new Contact().name(ruoyiConfig.getName())) - // 版本 - .version("版本号:" + ruoyiConfig.getVersion()); - } -} +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.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的接口配置 + * + * @author ruoyi + */ +@Configuration +public class SwaggerConfig +{ + /** 系统基础配置 */ + @Autowired + private RuoYiConfig ruoyiConfig; + + /** 是否开启swagger */ + @Value("${swagger.enabled}") + private boolean enabled; + + /** 设置请求的统一前缀 */ + @Value("${swagger.pathMapping}") + private String pathMapping; + + /** + * 创建API + */ + @Bean + public Docket createRestApi() + { + 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); + } + + /** + * 安全模式,这里指定token通过Authorization头请求头传递 + */ + private List securitySchemes() + { + List apiKeyList = new ArrayList(); + apiKeyList.add(new ApiKey("Authorization", "Authorization", In.HEADER.toValue())); + return apiKeyList; + } + + /** + * 安全上下文 + */ + private List securityContexts() + { + List securityContexts = new ArrayList<>(); + securityContexts.add( + SecurityContext.builder() + .securityReferences(defaultAuth()) + .operationSelector(o -> o.requestMappingPattern().matches("/.*")) + .build()); + return securityContexts; + } + + /** + * 默认的安全上引用 + */ + private List defaultAuth() + { + AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); + AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; + authorizationScopes[0] = authorizationScope; + List securityReferences = new ArrayList<>(); + securityReferences.add(new SecurityReference("Authorization", authorizationScopes)); + return securityReferences; + } + + /** + * 添加摘要信息 + */ + private ApiInfo apiInfo() + { + // 用ApiInfoBuilder进行定制 + return new ApiInfoBuilder() + // 设置标题 + .title("标题:若依管理系统_接口文档") + // 描述 + .description("描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...") + // 作者信息 + .contact(new Contact(ruoyiConfig.getName(), null, null)) + // 版本 + .version("版本号:" + ruoyiConfig.getVersion()) + .build(); + } +} diff --git a/ruoyi-admin/src/main/resources/application-druid.yml b/ruoyi-admin/src/main/resources/application-druid.yml new file mode 100644 index 0000000..bcfad3e --- /dev/null +++ b/ruoyi-admin/src/main/resources/application-druid.yml @@ -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 \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index 7ec4567..147359d 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -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: - enabled: true - path: /swagger-ui.html - tags-sorter: alpha +# Swagger配置 +swagger: + # 是否开启swagger + enabled: true + # 请求前缀 + pathMapping: /dev-api # 防盗链配置 referer: diff --git a/ruoyi-admin/src/main/resources/banner.txt b/ruoyi-admin/src/main/resources/banner.txt index 0931cb8..9466259 100644 --- a/ruoyi-admin/src/main/resources/banner.txt +++ b/ruoyi-admin/src/main/resources/banner.txt @@ -1,24 +1,24 @@ -Application Version: ${ruoyi.version} -Spring Boot Version: ${spring-boot.version} -//////////////////////////////////////////////////////////////////// -// _ooOoo_ // -// o8888888o // -// 88" . "88 // -// (| ^_^ |) // -// O\ = /O // -// ____/`---'\____ // -// .' \\| |// `. // -// / \\||| : |||// \ // -// / _||||| -:- |||||- \ // -// | | \\\ - /// | | // -// | \_| ''\---/'' | | // -// \ .-\__ `-` ___/-. / // -// ___`. .' /--.--\ `. . ___ // -// ."" '< `.___\_<|>_/___.' >'"". // -// | | : `- \`.;`\ _ /`;.`/ - ` : | | // -// \ \ `-. \_ __\ /__ _/ .-` / / // -// ========`-.____`-.___\_____/___.-`____.-'======== // -// `=---=' // -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // -// 佛祖保佑 永不宕机 永无BUG // +Application Version: ${ruoyi.version} +Spring Boot Version: ${spring-boot.version} +//////////////////////////////////////////////////////////////////// +// _ooOoo_ // +// o8888888o // +// 88" . "88 // +// (| ^_^ |) // +// O\ = /O // +// ____/`---'\____ // +// .' \\| |// `. // +// / \\||| : |||// \ // +// / _||||| -:- |||||- \ // +// | | \\\ - /// | | // +// | \_| ''\---/'' | | // +// \ .-\__ `-` ___/-. / // +// ___`. .' /--.--\ `. . ___ // +// ."" '< `.___\_<|>_/___.' >'"". // +// | | : `- \`.;`\ _ /`;.`/ - ` : | | // +// \ \ `-. \_ __\ /__ _/ .-` / / // +// ========`-.____`-.___\_____/___.-`____.-'======== // +// `=---=' // +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // +// 佛祖保佑 永不宕机 永无BUG // //////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/i18n/messages.properties b/ruoyi-admin/src/main/resources/i18n/messages.properties index 93de005..c3956b2 100644 --- a/ruoyi-admin/src/main/resources/i18n/messages.properties +++ b/ruoyi-admin/src/main/resources/i18n/messages.properties @@ -1,38 +1,38 @@ -#错误消息 -not.null=* 必须填写 -user.jcaptcha.error=验证码错误 -user.jcaptcha.expire=验证码已失效 -user.not.exists=用户不存在/密码错误 -user.password.not.match=用户不存在/密码错误 -user.password.retry.limit.count=密码输入错误{0}次 -user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟 -user.password.delete=对不起,您的账号已被删除 -user.blocked=用户已封禁,请联系管理员 -role.blocked=角色已封禁,请联系管理员 -login.blocked=很遗憾,访问IP已被列入系统黑名单 -user.logout.success=退出成功 - -length.not.valid=长度必须在{min}到{max}个字符之间 - -user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头 -user.password.not.valid=* 5-50个字符 - -user.email.not.valid=邮箱格式错误 -user.mobile.phone.number.not.valid=手机号格式错误 -user.login.success=登录成功 -user.register.success=注册成功 -user.notfound=请重新登录 -user.forcelogout=管理员强制退出,请重新登录 -user.unknown.error=未知错误,请重新登录 - -##文件上传消息 -upload.exceed.maxSize=上传的文件大小超出限制的文件大小!
允许的文件最大大小是:{0}MB! -upload.filename.exceed.length=上传的文件名最长{0}个字符 - -##权限 -no.permission=您没有数据的权限,请联系管理员添加权限 [{0}] -no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}] -no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}] -no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}] -no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}] -no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}] +#错误消息 +not.null=* 必须填写 +user.jcaptcha.error=验证码错误 +user.jcaptcha.expire=验证码已失效 +user.not.exists=用户不存在/密码错误 +user.password.not.match=用户不存在/密码错误 +user.password.retry.limit.count=密码输入错误{0}次 +user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟 +user.password.delete=对不起,您的账号已被删除 +user.blocked=用户已封禁,请联系管理员 +role.blocked=角色已封禁,请联系管理员 +login.blocked=很遗憾,访问IP已被列入系统黑名单 +user.logout.success=退出成功 + +length.not.valid=长度必须在{min}到{max}个字符之间 + +user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头 +user.password.not.valid=* 5-50个字符 + +user.email.not.valid=邮箱格式错误 +user.mobile.phone.number.not.valid=手机号格式错误 +user.login.success=登录成功 +user.register.success=注册成功 +user.notfound=请重新登录 +user.forcelogout=管理员强制退出,请重新登录 +user.unknown.error=未知错误,请重新登录 + +##文件上传消息 +upload.exceed.maxSize=上传的文件大小超出限制的文件大小!
允许的文件最大大小是:{0}MB! +upload.filename.exceed.length=上传的文件名最长{0}个字符 + +##权限 +no.permission=您没有数据的权限,请联系管理员添加权限 [{0}] +no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}] +no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}] +no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}] +no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}] +no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}] diff --git a/ruoyi-admin/src/main/resources/logback.xml b/ruoyi-admin/src/main/resources/logback.xml index 05ad0da..a82a67d 100644 --- a/ruoyi-admin/src/main/resources/logback.xml +++ b/ruoyi-admin/src/main/resources/logback.xml @@ -1,93 +1,93 @@ - - - + + + - - - - - - - ${log.pattern} - - - - - - ${log.path}/sys-info.log - - - - ${log.path}/sys-info.%d{yyyy-MM-dd}.log - - 60 - - - ${log.pattern} - - - - INFO - - ACCEPT - - DENY - - - - - ${log.path}/sys-error.log - - - - ${log.path}/sys-error.%d{yyyy-MM-dd}.log - - 60 - - - ${log.pattern} - - - - ERROR - - ACCEPT - - DENY - - - - - - ${log.path}/sys-user.log - - - ${log.path}/sys-user.%d{yyyy-MM-dd}.log - - 60 - - - ${log.pattern} - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + ${log.pattern} + + + + + + ${log.path}/sys-info.log + + + + ${log.path}/sys-info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/sys-error.log + + + + ${log.path}/sys-error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + ${log.path}/sys-user.log + + + ${log.path}/sys-user.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ruoyi-admin/src/main/resources/mybatis/mybatis-config.xml b/ruoyi-admin/src/main/resources/mybatis/mybatis-config.xml index ac47c03..4b8c496 100644 --- a/ruoyi-admin/src/main/resources/mybatis/mybatis-config.xml +++ b/ruoyi-admin/src/main/resources/mybatis/mybatis-config.xml @@ -1,20 +1,20 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/ruoyi-admin/src/main/resources/static/swagger-ui/swagger-initializer.js b/ruoyi-admin/src/main/resources/static/swagger-ui/swagger-initializer.js deleted file mode 100644 index 4f1c40c..0000000 --- a/ruoyi-admin/src/main/resources/static/swagger-ui/swagger-initializer.js +++ /dev/null @@ -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 - }); -}; diff --git a/ruoyi-admin/src/test/java/com/ruoyi/web/controller/monitor/CacheControllerTest.java b/ruoyi-admin/src/test/java/com/ruoyi/web/controller/monitor/CacheControllerTest.java deleted file mode 100644 index 74e5f7f..0000000 --- a/ruoyi-admin/src/test/java/com/ruoyi/web/controller/monitor/CacheControllerTest.java +++ /dev/null @@ -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()); - } -} diff --git a/ruoyi-admin/src/test/java/com/ruoyi/web/controller/system/SysLoginControllerPasswordTransferTest.java b/ruoyi-admin/src/test/java/com/ruoyi/web/controller/system/SysLoginControllerPasswordTransferTest.java deleted file mode 100644 index e4e1cdd..0000000 --- a/ruoyi-admin/src/test/java/com/ruoyi/web/controller/system/SysLoginControllerPasswordTransferTest.java +++ /dev/null @@ -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"); - } -} diff --git a/ruoyi-admin/src/test/java/com/ruoyi/web/controller/system/SysProfileControllerPasswordTransferTest.java b/ruoyi-admin/src/test/java/com/ruoyi/web/controller/system/SysProfileControllerPasswordTransferTest.java deleted file mode 100644 index aea25a4..0000000 --- a/ruoyi-admin/src/test/java/com/ruoyi/web/controller/system/SysProfileControllerPasswordTransferTest.java +++ /dev/null @@ -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); - } -} diff --git a/ruoyi-admin/src/test/java/com/ruoyi/web/controller/system/SysRegisterControllerPasswordTransferTest.java b/ruoyi-admin/src/test/java/com/ruoyi/web/controller/system/SysRegisterControllerPasswordTransferTest.java deleted file mode 100644 index 38608ef..0000000 --- a/ruoyi-admin/src/test/java/com/ruoyi/web/controller/system/SysRegisterControllerPasswordTransferTest.java +++ /dev/null @@ -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 captor = ArgumentCaptor.forClass(RegisterBody.class); - verify(registerService).register(captor.capture()); - assertEquals("admin123", captor.getValue().getPassword()); - } -} diff --git a/ruoyi-admin/src/test/java/com/ruoyi/web/controller/system/SysUserControllerPasswordTransferTest.java b/ruoyi-admin/src/test/java/com/ruoyi/web/controller/system/SysUserControllerPasswordTransferTest.java deleted file mode 100644 index 09c160c..0000000 --- a/ruoyi-admin/src/test/java/com/ruoyi/web/controller/system/SysUserControllerPasswordTransferTest.java +++ /dev/null @@ -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 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 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())); - } -} diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml index 3e57758..f072d96 100644 --- a/ruoyi-common/pom.xml +++ b/ruoyi-common/pom.xml @@ -1,130 +1,118 @@ - - - - ruoyi - com.ruoyi - 3.9.1 - - 4.0.0 - - ruoyi-common - - - common通用工具 - - - - - - - org.springframework - spring-context-support - - - - - org.springframework - spring-web - - - - - org.springframework.boot - spring-boot-starter-security - - - - - com.github.pagehelper - pagehelper-spring-boot-starter - - - - - org.springframework.boot - spring-boot-starter-validation - - - - - org.apache.commons - commons-lang3 - - - - - com.fasterxml.jackson.core - jackson-databind - - - - - com.alibaba.fastjson2 - fastjson2 - - - - - commons-io - commons-io - - - - - org.apache.poi - poi-ooxml - - - - - io.jsonwebtoken - jjwt - - - - - javax.xml.bind - jaxb-api - - - - - nl.basjes.parse.useragent - yauaa - - - - - jakarta.servlet - jakarta.servlet-api - - - - org.mybatis - mybatis - 3.5.16 - - - - com.baomidou - mybatis-plus-spring-boot3-starter - 3.5.10 - - - - com.baomidou - mybatis-plus-jsqlparser - 3.5.10 - - - - org.springframework.boot - spring-boot-starter-test - test - - - - - + + + + ruoyi + com.ruoyi + 3.9.2 + + 4.0.0 + + ruoyi-common + + + common通用工具 + + + + + + + org.springframework + spring-context-support + + + + + org.springframework + spring-web + + + + + org.springframework.boot + spring-boot-starter-security + + + + + com.github.pagehelper + pagehelper-spring-boot-starter + + + + + org.springframework.boot + spring-boot-starter-validation + + + + + org.apache.commons + commons-lang3 + + + + + com.fasterxml.jackson.core + jackson-databind + + + + + com.alibaba.fastjson2 + fastjson2 + + + + + commons-io + commons-io + + + + + org.apache.poi + poi-ooxml + + + + + io.jsonwebtoken + jjwt + + + + + javax.xml.bind + jaxb-api + + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + org.apache.commons + commons-pool2 + + + + + nl.basjes.parse.useragent + yauaa + + + + + javax.servlet + javax.servlet-api + + + + + \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Anonymous.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Anonymous.java index 1d6d4f4..8b6966e 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Anonymous.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Anonymous.java @@ -1,19 +1,19 @@ -package com.ruoyi.common.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 匿名访问不鉴权注解 - * - * @author ruoyi - */ -@Target({ ElementType.METHOD, ElementType.TYPE }) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface Anonymous -{ -} +package com.ruoyi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 匿名访问不鉴权注解 + * + * @author ruoyi + */ +@Target({ ElementType.METHOD, ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Anonymous +{ +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java index be49c80..bcb3385 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java @@ -1,33 +1,43 @@ -package com.ruoyi.common.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 数据权限过滤注解 - * - * @author ruoyi - */ -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface DataScope -{ - /** - * 部门表的别名 - */ - public String deptAlias() default ""; - - /** - * 用户表的别名 - */ - public String userAlias() default ""; - - /** - * 权限字符(用于多个角色匹配符合要求的权限)默认根据权限注解@ss获取,多个权限用逗号分隔开来 - */ - public String permission() default ""; -} +package com.ruoyi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 数据权限过滤注解 + * + * @author ruoyi + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DataScope +{ + /** + * 用户表的别名 + */ + public String userAlias() default ""; + + /** + * 部门表的别名 + */ + public String deptAlias() default ""; + + /** + * 用户字段名 + */ + public String userField() default "user_id"; + + /** + * 部门字段名 + */ + public String deptField() default "dept_id"; + + /** + * 权限字符(用于多个角色匹配符合要求的权限)默认根据权限注解@ss获取,多个权限用逗号分隔开来 + */ + public String permission() default ""; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java index 79cd191..6b41ee7 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java @@ -1,28 +1,28 @@ -package com.ruoyi.common.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import com.ruoyi.common.enums.DataSourceType; - -/** - * 自定义多数据源切换注解 - * - * 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准 - * - * @author ruoyi - */ -@Target({ ElementType.METHOD, ElementType.TYPE }) -@Retention(RetentionPolicy.RUNTIME) -@Documented -@Inherited -public @interface DataSource -{ - /** - * 切换数据源名称 - */ - public DataSourceType value() default DataSourceType.MASTER; -} +package com.ruoyi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import com.ruoyi.common.enums.DataSourceType; + +/** + * 自定义多数据源切换注解 + * + * 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准 + * + * @author ruoyi + */ +@Target({ ElementType.METHOD, ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +public @interface DataSource +{ + /** + * 切换数据源名称 + */ + public DataSourceType value() default DataSourceType.MASTER; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java index 57c08b1..cfb5f9c 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java @@ -1,198 +1,197 @@ -package com.ruoyi.common.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.math.BigDecimal; -import org.apache.poi.ss.usermodel.HorizontalAlignment; -import org.apache.poi.ss.usermodel.IndexedColors; -import com.ruoyi.common.utils.poi.ExcelHandlerAdapter; - -/** - * 自定义导出Excel数据注解 - * - * @author ruoyi - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.FIELD) -public @interface Excel -{ - /** - * 导出时在excel中排序 - */ - public int sort() default Integer.MAX_VALUE; - - /** - * 导出到Excel中的名字. - */ - public String name() default ""; - - /** - * 日期格式, 如: yyyy-MM-dd - */ - public String dateFormat() default ""; - - /** - * 如果是字典类型,请设置字典的type值 (如: sys_user_sex) - */ - public String dictType() default ""; - - /** - * 读取内容转表达式 (如: 0=男,1=女,2=未知) - */ - public String readConverterExp() default ""; - - /** - * 分隔符,读取字符串组内容 - */ - public String separator() default ","; - - /** - * BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化) - */ - public int scale() default -1; - - /** - * BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN - */ - @SuppressWarnings("deprecation") - public int roundingMode() default BigDecimal.ROUND_HALF_EVEN; - - /** - * 导出时在excel中每个列的高度 - */ - public double height() default 14; - - /** - * 导出时在excel中每个列的宽度 - */ - public double width() default 16; - - /** - * 文字后缀,如% 90 变成90% - */ - public String suffix() default ""; - - /** - * 当值为空时,字段的默认值 - */ - public String defaultValue() default ""; - - /** - * 提示信息 - */ - public String prompt() default ""; - - /** - * 是否允许内容换行 - */ - public boolean wrapText() default false; - - /** - * 设置只能选择不能输入的列内容. - */ - public String[] combo() default {}; - - /** - * 是否从字典读数据到combo,默认不读取,如读取需要设置dictType注解. - */ - public boolean comboReadDict() default false; - - /** - * 是否需要纵向合并单元格,应对需求:含有list集合单元格) - */ - public boolean needMerge() default false; - - /** - * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写. - */ - public boolean isExport() default true; - - /** - * 另一个类中的属性名称,支持多级获取,以小数点隔开 - */ - public String targetAttr() default ""; - - /** - * 是否自动统计数据,在最后追加一行统计数据总和 - */ - public boolean isStatistics() default false; - - /** - * 导出类型(0数字 1字符串 2图片) - */ - public ColumnType cellType() default ColumnType.STRING; - - /** - * 导出列头背景颜色 - */ - public IndexedColors headerBackgroundColor() default IndexedColors.GREY_50_PERCENT; - - /** - * 导出列头字体颜色 - */ - public IndexedColors headerColor() default IndexedColors.WHITE; - - /** - * 导出单元格背景颜色 - */ - public IndexedColors backgroundColor() default IndexedColors.WHITE; - - /** - * 导出单元格字体颜色 - */ - public IndexedColors color() default IndexedColors.BLACK; - - /** - * 导出字段对齐方式 - */ - public HorizontalAlignment align() default HorizontalAlignment.CENTER; - - /** - * 自定义数据处理器 - */ - public Class handler() default ExcelHandlerAdapter.class; - - /** - * 自定义数据处理器参数 - */ - public String[] args() default {}; - - /** - * 字段类型(0:导出导入;1:仅导出;2:仅导入) - */ - Type type() default Type.ALL; - - public enum Type - { - ALL(0), EXPORT(1), IMPORT(2); - private final int value; - - Type(int value) - { - this.value = value; - } - - public int value() - { - return this.value; - } - } - - public enum ColumnType - { - NUMERIC(0), STRING(1), IMAGE(2), TEXT(3); - private final int value; - - ColumnType(int value) - { - this.value = value; - } - - public int value() - { - return this.value; - } - } +package com.ruoyi.common.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.math.BigDecimal; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.IndexedColors; +import com.ruoyi.common.utils.poi.ExcelHandlerAdapter; + +/** + * 自定义导出Excel数据注解 + * + * @author ruoyi + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Excel +{ + /** + * 导出时在excel中排序 + */ + public int sort() default Integer.MAX_VALUE; + + /** + * 导出到Excel中的名字. + */ + public String name() default ""; + + /** + * 日期格式, 如: yyyy-MM-dd + */ + public String dateFormat() default ""; + + /** + * 如果是字典类型,请设置字典的type值 (如: sys_user_sex) + */ + public String dictType() default ""; + + /** + * 读取内容转表达式 (如: 0=男,1=女,2=未知) + */ + public String readConverterExp() default ""; + + /** + * 分隔符,读取字符串组内容 + */ + public String separator() default ","; + + /** + * BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化) + */ + public int scale() default -1; + + /** + * BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN + */ + public int roundingMode() default BigDecimal.ROUND_HALF_EVEN; + + /** + * 导出时在excel中每个列的高度 + */ + public double height() default 14; + + /** + * 导出时在excel中每个列的宽度 + */ + public double width() default 16; + + /** + * 文字后缀,如% 90 变成90% + */ + public String suffix() default ""; + + /** + * 当值为空时,字段的默认值 + */ + public String defaultValue() default ""; + + /** + * 提示信息 + */ + public String prompt() default ""; + + /** + * 是否允许内容换行 + */ + public boolean wrapText() default false; + + /** + * 设置只能选择不能输入的列内容. + */ + public String[] combo() default {}; + + /** + * 是否从字典读数据到combo,默认不读取,如读取需要设置dictType注解. + */ + public boolean comboReadDict() default false; + + /** + * 是否需要纵向合并单元格,应对需求:含有list集合单元格) + */ + public boolean needMerge() default false; + + /** + * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写. + */ + public boolean isExport() default true; + + /** + * 另一个类中的属性名称,支持多级获取,以小数点隔开 + */ + public String targetAttr() default ""; + + /** + * 是否自动统计数据,在最后追加一行统计数据总和 + */ + public boolean isStatistics() default false; + + /** + * 导出类型(0数字 1字符串 2图片) + */ + public ColumnType cellType() default ColumnType.STRING; + + /** + * 导出列头背景颜色 + */ + public IndexedColors headerBackgroundColor() default IndexedColors.GREY_50_PERCENT; + + /** + * 导出列头字体颜色 + */ + public IndexedColors headerColor() default IndexedColors.WHITE; + + /** + * 导出单元格背景颜色 + */ + public IndexedColors backgroundColor() default IndexedColors.WHITE; + + /** + * 导出单元格字体颜色 + */ + public IndexedColors color() default IndexedColors.BLACK; + + /** + * 导出字段对齐方式 + */ + public HorizontalAlignment align() default HorizontalAlignment.CENTER; + + /** + * 自定义数据处理器 + */ + public Class handler() default ExcelHandlerAdapter.class; + + /** + * 自定义数据处理器参数 + */ + public String[] args() default {}; + + /** + * 字段类型(0:导出导入;1:仅导出;2:仅导入) + */ + Type type() default Type.ALL; + + public enum Type + { + ALL(0), EXPORT(1), IMPORT(2); + private final int value; + + Type(int value) + { + this.value = value; + } + + public int value() + { + return this.value; + } + } + + public enum ColumnType + { + NUMERIC(0), STRING(1), IMAGE(2), TEXT(3); + private final int value; + + ColumnType(int value) + { + this.value = value; + } + + public int value() + { + return this.value; + } + } } \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excels.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excels.java index 1f1cc81..5850723 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excels.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excels.java @@ -1,18 +1,18 @@ -package com.ruoyi.common.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Excel注解集 - * - * @author ruoyi - */ -@Target(ElementType.FIELD) -@Retention(RetentionPolicy.RUNTIME) -public @interface Excels -{ - public Excel[] value(); -} +package com.ruoyi.common.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Excel注解集 + * + * @author ruoyi + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Excels +{ + public Excel[] value(); +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Log.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Log.java index 1eb8e49..5ae846a 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Log.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Log.java @@ -1,51 +1,51 @@ -package com.ruoyi.common.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import com.ruoyi.common.enums.BusinessType; -import com.ruoyi.common.enums.OperatorType; - -/** - * 自定义操作日志记录注解 - * - * @author ruoyi - * - */ -@Target({ ElementType.PARAMETER, ElementType.METHOD }) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface Log -{ - /** - * 模块 - */ - public String title() default ""; - - /** - * 功能 - */ - public BusinessType businessType() default BusinessType.OTHER; - - /** - * 操作人类别 - */ - public OperatorType operatorType() default OperatorType.MANAGE; - - /** - * 是否保存请求的参数 - */ - public boolean isSaveRequestData() default true; - - /** - * 是否保存响应的参数 - */ - public boolean isSaveResponseData() default true; - - /** - * 排除指定的请求参数 - */ - public String[] excludeParamNames() default {}; -} +package com.ruoyi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.enums.OperatorType; + +/** + * 自定义操作日志记录注解 + * + * @author ruoyi + * + */ +@Target({ ElementType.PARAMETER, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Log +{ + /** + * 模块 + */ + public String title() default ""; + + /** + * 功能 + */ + public BusinessType businessType() default BusinessType.OTHER; + + /** + * 操作人类别 + */ + public OperatorType operatorType() default OperatorType.MANAGE; + + /** + * 是否保存请求的参数 + */ + public boolean isSaveRequestData() default true; + + /** + * 是否保存响应的参数 + */ + public boolean isSaveResponseData() default true; + + /** + * 排除指定的请求参数 + */ + public String[] excludeParamNames() default {}; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RateLimiter.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RateLimiter.java index 0f024c7..050ac2d 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RateLimiter.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RateLimiter.java @@ -1,40 +1,40 @@ -package com.ruoyi.common.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import com.ruoyi.common.constant.CacheConstants; -import com.ruoyi.common.enums.LimitType; - -/** - * 限流注解 - * - * @author ruoyi - */ -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface RateLimiter -{ - /** - * 限流key - */ - public String key() default CacheConstants.RATE_LIMIT_KEY; - - /** - * 限流时间,单位秒 - */ - public int time() default 60; - - /** - * 限流次数 - */ - public int count() default 100; - - /** - * 限流类型 - */ - public LimitType limitType() default LimitType.DEFAULT; -} +package com.ruoyi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.enums.LimitType; + +/** + * 限流注解 + * + * @author ruoyi + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RateLimiter +{ + /** + * 限流key + */ + public String key() default CacheConstants.RATE_LIMIT_KEY; + + /** + * 限流时间,单位秒 + */ + public int time() default 60; + + /** + * 限流次数 + */ + public int count() default 100; + + /** + * 限流类型 + */ + public LimitType limitType() default LimitType.DEFAULT; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java index b769748..1e27743 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java @@ -1,31 +1,31 @@ -package com.ruoyi.common.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 自定义注解防止表单重复提交 - * - * @author ruoyi - * - */ -@Inherited -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface RepeatSubmit -{ - /** - * 间隔时间(ms),小于此时间视为重复提交 - */ - public int interval() default 5000; - - /** - * 提示消息 - */ - public String message() default "不允许重复提交,请稍候再试"; -} +package com.ruoyi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义注解防止表单重复提交 + * + * @author ruoyi + * + */ +@Inherited +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RepeatSubmit +{ + /** + * 间隔时间(ms),小于此时间视为重复提交 + */ + public int interval() default 5000; + + /** + * 提示消息 + */ + public String message() default "不允许重复提交,请稍候再试"; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java b/ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java index 29281cf..eac3da1 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java @@ -1,122 +1,122 @@ -package com.ruoyi.common.config; - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.stereotype.Component; - -/** - * 读取项目相关配置 - * - * @author ruoyi - */ -@Component -@ConfigurationProperties(prefix = "ruoyi") -public class RuoYiConfig -{ - /** 项目名称 */ - private String name; - - /** 版本 */ - private String version; - - /** 版权年份 */ - private String copyrightYear; - - /** 上传路径 */ - private static String profile; - - /** 获取地址开关 */ - private static boolean addressEnabled; - - /** 验证码类型 */ - private static String captchaType; - - public String getName() - { - return name; - } - - public void setName(String name) - { - this.name = name; - } - - public String getVersion() - { - return version; - } - - public void setVersion(String version) - { - this.version = version; - } - - public String getCopyrightYear() - { - return copyrightYear; - } - - public void setCopyrightYear(String copyrightYear) - { - this.copyrightYear = copyrightYear; - } - - public static String getProfile() - { - return profile; - } - - public void setProfile(String profile) - { - RuoYiConfig.profile = profile; - } - - public static boolean isAddressEnabled() - { - return addressEnabled; - } - - public void setAddressEnabled(boolean addressEnabled) - { - RuoYiConfig.addressEnabled = addressEnabled; - } - - public static String getCaptchaType() { - return captchaType; - } - - public void setCaptchaType(String captchaType) { - RuoYiConfig.captchaType = captchaType; - } - - /** - * 获取导入上传路径 - */ - public static String getImportPath() - { - return getProfile() + "/import"; - } - - /** - * 获取头像上传路径 - */ - public static String getAvatarPath() - { - return getProfile() + "/avatar"; - } - - /** - * 获取下载路径 - */ - public static String getDownloadPath() - { - return getProfile() + "/download/"; - } - - /** - * 获取上传路径 - */ - public static String getUploadPath() - { - return getProfile() + "/upload"; - } -} +package com.ruoyi.common.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 读取项目相关配置 + * + * @author ruoyi + */ +@Component +@ConfigurationProperties(prefix = "ruoyi") +public class RuoYiConfig +{ + /** 项目名称 */ + private String name; + + /** 版本 */ + private String version; + + /** 版权年份 */ + private String copyrightYear; + + /** 上传路径 */ + private static String profile; + + /** 获取地址开关 */ + private static boolean addressEnabled; + + /** 验证码类型 */ + private static String captchaType; + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getVersion() + { + return version; + } + + public void setVersion(String version) + { + this.version = version; + } + + public String getCopyrightYear() + { + return copyrightYear; + } + + public void setCopyrightYear(String copyrightYear) + { + this.copyrightYear = copyrightYear; + } + + public static String getProfile() + { + return profile; + } + + public void setProfile(String profile) + { + RuoYiConfig.profile = profile; + } + + public static boolean isAddressEnabled() + { + return addressEnabled; + } + + public void setAddressEnabled(boolean addressEnabled) + { + RuoYiConfig.addressEnabled = addressEnabled; + } + + public static String getCaptchaType() { + return captchaType; + } + + public void setCaptchaType(String captchaType) { + RuoYiConfig.captchaType = captchaType; + } + + /** + * 获取导入上传路径 + */ + public static String getImportPath() + { + return getProfile() + "/import"; + } + + /** + * 获取头像上传路径 + */ + public static String getAvatarPath() + { + return getProfile() + "/avatar"; + } + + /** + * 获取下载路径 + */ + public static String getDownloadPath() + { + return getProfile() + "/download/"; + } + + /** + * 获取上传路径 + */ + public static String getUploadPath() + { + return getProfile() + "/upload"; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java index 0080343..c89692c 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java @@ -1,44 +1,44 @@ -package com.ruoyi.common.constant; - -/** - * 缓存的key 常量 - * - * @author ruoyi - */ -public class CacheConstants -{ - /** - * 登录用户 redis key - */ - public static final String LOGIN_TOKEN_KEY = "login_tokens:"; - - /** - * 验证码 redis key - */ - public static final String CAPTCHA_CODE_KEY = "captcha_codes:"; - - /** - * 参数管理 cache key - */ - public static final String SYS_CONFIG_KEY = "sys_config:"; - - /** - * 字典管理 cache key - */ - public static final String SYS_DICT_KEY = "sys_dict:"; - - /** - * 防重提交 redis key - */ - public static final String REPEAT_SUBMIT_KEY = "repeat_submit:"; - - /** - * 限流 redis key - */ - public static final String RATE_LIMIT_KEY = "rate_limit:"; - - /** - * 登录账户密码错误次数 redis key - */ - public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:"; -} +package com.ruoyi.common.constant; + +/** + * 缓存的key 常量 + * + * @author ruoyi + */ +public class CacheConstants +{ + /** + * 登录用户 redis key + */ + public static final String LOGIN_TOKEN_KEY = "login_tokens:"; + + /** + * 验证码 redis key + */ + public static final String CAPTCHA_CODE_KEY = "captcha_codes:"; + + /** + * 参数管理 cache key + */ + public static final String SYS_CONFIG_KEY = "sys_config:"; + + /** + * 字典管理 cache key + */ + public static final String SYS_DICT_KEY = "sys_dict:"; + + /** + * 防重提交 redis key + */ + public static final String REPEAT_SUBMIT_KEY = "repeat_submit:"; + + /** + * 限流 redis key + */ + public static final String RATE_LIMIT_KEY = "rate_limit:"; + + /** + * 登录账户密码错误次数 redis key + */ + public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:"; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java index 7b4f3da..328b01d 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java @@ -1,173 +1,204 @@ -package com.ruoyi.common.constant; - -import java.util.Locale; -import io.jsonwebtoken.Claims; - -/** - * 通用常量信息 - * - * @author ruoyi - */ -public class Constants -{ - /** - * UTF-8 字符集 - */ - public static final String UTF8 = "UTF-8"; - - /** - * GBK 字符集 - */ - public static final String GBK = "GBK"; - - /** - * 系统语言 - */ - public static final Locale DEFAULT_LOCALE = Locale.SIMPLIFIED_CHINESE; - - /** - * www主域 - */ - public static final String WWW = "www."; - - /** - * http请求 - */ - public static final String HTTP = "http://"; - - /** - * https请求 - */ - public static final String HTTPS = "https://"; - - /** - * 通用成功标识 - */ - public static final String SUCCESS = "0"; - - /** - * 通用失败标识 - */ - public static final String FAIL = "1"; - - /** - * 登录成功 - */ - public static final String LOGIN_SUCCESS = "Success"; - - /** - * 注销 - */ - public static final String LOGOUT = "Logout"; - - /** - * 注册 - */ - public static final String REGISTER = "Register"; - - /** - * 登录失败 - */ - public static final String LOGIN_FAIL = "Error"; - - /** - * 所有权限标识 - */ - public static final String ALL_PERMISSION = "*:*:*"; - - /** - * 管理员角色权限标识 - */ - public static final String SUPER_ADMIN = "admin"; - - /** - * 角色权限分隔符 - */ - public static final String ROLE_DELIMITER = ","; - - /** - * 权限标识分隔符 - */ - public static final String PERMISSION_DELIMITER = ","; - - /** - * 验证码有效期(分钟) - */ - public static final Integer CAPTCHA_EXPIRATION = 2; - - /** - * 令牌 - */ - public static final String TOKEN = "token"; - - /** - * 令牌前缀 - */ - public static final String TOKEN_PREFIX = "Bearer "; - - /** - * 令牌前缀 - */ - public static final String LOGIN_USER_KEY = "login_user_key"; - - /** - * 用户ID - */ - public static final String JWT_USERID = "userid"; - - /** - * 用户名称 - */ - public static final String JWT_USERNAME = Claims.SUBJECT; - - /** - * 用户头像 - */ - public static final String JWT_AVATAR = "avatar"; - - /** - * 创建时间 - */ - public static final String JWT_CREATED = "created"; - - /** - * 用户权限 - */ - public static final String JWT_AUTHORITIES = "authorities"; - - /** - * 资源映射路径 前缀 - */ - public static final String RESOURCE_PREFIX = "/profile"; - - /** - * RMI 远程方法调用 - */ - public static final String LOOKUP_RMI = "rmi:"; - - /** - * LDAP 远程方法调用 - */ - public static final String LOOKUP_LDAP = "ldap:"; - - /** - * LDAPS 远程方法调用 - */ - public static final String LOOKUP_LDAPS = "ldaps:"; - - /** - * 自动识别json对象白名单配置(仅允许解析的包名,范围越小越安全) - */ - public static final String[] JSON_WHITELIST_STR = { "com.ruoyi" }; - - /** - * 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加) - */ - public static final String[] JOB_WHITELIST_STR = { "com.ruoyi.quartz.task" }; - - /** - * 定时任务违规的字符 - */ - 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" }; -} +package com.ruoyi.common.constant; + +import java.util.Locale; +import io.jsonwebtoken.Claims; + +/** + * 通用常量信息 + * + * @author ruoyi + */ +public class Constants +{ + /** + * UTF-8 字符集 + */ + public static final String UTF8 = "UTF-8"; + + /** + * GBK 字符集 + */ + public static final String GBK = "GBK"; + + /** + * 系统语言 + */ + public static final Locale DEFAULT_LOCALE = Locale.SIMPLIFIED_CHINESE; + + /** + * www主域 + */ + public static final String WWW = "www."; + + /** + * http请求 + */ + public static final String HTTP = "http://"; + + /** + * https请求 + */ + public static final String HTTPS = "https://"; + + /** + * 通用成功标识 + */ + public static final String SUCCESS = "0"; + + /** + * 通用失败标识 + */ + public static final String FAIL = "1"; + + /** + * 登录成功 + */ + public static final String LOGIN_SUCCESS = "Success"; + + /** + * 注销 + */ + public static final String LOGOUT = "Logout"; + + /** + * 注册 + */ + public static final String REGISTER = "Register"; + + /** + * 登录失败 + */ + public static final String LOGIN_FAIL = "Error"; + + /** + * 所有权限标识 + */ + public static final String ALL_PERMISSION = "*:*:*"; + + /** + * 管理员角色权限标识 + */ + public static final String SUPER_ADMIN = "admin"; + + /** + * 角色权限分隔符 + */ + public static final String ROLE_DELIMITER = ","; + + /** + * 权限标识分隔符 + */ + public static final String PERMISSION_DELIMITER = ","; + + /** + * 验证码有效期(分钟) + */ + public static final Integer CAPTCHA_EXPIRATION = 2; + + /** + * 令牌 + */ + public static final String TOKEN = "token"; + + /** + * 令牌前缀 + */ + public static final String TOKEN_PREFIX = "Bearer "; + + /** + * 令牌前缀 + */ + public static final String LOGIN_USER_KEY = "login_user_key"; + + /** + * 用户ID + */ + public static final String JWT_USERID = "userid"; + + /** + * 用户名称 + */ + public static final String JWT_USERNAME = Claims.SUBJECT; + + /** + * 用户头像 + */ + public static final String JWT_AVATAR = "avatar"; + + /** + * 创建时间 + */ + public static final String JWT_CREATED = "created"; + + /** + * 用户权限 + */ + public static final String JWT_AUTHORITIES = "authorities"; + + /** + * 资源映射路径 前缀 + */ + public static final String RESOURCE_PREFIX = "/profile"; + + /** + * RMI 远程方法调用 + */ + public static final String LOOKUP_RMI = "rmi:"; + + /** + * LDAP 远程方法调用 + */ + public static final String LOOKUP_LDAP = "ldap:"; + + /** + * LDAPS 远程方法调用 + */ + public static final String LOOKUP_LDAPS = "ldaps:"; + + /** + * 自动识别json对象白名单配置(仅允许解析的包名,范围越小越安全) + */ + public static final String[] JSON_WHITELIST_STR = { "com.ruoyi" }; + + /** + * 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加) + */ + public static final String[] JOB_WHITELIST_STR = { "com.ruoyi.quartz.task" }; + + /** + * 定时任务违规的字符 + */ + 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"; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/GenConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/GenConstants.java index 7d899d4..f2b0e97 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/constant/GenConstants.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/GenConstants.java @@ -1,117 +1,120 @@ -package com.ruoyi.common.constant; - -/** - * 代码生成通用常量 - * - * @author ruoyi - */ -public class GenConstants -{ - /** 单表(增删改查) */ - public static final String TPL_CRUD = "crud"; - - /** 树表(增删改查) */ - public static final String TPL_TREE = "tree"; - - /** 主子表(增删改查) */ - public static final String TPL_SUB = "sub"; - - /** 树编码字段 */ - public static final String TREE_CODE = "treeCode"; - - /** 树父编码字段 */ - public static final String TREE_PARENT_CODE = "treeParentCode"; - - /** 树名称字段 */ - public static final String TREE_NAME = "treeName"; - - /** 上级菜单ID字段 */ - public static final String PARENT_MENU_ID = "parentMenuId"; - - /** 上级菜单名称字段 */ - public static final String PARENT_MENU_NAME = "parentMenuName"; - - /** 数据库字符串类型 */ - public static final String[] COLUMNTYPE_STR = { "char", "varchar", "nvarchar", "varchar2" }; - - /** 数据库文本类型 */ - public static final String[] COLUMNTYPE_TEXT = { "tinytext", "text", "mediumtext", "longtext" }; - - /** 数据库时间类型 */ - public static final String[] COLUMNTYPE_TIME = { "datetime", "time", "date", "timestamp" }; - - /** 数据库数字类型 */ - public static final String[] COLUMNTYPE_NUMBER = { "tinyint", "smallint", "mediumint", "int", "number", "integer", - "bit", "bigint", "float", "double", "decimal" }; - - /** 页面不需要编辑字段 */ - public static final String[] COLUMNNAME_NOT_EDIT = { "id", "create_by", "create_time", "del_flag" }; - - /** 页面不需要显示的列表字段 */ - public static final String[] COLUMNNAME_NOT_LIST = { "id", "create_by", "create_time", "del_flag", "update_by", - "update_time" }; - - /** 页面不需要查询字段 */ - public static final String[] COLUMNNAME_NOT_QUERY = { "id", "create_by", "create_time", "del_flag", "update_by", - "update_time", "remark" }; - - /** Entity基类字段 */ - public static final String[] BASE_ENTITY = { "createBy", "createTime", "updateBy", "updateTime", "remark" }; - - /** Tree基类字段 */ - public static final String[] TREE_ENTITY = { "parentName", "parentId", "orderNum", "ancestors", "children" }; - - /** 文本框 */ - public static final String HTML_INPUT = "input"; - - /** 文本域 */ - public static final String HTML_TEXTAREA = "textarea"; - - /** 下拉框 */ - public static final String HTML_SELECT = "select"; - - /** 单选框 */ - public static final String HTML_RADIO = "radio"; - - /** 复选框 */ - public static final String HTML_CHECKBOX = "checkbox"; - - /** 日期控件 */ - public static final String HTML_DATETIME = "datetime"; - - /** 图片上传控件 */ - public static final String HTML_IMAGE_UPLOAD = "imageUpload"; - - /** 文件上传控件 */ - public static final String HTML_FILE_UPLOAD = "fileUpload"; - - /** 富文本控件 */ - public static final String HTML_EDITOR = "editor"; - - /** 字符串类型 */ - public static final String TYPE_STRING = "String"; - - /** 整型 */ - public static final String TYPE_INTEGER = "Integer"; - - /** 长整型 */ - public static final String TYPE_LONG = "Long"; - - /** 浮点型 */ - public static final String TYPE_DOUBLE = "Double"; - - /** 高精度计算类型 */ - public static final String TYPE_BIGDECIMAL = "BigDecimal"; - - /** 时间类型 */ - public static final String TYPE_DATE = "Date"; - - /** 模糊查询 */ - public static final String QUERY_LIKE = "LIKE"; - - /** 相等查询 */ - public static final String QUERY_EQ = "EQ"; - - /** 需要 */ - public static final String REQUIRE = "1"; -} +package com.ruoyi.common.constant; + +/** + * 代码生成通用常量 + * + * @author ruoyi + */ +public class GenConstants +{ + /** 单表(增删改查) */ + public static final String TPL_CRUD = "crud"; + + /** 树表(增删改查) */ + public static final String TPL_TREE = "tree"; + + /** 主子表(增删改查) */ + public static final String TPL_SUB = "sub"; + + /** 树编码字段 */ + public static final String TREE_CODE = "treeCode"; + + /** 树父编码字段 */ + public static final String TREE_PARENT_CODE = "treeParentCode"; + + /** 树名称字段 */ + public static final String TREE_NAME = "treeName"; + + /** 上级菜单ID字段 */ + public static final String PARENT_MENU_ID = "parentMenuId"; + + /** 上级菜单名称字段 */ + 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" }; + + /** 数据库文本类型 */ + public static final String[] COLUMNTYPE_TEXT = { "tinytext", "text", "mediumtext", "longtext" }; + + /** 数据库时间类型 */ + public static final String[] COLUMNTYPE_TIME = { "datetime", "time", "date", "timestamp" }; + + /** 数据库数字类型 */ + public static final String[] COLUMNTYPE_NUMBER = { "tinyint", "smallint", "mediumint", "int", "number", "integer", + "bit", "bigint", "float", "double", "decimal" }; + + /** 页面不需要编辑字段 */ + public static final String[] COLUMNNAME_NOT_EDIT = { "id", "create_by", "create_time", "del_flag" }; + + /** 页面不需要显示的列表字段 */ + public static final String[] COLUMNNAME_NOT_LIST = { "id", "create_by", "create_time", "del_flag", "update_by", + "update_time" }; + + /** 页面不需要查询字段 */ + public static final String[] COLUMNNAME_NOT_QUERY = { "id", "create_by", "create_time", "del_flag", "update_by", + "update_time", "remark" }; + + /** Entity基类字段 */ + public static final String[] BASE_ENTITY = { "createBy", "createTime", "updateBy", "updateTime", "remark" }; + + /** Tree基类字段 */ + public static final String[] TREE_ENTITY = { "parentName", "parentId", "orderNum", "ancestors", "children" }; + + /** 文本框 */ + public static final String HTML_INPUT = "input"; + + /** 文本域 */ + public static final String HTML_TEXTAREA = "textarea"; + + /** 下拉框 */ + public static final String HTML_SELECT = "select"; + + /** 单选框 */ + public static final String HTML_RADIO = "radio"; + + /** 复选框 */ + public static final String HTML_CHECKBOX = "checkbox"; + + /** 日期控件 */ + public static final String HTML_DATETIME = "datetime"; + + /** 图片上传控件 */ + public static final String HTML_IMAGE_UPLOAD = "imageUpload"; + + /** 文件上传控件 */ + public static final String HTML_FILE_UPLOAD = "fileUpload"; + + /** 富文本控件 */ + public static final String HTML_EDITOR = "editor"; + + /** 字符串类型 */ + public static final String TYPE_STRING = "String"; + + /** 整型 */ + public static final String TYPE_INTEGER = "Integer"; + + /** 长整型 */ + public static final String TYPE_LONG = "Long"; + + /** 浮点型 */ + public static final String TYPE_DOUBLE = "Double"; + + /** 高精度计算类型 */ + public static final String TYPE_BIGDECIMAL = "BigDecimal"; + + /** 时间类型 */ + public static final String TYPE_DATE = "Date"; + + /** 模糊查询 */ + public static final String QUERY_LIKE = "LIKE"; + + /** 相等查询 */ + public static final String QUERY_EQ = "EQ"; + + /** 需要 */ + public static final String REQUIRE = "1"; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/HttpStatus.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/HttpStatus.java index a983c77..db839c5 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/constant/HttpStatus.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/HttpStatus.java @@ -1,94 +1,94 @@ -package com.ruoyi.common.constant; - -/** - * 返回状态码 - * - * @author ruoyi - */ -public class HttpStatus -{ - /** - * 操作成功 - */ - public static final int SUCCESS = 200; - - /** - * 对象创建成功 - */ - public static final int CREATED = 201; - - /** - * 请求已经被接受 - */ - public static final int ACCEPTED = 202; - - /** - * 操作已经执行成功,但是没有返回数据 - */ - public static final int NO_CONTENT = 204; - - /** - * 资源已被移除 - */ - public static final int MOVED_PERM = 301; - - /** - * 重定向 - */ - public static final int SEE_OTHER = 303; - - /** - * 资源没有被修改 - */ - public static final int NOT_MODIFIED = 304; - - /** - * 参数列表错误(缺少,格式不匹配) - */ - public static final int BAD_REQUEST = 400; - - /** - * 未授权 - */ - public static final int UNAUTHORIZED = 401; - - /** - * 访问受限,授权过期 - */ - public static final int FORBIDDEN = 403; - - /** - * 资源,服务未找到 - */ - public static final int NOT_FOUND = 404; - - /** - * 不允许的http方法 - */ - public static final int BAD_METHOD = 405; - - /** - * 资源冲突,或者资源被锁 - */ - public static final int CONFLICT = 409; - - /** - * 不支持的数据,媒体类型 - */ - public static final int UNSUPPORTED_TYPE = 415; - - /** - * 系统内部错误 - */ - public static final int ERROR = 500; - - /** - * 接口未实现 - */ - public static final int NOT_IMPLEMENTED = 501; - - /** - * 系统警告消息 - */ - public static final int WARN = 601; -} +package com.ruoyi.common.constant; + +/** + * 返回状态码 + * + * @author ruoyi + */ +public class HttpStatus +{ + /** + * 操作成功 + */ + public static final int SUCCESS = 200; + + /** + * 对象创建成功 + */ + public static final int CREATED = 201; + + /** + * 请求已经被接受 + */ + public static final int ACCEPTED = 202; + + /** + * 操作已经执行成功,但是没有返回数据 + */ + public static final int NO_CONTENT = 204; + + /** + * 资源已被移除 + */ + public static final int MOVED_PERM = 301; + + /** + * 重定向 + */ + public static final int SEE_OTHER = 303; + + /** + * 资源没有被修改 + */ + public static final int NOT_MODIFIED = 304; + + /** + * 参数列表错误(缺少,格式不匹配) + */ + public static final int BAD_REQUEST = 400; + + /** + * 未授权 + */ + public static final int UNAUTHORIZED = 401; + + /** + * 访问受限,授权过期 + */ + public static final int FORBIDDEN = 403; + + /** + * 资源,服务未找到 + */ + public static final int NOT_FOUND = 404; + + /** + * 不允许的http方法 + */ + public static final int BAD_METHOD = 405; + + /** + * 资源冲突,或者资源被锁 + */ + public static final int CONFLICT = 409; + + /** + * 不支持的数据,媒体类型 + */ + public static final int UNSUPPORTED_TYPE = 415; + + /** + * 系统内部错误 + */ + public static final int ERROR = 500; + + /** + * 接口未实现 + */ + public static final int NOT_IMPLEMENTED = 501; + + /** + * 系统警告消息 + */ + public static final int WARN = 601; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java index 62ad815..8318b8f 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java @@ -1,50 +1,50 @@ -package com.ruoyi.common.constant; - -/** - * 任务调度通用常量 - * - * @author ruoyi - */ -public class ScheduleConstants -{ - public static final String TASK_CLASS_NAME = "TASK_CLASS_NAME"; - - /** 执行目标key */ - public static final String TASK_PROPERTIES = "TASK_PROPERTIES"; - - /** 默认 */ - public static final String MISFIRE_DEFAULT = "0"; - - /** 立即触发执行 */ - public static final String MISFIRE_IGNORE_MISFIRES = "1"; - - /** 触发一次执行 */ - public static final String MISFIRE_FIRE_AND_PROCEED = "2"; - - /** 不触发立即执行 */ - public static final String MISFIRE_DO_NOTHING = "3"; - - public enum Status - { - /** - * 正常 - */ - NORMAL("0"), - /** - * 暂停 - */ - PAUSE("1"); - - private String value; - - private Status(String value) - { - this.value = value; - } - - public String getValue() - { - return value; - } - } -} +package com.ruoyi.common.constant; + +/** + * 任务调度通用常量 + * + * @author ruoyi + */ +public class ScheduleConstants +{ + public static final String TASK_CLASS_NAME = "TASK_CLASS_NAME"; + + /** 执行目标key */ + public static final String TASK_PROPERTIES = "TASK_PROPERTIES"; + + /** 默认 */ + public static final String MISFIRE_DEFAULT = "0"; + + /** 立即触发执行 */ + public static final String MISFIRE_IGNORE_MISFIRES = "1"; + + /** 触发一次执行 */ + public static final String MISFIRE_FIRE_AND_PROCEED = "2"; + + /** 不触发立即执行 */ + public static final String MISFIRE_DO_NOTHING = "3"; + + public enum Status + { + /** + * 正常 + */ + NORMAL("0"), + /** + * 暂停 + */ + PAUSE("1"); + + private String value; + + private Status(String value) + { + this.value = value; + } + + public String getValue() + { + return value; + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java index 8dc7faa..f2b5ab5 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java @@ -1,81 +1,81 @@ -package com.ruoyi.common.constant; - -/** - * 用户常量信息 - * - * @author ruoyi - */ -public class UserConstants -{ - /** - * 平台内系统用户的唯一标志 - */ - public static final String SYS_USER = "SYS_USER"; - - /** 正常状态 */ - public static final String NORMAL = "0"; - - /** 异常状态 */ - public static final String EXCEPTION = "1"; - - /** 用户封禁状态 */ - public static final String USER_DISABLE = "1"; - - /** 角色正常状态 */ - public static final String ROLE_NORMAL = "0"; - - /** 角色封禁状态 */ - public static final String ROLE_DISABLE = "1"; - - /** 部门正常状态 */ - public static final String DEPT_NORMAL = "0"; - - /** 部门停用状态 */ - public static final String DEPT_DISABLE = "1"; - - /** 字典正常状态 */ - public static final String DICT_NORMAL = "0"; - - /** 是否为系统默认(是) */ - public static final String YES = "Y"; - - /** 是否菜单外链(是) */ - public static final String YES_FRAME = "0"; - - /** 是否菜单外链(否) */ - public static final String NO_FRAME = "1"; - - /** 菜单类型(目录) */ - public static final String TYPE_DIR = "M"; - - /** 菜单类型(菜单) */ - public static final String TYPE_MENU = "C"; - - /** 菜单类型(按钮) */ - public static final String TYPE_BUTTON = "F"; - - /** Layout组件标识 */ - public final static String LAYOUT = "Layout"; - - /** ParentView组件标识 */ - public final static String PARENT_VIEW = "ParentView"; - - /** InnerLink组件标识 */ - public final static String INNER_LINK = "InnerLink"; - - /** 校验是否唯一的返回标识 */ - public final static boolean UNIQUE = true; - public final static boolean NOT_UNIQUE = false; - - /** - * 用户名长度限制 - */ - public static final int USERNAME_MIN_LENGTH = 2; - public static final int USERNAME_MAX_LENGTH = 20; - - /** - * 密码长度限制 - */ - public static final int PASSWORD_MIN_LENGTH = 5; - public static final int PASSWORD_MAX_LENGTH = 20; -} +package com.ruoyi.common.constant; + +/** + * 用户常量信息 + * + * @author ruoyi + */ +public class UserConstants +{ + /** + * 平台内系统用户的唯一标志 + */ + public static final String SYS_USER = "SYS_USER"; + + /** 正常状态 */ + public static final String NORMAL = "0"; + + /** 异常状态 */ + public static final String EXCEPTION = "1"; + + /** 用户封禁状态 */ + public static final String USER_DISABLE = "1"; + + /** 角色正常状态 */ + public static final String ROLE_NORMAL = "0"; + + /** 角色封禁状态 */ + public static final String ROLE_DISABLE = "1"; + + /** 部门正常状态 */ + public static final String DEPT_NORMAL = "0"; + + /** 部门停用状态 */ + public static final String DEPT_DISABLE = "1"; + + /** 字典正常状态 */ + public static final String DICT_NORMAL = "0"; + + /** 是否为系统默认(是) */ + public static final String YES = "Y"; + + /** 是否菜单外链(是) */ + public static final String YES_FRAME = "0"; + + /** 是否菜单外链(否) */ + public static final String NO_FRAME = "1"; + + /** 菜单类型(目录) */ + public static final String TYPE_DIR = "M"; + + /** 菜单类型(菜单) */ + public static final String TYPE_MENU = "C"; + + /** 菜单类型(按钮) */ + public static final String TYPE_BUTTON = "F"; + + /** Layout组件标识 */ + public final static String LAYOUT = "Layout"; + + /** ParentView组件标识 */ + public final static String PARENT_VIEW = "ParentView"; + + /** InnerLink组件标识 */ + public final static String INNER_LINK = "InnerLink"; + + /** 校验是否唯一的返回标识 */ + public final static boolean UNIQUE = true; + public final static boolean NOT_UNIQUE = false; + + /** + * 用户名长度限制 + */ + public static final int USERNAME_MIN_LENGTH = 2; + public static final int USERNAME_MAX_LENGTH = 20; + + /** + * 密码长度限制 + */ + public static final int PASSWORD_MIN_LENGTH = 5; + public static final int PASSWORD_MAX_LENGTH = 20; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/cache/InMemoryCacheEntry.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/cache/InMemoryCacheEntry.java index 028a95f..9a8dca4 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/cache/InMemoryCacheEntry.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/cache/InMemoryCacheEntry.java @@ -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; } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/cache/InMemoryCacheStats.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/cache/InMemoryCacheStats.java index 7b35453..0c1c3e5 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/cache/InMemoryCacheStats.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/cache/InMemoryCacheStats.java @@ -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; + } } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/cache/InMemoryCacheStore.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/cache/InMemoryCacheStore.java index e7a66d2..db57baa 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/cache/InMemoryCacheStore.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/cache/InMemoryCacheStore.java @@ -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 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 matchedKeys = new TreeSet<>(); - entries.forEach((key, value) -> { - if (matches(pattern, key)) + for (Map.Entry entry : entries.entrySet()) + { + if (matches(pattern, entry.getKey())) { - matchedKeys.add(key); + 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 Map getMap(String key) { Map value = get(key); @@ -178,6 +198,7 @@ public class InMemoryCacheStore return true; } + @SuppressWarnings("unchecked") public Set getSet(String key) { Set value = get(key); @@ -189,15 +210,28 @@ public class InMemoryCacheStore set(key, new HashSet<>(dataSet)); } - public java.util.List getList(String key) + @SuppressWarnings("unchecked") + public List getList(String key) { - java.util.List value = get(key); - return value == null ? null : new java.util.ArrayList<>(value); + List value = get(key); + return value == null ? null : new ArrayList<>(value); } - public void putList(String key, java.util.List dataList) + public void putList(String key, List 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 entry : entries.entrySet()) + { + if (entry.getValue().isExpired(now)) { - removeExpiredEntry(key, entry); + 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); - } } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java index a685e06..c131bc4 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java @@ -1,202 +1,202 @@ -package com.ruoyi.common.core.controller; - -import java.beans.PropertyEditorSupport; -import java.util.Date; -import java.util.List; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.web.bind.WebDataBinder; -import org.springframework.web.bind.annotation.InitBinder; -import com.github.pagehelper.PageHelper; -import com.github.pagehelper.PageInfo; -import com.ruoyi.common.constant.HttpStatus; -import com.ruoyi.common.core.domain.AjaxResult; -import com.ruoyi.common.core.domain.model.LoginUser; -import com.ruoyi.common.core.page.PageDomain; -import com.ruoyi.common.core.page.TableDataInfo; -import com.ruoyi.common.core.page.TableSupport; -import com.ruoyi.common.utils.DateUtils; -import com.ruoyi.common.utils.PageUtils; -import com.ruoyi.common.utils.SecurityUtils; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.common.utils.sql.SqlUtil; - -/** - * web层通用数据处理 - * - * @author ruoyi - */ -public class BaseController -{ - protected final Logger logger = LoggerFactory.getLogger(this.getClass()); - - /** - * 将前台传递过来的日期格式的字符串,自动转化为Date类型 - */ - @InitBinder - public void initBinder(WebDataBinder binder) - { - // Date 类型转换 - binder.registerCustomEditor(Date.class, new PropertyEditorSupport() - { - @Override - public void setAsText(String text) - { - setValue(DateUtils.parseDate(text)); - } - }); - } - - /** - * 设置请求分页数据 - */ - protected void startPage() - { - PageUtils.startPage(); - } - - /** - * 设置请求排序数据 - */ - protected void startOrderBy() - { - PageDomain pageDomain = TableSupport.buildPageRequest(); - if (StringUtils.isNotEmpty(pageDomain.getOrderBy())) - { - String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy()); - PageHelper.orderBy(orderBy); - } - } - - /** - * 清理分页的线程变量 - */ - protected void clearPage() - { - PageUtils.clearPage(); - } - - /** - * 响应请求分页数据 - */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - protected TableDataInfo getDataTable(List list) - { - TableDataInfo rspData = new TableDataInfo(); - rspData.setCode(HttpStatus.SUCCESS); - rspData.setMsg("查询成功"); - rspData.setRows(list); - rspData.setTotal(new PageInfo(list).getTotal()); - return rspData; - } - - /** - * 返回成功 - */ - public AjaxResult success() - { - return AjaxResult.success(); - } - - /** - * 返回失败消息 - */ - public AjaxResult error() - { - return AjaxResult.error(); - } - - /** - * 返回成功消息 - */ - public AjaxResult success(String message) - { - return AjaxResult.success(message); - } - - /** - * 返回成功消息 - */ - public AjaxResult success(Object data) - { - return AjaxResult.success(data); - } - - /** - * 返回失败消息 - */ - public AjaxResult error(String message) - { - return AjaxResult.error(message); - } - - /** - * 返回警告消息 - */ - public AjaxResult warn(String message) - { - return AjaxResult.warn(message); - } - - /** - * 响应返回结果 - * - * @param rows 影响行数 - * @return 操作结果 - */ - protected AjaxResult toAjax(int rows) - { - return rows > 0 ? AjaxResult.success() : AjaxResult.error(); - } - - /** - * 响应返回结果 - * - * @param result 结果 - * @return 操作结果 - */ - protected AjaxResult toAjax(boolean result) - { - return result ? success() : error(); - } - - /** - * 页面跳转 - */ - public String redirect(String url) - { - return StringUtils.format("redirect:{}", url); - } - - /** - * 获取用户缓存信息 - */ - public LoginUser getLoginUser() - { - return SecurityUtils.getLoginUser(); - } - - /** - * 获取登录用户id - */ - public Long getUserId() - { - return getLoginUser().getUserId(); - } - - /** - * 获取登录部门id - */ - public Long getDeptId() - { - return getLoginUser().getDeptId(); - } - - /** - * 获取登录用户名 - */ - public String getUsername() - { - return getLoginUser().getUsername(); - } -} +package com.ruoyi.common.core.controller; + +import java.beans.PropertyEditorSupport; +import java.util.Date; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.page.PageDomain; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.core.page.TableSupport; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.PageUtils; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.sql.SqlUtil; + +/** + * web层通用数据处理 + * + * @author ruoyi + */ +public class BaseController +{ + protected final Logger logger = LoggerFactory.getLogger(this.getClass()); + + /** + * 将前台传递过来的日期格式的字符串,自动转化为Date类型 + */ + @InitBinder + public void initBinder(WebDataBinder binder) + { + // Date 类型转换 + binder.registerCustomEditor(Date.class, new PropertyEditorSupport() + { + @Override + public void setAsText(String text) + { + setValue(DateUtils.parseDate(text)); + } + }); + } + + /** + * 设置请求分页数据 + */ + protected void startPage() + { + PageUtils.startPage(); + } + + /** + * 设置请求排序数据 + */ + protected void startOrderBy() + { + PageDomain pageDomain = TableSupport.buildPageRequest(); + if (StringUtils.isNotEmpty(pageDomain.getOrderBy())) + { + String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy()); + PageHelper.orderBy(orderBy); + } + } + + /** + * 清理分页的线程变量 + */ + protected void clearPage() + { + PageUtils.clearPage(); + } + + /** + * 响应请求分页数据 + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + protected TableDataInfo getDataTable(List list) + { + TableDataInfo rspData = new TableDataInfo(); + rspData.setCode(HttpStatus.SUCCESS); + rspData.setMsg("查询成功"); + rspData.setRows(list); + rspData.setTotal(new PageInfo(list).getTotal()); + return rspData; + } + + /** + * 返回成功 + */ + public AjaxResult success() + { + return AjaxResult.success(); + } + + /** + * 返回失败消息 + */ + public AjaxResult error() + { + return AjaxResult.error(); + } + + /** + * 返回成功消息 + */ + public AjaxResult success(String message) + { + return AjaxResult.success(message); + } + + /** + * 返回成功消息 + */ + public AjaxResult success(Object data) + { + return AjaxResult.success(data); + } + + /** + * 返回失败消息 + */ + public AjaxResult error(String message) + { + return AjaxResult.error(message); + } + + /** + * 返回警告消息 + */ + public AjaxResult warn(String message) + { + return AjaxResult.warn(message); + } + + /** + * 响应返回结果 + * + * @param rows 影响行数 + * @return 操作结果 + */ + protected AjaxResult toAjax(int rows) + { + return rows > 0 ? AjaxResult.success() : AjaxResult.error(); + } + + /** + * 响应返回结果 + * + * @param result 结果 + * @return 操作结果 + */ + protected AjaxResult toAjax(boolean result) + { + return result ? success() : error(); + } + + /** + * 页面跳转 + */ + public String redirect(String url) + { + return StringUtils.format("redirect:{}", url); + } + + /** + * 获取用户缓存信息 + */ + public LoginUser getLoginUser() + { + return SecurityUtils.getLoginUser(); + } + + /** + * 获取登录用户id + */ + public Long getUserId() + { + return getLoginUser().getUserId(); + } + + /** + * 获取登录部门id + */ + public Long getDeptId() + { + return getLoginUser().getDeptId(); + } + + /** + * 获取登录用户名 + */ + public String getUsername() + { + return getLoginUser().getUsername(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/AjaxResult.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/AjaxResult.java index a7abfe4..42dcbcd 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/AjaxResult.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/AjaxResult.java @@ -1,216 +1,216 @@ -package com.ruoyi.common.core.domain; - -import java.util.HashMap; -import java.util.Objects; -import com.ruoyi.common.constant.HttpStatus; -import com.ruoyi.common.utils.StringUtils; - -/** - * 操作消息提醒 - * - * @author ruoyi - */ -public class AjaxResult extends HashMap -{ - private static final long serialVersionUID = 1L; - - /** 状态码 */ - public static final String CODE_TAG = "code"; - - /** 返回内容 */ - public static final String MSG_TAG = "msg"; - - /** 数据对象 */ - public static final String DATA_TAG = "data"; - - /** - * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。 - */ - public AjaxResult() - { - } - - /** - * 初始化一个新创建的 AjaxResult 对象 - * - * @param code 状态码 - * @param msg 返回内容 - */ - public AjaxResult(int code, String msg) - { - super.put(CODE_TAG, code); - super.put(MSG_TAG, msg); - } - - /** - * 初始化一个新创建的 AjaxResult 对象 - * - * @param code 状态码 - * @param msg 返回内容 - * @param data 数据对象 - */ - public AjaxResult(int code, String msg, Object data) - { - super.put(CODE_TAG, code); - super.put(MSG_TAG, msg); - if (StringUtils.isNotNull(data)) - { - super.put(DATA_TAG, data); - } - } - - /** - * 返回成功消息 - * - * @return 成功消息 - */ - public static AjaxResult success() - { - return AjaxResult.success("操作成功"); - } - - /** - * 返回成功数据 - * - * @return 成功消息 - */ - public static AjaxResult success(Object data) - { - return AjaxResult.success("操作成功", data); - } - - /** - * 返回成功消息 - * - * @param msg 返回内容 - * @return 成功消息 - */ - public static AjaxResult success(String msg) - { - return AjaxResult.success(msg, null); - } - - /** - * 返回成功消息 - * - * @param msg 返回内容 - * @param data 数据对象 - * @return 成功消息 - */ - public static AjaxResult success(String msg, Object data) - { - return new AjaxResult(HttpStatus.SUCCESS, msg, data); - } - - /** - * 返回警告消息 - * - * @param msg 返回内容 - * @return 警告消息 - */ - public static AjaxResult warn(String msg) - { - return AjaxResult.warn(msg, null); - } - - /** - * 返回警告消息 - * - * @param msg 返回内容 - * @param data 数据对象 - * @return 警告消息 - */ - public static AjaxResult warn(String msg, Object data) - { - return new AjaxResult(HttpStatus.WARN, msg, data); - } - - /** - * 返回错误消息 - * - * @return 错误消息 - */ - public static AjaxResult error() - { - return AjaxResult.error("操作失败"); - } - - /** - * 返回错误消息 - * - * @param msg 返回内容 - * @return 错误消息 - */ - public static AjaxResult error(String msg) - { - return AjaxResult.error(msg, null); - } - - /** - * 返回错误消息 - * - * @param msg 返回内容 - * @param data 数据对象 - * @return 错误消息 - */ - public static AjaxResult error(String msg, Object data) - { - return new AjaxResult(HttpStatus.ERROR, msg, data); - } - - /** - * 返回错误消息 - * - * @param code 状态码 - * @param msg 返回内容 - * @return 错误消息 - */ - public static AjaxResult error(int code, String msg) - { - return new AjaxResult(code, msg, null); - } - - /** - * 是否为成功消息 - * - * @return 结果 - */ - public boolean isSuccess() - { - return Objects.equals(HttpStatus.SUCCESS, this.get(CODE_TAG)); - } - - /** - * 是否为警告消息 - * - * @return 结果 - */ - public boolean isWarn() - { - return Objects.equals(HttpStatus.WARN, this.get(CODE_TAG)); - } - - /** - * 是否为错误消息 - * - * @return 结果 - */ - public boolean isError() - { - return Objects.equals(HttpStatus.ERROR, this.get(CODE_TAG)); - } - - /** - * 方便链式调用 - * - * @param key 键 - * @param value 值 - * @return 数据对象 - */ - @Override - public AjaxResult put(String key, Object value) - { - super.put(key, value); - return this; - } -} +package com.ruoyi.common.core.domain; + +import java.util.HashMap; +import java.util.Objects; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.utils.StringUtils; + +/** + * 操作消息提醒 + * + * @author ruoyi + */ +public class AjaxResult extends HashMap +{ + private static final long serialVersionUID = 1L; + + /** 状态码 */ + public static final String CODE_TAG = "code"; + + /** 返回内容 */ + public static final String MSG_TAG = "msg"; + + /** 数据对象 */ + public static final String DATA_TAG = "data"; + + /** + * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。 + */ + public AjaxResult() + { + } + + /** + * 初始化一个新创建的 AjaxResult 对象 + * + * @param code 状态码 + * @param msg 返回内容 + */ + public AjaxResult(int code, String msg) + { + super.put(CODE_TAG, code); + super.put(MSG_TAG, msg); + } + + /** + * 初始化一个新创建的 AjaxResult 对象 + * + * @param code 状态码 + * @param msg 返回内容 + * @param data 数据对象 + */ + public AjaxResult(int code, String msg, Object data) + { + super.put(CODE_TAG, code); + super.put(MSG_TAG, msg); + if (StringUtils.isNotNull(data)) + { + super.put(DATA_TAG, data); + } + } + + /** + * 返回成功消息 + * + * @return 成功消息 + */ + public static AjaxResult success() + { + return AjaxResult.success("操作成功"); + } + + /** + * 返回成功数据 + * + * @return 成功消息 + */ + public static AjaxResult success(Object data) + { + return AjaxResult.success("操作成功", data); + } + + /** + * 返回成功消息 + * + * @param msg 返回内容 + * @return 成功消息 + */ + public static AjaxResult success(String msg) + { + return AjaxResult.success(msg, null); + } + + /** + * 返回成功消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 成功消息 + */ + public static AjaxResult success(String msg, Object data) + { + return new AjaxResult(HttpStatus.SUCCESS, msg, data); + } + + /** + * 返回警告消息 + * + * @param msg 返回内容 + * @return 警告消息 + */ + public static AjaxResult warn(String msg) + { + return AjaxResult.warn(msg, null); + } + + /** + * 返回警告消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 警告消息 + */ + public static AjaxResult warn(String msg, Object data) + { + return new AjaxResult(HttpStatus.WARN, msg, data); + } + + /** + * 返回错误消息 + * + * @return 错误消息 + */ + public static AjaxResult error() + { + return AjaxResult.error("操作失败"); + } + + /** + * 返回错误消息 + * + * @param msg 返回内容 + * @return 错误消息 + */ + public static AjaxResult error(String msg) + { + return AjaxResult.error(msg, null); + } + + /** + * 返回错误消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 错误消息 + */ + public static AjaxResult error(String msg, Object data) + { + return new AjaxResult(HttpStatus.ERROR, msg, data); + } + + /** + * 返回错误消息 + * + * @param code 状态码 + * @param msg 返回内容 + * @return 错误消息 + */ + public static AjaxResult error(int code, String msg) + { + return new AjaxResult(code, msg, null); + } + + /** + * 是否为成功消息 + * + * @return 结果 + */ + public boolean isSuccess() + { + return Objects.equals(HttpStatus.SUCCESS, this.get(CODE_TAG)); + } + + /** + * 是否为警告消息 + * + * @return 结果 + */ + public boolean isWarn() + { + return Objects.equals(HttpStatus.WARN, this.get(CODE_TAG)); + } + + /** + * 是否为错误消息 + * + * @return 结果 + */ + public boolean isError() + { + return Objects.equals(HttpStatus.ERROR, this.get(CODE_TAG)); + } + + /** + * 方便链式调用 + * + * @param key 键 + * @param value 值 + * @return 数据对象 + */ + @Override + public AjaxResult put(String key, Object value) + { + super.put(key, value); + return this; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java index 15bf66b..67269ff 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java @@ -1,118 +1,118 @@ -package com.ruoyi.common.core.domain; - -import java.io.Serializable; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import com.fasterxml.jackson.annotation.JsonFormat; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonInclude; - -/** - * Entity基类 - * - * @author ruoyi - */ -public class BaseEntity implements Serializable -{ - private static final long serialVersionUID = 1L; - - /** 搜索值 */ - @JsonIgnore - private String searchValue; - - /** 创建者 */ - private String createBy; - - /** 创建时间 */ - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private Date createTime; - - /** 更新者 */ - private String updateBy; - - /** 更新时间 */ - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private Date updateTime; - - /** 备注 */ - private String remark; - - /** 请求参数 */ - @JsonInclude(JsonInclude.Include.NON_EMPTY) - private Map params; - - public String getSearchValue() - { - return searchValue; - } - - public void setSearchValue(String searchValue) - { - this.searchValue = searchValue; - } - - public String getCreateBy() - { - return createBy; - } - - public void setCreateBy(String createBy) - { - this.createBy = createBy; - } - - public Date getCreateTime() - { - return createTime; - } - - public void setCreateTime(Date createTime) - { - this.createTime = createTime; - } - - public String getUpdateBy() - { - return updateBy; - } - - public void setUpdateBy(String updateBy) - { - this.updateBy = updateBy; - } - - public Date getUpdateTime() - { - return updateTime; - } - - public void setUpdateTime(Date updateTime) - { - this.updateTime = updateTime; - } - - public String getRemark() - { - return remark; - } - - public void setRemark(String remark) - { - this.remark = remark; - } - - public Map getParams() - { - if (params == null) - { - params = new HashMap<>(); - } - return params; - } - - public void setParams(Map params) - { - this.params = params; - } -} +package com.ruoyi.common.core.domain; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; + +/** + * Entity基类 + * + * @author ruoyi + */ +public class BaseEntity implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 搜索值 */ + @JsonIgnore + private String searchValue; + + /** 创建者 */ + private String createBy; + + /** 创建时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** 更新者 */ + private String updateBy; + + /** 更新时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** 备注 */ + private String remark; + + /** 请求参数 */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private Map params; + + public String getSearchValue() + { + return searchValue; + } + + public void setSearchValue(String searchValue) + { + this.searchValue = searchValue; + } + + public String getCreateBy() + { + return createBy; + } + + public void setCreateBy(String createBy) + { + this.createBy = createBy; + } + + public Date getCreateTime() + { + return createTime; + } + + public void setCreateTime(Date createTime) + { + this.createTime = createTime; + } + + public String getUpdateBy() + { + return updateBy; + } + + public void setUpdateBy(String updateBy) + { + this.updateBy = updateBy; + } + + public Date getUpdateTime() + { + return updateTime; + } + + public void setUpdateTime(Date updateTime) + { + this.updateTime = updateTime; + } + + public String getRemark() + { + return remark; + } + + public void setRemark(String remark) + { + this.remark = remark; + } + + public Map getParams() + { + if (params == null) + { + params = new HashMap<>(); + } + return params; + } + + public void setParams(Map params) + { + this.params = params; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeEntity.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeEntity.java index a180a18..171f04c 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeEntity.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeEntity.java @@ -1,79 +1,79 @@ -package com.ruoyi.common.core.domain; - -import java.util.ArrayList; -import java.util.List; - -/** - * Tree基类 - * - * @author ruoyi - */ -public class TreeEntity extends BaseEntity -{ - private static final long serialVersionUID = 1L; - - /** 父菜单名称 */ - private String parentName; - - /** 父菜单ID */ - private Long parentId; - - /** 显示顺序 */ - private Integer orderNum; - - /** 祖级列表 */ - private String ancestors; - - /** 子部门 */ - private List children = new ArrayList<>(); - - public String getParentName() - { - return parentName; - } - - public void setParentName(String parentName) - { - this.parentName = parentName; - } - - public Long getParentId() - { - return parentId; - } - - public void setParentId(Long parentId) - { - this.parentId = parentId; - } - - public Integer getOrderNum() - { - return orderNum; - } - - public void setOrderNum(Integer orderNum) - { - this.orderNum = orderNum; - } - - public String getAncestors() - { - return ancestors; - } - - public void setAncestors(String ancestors) - { - this.ancestors = ancestors; - } - - public List getChildren() - { - return children; - } - - public void setChildren(List children) - { - this.children = children; - } -} +package com.ruoyi.common.core.domain; + +import java.util.ArrayList; +import java.util.List; + +/** + * Tree基类 + * + * @author ruoyi + */ +public class TreeEntity extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 父菜单名称 */ + private String parentName; + + /** 父菜单ID */ + private Long parentId; + + /** 显示顺序 */ + private Integer orderNum; + + /** 祖级列表 */ + private String ancestors; + + /** 子部门 */ + private List children = new ArrayList<>(); + + public String getParentName() + { + return parentName; + } + + public void setParentName(String parentName) + { + this.parentName = parentName; + } + + public Long getParentId() + { + return parentId; + } + + public void setParentId(Long parentId) + { + this.parentId = parentId; + } + + public Integer getOrderNum() + { + return orderNum; + } + + public void setOrderNum(Integer orderNum) + { + this.orderNum = orderNum; + } + + public String getAncestors() + { + return ancestors; + } + + public void setAncestors(String ancestors) + { + this.ancestors = ancestors; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java index ae25df2..a762704 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java @@ -1,93 +1,93 @@ -package com.ruoyi.common.core.domain; - -import java.io.Serializable; -import java.util.List; -import java.util.stream.Collectors; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.ruoyi.common.constant.UserConstants; -import com.ruoyi.common.core.domain.entity.SysDept; -import com.ruoyi.common.core.domain.entity.SysMenu; -import com.ruoyi.common.utils.StringUtils; - -/** - * Treeselect树结构实体类 - * - * @author ruoyi - */ -public class TreeSelect implements Serializable -{ - private static final long serialVersionUID = 1L; - - /** 节点ID */ - private Long id; - - /** 节点名称 */ - private String label; - - /** 节点禁用 */ - private boolean disabled = false; - - /** 子节点 */ - @JsonInclude(JsonInclude.Include.NON_EMPTY) - private List children; - - public TreeSelect() - { - - } - - public TreeSelect(SysDept dept) - { - this.id = dept.getDeptId(); - this.label = dept.getDeptName(); - this.disabled = StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus()); - this.children = dept.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList()); - } - - public TreeSelect(SysMenu menu) - { - this.id = menu.getMenuId(); - this.label = menu.getMenuName(); - this.children = menu.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList()); - } - - public Long getId() - { - return id; - } - - public void setId(Long id) - { - this.id = id; - } - - public String getLabel() - { - return label; - } - - public void setLabel(String label) - { - this.label = label; - } - - public boolean isDisabled() - { - return disabled; - } - - public void setDisabled(boolean disabled) - { - this.disabled = disabled; - } - - public List getChildren() - { - return children; - } - - public void setChildren(List children) - { - this.children = children; - } -} +package com.ruoyi.common.core.domain; + +import java.io.Serializable; +import java.util.List; +import java.util.stream.Collectors; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.common.core.domain.entity.SysMenu; +import com.ruoyi.common.utils.StringUtils; + +/** + * Treeselect树结构实体类 + * + * @author ruoyi + */ +public class TreeSelect implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 节点ID */ + private Long id; + + /** 节点名称 */ + private String label; + + /** 节点禁用 */ + private boolean disabled = false; + + /** 子节点 */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private List children; + + public TreeSelect() + { + + } + + public TreeSelect(SysDept dept) + { + this.id = dept.getDeptId(); + this.label = dept.getDeptName(); + this.disabled = StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus()); + this.children = dept.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + public TreeSelect(SysMenu menu) + { + this.id = menu.getMenuId(); + this.label = menu.getMenuName(); + this.children = menu.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + public Long getId() + { + return id; + } + + public void setId(Long id) + { + this.id = id; + } + + public String getLabel() + { + return label; + } + + public void setLabel(String label) + { + this.label = label; + } + + public boolean isDisabled() + { + return disabled; + } + + public void setDisabled(boolean disabled) + { + this.disabled = disabled; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java index 37236db..693461b 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java @@ -1,203 +1,203 @@ -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 org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; -import com.ruoyi.common.core.domain.BaseEntity; - -/** - * 部门表 sys_dept - * - * @author ruoyi - */ -public class SysDept extends BaseEntity -{ - private static final long serialVersionUID = 1L; - - /** 部门ID */ - private Long deptId; - - /** 父部门ID */ - private Long parentId; - - /** 祖级列表 */ - private String ancestors; - - /** 部门名称 */ - private String deptName; - - /** 显示顺序 */ - private Integer orderNum; - - /** 负责人 */ - private String leader; - - /** 联系电话 */ - private String phone; - - /** 邮箱 */ - private String email; - - /** 部门状态:0正常,1停用 */ - private String status; - - /** 删除标志(0代表存在 2代表删除) */ - private String delFlag; - - /** 父部门名称 */ - private String parentName; - - /** 子部门 */ - private List children = new ArrayList(); - - public Long getDeptId() - { - return deptId; - } - - public void setDeptId(Long deptId) - { - this.deptId = deptId; - } - - public Long getParentId() - { - return parentId; - } - - public void setParentId(Long parentId) - { - this.parentId = parentId; - } - - public String getAncestors() - { - return ancestors; - } - - public void setAncestors(String ancestors) - { - this.ancestors = ancestors; - } - - @NotBlank(message = "部门名称不能为空") - @Size(min = 0, max = 30, message = "部门名称长度不能超过30个字符") - public String getDeptName() - { - return deptName; - } - - public void setDeptName(String deptName) - { - this.deptName = deptName; - } - - @NotNull(message = "显示顺序不能为空") - public Integer getOrderNum() - { - return orderNum; - } - - public void setOrderNum(Integer orderNum) - { - this.orderNum = orderNum; - } - - public String getLeader() - { - return leader; - } - - public void setLeader(String leader) - { - this.leader = leader; - } - - @Size(min = 0, max = 11, message = "联系电话长度不能超过11个字符") - public String getPhone() - { - return phone; - } - - public void setPhone(String phone) - { - this.phone = phone; - } - - @Email(message = "邮箱格式不正确") - @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") - public String getEmail() - { - return email; - } - - public void setEmail(String email) - { - this.email = email; - } - - public String getStatus() - { - return status; - } - - public void setStatus(String status) - { - this.status = status; - } - - public String getDelFlag() - { - return delFlag; - } - - public void setDelFlag(String delFlag) - { - this.delFlag = delFlag; - } - - public String getParentName() - { - return parentName; - } - - public void setParentName(String parentName) - { - this.parentName = parentName; - } - - public List getChildren() - { - return children; - } - - public void setChildren(List children) - { - this.children = children; - } - - @Override - public String toString() { - return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) - .append("deptId", getDeptId()) - .append("parentId", getParentId()) - .append("ancestors", getAncestors()) - .append("deptName", getDeptName()) - .append("orderNum", getOrderNum()) - .append("leader", getLeader()) - .append("phone", getPhone()) - .append("email", getEmail()) - .append("status", getStatus()) - .append("delFlag", getDelFlag()) - .append("createBy", getCreateBy()) - .append("createTime", getCreateTime()) - .append("updateBy", getUpdateBy()) - .append("updateTime", getUpdateTime()) - .toString(); - } -} +package com.ruoyi.common.core.domain.entity; + +import java.util.ArrayList; +import java.util.List; +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; + +/** + * 部门表 sys_dept + * + * @author ruoyi + */ +public class SysDept extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 部门ID */ + private Long deptId; + + /** 父部门ID */ + private Long parentId; + + /** 祖级列表 */ + private String ancestors; + + /** 部门名称 */ + private String deptName; + + /** 显示顺序 */ + private Integer orderNum; + + /** 负责人 */ + private String leader; + + /** 联系电话 */ + private String phone; + + /** 邮箱 */ + private String email; + + /** 部门状态:0正常,1停用 */ + private String status; + + /** 删除标志(0代表存在 2代表删除) */ + private String delFlag; + + /** 父部门名称 */ + private String parentName; + + /** 子部门 */ + private List children = new ArrayList(); + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + public Long getParentId() + { + return parentId; + } + + public void setParentId(Long parentId) + { + this.parentId = parentId; + } + + public String getAncestors() + { + return ancestors; + } + + public void setAncestors(String ancestors) + { + this.ancestors = ancestors; + } + + @NotBlank(message = "部门名称不能为空") + @Size(min = 0, max = 30, message = "部门名称长度不能超过30个字符") + public String getDeptName() + { + return deptName; + } + + public void setDeptName(String deptName) + { + this.deptName = deptName; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getOrderNum() + { + return orderNum; + } + + public void setOrderNum(Integer orderNum) + { + this.orderNum = orderNum; + } + + public String getLeader() + { + return leader; + } + + public void setLeader(String leader) + { + this.leader = leader; + } + + @Size(min = 0, max = 11, message = "联系电话长度不能超过11个字符") + public String getPhone() + { + return phone; + } + + public void setPhone(String phone) + { + this.phone = phone; + } + + @Email(message = "邮箱格式不正确") + @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") + public String getEmail() + { + return email; + } + + public void setEmail(String email) + { + this.email = email; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getDelFlag() + { + return delFlag; + } + + public void setDelFlag(String delFlag) + { + this.delFlag = delFlag; + } + + public String getParentName() + { + return parentName; + } + + public void setParentName(String parentName) + { + this.parentName = parentName; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("deptId", getDeptId()) + .append("parentId", getParentId()) + .append("ancestors", getAncestors()) + .append("deptName", getDeptName()) + .append("orderNum", getOrderNum()) + .append("leader", getLeader()) + .append("phone", getPhone()) + .append("email", getEmail()) + .append("status", getStatus()) + .append("delFlag", getDelFlag()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java index 6b035a6..e79eaa2 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java @@ -1,176 +1,176 @@ -package com.ruoyi.common.core.domain.entity; - -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Size; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; -import com.ruoyi.common.annotation.Excel; -import com.ruoyi.common.annotation.Excel.ColumnType; -import com.ruoyi.common.constant.UserConstants; -import com.ruoyi.common.core.domain.BaseEntity; - -/** - * 字典数据表 sys_dict_data - * - * @author ruoyi - */ -public class SysDictData extends BaseEntity -{ - private static final long serialVersionUID = 1L; - - /** 字典编码 */ - @Excel(name = "字典编码", cellType = ColumnType.NUMERIC) - private Long dictCode; - - /** 字典排序 */ - @Excel(name = "字典排序", cellType = ColumnType.NUMERIC) - private Long dictSort; - - /** 字典标签 */ - @Excel(name = "字典标签") - private String dictLabel; - - /** 字典键值 */ - @Excel(name = "字典键值") - private String dictValue; - - /** 字典类型 */ - @Excel(name = "字典类型") - private String dictType; - - /** 样式属性(其他样式扩展) */ - private String cssClass; - - /** 表格字典样式 */ - private String listClass; - - /** 是否默认(Y是 N否) */ - @Excel(name = "是否默认", readConverterExp = "Y=是,N=否") - private String isDefault; - - /** 状态(0正常 1停用) */ - @Excel(name = "状态", readConverterExp = "0=正常,1=停用") - private String status; - - public Long getDictCode() - { - return dictCode; - } - - public void setDictCode(Long dictCode) - { - this.dictCode = dictCode; - } - - public Long getDictSort() - { - return dictSort; - } - - public void setDictSort(Long dictSort) - { - this.dictSort = dictSort; - } - - @NotBlank(message = "字典标签不能为空") - @Size(min = 0, max = 100, message = "字典标签长度不能超过100个字符") - public String getDictLabel() - { - return dictLabel; - } - - public void setDictLabel(String dictLabel) - { - this.dictLabel = dictLabel; - } - - @NotBlank(message = "字典键值不能为空") - @Size(min = 0, max = 100, message = "字典键值长度不能超过100个字符") - public String getDictValue() - { - return dictValue; - } - - public void setDictValue(String dictValue) - { - this.dictValue = dictValue; - } - - @NotBlank(message = "字典类型不能为空") - @Size(min = 0, max = 100, message = "字典类型长度不能超过100个字符") - public String getDictType() - { - return dictType; - } - - public void setDictType(String dictType) - { - this.dictType = dictType; - } - - @Size(min = 0, max = 100, message = "样式属性长度不能超过100个字符") - public String getCssClass() - { - return cssClass; - } - - public void setCssClass(String cssClass) - { - this.cssClass = cssClass; - } - - public String getListClass() - { - return listClass; - } - - public void setListClass(String listClass) - { - this.listClass = listClass; - } - - public boolean getDefault() - { - return UserConstants.YES.equals(this.isDefault); - } - - public String getIsDefault() - { - return isDefault; - } - - public void setIsDefault(String isDefault) - { - this.isDefault = isDefault; - } - - public String getStatus() - { - return status; - } - - public void setStatus(String status) - { - this.status = status; - } - - @Override - public String toString() { - return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) - .append("dictCode", getDictCode()) - .append("dictSort", getDictSort()) - .append("dictLabel", getDictLabel()) - .append("dictValue", getDictValue()) - .append("dictType", getDictType()) - .append("cssClass", getCssClass()) - .append("listClass", getListClass()) - .append("isDefault", getIsDefault()) - .append("status", getStatus()) - .append("createBy", getCreateBy()) - .append("createTime", getCreateTime()) - .append("updateBy", getUpdateBy()) - .append("updateTime", getUpdateTime()) - .append("remark", getRemark()) - .toString(); - } -} +package com.ruoyi.common.core.domain.entity; + +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; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 字典数据表 sys_dict_data + * + * @author ruoyi + */ +public class SysDictData extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 字典编码 */ + @Excel(name = "字典编码", cellType = ColumnType.NUMERIC) + private Long dictCode; + + /** 字典排序 */ + @Excel(name = "字典排序", cellType = ColumnType.NUMERIC) + private Long dictSort; + + /** 字典标签 */ + @Excel(name = "字典标签") + private String dictLabel; + + /** 字典键值 */ + @Excel(name = "字典键值") + private String dictValue; + + /** 字典类型 */ + @Excel(name = "字典类型") + private String dictType; + + /** 样式属性(其他样式扩展) */ + private String cssClass; + + /** 表格字典样式 */ + private String listClass; + + /** 是否默认(Y是 N否) */ + @Excel(name = "是否默认", readConverterExp = "Y=是,N=否") + private String isDefault; + + /** 状态(0正常 1停用) */ + @Excel(name = "状态", readConverterExp = "0=正常,1=停用") + private String status; + + public Long getDictCode() + { + return dictCode; + } + + public void setDictCode(Long dictCode) + { + this.dictCode = dictCode; + } + + public Long getDictSort() + { + return dictSort; + } + + public void setDictSort(Long dictSort) + { + this.dictSort = dictSort; + } + + @NotBlank(message = "字典标签不能为空") + @Size(min = 0, max = 100, message = "字典标签长度不能超过100个字符") + public String getDictLabel() + { + return dictLabel; + } + + public void setDictLabel(String dictLabel) + { + this.dictLabel = dictLabel; + } + + @NotBlank(message = "字典键值不能为空") + @Size(min = 0, max = 100, message = "字典键值长度不能超过100个字符") + public String getDictValue() + { + return dictValue; + } + + public void setDictValue(String dictValue) + { + this.dictValue = dictValue; + } + + @NotBlank(message = "字典类型不能为空") + @Size(min = 0, max = 100, message = "字典类型长度不能超过100个字符") + public String getDictType() + { + return dictType; + } + + public void setDictType(String dictType) + { + this.dictType = dictType; + } + + @Size(min = 0, max = 100, message = "样式属性长度不能超过100个字符") + public String getCssClass() + { + return cssClass; + } + + public void setCssClass(String cssClass) + { + this.cssClass = cssClass; + } + + public String getListClass() + { + return listClass; + } + + public void setListClass(String listClass) + { + this.listClass = listClass; + } + + public boolean getDefault() + { + return UserConstants.YES.equals(this.isDefault); + } + + public String getIsDefault() + { + return isDefault; + } + + public void setIsDefault(String isDefault) + { + this.isDefault = isDefault; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("dictCode", getDictCode()) + .append("dictSort", getDictSort()) + .append("dictLabel", getDictLabel()) + .append("dictValue", getDictValue()) + .append("dictType", getDictType()) + .append("cssClass", getCssClass()) + .append("listClass", getListClass()) + .append("isDefault", getIsDefault()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java index ac5d290..0befbf4 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java @@ -1,96 +1,96 @@ -package com.ruoyi.common.core.domain.entity; - -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Pattern; -import jakarta.validation.constraints.Size; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; -import com.ruoyi.common.annotation.Excel; -import com.ruoyi.common.annotation.Excel.ColumnType; -import com.ruoyi.common.core.domain.BaseEntity; - -/** - * 字典类型表 sys_dict_type - * - * @author ruoyi - */ -public class SysDictType extends BaseEntity -{ - private static final long serialVersionUID = 1L; - - /** 字典主键 */ - @Excel(name = "字典主键", cellType = ColumnType.NUMERIC) - private Long dictId; - - /** 字典名称 */ - @Excel(name = "字典名称") - private String dictName; - - /** 字典类型 */ - @Excel(name = "字典类型") - private String dictType; - - /** 状态(0正常 1停用) */ - @Excel(name = "状态", readConverterExp = "0=正常,1=停用") - private String status; - - public Long getDictId() - { - return dictId; - } - - public void setDictId(Long dictId) - { - this.dictId = dictId; - } - - @NotBlank(message = "字典名称不能为空") - @Size(min = 0, max = 100, message = "字典类型名称长度不能超过100个字符") - public String getDictName() - { - return dictName; - } - - public void setDictName(String dictName) - { - this.dictName = dictName; - } - - @NotBlank(message = "字典类型不能为空") - @Size(min = 0, max = 100, message = "字典类型类型长度不能超过100个字符") - @Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)") - public String getDictType() - { - return dictType; - } - - public void setDictType(String dictType) - { - this.dictType = dictType; - } - - public String getStatus() - { - return status; - } - - public void setStatus(String status) - { - this.status = status; - } - - @Override - public String toString() { - return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) - .append("dictId", getDictId()) - .append("dictName", getDictName()) - .append("dictType", getDictType()) - .append("status", getStatus()) - .append("createBy", getCreateBy()) - .append("createTime", getCreateTime()) - .append("updateBy", getUpdateBy()) - .append("updateTime", getUpdateTime()) - .append("remark", getRemark()) - .toString(); - } -} +package com.ruoyi.common.core.domain.entity; + +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; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 字典类型表 sys_dict_type + * + * @author ruoyi + */ +public class SysDictType extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 字典主键 */ + @Excel(name = "字典主键", cellType = ColumnType.NUMERIC) + private Long dictId; + + /** 字典名称 */ + @Excel(name = "字典名称") + private String dictName; + + /** 字典类型 */ + @Excel(name = "字典类型") + private String dictType; + + /** 状态(0正常 1停用) */ + @Excel(name = "状态", readConverterExp = "0=正常,1=停用") + private String status; + + public Long getDictId() + { + return dictId; + } + + public void setDictId(Long dictId) + { + this.dictId = dictId; + } + + @NotBlank(message = "字典名称不能为空") + @Size(min = 0, max = 100, message = "字典类型名称长度不能超过100个字符") + public String getDictName() + { + return dictName; + } + + public void setDictName(String dictName) + { + this.dictName = dictName; + } + + @NotBlank(message = "字典类型不能为空") + @Size(min = 0, max = 100, message = "字典类型类型长度不能超过100个字符") + @Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)") + public String getDictType() + { + return dictType; + } + + public void setDictType(String dictType) + { + this.dictType = dictType; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("dictId", getDictId()) + .append("dictName", getDictName()) + .append("dictType", getDictType()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java index d4ab762..f062120 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java @@ -1,274 +1,274 @@ -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 org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; -import com.ruoyi.common.core.domain.BaseEntity; - -/** - * 菜单权限表 sys_menu - * - * @author ruoyi - */ -public class SysMenu extends BaseEntity -{ - private static final long serialVersionUID = 1L; - - /** 菜单ID */ - private Long menuId; - - /** 菜单名称 */ - private String menuName; - - /** 父菜单名称 */ - private String parentName; - - /** 父菜单ID */ - private Long parentId; - - /** 显示顺序 */ - private Integer orderNum; - - /** 路由地址 */ - private String path; - - /** 组件路径 */ - private String component; - - /** 路由参数 */ - private String query; - - /** 路由名称,默认和路由地址相同的驼峰格式(注意:因为vue3版本的router会删除名称相同路由,为避免名字的冲突,特殊情况可以自定义) */ - private String routeName; - - /** 是否为外链(0是 1否) */ - private String isFrame; - - /** 是否缓存(0缓存 1不缓存) */ - private String isCache; - - /** 类型(M目录 C菜单 F按钮) */ - private String menuType; - - /** 显示状态(0显示 1隐藏) */ - private String visible; - - /** 菜单状态(0正常 1停用) */ - private String status; - - /** 权限字符串 */ - private String perms; - - /** 菜单图标 */ - private String icon; - - /** 子菜单 */ - private List children = new ArrayList(); - - public Long getMenuId() - { - return menuId; - } - - public void setMenuId(Long menuId) - { - this.menuId = menuId; - } - - @NotBlank(message = "菜单名称不能为空") - @Size(min = 0, max = 50, message = "菜单名称长度不能超过50个字符") - public String getMenuName() - { - return menuName; - } - - public void setMenuName(String menuName) - { - this.menuName = menuName; - } - - public String getParentName() - { - return parentName; - } - - public void setParentName(String parentName) - { - this.parentName = parentName; - } - - public Long getParentId() - { - return parentId; - } - - public void setParentId(Long parentId) - { - this.parentId = parentId; - } - - @NotNull(message = "显示顺序不能为空") - public Integer getOrderNum() - { - return orderNum; - } - - public void setOrderNum(Integer orderNum) - { - this.orderNum = orderNum; - } - - @Size(min = 0, max = 200, message = "路由地址不能超过200个字符") - public String getPath() - { - return path; - } - - public void setPath(String path) - { - this.path = path; - } - - @Size(min = 0, max = 200, message = "组件路径不能超过255个字符") - public String getComponent() - { - return component; - } - - public void setComponent(String component) - { - this.component = component; - } - - public String getQuery() - { - return query; - } - - public void setQuery(String query) - { - this.query = query; - } - - public String getRouteName() - { - return routeName; - } - - public void setRouteName(String routeName) - { - this.routeName = routeName; - } - - public String getIsFrame() - { - return isFrame; - } - - public void setIsFrame(String isFrame) - { - this.isFrame = isFrame; - } - - public String getIsCache() - { - return isCache; - } - - public void setIsCache(String isCache) - { - this.isCache = isCache; - } - - @NotBlank(message = "菜单类型不能为空") - public String getMenuType() - { - return menuType; - } - - public void setMenuType(String menuType) - { - this.menuType = menuType; - } - - public String getVisible() - { - return visible; - } - - public void setVisible(String visible) - { - this.visible = visible; - } - - public String getStatus() - { - return status; - } - - public void setStatus(String status) - { - this.status = status; - } - - @Size(min = 0, max = 100, message = "权限标识长度不能超过100个字符") - public String getPerms() - { - return perms; - } - - public void setPerms(String perms) - { - this.perms = perms; - } - - public String getIcon() - { - return icon; - } - - public void setIcon(String icon) - { - this.icon = icon; - } - - public List getChildren() - { - return children; - } - - public void setChildren(List children) - { - this.children = children; - } - - @Override - public String toString() { - return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) - .append("menuId", getMenuId()) - .append("menuName", getMenuName()) - .append("parentId", getParentId()) - .append("orderNum", getOrderNum()) - .append("path", getPath()) - .append("component", getComponent()) - .append("query", getQuery()) - .append("routeName", getRouteName()) - .append("isFrame", getIsFrame()) - .append("IsCache", getIsCache()) - .append("menuType", getMenuType()) - .append("visible", getVisible()) - .append("status ", getStatus()) - .append("perms", getPerms()) - .append("icon", getIcon()) - .append("createBy", getCreateBy()) - .append("createTime", getCreateTime()) - .append("updateBy", getUpdateBy()) - .append("updateTime", getUpdateTime()) - .append("remark", getRemark()) - .toString(); - } -} +package com.ruoyi.common.core.domain.entity; + +import java.util.ArrayList; +import java.util.List; +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; + +/** + * 菜单权限表 sys_menu + * + * @author ruoyi + */ +public class SysMenu extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 菜单ID */ + private Long menuId; + + /** 菜单名称 */ + private String menuName; + + /** 父菜单名称 */ + private String parentName; + + /** 父菜单ID */ + private Long parentId; + + /** 显示顺序 */ + private Integer orderNum; + + /** 路由地址 */ + private String path; + + /** 组件路径 */ + private String component; + + /** 路由参数 */ + private String query; + + /** 路由名称,默认和路由地址相同的驼峰格式(注意:因为vue3版本的router会删除名称相同路由,为避免名字的冲突,特殊情况可以自定义) */ + private String routeName; + + /** 是否为外链(0是 1否) */ + private String isFrame; + + /** 是否缓存(0缓存 1不缓存) */ + private String isCache; + + /** 类型(M目录 C菜单 F按钮) */ + private String menuType; + + /** 显示状态(0显示 1隐藏) */ + private String visible; + + /** 菜单状态(0正常 1停用) */ + private String status; + + /** 权限字符串 */ + private String perms; + + /** 菜单图标 */ + private String icon; + + /** 子菜单 */ + private List children = new ArrayList(); + + public Long getMenuId() + { + return menuId; + } + + public void setMenuId(Long menuId) + { + this.menuId = menuId; + } + + @NotBlank(message = "菜单名称不能为空") + @Size(min = 0, max = 50, message = "菜单名称长度不能超过50个字符") + public String getMenuName() + { + return menuName; + } + + public void setMenuName(String menuName) + { + this.menuName = menuName; + } + + public String getParentName() + { + return parentName; + } + + public void setParentName(String parentName) + { + this.parentName = parentName; + } + + public Long getParentId() + { + return parentId; + } + + public void setParentId(Long parentId) + { + this.parentId = parentId; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getOrderNum() + { + return orderNum; + } + + public void setOrderNum(Integer orderNum) + { + this.orderNum = orderNum; + } + + @Size(min = 0, max = 200, message = "路由地址不能超过200个字符") + public String getPath() + { + return path; + } + + public void setPath(String path) + { + this.path = path; + } + + @Size(min = 0, max = 200, message = "组件路径不能超过255个字符") + public String getComponent() + { + return component; + } + + public void setComponent(String component) + { + this.component = component; + } + + public String getQuery() + { + return query; + } + + public void setQuery(String query) + { + this.query = query; + } + + public String getRouteName() + { + return routeName; + } + + public void setRouteName(String routeName) + { + this.routeName = routeName; + } + + public String getIsFrame() + { + return isFrame; + } + + public void setIsFrame(String isFrame) + { + this.isFrame = isFrame; + } + + public String getIsCache() + { + return isCache; + } + + public void setIsCache(String isCache) + { + this.isCache = isCache; + } + + @NotBlank(message = "菜单类型不能为空") + public String getMenuType() + { + return menuType; + } + + public void setMenuType(String menuType) + { + this.menuType = menuType; + } + + public String getVisible() + { + return visible; + } + + public void setVisible(String visible) + { + this.visible = visible; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + @Size(min = 0, max = 100, message = "权限标识长度不能超过100个字符") + public String getPerms() + { + return perms; + } + + public void setPerms(String perms) + { + this.perms = perms; + } + + public String getIcon() + { + return icon; + } + + public void setIcon(String icon) + { + this.icon = icon; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("menuId", getMenuId()) + .append("menuName", getMenuName()) + .append("parentId", getParentId()) + .append("orderNum", getOrderNum()) + .append("path", getPath()) + .append("component", getComponent()) + .append("query", getQuery()) + .append("routeName", getRouteName()) + .append("isFrame", getIsFrame()) + .append("IsCache", getIsCache()) + .append("menuType", getMenuType()) + .append("visible", getVisible()) + .append("status ", getStatus()) + .append("perms", getPerms()) + .append("icon", getIcon()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java index 2f8ed14..2396f80 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java @@ -1,241 +1,241 @@ -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 org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; -import com.ruoyi.common.annotation.Excel; -import com.ruoyi.common.annotation.Excel.ColumnType; -import com.ruoyi.common.core.domain.BaseEntity; - -/** - * 角色表 sys_role - * - * @author ruoyi - */ -public class SysRole extends BaseEntity -{ - private static final long serialVersionUID = 1L; - - /** 角色ID */ - @Excel(name = "角色序号", cellType = ColumnType.NUMERIC) - private Long roleId; - - /** 角色名称 */ - @Excel(name = "角色名称") - private String roleName; - - /** 角色权限 */ - @Excel(name = "角色权限") - private String roleKey; - - /** 角色排序 */ - @Excel(name = "角色排序") - private Integer roleSort; - - /** 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) */ - @Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限") - private String dataScope; - - /** 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示) */ - private boolean menuCheckStrictly; - - /** 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 ) */ - private boolean deptCheckStrictly; - - /** 角色状态(0正常 1停用) */ - @Excel(name = "角色状态", readConverterExp = "0=正常,1=停用") - private String status; - - /** 删除标志(0代表存在 2代表删除) */ - private String delFlag; - - /** 用户是否存在此角色标识 默认不存在 */ - private boolean flag = false; - - /** 菜单组 */ - private Long[] menuIds; - - /** 部门组(数据权限) */ - private Long[] deptIds; - - /** 角色菜单权限 */ - private Set permissions; - - public SysRole() - { - - } - - public SysRole(Long roleId) - { - this.roleId = roleId; - } - - public Long getRoleId() - { - return roleId; - } - - public void setRoleId(Long roleId) - { - this.roleId = roleId; - } - - public boolean isAdmin() - { - return isAdmin(this.roleId); - } - - public static boolean isAdmin(Long roleId) - { - return roleId != null && 1L == roleId; - } - - @NotBlank(message = "角色名称不能为空") - @Size(min = 0, max = 30, message = "角色名称长度不能超过30个字符") - public String getRoleName() - { - return roleName; - } - - public void setRoleName(String roleName) - { - this.roleName = roleName; - } - - @NotBlank(message = "权限字符不能为空") - @Size(min = 0, max = 100, message = "权限字符长度不能超过100个字符") - public String getRoleKey() - { - return roleKey; - } - - public void setRoleKey(String roleKey) - { - this.roleKey = roleKey; - } - - @NotNull(message = "显示顺序不能为空") - public Integer getRoleSort() - { - return roleSort; - } - - public void setRoleSort(Integer roleSort) - { - this.roleSort = roleSort; - } - - public String getDataScope() - { - return dataScope; - } - - public void setDataScope(String dataScope) - { - this.dataScope = dataScope; - } - - public boolean isMenuCheckStrictly() - { - return menuCheckStrictly; - } - - public void setMenuCheckStrictly(boolean menuCheckStrictly) - { - this.menuCheckStrictly = menuCheckStrictly; - } - - public boolean isDeptCheckStrictly() - { - return deptCheckStrictly; - } - - public void setDeptCheckStrictly(boolean deptCheckStrictly) - { - this.deptCheckStrictly = deptCheckStrictly; - } - - public String getStatus() - { - return status; - } - - public void setStatus(String status) - { - this.status = status; - } - - public String getDelFlag() - { - return delFlag; - } - - public void setDelFlag(String delFlag) - { - this.delFlag = delFlag; - } - - public boolean isFlag() - { - return flag; - } - - public void setFlag(boolean flag) - { - this.flag = flag; - } - - public Long[] getMenuIds() - { - return menuIds; - } - - public void setMenuIds(Long[] menuIds) - { - this.menuIds = menuIds; - } - - public Long[] getDeptIds() - { - return deptIds; - } - - public void setDeptIds(Long[] deptIds) - { - this.deptIds = deptIds; - } - - public Set getPermissions() - { - return permissions; - } - - public void setPermissions(Set permissions) - { - this.permissions = permissions; - } - - @Override - public String toString() { - return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) - .append("roleId", getRoleId()) - .append("roleName", getRoleName()) - .append("roleKey", getRoleKey()) - .append("roleSort", getRoleSort()) - .append("dataScope", getDataScope()) - .append("menuCheckStrictly", isMenuCheckStrictly()) - .append("deptCheckStrictly", isDeptCheckStrictly()) - .append("status", getStatus()) - .append("delFlag", getDelFlag()) - .append("createBy", getCreateBy()) - .append("createTime", getCreateTime()) - .append("updateBy", getUpdateBy()) - .append("updateTime", getUpdateTime()) - .append("remark", getRemark()) - .toString(); - } -} +package com.ruoyi.common.core.domain.entity; + +import java.util.Set; +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; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 角色表 sys_role + * + * @author ruoyi + */ +public class SysRole extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 角色ID */ + @Excel(name = "角色序号", cellType = ColumnType.NUMERIC) + private Long roleId; + + /** 角色名称 */ + @Excel(name = "角色名称") + private String roleName; + + /** 角色权限 */ + @Excel(name = "角色权限") + private String roleKey; + + /** 角色排序 */ + @Excel(name = "角色排序") + private Integer roleSort; + + /** 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) */ + @Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限") + private String dataScope; + + /** 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示) */ + private boolean menuCheckStrictly; + + /** 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 ) */ + private boolean deptCheckStrictly; + + /** 角色状态(0正常 1停用) */ + @Excel(name = "角色状态", readConverterExp = "0=正常,1=停用") + private String status; + + /** 删除标志(0代表存在 2代表删除) */ + private String delFlag; + + /** 用户是否存在此角色标识 默认不存在 */ + private boolean flag = false; + + /** 菜单组 */ + private Long[] menuIds; + + /** 部门组(数据权限) */ + private Long[] deptIds; + + /** 角色菜单权限 */ + private Set permissions; + + public SysRole() + { + + } + + public SysRole(Long roleId) + { + this.roleId = roleId; + } + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + public boolean isAdmin() + { + return isAdmin(this.roleId); + } + + public static boolean isAdmin(Long roleId) + { + return roleId != null && 1L == roleId; + } + + @NotBlank(message = "角色名称不能为空") + @Size(min = 0, max = 30, message = "角色名称长度不能超过30个字符") + public String getRoleName() + { + return roleName; + } + + public void setRoleName(String roleName) + { + this.roleName = roleName; + } + + @NotBlank(message = "权限字符不能为空") + @Size(min = 0, max = 100, message = "权限字符长度不能超过100个字符") + public String getRoleKey() + { + return roleKey; + } + + public void setRoleKey(String roleKey) + { + this.roleKey = roleKey; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getRoleSort() + { + return roleSort; + } + + public void setRoleSort(Integer roleSort) + { + this.roleSort = roleSort; + } + + public String getDataScope() + { + return dataScope; + } + + public void setDataScope(String dataScope) + { + this.dataScope = dataScope; + } + + public boolean isMenuCheckStrictly() + { + return menuCheckStrictly; + } + + public void setMenuCheckStrictly(boolean menuCheckStrictly) + { + this.menuCheckStrictly = menuCheckStrictly; + } + + public boolean isDeptCheckStrictly() + { + return deptCheckStrictly; + } + + public void setDeptCheckStrictly(boolean deptCheckStrictly) + { + this.deptCheckStrictly = deptCheckStrictly; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getDelFlag() + { + return delFlag; + } + + public void setDelFlag(String delFlag) + { + this.delFlag = delFlag; + } + + public boolean isFlag() + { + return flag; + } + + public void setFlag(boolean flag) + { + this.flag = flag; + } + + public Long[] getMenuIds() + { + return menuIds; + } + + public void setMenuIds(Long[] menuIds) + { + this.menuIds = menuIds; + } + + public Long[] getDeptIds() + { + return deptIds; + } + + public void setDeptIds(Long[] deptIds) + { + this.deptIds = deptIds; + } + + public Set getPermissions() + { + return permissions; + } + + public void setPermissions(Set permissions) + { + this.permissions = permissions; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("roleId", getRoleId()) + .append("roleName", getRoleName()) + .append("roleKey", getRoleKey()) + .append("roleSort", getRoleSort()) + .append("dataScope", getDataScope()) + .append("menuCheckStrictly", isMenuCheckStrictly()) + .append("deptCheckStrictly", isDeptCheckStrictly()) + .append("status", getStatus()) + .append("delFlag", getDelFlag()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java index 9c2d862..7874b20 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java @@ -1,336 +1,338 @@ -package com.ruoyi.common.core.domain.entity; - -import java.util.Date; -import java.util.List; -import jakarta.validation.constraints.*; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.ruoyi.common.annotation.Excel; -import com.ruoyi.common.annotation.Excel.ColumnType; -import com.ruoyi.common.annotation.Excel.Type; -import com.ruoyi.common.annotation.Excels; -import com.ruoyi.common.core.domain.BaseEntity; -import com.ruoyi.common.utils.SecurityUtils; -import com.ruoyi.common.xss.Xss; - -/** - * 用户对象 sys_user - * - * @author ruoyi - */ -public class SysUser extends BaseEntity -{ - private static final long serialVersionUID = 1L; - - /** 用户ID */ - @Excel(name = "用户序号", type = Type.EXPORT, cellType = ColumnType.NUMERIC, prompt = "用户编号") - private Long userId; - - /** 部门ID */ - @Excel(name = "部门编号", type = Type.IMPORT) - private Long deptId; - - /** 用户账号 */ - @Excel(name = "登录名称") - private String userName; - - /** 用户昵称 */ - @Excel(name = "用户名称") - private String nickName; - - /** 用户邮箱 */ - @Excel(name = "用户邮箱") - private String email; - - /** 手机号码 */ - @Excel(name = "手机号码", cellType = ColumnType.TEXT) - private String phonenumber; - - /** 用户性别 */ - @Excel(name = "用户性别", readConverterExp = "0=男,1=女,2=未知") - private String sex; - - /** 用户头像 */ - private String avatar; - - /** 密码 */ - private String password; - - /** 账号状态(0正常 1停用) */ - @Excel(name = "账号状态", readConverterExp = "0=正常,1=停用") - private String status; - - /** 删除标志(0代表存在 2代表删除) */ - private String delFlag; - - /** 最后登录IP */ - @Excel(name = "最后登录IP", type = Type.EXPORT) - private String loginIp; - - /** 最后登录时间 */ - @Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT) - private Date loginDate; - - /** 密码最后更新时间 */ - private Date pwdUpdateDate; - - /** 部门对象 */ - @Excels({ - @Excel(name = "部门名称", targetAttr = "deptName", type = Type.EXPORT), - @Excel(name = "部门负责人", targetAttr = "leader", type = Type.EXPORT) - }) - private SysDept dept; - - /** 角色对象 */ - private List roles; - - /** 角色组 */ - private Long[] roleIds; - - /** 岗位组 */ - private Long[] postIds; - - /** 角色ID */ - private Long roleId; - - public SysUser() - { - - } - - public SysUser(Long userId) - { - this.userId = userId; - } - - public Long getUserId() - { - return userId; - } - - public void setUserId(Long userId) - { - this.userId = userId; - } - - public boolean isAdmin() - { - return SecurityUtils.isAdmin(this.userId); - } - - public Long getDeptId() - { - return deptId; - } - - public void setDeptId(Long deptId) - { - this.deptId = deptId; - } - - @Xss(message = "用户昵称不能包含脚本字符") - @Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符") - public String getNickName() - { - return nickName; - } - - public void setNickName(String nickName) - { - this.nickName = nickName; - } - - @Xss(message = "用户账号不能包含脚本字符") - @NotBlank(message = "用户账号不能为空") - @Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符") - public String getUserName() - { - return userName; - } - - public void setUserName(String userName) - { - this.userName = userName; - } - - @Email(message = "邮箱格式不正确") - @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") - public String getEmail() - { - return email; - } - - public void setEmail(String email) - { - this.email = email; - } - - @Size(min = 0, max = 11, message = "手机号码长度不能超过11个字符") - public String getPhonenumber() - { - return phonenumber; - } - - public void setPhonenumber(String phonenumber) - { - this.phonenumber = phonenumber; - } - - public String getSex() - { - return sex; - } - - public void setSex(String sex) - { - this.sex = sex; - } - - public String getAvatar() - { - return avatar; - } - - public void setAvatar(String avatar) - { - this.avatar = avatar; - } - - @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) - public String getPassword() - { - return password; - } - - public void setPassword(String password) - { - this.password = password; - } - - public String getStatus() - { - return status; - } - - public void setStatus(String status) - { - this.status = status; - } - - public String getDelFlag() - { - return delFlag; - } - - public void setDelFlag(String delFlag) - { - this.delFlag = delFlag; - } - - public String getLoginIp() - { - return loginIp; - } - - public void setLoginIp(String loginIp) - { - this.loginIp = loginIp; - } - - public Date getLoginDate() - { - return loginDate; - } - - public void setLoginDate(Date loginDate) - { - this.loginDate = loginDate; - } - - public Date getPwdUpdateDate() - { - return pwdUpdateDate; - } - - public void setPwdUpdateDate(Date pwdUpdateDate) - { - this.pwdUpdateDate = pwdUpdateDate; - } - - public SysDept getDept() - { - return dept; - } - - public void setDept(SysDept dept) - { - this.dept = dept; - } - - public List getRoles() - { - return roles; - } - - public void setRoles(List roles) - { - this.roles = roles; - } - - public Long[] getRoleIds() - { - return roleIds; - } - - public void setRoleIds(Long[] roleIds) - { - this.roleIds = roleIds; - } - - public Long[] getPostIds() - { - return postIds; - } - - public void setPostIds(Long[] postIds) - { - this.postIds = postIds; - } - - public Long getRoleId() - { - return roleId; - } - - public void setRoleId(Long roleId) - { - this.roleId = roleId; - } - - @Override - public String toString() { - return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) - .append("userId", getUserId()) - .append("deptId", getDeptId()) - .append("userName", getUserName()) - .append("nickName", getNickName()) - .append("email", getEmail()) - .append("phonenumber", getPhonenumber()) - .append("sex", getSex()) - .append("avatar", getAvatar()) - .append("password", getPassword()) - .append("status", getStatus()) - .append("delFlag", getDelFlag()) - .append("loginIp", getLoginIp()) - .append("loginDate", getLoginDate()) - .append("pwdUpdateDate", getPwdUpdateDate()) - .append("createBy", getCreateBy()) - .append("createTime", getCreateTime()) - .append("updateBy", getUpdateBy()) - .append("updateTime", getUpdateTime()) - .append("remark", getRemark()) - .append("dept", getDept()) - .toString(); - } -} +package com.ruoyi.common.core.domain.entity; + +import java.util.Date; +import java.util.List; +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; +import com.ruoyi.common.annotation.Excel.Type; +import com.ruoyi.common.annotation.Excels; +import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.xss.Xss; + +/** + * 用户对象 sys_user + * + * @author ruoyi + */ +public class SysUser extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 用户ID */ + @Excel(name = "用户序号", type = Type.EXPORT, cellType = ColumnType.NUMERIC, prompt = "用户编号") + private Long userId; + + /** 部门ID */ + @Excel(name = "部门编号", type = Type.IMPORT) + private Long deptId; + + /** 用户账号 */ + @Excel(name = "登录名称") + private String userName; + + /** 用户昵称 */ + @Excel(name = "用户名称") + private String nickName; + + /** 用户邮箱 */ + @Excel(name = "用户邮箱") + private String email; + + /** 手机号码 */ + @Excel(name = "手机号码", cellType = ColumnType.TEXT) + private String phonenumber; + + /** 用户性别 */ + @Excel(name = "用户性别", readConverterExp = "0=男,1=女,2=未知") + private String sex; + + /** 用户头像 */ + private String avatar; + + /** 密码 */ + private String password; + + /** 账号状态(0正常 1停用) */ + @Excel(name = "账号状态", readConverterExp = "0=正常,1=停用") + private String status; + + /** 删除标志(0代表存在 2代表删除) */ + private String delFlag; + + /** 最后登录IP */ + @Excel(name = "最后登录IP", type = Type.EXPORT) + 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; + + /** 密码最后更新时间 */ + private Date pwdUpdateDate; + + /** 部门对象 */ + @Excels({ + @Excel(name = "部门名称", targetAttr = "deptName", type = Type.EXPORT), + @Excel(name = "部门负责人", targetAttr = "leader", type = Type.EXPORT) + }) + private SysDept dept; + + /** 角色对象 */ + private List roles; + + /** 角色组 */ + private Long[] roleIds; + + /** 岗位组 */ + private Long[] postIds; + + /** 角色ID */ + private Long roleId; + + public SysUser() + { + + } + + public SysUser(Long userId) + { + this.userId = userId; + } + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public boolean isAdmin() + { + return SecurityUtils.isAdmin(this.userId); + } + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + @Xss(message = "用户昵称不能包含脚本字符") + @Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符") + public String getNickName() + { + return nickName; + } + + public void setNickName(String nickName) + { + this.nickName = nickName; + } + + @Xss(message = "用户账号不能包含脚本字符") + @NotBlank(message = "用户账号不能为空") + @Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符") + public String getUserName() + { + return userName; + } + + public void setUserName(String userName) + { + this.userName = userName; + } + + @Email(message = "邮箱格式不正确") + @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") + public String getEmail() + { + return email; + } + + public void setEmail(String email) + { + this.email = email; + } + + @Size(min = 0, max = 11, message = "手机号码长度不能超过11个字符") + public String getPhonenumber() + { + return phonenumber; + } + + public void setPhonenumber(String phonenumber) + { + this.phonenumber = phonenumber; + } + + public String getSex() + { + return sex; + } + + public void setSex(String sex) + { + this.sex = sex; + } + + public String getAvatar() + { + return avatar; + } + + public void setAvatar(String avatar) + { + this.avatar = avatar; + } + + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + public String getPassword() + { + return password; + } + + public void setPassword(String password) + { + this.password = password; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getDelFlag() + { + return delFlag; + } + + public void setDelFlag(String delFlag) + { + this.delFlag = delFlag; + } + + public String getLoginIp() + { + return loginIp; + } + + public void setLoginIp(String loginIp) + { + this.loginIp = loginIp; + } + + public Date getLoginDate() + { + return loginDate; + } + + public void setLoginDate(Date loginDate) + { + this.loginDate = loginDate; + } + + public Date getPwdUpdateDate() + { + return pwdUpdateDate; + } + + public void setPwdUpdateDate(Date pwdUpdateDate) + { + this.pwdUpdateDate = pwdUpdateDate; + } + + public SysDept getDept() + { + return dept; + } + + public void setDept(SysDept dept) + { + this.dept = dept; + } + + public List getRoles() + { + return roles; + } + + public void setRoles(List roles) + { + this.roles = roles; + } + + public Long[] getRoleIds() + { + return roleIds; + } + + public void setRoleIds(Long[] roleIds) + { + this.roleIds = roleIds; + } + + public Long[] getPostIds() + { + return postIds; + } + + public void setPostIds(Long[] postIds) + { + this.postIds = postIds; + } + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("userId", getUserId()) + .append("deptId", getDeptId()) + .append("userName", getUserName()) + .append("nickName", getNickName()) + .append("email", getEmail()) + .append("phonenumber", getPhonenumber()) + .append("sex", getSex()) + .append("avatar", getAvatar()) + .append("password", getPassword()) + .append("status", getStatus()) + .append("delFlag", getDelFlag()) + .append("loginIp", getLoginIp()) + .append("loginDate", getLoginDate()) + .append("pwdUpdateDate", getPwdUpdateDate()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .append("dept", getDept()) + .toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java index b5bc8c8..583b7a6 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java @@ -1,69 +1,69 @@ -package com.ruoyi.common.core.domain.model; - -/** - * 用户登录对象 - * - * @author ruoyi - */ -public class LoginBody -{ - /** - * 用户名 - */ - private String username; - - /** - * 用户密码 - */ - private String password; - - /** - * 验证码 - */ - private String code; - - /** - * 唯一标识 - */ - private String uuid; - - public String getUsername() - { - return username; - } - - public void setUsername(String username) - { - this.username = username; - } - - public String getPassword() - { - return password; - } - - public void setPassword(String password) - { - this.password = password; - } - - public String getCode() - { - return code; - } - - public void setCode(String code) - { - this.code = code; - } - - public String getUuid() - { - return uuid; - } - - public void setUuid(String uuid) - { - this.uuid = uuid; - } -} +package com.ruoyi.common.core.domain.model; + +/** + * 用户登录对象 + * + * @author ruoyi + */ +public class LoginBody +{ + /** + * 用户名 + */ + private String username; + + /** + * 用户密码 + */ + private String password; + + /** + * 验证码 + */ + private String code; + + /** + * 唯一标识 + */ + private String uuid; + + public String getUsername() + { + return username; + } + + public void setUsername(String username) + { + this.username = username; + } + + public String getPassword() + { + return password; + } + + public void setPassword(String password) + { + this.password = password; + } + + public String getCode() + { + return code; + } + + public void setCode(String code) + { + this.code = code; + } + + public String getUuid() + { + return uuid; + } + + public void setUuid(String uuid) + { + this.uuid = uuid; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java index 670e6b3..95e169c 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java @@ -1,266 +1,266 @@ -package com.ruoyi.common.core.domain.model; - -import com.alibaba.fastjson2.annotation.JSONField; -import com.ruoyi.common.core.domain.entity.SysUser; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.userdetails.UserDetails; -import java.util.Collection; -import java.util.Set; - -/** - * 登录用户身份权限 - * - * @author ruoyi - */ -public class LoginUser implements UserDetails -{ - private static final long serialVersionUID = 1L; - - /** - * 用户ID - */ - private Long userId; - - /** - * 部门ID - */ - private Long deptId; - - /** - * 用户唯一标识 - */ - private String token; - - /** - * 登录时间 - */ - private Long loginTime; - - /** - * 过期时间 - */ - private Long expireTime; - - /** - * 登录IP地址 - */ - private String ipaddr; - - /** - * 登录地点 - */ - private String loginLocation; - - /** - * 浏览器类型 - */ - private String browser; - - /** - * 操作系统 - */ - private String os; - - /** - * 权限列表 - */ - private Set permissions; - - /** - * 用户信息 - */ - private SysUser user; - - public LoginUser() - { - } - - public LoginUser(SysUser user, Set permissions) - { - this.user = user; - this.permissions = permissions; - } - - public LoginUser(Long userId, Long deptId, SysUser user, Set permissions) - { - this.userId = userId; - this.deptId = deptId; - this.user = user; - this.permissions = permissions; - } - - public Long getUserId() - { - return userId; - } - - public void setUserId(Long userId) - { - this.userId = userId; - } - - public Long getDeptId() - { - return deptId; - } - - public void setDeptId(Long deptId) - { - this.deptId = deptId; - } - - public String getToken() - { - return token; - } - - public void setToken(String token) - { - this.token = token; - } - - @JSONField(serialize = false) - @Override - public String getPassword() - { - return user.getPassword(); - } - - @Override - public String getUsername() - { - return user.getUserName(); - } - - /** - * 账户是否未过期,过期无法验证 - */ - @JSONField(serialize = false) - @Override - public boolean isAccountNonExpired() - { - return true; - } - - /** - * 指定用户是否解锁,锁定的用户无法进行身份验证 - * - * @return - */ - @JSONField(serialize = false) - @Override - public boolean isAccountNonLocked() - { - return true; - } - - /** - * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证 - * - * @return - */ - @JSONField(serialize = false) - @Override - public boolean isCredentialsNonExpired() - { - return true; - } - - /** - * 是否可用 ,禁用的用户不能身份验证 - * - * @return - */ - @JSONField(serialize = false) - @Override - public boolean isEnabled() - { - return true; - } - - public Long getLoginTime() - { - return loginTime; - } - - public void setLoginTime(Long loginTime) - { - this.loginTime = loginTime; - } - - public String getIpaddr() - { - return ipaddr; - } - - public void setIpaddr(String ipaddr) - { - this.ipaddr = ipaddr; - } - - public String getLoginLocation() - { - return loginLocation; - } - - public void setLoginLocation(String loginLocation) - { - this.loginLocation = loginLocation; - } - - public String getBrowser() - { - return browser; - } - - public void setBrowser(String browser) - { - this.browser = browser; - } - - public String getOs() - { - return os; - } - - public void setOs(String os) - { - this.os = os; - } - - public Long getExpireTime() - { - return expireTime; - } - - public void setExpireTime(Long expireTime) - { - this.expireTime = expireTime; - } - - public Set getPermissions() - { - return permissions; - } - - public void setPermissions(Set permissions) - { - this.permissions = permissions; - } - - public SysUser getUser() - { - return user; - } - - public void setUser(SysUser user) - { - this.user = user; - } - - @Override - public Collection getAuthorities() - { - return null; - } -} +package com.ruoyi.common.core.domain.model; + +import com.alibaba.fastjson2.annotation.JSONField; +import com.ruoyi.common.core.domain.entity.SysUser; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import java.util.Collection; +import java.util.Set; + +/** + * 登录用户身份权限 + * + * @author ruoyi + */ +public class LoginUser implements UserDetails +{ + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + private Long userId; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 用户唯一标识 + */ + private String token; + + /** + * 登录时间 + */ + private Long loginTime; + + /** + * 过期时间 + */ + private Long expireTime; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地点 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 权限列表 + */ + private Set permissions; + + /** + * 用户信息 + */ + private SysUser user; + + public LoginUser() + { + } + + public LoginUser(SysUser user, Set permissions) + { + this.user = user; + this.permissions = permissions; + } + + public LoginUser(Long userId, Long deptId, SysUser user, Set permissions) + { + this.userId = userId; + this.deptId = deptId; + this.user = user; + this.permissions = permissions; + } + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + public String getToken() + { + return token; + } + + public void setToken(String token) + { + this.token = token; + } + + @JSONField(serialize = false) + @Override + public String getPassword() + { + return user.getPassword(); + } + + @Override + public String getUsername() + { + return user.getUserName(); + } + + /** + * 账户是否未过期,过期无法验证 + */ + @JSONField(serialize = false) + @Override + public boolean isAccountNonExpired() + { + return true; + } + + /** + * 指定用户是否解锁,锁定的用户无法进行身份验证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isAccountNonLocked() + { + return true; + } + + /** + * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isCredentialsNonExpired() + { + return true; + } + + /** + * 是否可用 ,禁用的用户不能身份验证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isEnabled() + { + return true; + } + + public Long getLoginTime() + { + return loginTime; + } + + public void setLoginTime(Long loginTime) + { + this.loginTime = loginTime; + } + + public String getIpaddr() + { + return ipaddr; + } + + public void setIpaddr(String ipaddr) + { + this.ipaddr = ipaddr; + } + + public String getLoginLocation() + { + return loginLocation; + } + + public void setLoginLocation(String loginLocation) + { + this.loginLocation = loginLocation; + } + + public String getBrowser() + { + return browser; + } + + public void setBrowser(String browser) + { + this.browser = browser; + } + + public String getOs() + { + return os; + } + + public void setOs(String os) + { + this.os = os; + } + + public Long getExpireTime() + { + return expireTime; + } + + public void setExpireTime(Long expireTime) + { + this.expireTime = expireTime; + } + + public Set getPermissions() + { + return permissions; + } + + public void setPermissions(Set permissions) + { + this.permissions = permissions; + } + + public SysUser getUser() + { + return user; + } + + public void setUser(SysUser user) + { + this.user = user; + } + + @Override + public Collection getAuthorities() + { + return null; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/RegisterBody.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/RegisterBody.java index 868a1fc..5baa887 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/RegisterBody.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/RegisterBody.java @@ -1,11 +1,11 @@ -package com.ruoyi.common.core.domain.model; - -/** - * 用户注册对象 - * - * @author ruoyi - */ -public class RegisterBody extends LoginBody -{ - -} +package com.ruoyi.common.core.domain.model; + +/** + * 用户注册对象 + * + * @author ruoyi + */ +public class RegisterBody extends LoginBody +{ + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java index 2fff93e..f0befe7 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java @@ -1,85 +1,85 @@ -package com.ruoyi.common.core.page; - -import java.io.Serializable; -import java.util.List; - -/** - * 表格分页数据对象 - * - * @author ruoyi - */ -public class TableDataInfo implements Serializable -{ - private static final long serialVersionUID = 1L; - - /** 总记录数 */ - private long total; - - /** 列表数据 */ - private List rows; - - /** 消息状态码 */ - private int code; - - /** 消息内容 */ - private String msg; - - /** - * 表格数据对象 - */ - public TableDataInfo() - { - } - - /** - * 分页 - * - * @param list 列表数据 - * @param total 总记录数 - */ - public TableDataInfo(List list, long total) - { - this.rows = list; - this.total = total; - } - - public long getTotal() - { - return total; - } - - public void setTotal(long total) - { - this.total = total; - } - - public List getRows() - { - return rows; - } - - public void setRows(List rows) - { - this.rows = rows; - } - - public int getCode() - { - return code; - } - - public void setCode(int code) - { - this.code = code; - } - - public String getMsg() - { - return msg; - } - - public void setMsg(String msg) - { - this.msg = msg; - } -} +package com.ruoyi.common.core.page; + +import java.io.Serializable; +import java.util.List; + +/** + * 表格分页数据对象 + * + * @author ruoyi + */ +public class TableDataInfo implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 总记录数 */ + private long total; + + /** 列表数据 */ + private List rows; + + /** 消息状态码 */ + private int code; + + /** 消息内容 */ + private String msg; + + /** + * 表格数据对象 + */ + public TableDataInfo() + { + } + + /** + * 分页 + * + * @param list 列表数据 + * @param total 总记录数 + */ + public TableDataInfo(List list, long total) + { + this.rows = list; + this.total = total; + } + + public long getTotal() + { + return total; + } + + public void setTotal(long total) + { + this.total = total; + } + + public List getRows() + { + return rows; + } + + public void setRows(List rows) + { + this.rows = rows; + } + + public int getCode() + { + return code; + } + + public void setCode(int code) + { + this.code = code; + } + + public String getMsg() + { + return msg; + } + + public void setMsg(String msg) + { + this.msg = msg; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableSupport.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableSupport.java index a120c30..62ac4f9 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableSupport.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableSupport.java @@ -1,56 +1,56 @@ -package com.ruoyi.common.core.page; - -import com.ruoyi.common.core.text.Convert; -import com.ruoyi.common.utils.ServletUtils; - -/** - * 表格数据处理 - * - * @author ruoyi - */ -public class TableSupport -{ - /** - * 当前记录起始索引 - */ - public static final String PAGE_NUM = "pageNum"; - - /** - * 每页显示记录数 - */ - public static final String PAGE_SIZE = "pageSize"; - - /** - * 排序列 - */ - public static final String ORDER_BY_COLUMN = "orderByColumn"; - - /** - * 排序的方向 "desc" 或者 "asc". - */ - public static final String IS_ASC = "isAsc"; - - /** - * 分页参数合理化 - */ - public static final String REASONABLE = "reasonable"; - - /** - * 封装分页对象 - */ - public static PageDomain getPageDomain() - { - PageDomain pageDomain = new PageDomain(); - pageDomain.setPageNum(Convert.toInt(ServletUtils.getParameter(PAGE_NUM), 1)); - pageDomain.setPageSize(Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 10)); - pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN)); - pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC)); - pageDomain.setReasonable(ServletUtils.getParameterToBool(REASONABLE)); - return pageDomain; - } - - public static PageDomain buildPageRequest() - { - return getPageDomain(); - } -} +package com.ruoyi.common.core.page; + +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.utils.ServletUtils; + +/** + * 表格数据处理 + * + * @author ruoyi + */ +public class TableSupport +{ + /** + * 当前记录起始索引 + */ + public static final String PAGE_NUM = "pageNum"; + + /** + * 每页显示记录数 + */ + public static final String PAGE_SIZE = "pageSize"; + + /** + * 排序列 + */ + public static final String ORDER_BY_COLUMN = "orderByColumn"; + + /** + * 排序的方向 "desc" 或者 "asc". + */ + public static final String IS_ASC = "isAsc"; + + /** + * 分页参数合理化 + */ + public static final String REASONABLE = "reasonable"; + + /** + * 封装分页对象 + */ + public static PageDomain getPageDomain() + { + PageDomain pageDomain = new PageDomain(); + pageDomain.setPageNum(Convert.toInt(ServletUtils.getParameter(PAGE_NUM), 1)); + pageDomain.setPageSize(Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 10)); + pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN)); + pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC)); + pageDomain.setReasonable(ServletUtils.getParameterToBool(REASONABLE)); + return pageDomain; + } + + public static PageDomain buildPageRequest() + { + return getPageDomain(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java index 984bbf8..a49d943 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java @@ -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 diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/text/CharsetKit.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/CharsetKit.java index 84124aa..4fa575c 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/text/CharsetKit.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/CharsetKit.java @@ -1,86 +1,86 @@ -package com.ruoyi.common.core.text; - -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import com.ruoyi.common.utils.StringUtils; - -/** - * 字符集工具类 - * - * @author ruoyi - */ -public class CharsetKit -{ - /** ISO-8859-1 */ - public static final String ISO_8859_1 = "ISO-8859-1"; - /** UTF-8 */ - public static final String UTF_8 = "UTF-8"; - /** GBK */ - public static final String GBK = "GBK"; - - /** ISO-8859-1 */ - public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1); - /** UTF-8 */ - public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8); - /** GBK */ - public static final Charset CHARSET_GBK = Charset.forName(GBK); - - /** - * 转换为Charset对象 - * - * @param charset 字符集,为空则返回默认字符集 - * @return Charset - */ - public static Charset charset(String charset) - { - return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset); - } - - /** - * 转换字符串的字符集编码 - * - * @param source 字符串 - * @param srcCharset 源字符集,默认ISO-8859-1 - * @param destCharset 目标字符集,默认UTF-8 - * @return 转换后的字符集 - */ - public static String convert(String source, String srcCharset, String destCharset) - { - return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset)); - } - - /** - * 转换字符串的字符集编码 - * - * @param source 字符串 - * @param srcCharset 源字符集,默认ISO-8859-1 - * @param destCharset 目标字符集,默认UTF-8 - * @return 转换后的字符集 - */ - public static String convert(String source, Charset srcCharset, Charset destCharset) - { - if (null == srcCharset) - { - srcCharset = StandardCharsets.ISO_8859_1; - } - - if (null == destCharset) - { - destCharset = StandardCharsets.UTF_8; - } - - if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset)) - { - return source; - } - return new String(source.getBytes(srcCharset), destCharset); - } - - /** - * @return 系统字符集编码 - */ - public static String systemCharset() - { - return Charset.defaultCharset().name(); - } -} +package com.ruoyi.common.core.text; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import com.ruoyi.common.utils.StringUtils; + +/** + * 字符集工具类 + * + * @author ruoyi + */ +public class CharsetKit +{ + /** ISO-8859-1 */ + public static final String ISO_8859_1 = "ISO-8859-1"; + /** UTF-8 */ + public static final String UTF_8 = "UTF-8"; + /** GBK */ + public static final String GBK = "GBK"; + + /** ISO-8859-1 */ + public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1); + /** UTF-8 */ + public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8); + /** GBK */ + public static final Charset CHARSET_GBK = Charset.forName(GBK); + + /** + * 转换为Charset对象 + * + * @param charset 字符集,为空则返回默认字符集 + * @return Charset + */ + public static Charset charset(String charset) + { + return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset); + } + + /** + * 转换字符串的字符集编码 + * + * @param source 字符串 + * @param srcCharset 源字符集,默认ISO-8859-1 + * @param destCharset 目标字符集,默认UTF-8 + * @return 转换后的字符集 + */ + public static String convert(String source, String srcCharset, String destCharset) + { + return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset)); + } + + /** + * 转换字符串的字符集编码 + * + * @param source 字符串 + * @param srcCharset 源字符集,默认ISO-8859-1 + * @param destCharset 目标字符集,默认UTF-8 + * @return 转换后的字符集 + */ + public static String convert(String source, Charset srcCharset, Charset destCharset) + { + if (null == srcCharset) + { + srcCharset = StandardCharsets.ISO_8859_1; + } + + if (null == destCharset) + { + destCharset = StandardCharsets.UTF_8; + } + + if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset)) + { + return source; + } + return new String(source.getBytes(srcCharset), destCharset); + } + + /** + * @return 系统字符集编码 + */ + public static String systemCharset() + { + return Charset.defaultCharset().name(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java index a87828a..32bbfdb 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java @@ -1,1018 +1,1018 @@ -package com.ruoyi.common.core.text; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.math.RoundingMode; -import java.nio.ByteBuffer; -import java.nio.charset.Charset; -import java.text.NumberFormat; -import java.util.Set; -import com.ruoyi.common.utils.StringUtils; - -/** - * 类型转换器 - * - * @author ruoyi - */ -public class Convert -{ - /** - * 转换为字符串
- * 如果给定的值为null,或者转换失败,返回默认值
- * 转换失败不会报错 - * - * @param value 被转换的值 - * @param defaultValue 转换错误时的默认值 - * @return 结果 - */ - public static String toStr(Object value, String defaultValue) - { - if (null == value) - { - return defaultValue; - } - if (value instanceof String) - { - return (String) value; - } - return value.toString(); - } - - /** - * 转换为字符串
- * 如果给定的值为null,或者转换失败,返回默认值null
- * 转换失败不会报错 - * - * @param value 被转换的值 - * @return 结果 - */ - public static String toStr(Object value) - { - return toStr(value, null); - } - - /** - * 转换为字符
- * 如果给定的值为null,或者转换失败,返回默认值
- * 转换失败不会报错 - * - * @param value 被转换的值 - * @param defaultValue 转换错误时的默认值 - * @return 结果 - */ - public static Character toChar(Object value, Character defaultValue) - { - if (null == value) - { - return defaultValue; - } - if (value instanceof Character) - { - return (Character) value; - } - - final String valueStr = toStr(value, null); - return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0); - } - - /** - * 转换为字符
- * 如果给定的值为null,或者转换失败,返回默认值null
- * 转换失败不会报错 - * - * @param value 被转换的值 - * @return 结果 - */ - public static Character toChar(Object value) - { - return toChar(value, null); - } - - /** - * 转换为byte
- * 如果给定的值为null,或者转换失败,返回默认值
- * 转换失败不会报错 - * - * @param value 被转换的值 - * @param defaultValue 转换错误时的默认值 - * @return 结果 - */ - public static Byte toByte(Object value, Byte defaultValue) - { - if (value == null) - { - return defaultValue; - } - if (value instanceof Byte) - { - return (Byte) value; - } - if (value instanceof Number) - { - return ((Number) value).byteValue(); - } - final String valueStr = toStr(value, null); - if (StringUtils.isEmpty(valueStr)) - { - return defaultValue; - } - try - { - return Byte.parseByte(valueStr); - } - catch (Exception e) - { - return defaultValue; - } - } - - /** - * 转换为byte
- * 如果给定的值为null,或者转换失败,返回默认值null
- * 转换失败不会报错 - * - * @param value 被转换的值 - * @return 结果 - */ - public static Byte toByte(Object value) - { - return toByte(value, null); - } - - /** - * 转换为Short
- * 如果给定的值为null,或者转换失败,返回默认值
- * 转换失败不会报错 - * - * @param value 被转换的值 - * @param defaultValue 转换错误时的默认值 - * @return 结果 - */ - public static Short toShort(Object value, Short defaultValue) - { - if (value == null) - { - return defaultValue; - } - if (value instanceof Short) - { - return (Short) value; - } - if (value instanceof Number) - { - return ((Number) value).shortValue(); - } - final String valueStr = toStr(value, null); - if (StringUtils.isEmpty(valueStr)) - { - return defaultValue; - } - try - { - return Short.parseShort(valueStr.trim()); - } - catch (Exception e) - { - return defaultValue; - } - } - - /** - * 转换为Short
- * 如果给定的值为null,或者转换失败,返回默认值null
- * 转换失败不会报错 - * - * @param value 被转换的值 - * @return 结果 - */ - public static Short toShort(Object value) - { - return toShort(value, null); - } - - /** - * 转换为Number
- * 如果给定的值为空,或者转换失败,返回默认值
- * 转换失败不会报错 - * - * @param value 被转换的值 - * @param defaultValue 转换错误时的默认值 - * @return 结果 - */ - public static Number toNumber(Object value, Number defaultValue) - { - if (value == null) - { - return defaultValue; - } - if (value instanceof Number) - { - return (Number) value; - } - final String valueStr = toStr(value, null); - if (StringUtils.isEmpty(valueStr)) - { - return defaultValue; - } - try - { - return NumberFormat.getInstance().parse(valueStr); - } - catch (Exception e) - { - return defaultValue; - } - } - - /** - * 转换为Number
- * 如果给定的值为空,或者转换失败,返回默认值null
- * 转换失败不会报错 - * - * @param value 被转换的值 - * @return 结果 - */ - public static Number toNumber(Object value) - { - return toNumber(value, null); - } - - /** - * 转换为int
- * 如果给定的值为空,或者转换失败,返回默认值
- * 转换失败不会报错 - * - * @param value 被转换的值 - * @param defaultValue 转换错误时的默认值 - * @return 结果 - */ - public static Integer toInt(Object value, Integer defaultValue) - { - if (value == null) - { - return defaultValue; - } - if (value instanceof Integer) - { - return (Integer) value; - } - if (value instanceof Number) - { - return ((Number) value).intValue(); - } - final String valueStr = toStr(value, null); - if (StringUtils.isEmpty(valueStr)) - { - return defaultValue; - } - try - { - return Integer.parseInt(valueStr.trim()); - } - catch (Exception e) - { - return defaultValue; - } - } - - /** - * 转换为int
- * 如果给定的值为null,或者转换失败,返回默认值null
- * 转换失败不会报错 - * - * @param value 被转换的值 - * @return 结果 - */ - public static Integer toInt(Object value) - { - return toInt(value, null); - } - - /** - * 转换为Integer数组
- * - * @param str 被转换的值 - * @return 结果 - */ - public static Integer[] toIntArray(String str) - { - return toIntArray(",", str); - } - - /** - * 转换为Long数组
- * - * @param str 被转换的值 - * @return 结果 - */ - public static Long[] toLongArray(String str) - { - return toLongArray(",", str); - } - - /** - * 转换为Integer数组
- * - * @param split 分隔符 - * @param split 被转换的值 - * @return 结果 - */ - public static Integer[] toIntArray(String split, String str) - { - if (StringUtils.isEmpty(str)) - { - return new Integer[] {}; - } - String[] arr = str.split(split); - final Integer[] ints = new Integer[arr.length]; - for (int i = 0; i < arr.length; i++) - { - final Integer v = toInt(arr[i], 0); - ints[i] = v; - } - return ints; - } - - /** - * 转换为Long数组
- * - * @param split 分隔符 - * @param str 被转换的值 - * @return 结果 - */ - public static Long[] toLongArray(String split, String str) - { - if (StringUtils.isEmpty(str)) - { - return new Long[] {}; - } - String[] arr = str.split(split); - final Long[] longs = new Long[arr.length]; - for (int i = 0; i < arr.length; i++) - { - final Long v = toLong(arr[i], null); - longs[i] = v; - } - return longs; - } - - /** - * 转换为String数组
- * - * @param str 被转换的值 - * @return 结果 - */ - public static String[] toStrArray(String str) - { - if (StringUtils.isEmpty(str)) - { - return new String[] {}; - } - return toStrArray(",", str); - } - - /** - * 转换为String数组
- * - * @param split 分隔符 - * @param split 被转换的值 - * @return 结果 - */ - public static String[] toStrArray(String split, String str) - { - return str.split(split); - } - - /** - * 转换为long
- * 如果给定的值为空,或者转换失败,返回默认值
- * 转换失败不会报错 - * - * @param value 被转换的值 - * @param defaultValue 转换错误时的默认值 - * @return 结果 - */ - public static Long toLong(Object value, Long defaultValue) - { - if (value == null) - { - return defaultValue; - } - if (value instanceof Long) - { - return (Long) value; - } - if (value instanceof Number) - { - return ((Number) value).longValue(); - } - final String valueStr = toStr(value, null); - if (StringUtils.isEmpty(valueStr)) - { - return defaultValue; - } - try - { - // 支持科学计数法 - return new BigDecimal(valueStr.trim()).longValue(); - } - catch (Exception e) - { - return defaultValue; - } - } - - /** - * 转换为long
- * 如果给定的值为null,或者转换失败,返回默认值null
- * 转换失败不会报错 - * - * @param value 被转换的值 - * @return 结果 - */ - public static Long toLong(Object value) - { - return toLong(value, null); - } - - /** - * 转换为double
- * 如果给定的值为空,或者转换失败,返回默认值
- * 转换失败不会报错 - * - * @param value 被转换的值 - * @param defaultValue 转换错误时的默认值 - * @return 结果 - */ - public static Double toDouble(Object value, Double defaultValue) - { - if (value == null) - { - return defaultValue; - } - if (value instanceof Double) - { - return (Double) value; - } - if (value instanceof Number) - { - return ((Number) value).doubleValue(); - } - final String valueStr = toStr(value, null); - if (StringUtils.isEmpty(valueStr)) - { - return defaultValue; - } - try - { - // 支持科学计数法 - return new BigDecimal(valueStr.trim()).doubleValue(); - } - catch (Exception e) - { - return defaultValue; - } - } - - /** - * 转换为double
- * 如果给定的值为空,或者转换失败,返回默认值null
- * 转换失败不会报错 - * - * @param value 被转换的值 - * @return 结果 - */ - public static Double toDouble(Object value) - { - return toDouble(value, null); - } - - /** - * 转换为Float
- * 如果给定的值为空,或者转换失败,返回默认值
- * 转换失败不会报错 - * - * @param value 被转换的值 - * @param defaultValue 转换错误时的默认值 - * @return 结果 - */ - public static Float toFloat(Object value, Float defaultValue) - { - if (value == null) - { - return defaultValue; - } - if (value instanceof Float) - { - return (Float) value; - } - if (value instanceof Number) - { - return ((Number) value).floatValue(); - } - final String valueStr = toStr(value, null); - if (StringUtils.isEmpty(valueStr)) - { - return defaultValue; - } - try - { - return Float.parseFloat(valueStr.trim()); - } - catch (Exception e) - { - return defaultValue; - } - } - - /** - * 转换为Float
- * 如果给定的值为空,或者转换失败,返回默认值null
- * 转换失败不会报错 - * - * @param value 被转换的值 - * @return 结果 - */ - public static Float toFloat(Object value) - { - return toFloat(value, null); - } - - /** - * 转换为boolean
- * String支持的值为:true、false、yes、ok、no、1、0、是、否, 如果给定的值为空,或者转换失败,返回默认值
- * 转换失败不会报错 - * - * @param value 被转换的值 - * @param defaultValue 转换错误时的默认值 - * @return 结果 - */ - public static Boolean toBool(Object value, Boolean defaultValue) - { - if (value == null) - { - return defaultValue; - } - if (value instanceof Boolean) - { - return (Boolean) value; - } - String valueStr = toStr(value, null); - if (StringUtils.isEmpty(valueStr)) - { - return defaultValue; - } - valueStr = valueStr.trim().toLowerCase(); - switch (valueStr) - { - case "true": - case "yes": - case "ok": - case "1": - case "是": - return true; - case "false": - case "no": - case "0": - case "否": - return false; - default: - return defaultValue; - } - } - - /** - * 转换为boolean
- * 如果给定的值为空,或者转换失败,返回默认值null
- * 转换失败不会报错 - * - * @param value 被转换的值 - * @return 结果 - */ - public static Boolean toBool(Object value) - { - return toBool(value, null); - } - - /** - * 转换为Enum对象
- * 如果给定的值为空,或者转换失败,返回默认值
- * - * @param clazz Enum的Class - * @param value 值 - * @param defaultValue 默认值 - * @return Enum - */ - public static > E toEnum(Class clazz, Object value, E defaultValue) - { - if (value == null) - { - return defaultValue; - } - if (clazz.isAssignableFrom(value.getClass())) - { - @SuppressWarnings("unchecked") - E myE = (E) value; - return myE; - } - final String valueStr = toStr(value, null); - if (StringUtils.isEmpty(valueStr)) - { - return defaultValue; - } - try - { - return Enum.valueOf(clazz, valueStr); - } - catch (Exception e) - { - return defaultValue; - } - } - - /** - * 转换为Enum对象
- * 如果给定的值为空,或者转换失败,返回默认值null
- * - * @param clazz Enum的Class - * @param value 值 - * @return Enum - */ - public static > E toEnum(Class clazz, Object value) - { - return toEnum(clazz, value, null); - } - - /** - * 转换为BigInteger
- * 如果给定的值为空,或者转换失败,返回默认值
- * 转换失败不会报错 - * - * @param value 被转换的值 - * @param defaultValue 转换错误时的默认值 - * @return 结果 - */ - public static BigInteger toBigInteger(Object value, BigInteger defaultValue) - { - if (value == null) - { - return defaultValue; - } - if (value instanceof BigInteger) - { - return (BigInteger) value; - } - if (value instanceof Long) - { - return BigInteger.valueOf((Long) value); - } - final String valueStr = toStr(value, null); - if (StringUtils.isEmpty(valueStr)) - { - return defaultValue; - } - try - { - return new BigInteger(valueStr); - } - catch (Exception e) - { - return defaultValue; - } - } - - /** - * 转换为BigInteger
- * 如果给定的值为空,或者转换失败,返回默认值null
- * 转换失败不会报错 - * - * @param value 被转换的值 - * @return 结果 - */ - public static BigInteger toBigInteger(Object value) - { - return toBigInteger(value, null); - } - - /** - * 转换为BigDecimal
- * 如果给定的值为空,或者转换失败,返回默认值
- * 转换失败不会报错 - * - * @param value 被转换的值 - * @param defaultValue 转换错误时的默认值 - * @return 结果 - */ - public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue) - { - if (value == null) - { - return defaultValue; - } - if (value instanceof BigDecimal) - { - return (BigDecimal) value; - } - if (value instanceof Long) - { - return new BigDecimal((Long) value); - } - if (value instanceof Double) - { - return BigDecimal.valueOf((Double) value); - } - if (value instanceof Integer) - { - return new BigDecimal((Integer) value); - } - final String valueStr = toStr(value, null); - if (StringUtils.isEmpty(valueStr)) - { - return defaultValue; - } - try - { - return new BigDecimal(valueStr); - } - catch (Exception e) - { - return defaultValue; - } - } - - /** - * 转换为BigDecimal
- * 如果给定的值为空,或者转换失败,返回默认值
- * 转换失败不会报错 - * - * @param value 被转换的值 - * @return 结果 - */ - public static BigDecimal toBigDecimal(Object value) - { - return toBigDecimal(value, null); - } - - /** - * 将对象转为字符串
- * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 - * - * @param obj 对象 - * @return 字符串 - */ - public static String utf8Str(Object obj) - { - return str(obj, CharsetKit.CHARSET_UTF_8); - } - - /** - * 将对象转为字符串
- * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 - * - * @param obj 对象 - * @param charsetName 字符集 - * @return 字符串 - */ - public static String str(Object obj, String charsetName) - { - return str(obj, Charset.forName(charsetName)); - } - - /** - * 将对象转为字符串
- * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 - * - * @param obj 对象 - * @param charset 字符集 - * @return 字符串 - */ - public static String str(Object obj, Charset charset) - { - if (null == obj) - { - return null; - } - - if (obj instanceof String) - { - return (String) obj; - } - else if (obj instanceof byte[] || obj instanceof Byte[]) - { - if (obj instanceof byte[]) - { - return str((byte[]) obj, charset); - } - else - { - Byte[] bytes = (Byte[]) obj; - int length = bytes.length; - byte[] dest = new byte[length]; - for (int i = 0; i < length; i++) - { - dest[i] = bytes[i]; - } - return str(dest, charset); - } - } - else if (obj instanceof ByteBuffer) - { - return str((ByteBuffer) obj, charset); - } - return obj.toString(); - } - - /** - * 将byte数组转为字符串 - * - * @param bytes byte数组 - * @param charset 字符集 - * @return 字符串 - */ - public static String str(byte[] bytes, String charset) - { - return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset)); - } - - /** - * 解码字节码 - * - * @param data 字符串 - * @param charset 字符集,如果此字段为空,则解码的结果取决于平台 - * @return 解码后的字符串 - */ - public static String str(byte[] data, Charset charset) - { - if (data == null) - { - return null; - } - - if (null == charset) - { - return new String(data); - } - return new String(data, charset); - } - - /** - * 将编码的byteBuffer数据转换为字符串 - * - * @param data 数据 - * @param charset 字符集,如果为空使用当前系统字符集 - * @return 字符串 - */ - public static String str(ByteBuffer data, String charset) - { - if (data == null) - { - return null; - } - - return str(data, Charset.forName(charset)); - } - - /** - * 将编码的byteBuffer数据转换为字符串 - * - * @param data 数据 - * @param charset 字符集,如果为空使用当前系统字符集 - * @return 字符串 - */ - public static String str(ByteBuffer data, Charset charset) - { - if (null == charset) - { - charset = Charset.defaultCharset(); - } - return charset.decode(data).toString(); - } - - // ----------------------------------------------------------------------- 全角半角转换 - /** - * 半角转全角 - * - * @param input String. - * @return 全角字符串. - */ - public static String toSBC(String input) - { - return toSBC(input, null); - } - - /** - * 半角转全角 - * - * @param input String - * @param notConvertSet 不替换的字符集合 - * @return 全角字符串. - */ - public static String toSBC(String input, Set notConvertSet) - { - char[] c = input.toCharArray(); - for (int i = 0; i < c.length; i++) - { - if (null != notConvertSet && notConvertSet.contains(c[i])) - { - // 跳过不替换的字符 - continue; - } - - if (c[i] == ' ') - { - c[i] = '\u3000'; - } - else if (c[i] < '\177') - { - c[i] = (char) (c[i] + 65248); - - } - } - return new String(c); - } - - /** - * 全角转半角 - * - * @param input String. - * @return 半角字符串 - */ - public static String toDBC(String input) - { - return toDBC(input, null); - } - - /** - * 替换全角为半角 - * - * @param text 文本 - * @param notConvertSet 不替换的字符集合 - * @return 替换后的字符 - */ - public static String toDBC(String text, Set notConvertSet) - { - char[] c = text.toCharArray(); - for (int i = 0; i < c.length; i++) - { - if (null != notConvertSet && notConvertSet.contains(c[i])) - { - // 跳过不替换的字符 - continue; - } - - if (c[i] == '\u3000') - { - c[i] = ' '; - } - else if (c[i] > '\uFF00' && c[i] < '\uFF5F') - { - c[i] = (char) (c[i] - 65248); - } - } - return new String(c); - } - - /** - * 数字金额大写转换 先写个完整的然后将如零拾替换成零 - * - * @param n 数字 - * @return 中文大写数字 - */ - public static String digitUppercase(double n) - { - String[] fraction = { "角", "分" }; - String[] digit = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" }; - String[][] unit = { { "元", "万", "亿" }, { "", "拾", "佰", "仟" } }; - - String head = n < 0 ? "负" : ""; - n = Math.abs(n); - - String s = ""; - for (int i = 0; i < fraction.length; i++) - { - // 优化double计算精度丢失问题 - BigDecimal nNum = new BigDecimal(n); - BigDecimal decimal = new BigDecimal(10); - BigDecimal scale = nNum.multiply(decimal).setScale(2, RoundingMode.HALF_EVEN); - double d = scale.doubleValue(); - s += (digit[(int) (Math.floor(d * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", ""); - } - if (s.length() < 1) - { - s = "整"; - } - int integerPart = (int) Math.floor(n); - - for (int i = 0; i < unit[0].length && integerPart > 0; i++) - { - String p = ""; - for (int j = 0; j < unit[1].length && n > 0; j++) - { - p = digit[integerPart % 10] + unit[1][j] + p; - integerPart = integerPart / 10; - } - s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s; - } - return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整"); - } -} +package com.ruoyi.common.core.text; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.RoundingMode; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.text.NumberFormat; +import java.util.Set; +import com.ruoyi.common.utils.StringUtils; + +/** + * 类型转换器 + * + * @author ruoyi + */ +public class Convert +{ + /** + * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static String toStr(Object value, String defaultValue) + { + if (null == value) + { + return defaultValue; + } + if (value instanceof String) + { + return (String) value; + } + return value.toString(); + } + + /** + * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static String toStr(Object value) + { + return toStr(value, null); + } + + /** + * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Character toChar(Object value, Character defaultValue) + { + if (null == value) + { + return defaultValue; + } + if (value instanceof Character) + { + return (Character) value; + } + + final String valueStr = toStr(value, null); + return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0); + } + + /** + * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Character toChar(Object value) + { + return toChar(value, null); + } + + /** + * 转换为byte
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Byte toByte(Object value, Byte defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Byte) + { + return (Byte) value; + } + if (value instanceof Number) + { + return ((Number) value).byteValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Byte.parseByte(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为byte
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Byte toByte(Object value) + { + return toByte(value, null); + } + + /** + * 转换为Short
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Short toShort(Object value, Short defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Short) + { + return (Short) value; + } + if (value instanceof Number) + { + return ((Number) value).shortValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Short.parseShort(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Short
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Short toShort(Object value) + { + return toShort(value, null); + } + + /** + * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Number toNumber(Object value, Number defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Number) + { + return (Number) value; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return NumberFormat.getInstance().parse(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Number toNumber(Object value) + { + return toNumber(value, null); + } + + /** + * 转换为int
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Integer toInt(Object value, Integer defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Integer) + { + return (Integer) value; + } + if (value instanceof Number) + { + return ((Number) value).intValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Integer.parseInt(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为int
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Integer toInt(Object value) + { + return toInt(value, null); + } + + /** + * 转换为Integer数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String str) + { + return toIntArray(",", str); + } + + /** + * 转换为Long数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String str) + { + return toLongArray(",", str); + } + + /** + * 转换为Integer数组
+ * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String split, String str) + { + if (StringUtils.isEmpty(str)) + { + return new Integer[] {}; + } + String[] arr = str.split(split); + final Integer[] ints = new Integer[arr.length]; + for (int i = 0; i < arr.length; i++) + { + final Integer v = toInt(arr[i], 0); + ints[i] = v; + } + return ints; + } + + /** + * 转换为Long数组
+ * + * @param split 分隔符 + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String split, String str) + { + if (StringUtils.isEmpty(str)) + { + return new Long[] {}; + } + String[] arr = str.split(split); + final Long[] longs = new Long[arr.length]; + for (int i = 0; i < arr.length; i++) + { + final Long v = toLong(arr[i], null); + longs[i] = v; + } + return longs; + } + + /** + * 转换为String数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String str) + { + if (StringUtils.isEmpty(str)) + { + return new String[] {}; + } + return toStrArray(",", str); + } + + /** + * 转换为String数组
+ * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String split, String str) + { + return str.split(split); + } + + /** + * 转换为long
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Long toLong(Object value, Long defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Long) + { + return (Long) value; + } + if (value instanceof Number) + { + return ((Number) value).longValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).longValue(); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为long
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Long toLong(Object value) + { + return toLong(value, null); + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Double toDouble(Object value, Double defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Double) + { + return (Double) value; + } + if (value instanceof Number) + { + return ((Number) value).doubleValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).doubleValue(); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Double toDouble(Object value) + { + return toDouble(value, null); + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Float toFloat(Object value, Float defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Float) + { + return (Float) value; + } + if (value instanceof Number) + { + return ((Number) value).floatValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Float.parseFloat(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Float toFloat(Object value) + { + return toFloat(value, null); + } + + /** + * 转换为boolean
+ * String支持的值为:true、false、yes、ok、no、1、0、是、否, 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Boolean toBool(Object value, Boolean defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Boolean) + { + return (Boolean) value; + } + String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + valueStr = valueStr.trim().toLowerCase(); + switch (valueStr) + { + case "true": + case "yes": + case "ok": + case "1": + case "是": + return true; + case "false": + case "no": + case "0": + case "否": + return false; + default: + return defaultValue; + } + } + + /** + * 转换为boolean
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Boolean toBool(Object value) + { + return toBool(value, null); + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * + * @param clazz Enum的Class + * @param value 值 + * @param defaultValue 默认值 + * @return Enum + */ + public static > E toEnum(Class clazz, Object value, E defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (clazz.isAssignableFrom(value.getClass())) + { + @SuppressWarnings("unchecked") + E myE = (E) value; + return myE; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Enum.valueOf(clazz, valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * + * @param clazz Enum的Class + * @param value 值 + * @return Enum + */ + public static > E toEnum(Class clazz, Object value) + { + return toEnum(clazz, value, null); + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value, BigInteger defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof BigInteger) + { + return (BigInteger) value; + } + if (value instanceof Long) + { + return BigInteger.valueOf((Long) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return new BigInteger(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value) + { + return toBigInteger(value, null); + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof BigDecimal) + { + return (BigDecimal) value; + } + if (value instanceof Long) + { + return new BigDecimal((Long) value); + } + if (value instanceof Double) + { + return BigDecimal.valueOf((Double) value); + } + if (value instanceof Integer) + { + return new BigDecimal((Integer) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return new BigDecimal(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value) + { + return toBigDecimal(value, null); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @return 字符串 + */ + public static String utf8Str(Object obj) + { + return str(obj, CharsetKit.CHARSET_UTF_8); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charsetName 字符集 + * @return 字符串 + */ + public static String str(Object obj, String charsetName) + { + return str(obj, Charset.forName(charsetName)); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(Object obj, Charset charset) + { + if (null == obj) + { + return null; + } + + if (obj instanceof String) + { + return (String) obj; + } + else if (obj instanceof byte[] || obj instanceof Byte[]) + { + if (obj instanceof byte[]) + { + return str((byte[]) obj, charset); + } + else + { + Byte[] bytes = (Byte[]) obj; + int length = bytes.length; + byte[] dest = new byte[length]; + for (int i = 0; i < length; i++) + { + dest[i] = bytes[i]; + } + return str(dest, charset); + } + } + else if (obj instanceof ByteBuffer) + { + return str((ByteBuffer) obj, charset); + } + return obj.toString(); + } + + /** + * 将byte数组转为字符串 + * + * @param bytes byte数组 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(byte[] bytes, String charset) + { + return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset)); + } + + /** + * 解码字节码 + * + * @param data 字符串 + * @param charset 字符集,如果此字段为空,则解码的结果取决于平台 + * @return 解码后的字符串 + */ + public static String str(byte[] data, Charset charset) + { + if (data == null) + { + return null; + } + + if (null == charset) + { + return new String(data); + } + return new String(data, charset); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, String charset) + { + if (data == null) + { + return null; + } + + return str(data, Charset.forName(charset)); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, Charset charset) + { + if (null == charset) + { + charset = Charset.defaultCharset(); + } + return charset.decode(data).toString(); + } + + // ----------------------------------------------------------------------- 全角半角转换 + /** + * 半角转全角 + * + * @param input String. + * @return 全角字符串. + */ + public static String toSBC(String input) + { + return toSBC(input, null); + } + + /** + * 半角转全角 + * + * @param input String + * @param notConvertSet 不替换的字符集合 + * @return 全角字符串. + */ + public static String toSBC(String input, Set notConvertSet) + { + char[] c = input.toCharArray(); + for (int i = 0; i < c.length; i++) + { + if (null != notConvertSet && notConvertSet.contains(c[i])) + { + // 跳过不替换的字符 + continue; + } + + if (c[i] == ' ') + { + c[i] = '\u3000'; + } + else if (c[i] < '\177') + { + c[i] = (char) (c[i] + 65248); + + } + } + return new String(c); + } + + /** + * 全角转半角 + * + * @param input String. + * @return 半角字符串 + */ + public static String toDBC(String input) + { + return toDBC(input, null); + } + + /** + * 替换全角为半角 + * + * @param text 文本 + * @param notConvertSet 不替换的字符集合 + * @return 替换后的字符 + */ + public static String toDBC(String text, Set notConvertSet) + { + char[] c = text.toCharArray(); + for (int i = 0; i < c.length; i++) + { + if (null != notConvertSet && notConvertSet.contains(c[i])) + { + // 跳过不替换的字符 + continue; + } + + if (c[i] == '\u3000') + { + c[i] = ' '; + } + else if (c[i] > '\uFF00' && c[i] < '\uFF5F') + { + c[i] = (char) (c[i] - 65248); + } + } + return new String(c); + } + + /** + * 数字金额大写转换 先写个完整的然后将如零拾替换成零 + * + * @param n 数字 + * @return 中文大写数字 + */ + public static String digitUppercase(double n) + { + String[] fraction = { "角", "分" }; + String[] digit = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" }; + String[][] unit = { { "元", "万", "亿" }, { "", "拾", "佰", "仟" } }; + + String head = n < 0 ? "负" : ""; + n = Math.abs(n); + + String s = ""; + for (int i = 0; i < fraction.length; i++) + { + // 优化double计算精度丢失问题 + BigDecimal nNum = new BigDecimal(n); + BigDecimal decimal = new BigDecimal(10); + BigDecimal scale = nNum.multiply(decimal).setScale(2, RoundingMode.HALF_EVEN); + double d = scale.doubleValue(); + s += (digit[(int) (Math.floor(d * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", ""); + } + if (s.length() < 1) + { + s = "整"; + } + int integerPart = (int) Math.floor(n); + + for (int i = 0; i < unit[0].length && integerPart > 0; i++) + { + String p = ""; + for (int j = 0; j < unit[1].length && n > 0; j++) + { + p = digit[integerPart % 10] + unit[1][j] + p; + integerPart = integerPart / 10; + } + s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s; + } + return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整"); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/text/StrFormatter.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/StrFormatter.java index c78ac77..dbf3e23 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/text/StrFormatter.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/StrFormatter.java @@ -1,92 +1,92 @@ -package com.ruoyi.common.core.text; - -import com.ruoyi.common.utils.StringUtils; - -/** - * 字符串格式化 - * - * @author ruoyi - */ -public class StrFormatter -{ - public static final String EMPTY_JSON = "{}"; - public static final char C_BACKSLASH = '\\'; - public static final char C_DELIM_START = '{'; - public static final char C_DELIM_END = '}'; - - /** - * 格式化字符串
- * 此方法只是简单将占位符 {} 按照顺序替换为参数
- * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
- * 例:
- * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
- * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
- * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
- * - * @param strPattern 字符串模板 - * @param argArray 参数列表 - * @return 结果 - */ - public static String format(final String strPattern, final Object... argArray) - { - if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray)) - { - return strPattern; - } - final int strPatternLength = strPattern.length(); - - // 初始化定义好的长度以获得更好的性能 - StringBuilder sbuf = new StringBuilder(strPatternLength + 50); - - int handledPosition = 0; - int delimIndex;// 占位符所在位置 - for (int argIndex = 0; argIndex < argArray.length; argIndex++) - { - delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition); - if (delimIndex == -1) - { - if (handledPosition == 0) - { - return strPattern; - } - else - { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果 - sbuf.append(strPattern, handledPosition, strPatternLength); - return sbuf.toString(); - } - } - else - { - if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH) - { - if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH) - { - // 转义符之前还有一个转义符,占位符依旧有效 - sbuf.append(strPattern, handledPosition, delimIndex - 1); - sbuf.append(Convert.utf8Str(argArray[argIndex])); - handledPosition = delimIndex + 2; - } - else - { - // 占位符被转义 - argIndex--; - sbuf.append(strPattern, handledPosition, delimIndex - 1); - sbuf.append(C_DELIM_START); - handledPosition = delimIndex + 1; - } - } - else - { - // 正常占位符 - sbuf.append(strPattern, handledPosition, delimIndex); - sbuf.append(Convert.utf8Str(argArray[argIndex])); - handledPosition = delimIndex + 2; - } - } - } - // 加入最后一个占位符后所有的字符 - sbuf.append(strPattern, handledPosition, strPattern.length()); - - return sbuf.toString(); - } -} +package com.ruoyi.common.core.text; + +import com.ruoyi.common.utils.StringUtils; + +/** + * 字符串格式化 + * + * @author ruoyi + */ +public class StrFormatter +{ + public static final String EMPTY_JSON = "{}"; + public static final char C_BACKSLASH = '\\'; + public static final char C_DELIM_START = '{'; + public static final char C_DELIM_END = '}'; + + /** + * 格式化字符串
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ * + * @param strPattern 字符串模板 + * @param argArray 参数列表 + * @return 结果 + */ + public static String format(final String strPattern, final Object... argArray) + { + if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray)) + { + return strPattern; + } + final int strPatternLength = strPattern.length(); + + // 初始化定义好的长度以获得更好的性能 + StringBuilder sbuf = new StringBuilder(strPatternLength + 50); + + int handledPosition = 0; + int delimIndex;// 占位符所在位置 + for (int argIndex = 0; argIndex < argArray.length; argIndex++) + { + delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition); + if (delimIndex == -1) + { + if (handledPosition == 0) + { + return strPattern; + } + else + { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果 + sbuf.append(strPattern, handledPosition, strPatternLength); + return sbuf.toString(); + } + } + else + { + if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH) + { + if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH) + { + // 转义符之前还有一个转义符,占位符依旧有效 + sbuf.append(strPattern, handledPosition, delimIndex - 1); + sbuf.append(Convert.utf8Str(argArray[argIndex])); + handledPosition = delimIndex + 2; + } + else + { + // 占位符被转义 + argIndex--; + sbuf.append(strPattern, handledPosition, delimIndex - 1); + sbuf.append(C_DELIM_START); + handledPosition = delimIndex + 1; + } + } + else + { + // 正常占位符 + sbuf.append(strPattern, handledPosition, delimIndex); + sbuf.append(Convert.utf8Str(argArray[argIndex])); + handledPosition = delimIndex + 2; + } + } + } + // 加入最后一个占位符后所有的字符 + sbuf.append(strPattern, handledPosition, strPattern.length()); + + return sbuf.toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessStatus.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessStatus.java index 10b7306..78188fc 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessStatus.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessStatus.java @@ -1,20 +1,20 @@ -package com.ruoyi.common.enums; - -/** - * 操作状态 - * - * @author ruoyi - * - */ -public enum BusinessStatus -{ - /** - * 成功 - */ - SUCCESS, - - /** - * 失败 - */ - FAIL, -} +package com.ruoyi.common.enums; + +/** + * 操作状态 + * + * @author ruoyi + * + */ +public enum BusinessStatus +{ + /** + * 成功 + */ + SUCCESS, + + /** + * 失败 + */ + FAIL, +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessType.java index 2e17c4a..2bbeaf7 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessType.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessType.java @@ -1,59 +1,59 @@ -package com.ruoyi.common.enums; - -/** - * 业务操作类型 - * - * @author ruoyi - */ -public enum BusinessType -{ - /** - * 其它 - */ - OTHER, - - /** - * 新增 - */ - INSERT, - - /** - * 修改 - */ - UPDATE, - - /** - * 删除 - */ - DELETE, - - /** - * 授权 - */ - GRANT, - - /** - * 导出 - */ - EXPORT, - - /** - * 导入 - */ - IMPORT, - - /** - * 强退 - */ - FORCE, - - /** - * 生成代码 - */ - GENCODE, - - /** - * 清空数据 - */ - CLEAN, -} +package com.ruoyi.common.enums; + +/** + * 业务操作类型 + * + * @author ruoyi + */ +public enum BusinessType +{ + /** + * 其它 + */ + OTHER, + + /** + * 新增 + */ + INSERT, + + /** + * 修改 + */ + UPDATE, + + /** + * 删除 + */ + DELETE, + + /** + * 授权 + */ + GRANT, + + /** + * 导出 + */ + EXPORT, + + /** + * 导入 + */ + IMPORT, + + /** + * 强退 + */ + FORCE, + + /** + * 生成代码 + */ + GENCODE, + + /** + * 清空数据 + */ + CLEAN, +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java index 0d945be..4b5341d 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java @@ -1,19 +1,19 @@ -package com.ruoyi.common.enums; - -/** - * 数据源 - * - * @author ruoyi - */ -public enum DataSourceType -{ - /** - * 主库 - */ - MASTER, - - /** - * 从库 - */ - SLAVE -} +package com.ruoyi.common.enums; + +/** + * 数据源 + * + * @author ruoyi + */ +public enum DataSourceType +{ + /** + * 主库 + */ + MASTER, + + /** + * 从库 + */ + SLAVE +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/HttpMethod.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/HttpMethod.java index be6f739..2fa9f86 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/enums/HttpMethod.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/HttpMethod.java @@ -1,36 +1,36 @@ -package com.ruoyi.common.enums; - -import java.util.HashMap; -import java.util.Map; -import org.springframework.lang.Nullable; - -/** - * 请求方式 - * - * @author ruoyi - */ -public enum HttpMethod -{ - GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE; - - private static final Map mappings = new HashMap<>(16); - - static - { - for (HttpMethod httpMethod : values()) - { - mappings.put(httpMethod.name(), httpMethod); - } - } - - @Nullable - public static HttpMethod resolve(@Nullable String method) - { - return (method != null ? mappings.get(method) : null); - } - - public boolean matches(String method) - { - return (this == resolve(method)); - } -} +package com.ruoyi.common.enums; + +import java.util.HashMap; +import java.util.Map; +import org.springframework.lang.Nullable; + +/** + * 请求方式 + * + * @author ruoyi + */ +public enum HttpMethod +{ + GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE; + + private static final Map mappings = new HashMap<>(16); + + static + { + for (HttpMethod httpMethod : values()) + { + mappings.put(httpMethod.name(), httpMethod); + } + } + + @Nullable + public static HttpMethod resolve(@Nullable String method) + { + return (method != null ? mappings.get(method) : null); + } + + public boolean matches(String method) + { + return (this == resolve(method)); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/LimitType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/LimitType.java index c609fd8..ad01659 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/enums/LimitType.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/LimitType.java @@ -1,20 +1,20 @@ -package com.ruoyi.common.enums; - -/** - * 限流类型 - * - * @author ruoyi - */ - -public enum LimitType -{ - /** - * 默认策略全局限流 - */ - DEFAULT, - - /** - * 根据请求者IP进行限流 - */ - IP -} +package com.ruoyi.common.enums; + +/** + * 限流类型 + * + * @author ruoyi + */ + +public enum LimitType +{ + /** + * 默认策略全局限流 + */ + DEFAULT, + + /** + * 根据请求者IP进行限流 + */ + IP +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/OperatorType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/OperatorType.java index bdd143c..06532af 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/enums/OperatorType.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/OperatorType.java @@ -1,24 +1,24 @@ -package com.ruoyi.common.enums; - -/** - * 操作人类别 - * - * @author ruoyi - */ -public enum OperatorType -{ - /** - * 其它 - */ - OTHER, - - /** - * 后台用户 - */ - MANAGE, - - /** - * 手机端用户 - */ - MOBILE -} +package com.ruoyi.common.enums; + +/** + * 操作人类别 + * + * @author ruoyi + */ +public enum OperatorType +{ + /** + * 其它 + */ + OTHER, + + /** + * 后台用户 + */ + MANAGE, + + /** + * 手机端用户 + */ + MOBILE +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/UserStatus.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/UserStatus.java index d7ff44a..a4613ff 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/enums/UserStatus.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/UserStatus.java @@ -1,30 +1,30 @@ -package com.ruoyi.common.enums; - -/** - * 用户状态 - * - * @author ruoyi - */ -public enum UserStatus -{ - OK("0", "正常"), DISABLE("1", "停用"), DELETED("2", "删除"); - - private final String code; - private final String info; - - UserStatus(String code, String info) - { - this.code = code; - this.info = info; - } - - public String getCode() - { - return code; - } - - public String getInfo() - { - return info; - } -} +package com.ruoyi.common.enums; + +/** + * 用户状态 + * + * @author ruoyi + */ +public enum UserStatus +{ + OK("0", "正常"), DISABLE("1", "停用"), DELETED("2", "删除"); + + private final String code; + private final String info; + + UserStatus(String code, String info) + { + this.code = code; + this.info = info; + } + + public String getCode() + { + return code; + } + + public String getInfo() + { + return info; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/DemoModeException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/DemoModeException.java index f6ad2ab..57bffc4 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/exception/DemoModeException.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/DemoModeException.java @@ -1,15 +1,15 @@ -package com.ruoyi.common.exception; - -/** - * 演示模式异常 - * - * @author ruoyi - */ -public class DemoModeException extends RuntimeException -{ - private static final long serialVersionUID = 1L; - - public DemoModeException() - { - } -} +package com.ruoyi.common.exception; + +/** + * 演示模式异常 + * + * @author ruoyi + */ +public class DemoModeException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + public DemoModeException() + { + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/GlobalException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/GlobalException.java index 81a71b5..9f0dec4 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/exception/GlobalException.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/GlobalException.java @@ -1,58 +1,58 @@ -package com.ruoyi.common.exception; - -/** - * 全局异常 - * - * @author ruoyi - */ -public class GlobalException extends RuntimeException -{ - private static final long serialVersionUID = 1L; - - /** - * 错误提示 - */ - private String message; - - /** - * 错误明细,内部调试错误 - * - * 和 {@link CommonResult#getDetailMessage()} 一致的设计 - */ - private String detailMessage; - - /** - * 空构造方法,避免反序列化问题 - */ - public GlobalException() - { - } - - public GlobalException(String message) - { - this.message = message; - } - - public String getDetailMessage() - { - return detailMessage; - } - - public GlobalException setDetailMessage(String detailMessage) - { - this.detailMessage = detailMessage; - return this; - } - - @Override - public String getMessage() - { - return message; - } - - public GlobalException setMessage(String message) - { - this.message = message; - return this; - } +package com.ruoyi.common.exception; + +/** + * 全局异常 + * + * @author ruoyi + */ +public class GlobalException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + * + * 和 {@link CommonResult#getDetailMessage()} 一致的设计 + */ + private String detailMessage; + + /** + * 空构造方法,避免反序列化问题 + */ + public GlobalException() + { + } + + public GlobalException(String message) + { + this.message = message; + } + + public String getDetailMessage() + { + return detailMessage; + } + + public GlobalException setDetailMessage(String detailMessage) + { + this.detailMessage = detailMessage; + return this; + } + + @Override + public String getMessage() + { + return message; + } + + public GlobalException setMessage(String message) + { + this.message = message; + return this; + } } \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/ServiceException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/ServiceException.java index fcc7ab6..a9247b5 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/exception/ServiceException.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/ServiceException.java @@ -1,74 +1,74 @@ -package com.ruoyi.common.exception; - -/** - * 业务异常 - * - * @author ruoyi - */ -public final class ServiceException extends RuntimeException -{ - private static final long serialVersionUID = 1L; - - /** - * 错误码 - */ - private Integer code; - - /** - * 错误提示 - */ - private String message; - - /** - * 错误明细,内部调试错误 - * - * 和 {@link CommonResult#getDetailMessage()} 一致的设计 - */ - private String detailMessage; - - /** - * 空构造方法,避免反序列化问题 - */ - public ServiceException() - { - } - - public ServiceException(String message) - { - this.message = message; - } - - public ServiceException(String message, Integer code) - { - this.message = message; - this.code = code; - } - - public String getDetailMessage() - { - return detailMessage; - } - - @Override - public String getMessage() - { - return message; - } - - public Integer getCode() - { - return code; - } - - public ServiceException setMessage(String message) - { - this.message = message; - return this; - } - - public ServiceException setDetailMessage(String detailMessage) - { - this.detailMessage = detailMessage; - return this; - } +package com.ruoyi.common.exception; + +/** + * 业务异常 + * + * @author ruoyi + */ +public final class ServiceException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 错误码 + */ + private Integer code; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + * + * 和 {@link CommonResult#getDetailMessage()} 一致的设计 + */ + private String detailMessage; + + /** + * 空构造方法,避免反序列化问题 + */ + public ServiceException() + { + } + + public ServiceException(String message) + { + this.message = message; + } + + public ServiceException(String message, Integer code) + { + this.message = message; + this.code = code; + } + + public String getDetailMessage() + { + return detailMessage; + } + + @Override + public String getMessage() + { + return message; + } + + public Integer getCode() + { + return code; + } + + public ServiceException setMessage(String message) + { + this.message = message; + return this; + } + + public ServiceException setDetailMessage(String detailMessage) + { + this.detailMessage = detailMessage; + return this; + } } \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/UtilException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/UtilException.java index 980fa46..a544fb6 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/exception/UtilException.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/UtilException.java @@ -1,26 +1,26 @@ -package com.ruoyi.common.exception; - -/** - * 工具类异常 - * - * @author ruoyi - */ -public class UtilException extends RuntimeException -{ - private static final long serialVersionUID = 8247610319171014183L; - - public UtilException(Throwable e) - { - super(e.getMessage(), e); - } - - public UtilException(String message) - { - super(message); - } - - public UtilException(String message, Throwable throwable) - { - super(message, throwable); - } -} +package com.ruoyi.common.exception; + +/** + * 工具类异常 + * + * @author ruoyi + */ +public class UtilException extends RuntimeException +{ + private static final long serialVersionUID = 8247610319171014183L; + + public UtilException(Throwable e) + { + super(e.getMessage(), e); + } + + public UtilException(String message) + { + super(message); + } + + public UtilException(String message, Throwable throwable) + { + super(message, throwable); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/base/BaseException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/base/BaseException.java index b55d72e..3dd2c1e 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/exception/base/BaseException.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/base/BaseException.java @@ -1,97 +1,97 @@ -package com.ruoyi.common.exception.base; - -import com.ruoyi.common.utils.MessageUtils; -import com.ruoyi.common.utils.StringUtils; - -/** - * 基础异常 - * - * @author ruoyi - */ -public class BaseException extends RuntimeException -{ - private static final long serialVersionUID = 1L; - - /** - * 所属模块 - */ - private String module; - - /** - * 错误码 - */ - private String code; - - /** - * 错误码对应的参数 - */ - private Object[] args; - - /** - * 错误消息 - */ - private String defaultMessage; - - public BaseException(String module, String code, Object[] args, String defaultMessage) - { - this.module = module; - this.code = code; - this.args = args; - this.defaultMessage = defaultMessage; - } - - public BaseException(String module, String code, Object[] args) - { - this(module, code, args, null); - } - - public BaseException(String module, String defaultMessage) - { - this(module, null, null, defaultMessage); - } - - public BaseException(String code, Object[] args) - { - this(null, code, args, null); - } - - public BaseException(String defaultMessage) - { - this(null, null, null, defaultMessage); - } - - @Override - public String getMessage() - { - String message = null; - if (!StringUtils.isEmpty(code)) - { - message = MessageUtils.message(code, args); - } - if (message == null) - { - message = defaultMessage; - } - return message; - } - - public String getModule() - { - return module; - } - - public String getCode() - { - return code; - } - - public Object[] getArgs() - { - return args; - } - - public String getDefaultMessage() - { - return defaultMessage; - } -} +package com.ruoyi.common.exception.base; + +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.common.utils.StringUtils; + +/** + * 基础异常 + * + * @author ruoyi + */ +public class BaseException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 所属模块 + */ + private String module; + + /** + * 错误码 + */ + private String code; + + /** + * 错误码对应的参数 + */ + private Object[] args; + + /** + * 错误消息 + */ + private String defaultMessage; + + public BaseException(String module, String code, Object[] args, String defaultMessage) + { + this.module = module; + this.code = code; + this.args = args; + this.defaultMessage = defaultMessage; + } + + public BaseException(String module, String code, Object[] args) + { + this(module, code, args, null); + } + + public BaseException(String module, String defaultMessage) + { + this(module, null, null, defaultMessage); + } + + public BaseException(String code, Object[] args) + { + this(null, code, args, null); + } + + public BaseException(String defaultMessage) + { + this(null, null, null, defaultMessage); + } + + @Override + public String getMessage() + { + String message = null; + if (!StringUtils.isEmpty(code)) + { + message = MessageUtils.message(code, args); + } + if (message == null) + { + message = defaultMessage; + } + return message; + } + + public String getModule() + { + return module; + } + + public String getCode() + { + return code; + } + + public Object[] getArgs() + { + return args; + } + + public String getDefaultMessage() + { + return defaultMessage; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileException.java index 871f09b..ed14af1 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileException.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileException.java @@ -1,19 +1,19 @@ -package com.ruoyi.common.exception.file; - -import com.ruoyi.common.exception.base.BaseException; - -/** - * 文件信息异常类 - * - * @author ruoyi - */ -public class FileException extends BaseException -{ - private static final long serialVersionUID = 1L; - - public FileException(String code, Object[] args) - { - super("file", code, args, null); - } - -} +package com.ruoyi.common.exception.file; + +import com.ruoyi.common.exception.base.BaseException; + +/** + * 文件信息异常类 + * + * @author ruoyi + */ +public class FileException extends BaseException +{ + private static final long serialVersionUID = 1L; + + public FileException(String code, Object[] args) + { + super("file", code, args, null); + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java index 70e0ec9..44941b8 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java @@ -1,16 +1,16 @@ -package com.ruoyi.common.exception.file; - -/** - * 文件名称超长限制异常类 - * - * @author ruoyi - */ -public class FileNameLengthLimitExceededException extends FileException -{ - private static final long serialVersionUID = 1L; - - public FileNameLengthLimitExceededException(int defaultFileNameLength) - { - super("upload.filename.exceed.length", new Object[] { defaultFileNameLength }); - } -} +package com.ruoyi.common.exception.file; + +/** + * 文件名称超长限制异常类 + * + * @author ruoyi + */ +public class FileNameLengthLimitExceededException extends FileException +{ + private static final long serialVersionUID = 1L; + + public FileNameLengthLimitExceededException(int defaultFileNameLength) + { + super("upload.filename.exceed.length", new Object[] { defaultFileNameLength }); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java index ec6ab05..f855e72 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java @@ -1,16 +1,16 @@ -package com.ruoyi.common.exception.file; - -/** - * 文件名大小限制异常类 - * - * @author ruoyi - */ -public class FileSizeLimitExceededException extends FileException -{ - private static final long serialVersionUID = 1L; - - public FileSizeLimitExceededException(long defaultMaxSize) - { - super("upload.exceed.maxSize", new Object[] { defaultMaxSize }); - } -} +package com.ruoyi.common.exception.file; + +/** + * 文件名大小限制异常类 + * + * @author ruoyi + */ +public class FileSizeLimitExceededException extends FileException +{ + private static final long serialVersionUID = 1L; + + public FileSizeLimitExceededException(long defaultMaxSize) + { + super("upload.exceed.maxSize", new Object[] { defaultMaxSize }); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java index bb8a53f..3b49a54 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java @@ -1,80 +1,80 @@ -package com.ruoyi.common.exception.file; - -import java.util.Arrays; - -/** - * 文件上传无效扩展名异常类 - * - * @author ruoyi - */ -public class InvalidExtensionException extends FileUploadException -{ - private static final long serialVersionUID = 1L; - - private String[] allowedExtension; - private String extension; - private String filename; - - public InvalidExtensionException(String[] allowedExtension, String extension, String filename) - { - super("文件[" + filename + "]后缀[" + extension + "]不正确,请上传" + Arrays.toString(allowedExtension) + "格式"); - this.allowedExtension = allowedExtension; - this.extension = extension; - this.filename = filename; - } - - public String[] getAllowedExtension() - { - return allowedExtension; - } - - public String getExtension() - { - return extension; - } - - public String getFilename() - { - return filename; - } - - public static class InvalidImageExtensionException extends InvalidExtensionException - { - private static final long serialVersionUID = 1L; - - public InvalidImageExtensionException(String[] allowedExtension, String extension, String filename) - { - super(allowedExtension, extension, filename); - } - } - - public static class InvalidFlashExtensionException extends InvalidExtensionException - { - private static final long serialVersionUID = 1L; - - public InvalidFlashExtensionException(String[] allowedExtension, String extension, String filename) - { - super(allowedExtension, extension, filename); - } - } - - public static class InvalidMediaExtensionException extends InvalidExtensionException - { - private static final long serialVersionUID = 1L; - - public InvalidMediaExtensionException(String[] allowedExtension, String extension, String filename) - { - super(allowedExtension, extension, filename); - } - } - - public static class InvalidVideoExtensionException extends InvalidExtensionException - { - private static final long serialVersionUID = 1L; - - public InvalidVideoExtensionException(String[] allowedExtension, String extension, String filename) - { - super(allowedExtension, extension, filename); - } - } -} +package com.ruoyi.common.exception.file; + +import java.util.Arrays; + +/** + * 文件上传无效扩展名异常类 + * + * @author ruoyi + */ +public class InvalidExtensionException extends FileUploadException +{ + private static final long serialVersionUID = 1L; + + private String[] allowedExtension; + private String extension; + private String filename; + + public InvalidExtensionException(String[] allowedExtension, String extension, String filename) + { + super("文件[" + filename + "]后缀[" + extension + "]不正确,请上传" + Arrays.toString(allowedExtension) + "格式"); + this.allowedExtension = allowedExtension; + this.extension = extension; + this.filename = filename; + } + + public String[] getAllowedExtension() + { + return allowedExtension; + } + + public String getExtension() + { + return extension; + } + + public String getFilename() + { + return filename; + } + + public static class InvalidImageExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidImageExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidFlashExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidFlashExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidMediaExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidMediaExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidVideoExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidVideoExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/job/TaskException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/job/TaskException.java index a567b40..14b2361 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/exception/job/TaskException.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/job/TaskException.java @@ -1,34 +1,34 @@ -package com.ruoyi.common.exception.job; - -/** - * 计划策略异常 - * - * @author ruoyi - */ -public class TaskException extends Exception -{ - private static final long serialVersionUID = 1L; - - private Code code; - - public TaskException(String msg, Code code) - { - this(msg, code, null); - } - - public TaskException(String msg, Code code, Exception nestedEx) - { - super(msg, nestedEx); - this.code = code; - } - - public Code getCode() - { - return code; - } - - public enum Code - { - TASK_EXISTS, NO_TASK_EXISTS, TASK_ALREADY_STARTED, UNKNOWN, CONFIG_ERROR, TASK_NODE_NOT_AVAILABLE - } +package com.ruoyi.common.exception.job; + +/** + * 计划策略异常 + * + * @author ruoyi + */ +public class TaskException extends Exception +{ + private static final long serialVersionUID = 1L; + + private Code code; + + public TaskException(String msg, Code code) + { + this(msg, code, null); + } + + public TaskException(String msg, Code code, Exception nestedEx) + { + super(msg, nestedEx); + this.code = code; + } + + public Code getCode() + { + return code; + } + + public enum Code + { + TASK_EXISTS, NO_TASK_EXISTS, TASK_ALREADY_STARTED, UNKNOWN, CONFIG_ERROR, TASK_NODE_NOT_AVAILABLE + } } \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java index 389dbc7..e3334ae 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java @@ -1,16 +1,16 @@ -package com.ruoyi.common.exception.user; - -/** - * 验证码错误异常类 - * - * @author ruoyi - */ -public class CaptchaException extends UserException -{ - private static final long serialVersionUID = 1L; - - public CaptchaException() - { - super("user.jcaptcha.error", null); - } -} +package com.ruoyi.common.exception.user; + +/** + * 验证码错误异常类 + * + * @author ruoyi + */ +public class CaptchaException extends UserException +{ + private static final long serialVersionUID = 1L; + + public CaptchaException() + { + super("user.jcaptcha.error", null); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java index 85f9486..5e6fd0f 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java @@ -1,16 +1,16 @@ -package com.ruoyi.common.exception.user; - -/** - * 验证码失效异常类 - * - * @author ruoyi - */ -public class CaptchaExpireException extends UserException -{ - private static final long serialVersionUID = 1L; - - public CaptchaExpireException() - { - super("user.jcaptcha.expire", null); - } -} +package com.ruoyi.common.exception.user; + +/** + * 验证码失效异常类 + * + * @author ruoyi + */ +public class CaptchaExpireException extends UserException +{ + private static final long serialVersionUID = 1L; + + public CaptchaExpireException() + { + super("user.jcaptcha.expire", null); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserException.java index c292d70..79160d8 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserException.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserException.java @@ -1,18 +1,18 @@ -package com.ruoyi.common.exception.user; - -import com.ruoyi.common.exception.base.BaseException; - -/** - * 用户信息异常类 - * - * @author ruoyi - */ -public class UserException extends BaseException -{ - private static final long serialVersionUID = 1L; - - public UserException(String code, Object[] args) - { - super("user", code, args, null); - } -} +package com.ruoyi.common.exception.user; + +import com.ruoyi.common.exception.base.BaseException; + +/** + * 用户信息异常类 + * + * @author ruoyi + */ +public class UserException extends BaseException +{ + private static final long serialVersionUID = 1L; + + public UserException(String code, Object[] args) + { + super("user", code, args, null); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java index a7f3e5f..71449e2 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java @@ -1,16 +1,16 @@ -package com.ruoyi.common.exception.user; - -/** - * 用户密码不正确或不符合规范异常类 - * - * @author ruoyi - */ -public class UserPasswordNotMatchException extends UserException -{ - private static final long serialVersionUID = 1L; - - public UserPasswordNotMatchException() - { - super("user.password.not.match", null); - } -} +package com.ruoyi.common.exception.user; + +/** + * 用户密码不正确或不符合规范异常类 + * + * @author ruoyi + */ +public class UserPasswordNotMatchException extends UserException +{ + private static final long serialVersionUID = 1L; + + public UserPasswordNotMatchException() + { + super("user.password.not.match", null); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java index c887cf1..0de8d24 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java @@ -1,16 +1,16 @@ -package com.ruoyi.common.exception.user; - -/** - * 用户错误最大次数异常类 - * - * @author ruoyi - */ -public class UserPasswordRetryLimitExceedException extends UserException -{ - private static final long serialVersionUID = 1L; - - public UserPasswordRetryLimitExceedException(int retryLimitCount, int lockTime) - { - super("user.password.retry.limit.exceed", new Object[] { retryLimitCount, lockTime }); - } -} +package com.ruoyi.common.exception.user; + +/** + * 用户错误最大次数异常类 + * + * @author ruoyi + */ +public class UserPasswordRetryLimitExceedException extends UserException +{ + private static final long serialVersionUID = 1L; + + public UserPasswordRetryLimitExceedException(int retryLimitCount, int lockTime) + { + super("user.password.retry.limit.exceed", new Object[] { retryLimitCount, lockTime }); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java index e1e431b..4cde4a9 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java @@ -1,24 +1,24 @@ -package com.ruoyi.common.filter; - -import com.alibaba.fastjson2.filter.SimplePropertyPreFilter; - -/** - * 排除JSON敏感属性 - * - * @author ruoyi - */ -public class PropertyPreExcludeFilter extends SimplePropertyPreFilter -{ - public PropertyPreExcludeFilter() - { - } - - public PropertyPreExcludeFilter addExcludes(String... filters) - { - for (int i = 0; i < filters.length; i++) - { - this.getExcludes().add(filters[i]); - } - return this; - } -} +package com.ruoyi.common.filter; + +import com.alibaba.fastjson2.filter.SimplePropertyPreFilter; + +/** + * 排除JSON敏感属性 + * + * @author ruoyi + */ +public class PropertyPreExcludeFilter extends SimplePropertyPreFilter +{ + public PropertyPreExcludeFilter() + { + } + + public PropertyPreExcludeFilter addExcludes(String... filters) + { + for (int i = 0; i < filters.length; i++) + { + this.getExcludes().add(filters[i]); + } + return this; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/RefererFilter.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RefererFilter.java index ac9ed16..79e8989 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/filter/RefererFilter.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RefererFilter.java @@ -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; /** * 防盗链过滤器 diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java index efeded9..3946d48 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java @@ -1,52 +1,52 @@ -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 org.springframework.http.MediaType; -import com.ruoyi.common.utils.StringUtils; - -/** - * Repeatable 过滤器 - * - * @author ruoyi - */ -public class RepeatableFilter implements Filter -{ - @Override - public void init(FilterConfig filterConfig) throws ServletException - { - - } - - @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) - throws IOException, ServletException - { - ServletRequest requestWrapper = null; - if (request instanceof HttpServletRequest - && StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) - { - requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response); - } - if (null == requestWrapper) - { - chain.doFilter(request, response); - } - else - { - chain.doFilter(requestWrapper, response); - } - } - - @Override - public void destroy() - { - - } -} +package com.ruoyi.common.filter; + +import java.io.IOException; +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; + +/** + * Repeatable 过滤器 + * + * @author ruoyi + */ +public class RepeatableFilter implements Filter +{ + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + ServletRequest requestWrapper = null; + if (request instanceof HttpServletRequest + && StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) + { + requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response); + } + if (null == requestWrapper) + { + chain.doFilter(request, response); + } + else + { + chain.doFilter(requestWrapper, response); + } + } + + @Override + public void destroy() + { + + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java index 5c6ee08..ca822ed 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java @@ -1,76 +1,76 @@ -package com.ruoyi.common.filter; - -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 com.ruoyi.common.utils.http.HttpHelper; -import com.ruoyi.common.constant.Constants; - -/** - * 构建可重复读取inputStream的request - * - * @author ruoyi - */ -public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper -{ - private final byte[] body; - - public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException - { - super(request); - request.setCharacterEncoding(Constants.UTF8); - response.setCharacterEncoding(Constants.UTF8); - - body = HttpHelper.getBodyString(request).getBytes(Constants.UTF8); - } - - @Override - public BufferedReader getReader() throws IOException - { - return new BufferedReader(new InputStreamReader(getInputStream())); - } - - @Override - public ServletInputStream getInputStream() throws IOException - { - final ByteArrayInputStream bais = new ByteArrayInputStream(body); - return new ServletInputStream() - { - @Override - public int read() throws IOException - { - return bais.read(); - } - - @Override - public int available() throws IOException - { - return body.length; - } - - @Override - public boolean isFinished() - { - return false; - } - - @Override - public boolean isReady() - { - return false; - } - - @Override - public void setReadListener(ReadListener readListener) - { - - } - }; - } -} +package com.ruoyi.common.filter; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +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; + +/** + * 构建可重复读取inputStream的request + * + * @author ruoyi + */ +public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper +{ + private final byte[] body; + + public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException + { + super(request); + request.setCharacterEncoding(Constants.UTF8); + response.setCharacterEncoding(Constants.UTF8); + + body = HttpHelper.getBodyString(request).getBytes(Constants.UTF8); + } + + @Override + public BufferedReader getReader() throws IOException + { + return new BufferedReader(new InputStreamReader(getInputStream())); + } + + @Override + public ServletInputStream getInputStream() throws IOException + { + final ByteArrayInputStream bais = new ByteArrayInputStream(body); + return new ServletInputStream() + { + @Override + public int read() throws IOException + { + return bais.read(); + } + + @Override + public int available() throws IOException + { + return body.length; + } + + @Override + public boolean isFinished() + { + return false; + } + + @Override + public boolean isReady() + { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) + { + + } + }; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java index 7438fc2..b4eaabc 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java @@ -1,75 +1,75 @@ -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 com.ruoyi.common.utils.StringUtils; -import com.ruoyi.common.enums.HttpMethod; - -/** - * 防止XSS攻击的过滤器 - * - * @author ruoyi - */ -public class XssFilter implements Filter -{ - /** - * 排除链接 - */ - public List excludes = new ArrayList<>(); - - @Override - public void init(FilterConfig filterConfig) throws ServletException - { - String tempExcludes = filterConfig.getInitParameter("excludes"); - if (StringUtils.isNotEmpty(tempExcludes)) - { - String[] urls = tempExcludes.split(","); - for (String url : urls) - { - excludes.add(url); - } - } - } - - @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) - throws IOException, ServletException - { - HttpServletRequest req = (HttpServletRequest) request; - HttpServletResponse resp = (HttpServletResponse) response; - if (handleExcludeURL(req, resp)) - { - chain.doFilter(request, response); - return; - } - XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request); - chain.doFilter(xssRequest, response); - } - - private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) - { - String url = request.getServletPath(); - String method = request.getMethod(); - // GET DELETE 不过滤 - if (method == null || HttpMethod.GET.matches(method) || HttpMethod.DELETE.matches(method)) - { - return true; - } - return StringUtils.matches(url, excludes); - } - - @Override - public void destroy() - { - - } +package com.ruoyi.common.filter; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +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; + +/** + * 防止XSS攻击的过滤器 + * + * @author ruoyi + */ +public class XssFilter implements Filter +{ + /** + * 排除链接 + */ + public List excludes = new ArrayList<>(); + + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + String tempExcludes = filterConfig.getInitParameter("excludes"); + if (StringUtils.isNotEmpty(tempExcludes)) + { + String[] urls = tempExcludes.split(","); + for (String url : urls) + { + excludes.add(url); + } + } + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + HttpServletRequest req = (HttpServletRequest) request; + HttpServletResponse resp = (HttpServletResponse) response; + if (handleExcludeURL(req, resp)) + { + chain.doFilter(request, response); + return; + } + XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request); + chain.doFilter(xssRequest, response); + } + + private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) + { + String url = request.getServletPath(); + String method = request.getMethod(); + // GET DELETE 不过滤 + if (method == null || HttpMethod.GET.matches(method) || HttpMethod.DELETE.matches(method)) + { + return true; + } + return StringUtils.matches(url, excludes); + } + + @Override + public void destroy() + { + + } } \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java index bf7837b..e0cc40a 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java @@ -1,111 +1,111 @@ -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 org.apache.commons.io.IOUtils; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.common.utils.html.EscapeUtil; - -/** - * XSS过滤处理 - * - * @author ruoyi - */ -public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper -{ - /** - * @param request - */ - public XssHttpServletRequestWrapper(HttpServletRequest request) - { - super(request); - } - - @Override - public String[] getParameterValues(String name) - { - String[] values = super.getParameterValues(name); - if (values != null) - { - int length = values.length; - String[] escapesValues = new String[length]; - for (int i = 0; i < length; i++) - { - // 防xss攻击和过滤前后空格 - escapesValues[i] = EscapeUtil.clean(values[i]).trim(); - } - return escapesValues; - } - return super.getParameterValues(name); - } - - @Override - public ServletInputStream getInputStream() throws IOException - { - // 非json类型,直接返回 - if (!isJsonRequest()) - { - return super.getInputStream(); - } - - // 为空,直接返回 - String json = IOUtils.toString(super.getInputStream(), "utf-8"); - if (StringUtils.isEmpty(json)) - { - return super.getInputStream(); - } - - // xss过滤 - json = EscapeUtil.clean(json).trim(); - byte[] jsonBytes = json.getBytes("utf-8"); - final ByteArrayInputStream bis = new ByteArrayInputStream(jsonBytes); - return new ServletInputStream() - { - @Override - public boolean isFinished() - { - return true; - } - - @Override - public boolean isReady() - { - return true; - } - - @Override - public int available() throws IOException - { - return jsonBytes.length; - } - - @Override - public void setReadListener(ReadListener readListener) - { - } - - @Override - public int read() throws IOException - { - return bis.read(); - } - }; - } - - /** - * 是否是Json请求 - * - * @param request - */ - public boolean isJsonRequest() - { - String header = super.getHeader(HttpHeaders.CONTENT_TYPE); - return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE); - } +package com.ruoyi.common.filter; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +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; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.html.EscapeUtil; + +/** + * XSS过滤处理 + * + * @author ruoyi + */ +public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper +{ + /** + * @param request + */ + public XssHttpServletRequestWrapper(HttpServletRequest request) + { + super(request); + } + + @Override + public String[] getParameterValues(String name) + { + String[] values = super.getParameterValues(name); + if (values != null) + { + int length = values.length; + String[] escapesValues = new String[length]; + for (int i = 0; i < length; i++) + { + // 防xss攻击和过滤前后空格 + escapesValues[i] = EscapeUtil.clean(values[i]).trim(); + } + return escapesValues; + } + return super.getParameterValues(name); + } + + @Override + public ServletInputStream getInputStream() throws IOException + { + // 非json类型,直接返回 + if (!isJsonRequest()) + { + return super.getInputStream(); + } + + // 为空,直接返回 + String json = IOUtils.toString(super.getInputStream(), "utf-8"); + if (StringUtils.isEmpty(json)) + { + return super.getInputStream(); + } + + // xss过滤 + json = EscapeUtil.clean(json).trim(); + byte[] jsonBytes = json.getBytes("utf-8"); + final ByteArrayInputStream bis = new ByteArrayInputStream(jsonBytes); + return new ServletInputStream() + { + @Override + public boolean isFinished() + { + return true; + } + + @Override + public boolean isReady() + { + return true; + } + + @Override + public int available() throws IOException + { + return jsonBytes.length; + } + + @Override + public void setReadListener(ReadListener readListener) + { + } + + @Override + public int read() throws IOException + { + return bis.read(); + } + }; + } + + /** + * 是否是Json请求 + * + * @param request + */ + public boolean isJsonRequest() + { + String header = super.getHeader(HttpHeaders.CONTENT_TYPE); + return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE); + } } \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java index c1d4b55..20dbbb7 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java @@ -1,192 +1,191 @@ -package com.ruoyi.common.utils; - -import java.lang.management.ManagementFactory; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.util.Date; -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"; - - public static String YYYY_MM = "yyyy-MM"; - - public static String YYYY_MM_DD = "yyyy-MM-dd"; - - public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; - - public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; - - private static String[] parsePatterns = { - "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", - "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", - "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; - - /** - * 获取当前Date型日期 - * - * @return Date() 当前日期 - */ - public static Date getNowDate() - { - return new Date(); - } - - /** - * 获取当前日期, 默认格式为yyyy-MM-dd - * - * @return String - */ - public static String getDate() - { - return dateTimeNow(YYYY_MM_DD); - } - - public static final String getTime() - { - return dateTimeNow(YYYY_MM_DD_HH_MM_SS); - } - - public static final String dateTimeNow() - { - return dateTimeNow(YYYYMMDDHHMMSS); - } - - public static final String dateTimeNow(final String format) - { - return parseDateToStr(format, new Date()); - } - - public static final String dateTime(final Date date) - { - return parseDateToStr(YYYY_MM_DD, date); - } - - public static final String parseDateToStr(final String format, final Date date) - { - return new SimpleDateFormat(format).format(date); - } - - public static final Date dateTime(final String format, final String ts) - { - try - { - return new SimpleDateFormat(format).parse(ts); - } - catch (ParseException e) - { - throw new RuntimeException(e); - } - } - - /** - * 日期路径 即年/月/日 如2018/08/08 - */ - public static final String datePath() - { - Date now = new Date(); - return DateFormatUtils.format(now, "yyyy/MM/dd"); - } - - /** - * 日期路径 即年/月/日 如20180808 - */ - public static final String dateTime() - { - Date now = new Date(); - return DateFormatUtils.format(now, "yyyyMMdd"); - } - - /** - * 日期型字符串转化为日期 格式 - */ - public static Date parseDate(Object str) - { - if (str == null) - { - return null; - } - try - { - return parseDate(str.toString(), parsePatterns); - } - catch (ParseException e) - { - return null; - } - } - - /** - * 获取服务器启动时间 - */ - public static Date getServerStartDate() - { - long time = ManagementFactory.getRuntimeMXBean().getStartTime(); - return new Date(time); - } - - /** - * 计算相差天数 - */ - public static int differentDaysByMillisecond(Date date1, Date date2) - { - return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24))); - } - - /** - * 计算时间差 - * - * @param endDate 最后时间 - * @param startTime 开始时间 - * @return 时间差(天/小时/分钟) - */ - public static String timeDistance(Date endDate, Date startTime) - { - long nd = 1000 * 24 * 60 * 60; - long nh = 1000 * 60 * 60; - long nm = 1000 * 60; - // long ns = 1000; - // 获得两个时间的毫秒时间差异 - long diff = endDate.getTime() - startTime.getTime(); - // 计算差多少天 - long day = diff / nd; - // 计算差多少小时 - long hour = diff % nd / nh; - // 计算差多少分钟 - long min = diff % nd % nh / nm; - // 计算差多少秒//输出结果 - // long sec = diff % nd % nh % nm / ns; - return day + "天" + hour + "小时" + min + "分钟"; - } - - /** - * 增加 LocalDateTime ==> Date - */ - public static Date toDate(LocalDateTime temporalAccessor) - { - ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault()); - return Date.from(zdt.toInstant()); - } - - /** - * 增加 LocalDate ==> Date - */ - public static Date toDate(LocalDate temporalAccessor) - { - LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0)); - ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault()); - return Date.from(zdt.toInstant()); - } -} +package com.ruoyi.common.utils; + +import java.lang.management.ManagementFactory; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Date; +import org.apache.commons.lang3.time.DateFormatUtils; + +/** + * 时间工具类 + * + * @author ruoyi + */ +public class DateUtils extends org.apache.commons.lang3.time.DateUtils +{ + public static String YYYY = "yyyy"; + + public static String YYYY_MM = "yyyy-MM"; + + public static String YYYY_MM_DD = "yyyy-MM-dd"; + + public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; + + public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; + + private static String[] parsePatterns = { + "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", + "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", + "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; + + /** + * 获取当前Date型日期 + * + * @return Date() 当前日期 + */ + public static Date getNowDate() + { + return new Date(); + } + + /** + * 获取当前日期, 默认格式为yyyy-MM-dd + * + * @return String + */ + public static String getDate() + { + return dateTimeNow(YYYY_MM_DD); + } + + public static final String getTime() + { + return dateTimeNow(YYYY_MM_DD_HH_MM_SS); + } + + public static final String dateTimeNow() + { + return dateTimeNow(YYYYMMDDHHMMSS); + } + + public static final String dateTimeNow(final String format) + { + return parseDateToStr(format, new Date()); + } + + public static final String dateTime(final Date date) + { + return parseDateToStr(YYYY_MM_DD, date); + } + + public static final String parseDateToStr(final String format, final Date date) + { + return new SimpleDateFormat(format).format(date); + } + + public static final Date dateTime(final String format, final String ts) + { + try + { + return new SimpleDateFormat(format).parse(ts); + } + catch (ParseException e) + { + throw new RuntimeException(e); + } + } + + /** + * 日期路径 即年/月/日 如2018/08/08 + */ + public static final String datePath() + { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyy/MM/dd"); + } + + /** + * 日期路径 即年/月/日 如20180808 + */ + public static final String dateTime() + { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyyMMdd"); + } + + /** + * 日期型字符串转化为日期 格式 + */ + public static Date parseDate(Object str) + { + if (str == null) + { + return null; + } + try + { + return parseDate(str.toString(), parsePatterns); + } + catch (ParseException e) + { + return null; + } + } + + /** + * 获取服务器启动时间 + */ + public static Date getServerStartDate() + { + long time = ManagementFactory.getRuntimeMXBean().getStartTime(); + return new Date(time); + } + + /** + * 计算相差天数 + */ + public static int differentDaysByMillisecond(Date date1, Date date2) + { + return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24))); + } + + /** + * 计算时间差 + * + * @param endDate 最后时间 + * @param startTime 开始时间 + * @return 时间差(天/小时/分钟) + */ + public static String timeDistance(Date endDate, Date startTime) + { + long nd = 1000 * 24 * 60 * 60; + long nh = 1000 * 60 * 60; + long nm = 1000 * 60; + // long ns = 1000; + // 获得两个时间的毫秒时间差异 + long diff = endDate.getTime() - startTime.getTime(); + // 计算差多少天 + long day = diff / nd; + // 计算差多少小时 + long hour = diff % nd / nh; + // 计算差多少分钟 + long min = diff % nd % nh / nm; + // 计算差多少秒//输出结果 + // long sec = diff % nd % nh % nm / ns; + return day + "天" + hour + "小时" + min + "分钟"; + } + + /** + * 增加 LocalDateTime ==> Date + */ + public static Date toDate(LocalDateTime temporalAccessor) + { + ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } + + /** + * 增加 LocalDate ==> Date + */ + public static Date toDate(LocalDate temporalAccessor) + { + LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0)); + ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java index 8eddd8c..c89650e 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java @@ -1,221 +1,217 @@ -package com.ruoyi.common.utils; - -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import com.alibaba.fastjson2.JSON; -import com.ruoyi.common.constant.CacheConstants; -import com.ruoyi.common.core.domain.entity.SysDictData; -import com.ruoyi.common.core.redis.RedisCache; -import com.ruoyi.common.utils.spring.SpringUtils; - -/** - * 字典工具类 - * - * @author ruoyi - */ -public class DictUtils -{ - /** - * 分隔符 - */ - public static final String SEPARATOR = ","; - - /** - * 设置字典缓存 - * - * @param key 参数键 - * @param dictDatas 字典数据列表 - */ - public static void setDictCache(String key, List dictDatas) - { - SpringUtils.getBean(RedisCache.class).setCacheObject(getCacheKey(key), dictDatas); - } - - /** - * 获取字典缓存 - * - * @param key 参数键 - * @return dictDatas 字典数据列表 - */ - public static List getDictCache(String key) - { - Object cacheObject = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key)); - if (cacheObject instanceof List listCache) - { - return (List) listCache; - } - if (StringUtils.isNotNull(cacheObject)) - { - return JSON.parseArray(JSON.toJSONString(cacheObject), SysDictData.class); - } - return null; - } - - /** - * 根据字典类型和字典值获取字典标签 - * - * @param dictType 字典类型 - * @param dictValue 字典值 - * @return 字典标签 - */ - public static String getDictLabel(String dictType, String dictValue) - { - if (StringUtils.isEmpty(dictValue)) - { - return StringUtils.EMPTY; - } - return getDictLabel(dictType, dictValue, SEPARATOR); - } - - /** - * 根据字典类型和字典标签获取字典值 - * - * @param dictType 字典类型 - * @param dictLabel 字典标签 - * @return 字典值 - */ - public static String getDictValue(String dictType, String dictLabel) - { - if (StringUtils.isEmpty(dictLabel)) - { - return StringUtils.EMPTY; - } - return getDictValue(dictType, dictLabel, SEPARATOR); - } - - /** - * 根据字典类型和字典值获取字典标签 - * - * @param dictType 字典类型 - * @param dictValue 字典值 - * @param separator 分隔符 - * @return 字典标签 - */ - public static String getDictLabel(String dictType, String dictValue, String separator) - { - List datas = getDictCache(dictType); - if (StringUtils.isNull(datas) || StringUtils.isEmpty(dictValue)) - { - return StringUtils.EMPTY; - } - Map dictMap = datas.stream().collect(HashMap::new, (map, dict) -> map.put(dict.getDictValue(), dict.getDictLabel()), Map::putAll); - if (!StringUtils.contains(dictValue, separator)) - { - return dictMap.getOrDefault(dictValue, StringUtils.EMPTY); - } - StringBuilder labelBuilder = new StringBuilder(); - for (String seperatedValue : dictValue.split(separator)) - { - if (dictMap.containsKey(seperatedValue)) - { - labelBuilder.append(dictMap.get(seperatedValue)).append(separator); - } - } - return StringUtils.removeEnd(labelBuilder.toString(), separator); - } - - /** - * 根据字典类型和字典标签获取字典值 - * - * @param dictType 字典类型 - * @param dictLabel 字典标签 - * @param separator 分隔符 - * @return 字典值 - */ - public static String getDictValue(String dictType, String dictLabel, String separator) - { - List datas = getDictCache(dictType); - if (StringUtils.isNull(datas) || StringUtils.isEmpty(dictLabel)) - { - return StringUtils.EMPTY; - } - Map dictMap = datas.stream().collect(HashMap::new, (map, dict) -> map.put(dict.getDictLabel(), dict.getDictValue()), Map::putAll); - if (!StringUtils.contains(dictLabel, separator)) - { - return dictMap.getOrDefault(dictLabel, StringUtils.EMPTY); - } - StringBuilder valueBuilder = new StringBuilder(); - for (String seperatedValue : dictLabel.split(separator)) - { - if (dictMap.containsKey(seperatedValue)) - { - valueBuilder.append(dictMap.get(seperatedValue)).append(separator); - } - } - return StringUtils.removeEnd(valueBuilder.toString(), separator); - } - - /** - * 根据字典类型获取字典所有值 - * - * @param dictType 字典类型 - * @return 字典值 - */ - public static String getDictValues(String dictType) - { - StringBuilder propertyString = new StringBuilder(); - List datas = getDictCache(dictType); - if (StringUtils.isNull(datas)) - { - return StringUtils.EMPTY; - } - for (SysDictData dict : datas) - { - propertyString.append(dict.getDictValue()).append(SEPARATOR); - } - return StringUtils.stripEnd(propertyString.toString(), SEPARATOR); - } - - /** - * 根据字典类型获取字典所有标签 - * - * @param dictType 字典类型 - * @return 字典值 - */ - public static String getDictLabels(String dictType) - { - StringBuilder propertyString = new StringBuilder(); - List datas = getDictCache(dictType); - if (StringUtils.isNull(datas)) - { - return StringUtils.EMPTY; - } - for (SysDictData dict : datas) - { - propertyString.append(dict.getDictLabel()).append(SEPARATOR); - } - return StringUtils.stripEnd(propertyString.toString(), SEPARATOR); - } - - /** - * 删除指定字典缓存 - * - * @param key 字典键 - */ - public static void removeDictCache(String key) - { - SpringUtils.getBean(RedisCache.class).deleteObject(getCacheKey(key)); - } - - /** - * 清空字典缓存 - */ - public static void clearDictCache() - { - Collection keys = SpringUtils.getBean(RedisCache.class).keys(CacheConstants.SYS_DICT_KEY + "*"); - SpringUtils.getBean(RedisCache.class).deleteObject(keys); - } - - /** - * 设置cache key - * - * @param configKey 参数键 - * @return 缓存键key - */ - public static String getCacheKey(String configKey) - { - return CacheConstants.SYS_DICT_KEY + configKey; - } -} +package com.ruoyi.common.utils; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +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; +import com.ruoyi.common.utils.spring.SpringUtils; + +/** + * 字典工具类 + * + * @author ruoyi + */ +public class DictUtils +{ + /** + * 分隔符 + */ + public static final String SEPARATOR = ","; + + /** + * 设置字典缓存 + * + * @param key 参数键 + * @param dictDatas 字典数据列表 + */ + public static void setDictCache(String key, List dictDatas) + { + SpringUtils.getBean(RedisCache.class).setCacheObject(getCacheKey(key), dictDatas); + } + + /** + * 获取字典缓存 + * + * @param key 参数键 + * @return dictDatas 字典数据列表 + */ + public static List getDictCache(String key) + { + JSONArray arrayCache = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key)); + if (StringUtils.isNotNull(arrayCache)) + { + return arrayCache.toList(SysDictData.class); + } + return null; + } + + /** + * 根据字典类型和字典值获取字典标签 + * + * @param dictType 字典类型 + * @param dictValue 字典值 + * @return 字典标签 + */ + public static String getDictLabel(String dictType, String dictValue) + { + if (StringUtils.isEmpty(dictValue)) + { + return StringUtils.EMPTY; + } + return getDictLabel(dictType, dictValue, SEPARATOR); + } + + /** + * 根据字典类型和字典标签获取字典值 + * + * @param dictType 字典类型 + * @param dictLabel 字典标签 + * @return 字典值 + */ + public static String getDictValue(String dictType, String dictLabel) + { + if (StringUtils.isEmpty(dictLabel)) + { + return StringUtils.EMPTY; + } + return getDictValue(dictType, dictLabel, SEPARATOR); + } + + /** + * 根据字典类型和字典值获取字典标签 + * + * @param dictType 字典类型 + * @param dictValue 字典值 + * @param separator 分隔符 + * @return 字典标签 + */ + public static String getDictLabel(String dictType, String dictValue, String separator) + { + List datas = getDictCache(dictType); + if (StringUtils.isNull(datas) || StringUtils.isEmpty(dictValue)) + { + return StringUtils.EMPTY; + } + Map dictMap = datas.stream().collect(HashMap::new, (map, dict) -> map.put(dict.getDictValue(), dict.getDictLabel()), Map::putAll); + if (!StringUtils.contains(dictValue, separator)) + { + return dictMap.getOrDefault(dictValue, StringUtils.EMPTY); + } + StringBuilder labelBuilder = new StringBuilder(); + for (String seperatedValue : dictValue.split(separator)) + { + if (dictMap.containsKey(seperatedValue)) + { + labelBuilder.append(dictMap.get(seperatedValue)).append(separator); + } + } + return StringUtils.removeEnd(labelBuilder.toString(), separator); + } + + /** + * 根据字典类型和字典标签获取字典值 + * + * @param dictType 字典类型 + * @param dictLabel 字典标签 + * @param separator 分隔符 + * @return 字典值 + */ + public static String getDictValue(String dictType, String dictLabel, String separator) + { + List datas = getDictCache(dictType); + if (StringUtils.isNull(datas) || StringUtils.isEmpty(dictLabel)) + { + return StringUtils.EMPTY; + } + Map dictMap = datas.stream().collect(HashMap::new, (map, dict) -> map.put(dict.getDictLabel(), dict.getDictValue()), Map::putAll); + if (!StringUtils.contains(dictLabel, separator)) + { + return dictMap.getOrDefault(dictLabel, StringUtils.EMPTY); + } + StringBuilder valueBuilder = new StringBuilder(); + for (String seperatedValue : dictLabel.split(separator)) + { + if (dictMap.containsKey(seperatedValue)) + { + valueBuilder.append(dictMap.get(seperatedValue)).append(separator); + } + } + return StringUtils.removeEnd(valueBuilder.toString(), separator); + } + + /** + * 根据字典类型获取字典所有值 + * + * @param dictType 字典类型 + * @return 字典值 + */ + public static String getDictValues(String dictType) + { + StringBuilder propertyString = new StringBuilder(); + List datas = getDictCache(dictType); + if (StringUtils.isNull(datas)) + { + return StringUtils.EMPTY; + } + for (SysDictData dict : datas) + { + propertyString.append(dict.getDictValue()).append(SEPARATOR); + } + return StringUtils.stripEnd(propertyString.toString(), SEPARATOR); + } + + /** + * 根据字典类型获取字典所有标签 + * + * @param dictType 字典类型 + * @return 字典值 + */ + public static String getDictLabels(String dictType) + { + StringBuilder propertyString = new StringBuilder(); + List datas = getDictCache(dictType); + if (StringUtils.isNull(datas)) + { + return StringUtils.EMPTY; + } + for (SysDictData dict : datas) + { + propertyString.append(dict.getDictLabel()).append(SEPARATOR); + } + return StringUtils.stripEnd(propertyString.toString(), SEPARATOR); + } + + /** + * 删除指定字典缓存 + * + * @param key 字典键 + */ + public static void removeDictCache(String key) + { + SpringUtils.getBean(RedisCache.class).deleteObject(getCacheKey(key)); + } + + /** + * 清空字典缓存 + */ + public static void clearDictCache() + { + Collection keys = SpringUtils.getBean(RedisCache.class).keys(CacheConstants.SYS_DICT_KEY + "*"); + SpringUtils.getBean(RedisCache.class).deleteObject(keys); + } + + /** + * 设置cache key + * + * @param configKey 参数键 + * @return 缓存键key + */ + public static String getCacheKey(String configKey) + { + return CacheConstants.SYS_DICT_KEY + configKey; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java index 214e4a0..50b10fe 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java @@ -1,39 +1,39 @@ -package com.ruoyi.common.utils; - -import java.io.PrintWriter; -import java.io.StringWriter; -import org.apache.commons.lang3.exception.ExceptionUtils; - -/** - * 错误信息处理类。 - * - * @author ruoyi - */ -public class ExceptionUtil -{ - /** - * 获取exception的详细错误信息。 - */ - public static String getExceptionMessage(Throwable e) - { - StringWriter sw = new StringWriter(); - e.printStackTrace(new PrintWriter(sw, true)); - return sw.toString(); - } - - public static String getRootErrorMessage(Exception e) - { - Throwable root = ExceptionUtils.getRootCause(e); - root = (root == null ? e : root); - if (root == null) - { - return ""; - } - String msg = root.getMessage(); - if (msg == null) - { - return "null"; - } - return StringUtils.defaultString(msg); - } -} +package com.ruoyi.common.utils; + +import java.io.PrintWriter; +import java.io.StringWriter; +import org.apache.commons.lang3.exception.ExceptionUtils; + +/** + * 错误信息处理类。 + * + * @author ruoyi + */ +public class ExceptionUtil +{ + /** + * 获取exception的详细错误信息。 + */ + public static String getExceptionMessage(Throwable e) + { + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw, true)); + return sw.toString(); + } + + public static String getRootErrorMessage(Exception e) + { + Throwable root = ExceptionUtils.getRootCause(e); + root = (root == null ? e : root); + if (root == null) + { + return ""; + } + String msg = root.getMessage(); + if (msg == null) + { + return "null"; + } + return StringUtils.defaultString(msg); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/LogUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/LogUtils.java index 0de30c6..e415c9c 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/LogUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/LogUtils.java @@ -1,18 +1,18 @@ -package com.ruoyi.common.utils; - -/** - * 处理并记录日志文件 - * - * @author ruoyi - */ -public class LogUtils -{ - public static String getBlock(Object msg) - { - if (msg == null) - { - msg = ""; - } - return "[" + msg.toString() + "]"; - } -} +package com.ruoyi.common.utils; + +/** + * 处理并记录日志文件 + * + * @author ruoyi + */ +public class LogUtils +{ + public static String getBlock(Object msg) + { + if (msg == null) + { + msg = ""; + } + return "[" + msg.toString() + "]"; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/MessageUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/MessageUtils.java index 7dac75a..5f2d9ea 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/MessageUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/MessageUtils.java @@ -1,26 +1,26 @@ -package com.ruoyi.common.utils; - -import org.springframework.context.MessageSource; -import org.springframework.context.i18n.LocaleContextHolder; -import com.ruoyi.common.utils.spring.SpringUtils; - -/** - * 获取i18n资源文件 - * - * @author ruoyi - */ -public class MessageUtils -{ - /** - * 根据消息键和参数 获取消息 委托给spring messageSource - * - * @param code 消息键 - * @param args 参数 - * @return 获取国际化翻译值 - */ - public static String message(String code, Object... args) - { - MessageSource messageSource = SpringUtils.getBean(MessageSource.class); - return messageSource.getMessage(code, args, LocaleContextHolder.getLocale()); - } -} +package com.ruoyi.common.utils; + +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; +import com.ruoyi.common.utils.spring.SpringUtils; + +/** + * 获取i18n资源文件 + * + * @author ruoyi + */ +public class MessageUtils +{ + /** + * 根据消息键和参数 获取消息 委托给spring messageSource + * + * @param code 消息键 + * @param args 参数 + * @return 获取国际化翻译值 + */ + public static String message(String code, Object... args) + { + MessageSource messageSource = SpringUtils.getBean(MessageSource.class); + return messageSource.getMessage(code, args, LocaleContextHolder.getLocale()); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java index 8cd0a2f..fd4aa74 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java @@ -1,188 +1,188 @@ -package com.ruoyi.common.utils; - -import java.util.Collection; -import java.util.List; -import java.util.stream.Collectors; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.util.PatternMatchUtils; -import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.constant.HttpStatus; -import com.ruoyi.common.core.domain.entity.SysRole; -import com.ruoyi.common.core.domain.model.LoginUser; -import com.ruoyi.common.exception.ServiceException; - -/** - * 安全服务工具类 - * - * @author ruoyi - */ -public class SecurityUtils -{ - - /** - * 用户ID - **/ - public static Long getUserId() - { - try - { - return getLoginUser().getUserId(); - } - catch (Exception e) - { - throw new ServiceException("获取用户ID异常", HttpStatus.UNAUTHORIZED); - } - } - - /** - * 获取部门ID - **/ - public static Long getDeptId() - { - try - { - return getLoginUser().getDeptId(); - } - catch (Exception e) - { - throw new ServiceException("获取部门ID异常", HttpStatus.UNAUTHORIZED); - } - } - - /** - * 获取用户账户 - **/ - public static String getUsername() - { - try - { - return getLoginUser().getUsername(); - } - catch (Exception e) - { - throw new ServiceException("获取用户账户异常", HttpStatus.UNAUTHORIZED); - } - } - - /** - * 获取用户 - **/ - public static LoginUser getLoginUser() - { - try - { - return (LoginUser) getAuthentication().getPrincipal(); - } - catch (Exception e) - { - throw new ServiceException("获取用户信息异常", HttpStatus.UNAUTHORIZED); - } - } - - /** - * 获取Authentication - */ - public static Authentication getAuthentication() - { - return SecurityContextHolder.getContext().getAuthentication(); - } - - /** - * 生成BCryptPasswordEncoder密码 - * - * @param password 密码 - * @return 加密字符串 - */ - public static String encryptPassword(String password) - { - BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); - return passwordEncoder.encode(password); - } - - /** - * 判断密码是否相同 - * - * @param rawPassword 真实密码 - * @param encodedPassword 加密后字符 - * @return 结果 - */ - public static boolean matchesPassword(String rawPassword, String encodedPassword) - { - BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); - return passwordEncoder.matches(rawPassword, encodedPassword); - } - - /** - * 是否为管理员 - * - * @return 结果 - */ - public static boolean isAdmin() - { - return isAdmin(getUserId()); - } - - /** - * 是否为管理员 - * - * @param userId 用户ID - * @return 结果 - */ - public static boolean isAdmin(Long userId) - { - return userId != null && 1L == userId; - } - - /** - * 验证用户是否具备某权限 - * - * @param permission 权限字符串 - * @return 用户是否具备某权限 - */ - public static boolean hasPermi(String permission) - { - return hasPermi(getLoginUser().getPermissions(), permission); - } - - /** - * 判断是否包含权限 - * - * @param authorities 权限列表 - * @param permission 权限字符串 - * @return 用户是否具备某权限 - */ - public static boolean hasPermi(Collection authorities, String permission) - { - return authorities.stream().filter(StringUtils::hasText) - .anyMatch(x -> Constants.ALL_PERMISSION.equals(x) || PatternMatchUtils.simpleMatch(x, permission)); - } - - /** - * 验证用户是否拥有某个角色 - * - * @param role 角色标识 - * @return 用户是否具备某角色 - */ - public static boolean hasRole(String role) - { - List roleList = getLoginUser().getUser().getRoles(); - Collection roles = roleList.stream().map(SysRole::getRoleKey).collect(Collectors.toSet()); - return hasRole(roles, role); - } - - /** - * 判断是否包含角色 - * - * @param roles 角色列表 - * @param role 角色 - * @return 用户是否具备某角色权限 - */ - public static boolean hasRole(Collection roles, String role) - { - return roles.stream().filter(StringUtils::hasText) - .anyMatch(x -> Constants.SUPER_ADMIN.equals(x) || PatternMatchUtils.simpleMatch(x, role)); - } - -} +package com.ruoyi.common.utils; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.util.PatternMatchUtils; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.exception.ServiceException; + +/** + * 安全服务工具类 + * + * @author ruoyi + */ +public class SecurityUtils +{ + + /** + * 用户ID + **/ + public static Long getUserId() + { + try + { + return getLoginUser().getUserId(); + } + catch (Exception e) + { + throw new ServiceException("获取用户ID异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取部门ID + **/ + public static Long getDeptId() + { + try + { + return getLoginUser().getDeptId(); + } + catch (Exception e) + { + throw new ServiceException("获取部门ID异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取用户账户 + **/ + public static String getUsername() + { + try + { + return getLoginUser().getUsername(); + } + catch (Exception e) + { + throw new ServiceException("获取用户账户异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取用户 + **/ + public static LoginUser getLoginUser() + { + try + { + return (LoginUser) getAuthentication().getPrincipal(); + } + catch (Exception e) + { + throw new ServiceException("获取用户信息异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取Authentication + */ + public static Authentication getAuthentication() + { + return SecurityContextHolder.getContext().getAuthentication(); + } + + /** + * 生成BCryptPasswordEncoder密码 + * + * @param password 密码 + * @return 加密字符串 + */ + public static String encryptPassword(String password) + { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.encode(password); + } + + /** + * 判断密码是否相同 + * + * @param rawPassword 真实密码 + * @param encodedPassword 加密后字符 + * @return 结果 + */ + public static boolean matchesPassword(String rawPassword, String encodedPassword) + { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.matches(rawPassword, encodedPassword); + } + + /** + * 是否为管理员 + * + * @return 结果 + */ + public static boolean isAdmin() + { + return isAdmin(getUserId()); + } + + /** + * 是否为管理员 + * + * @param userId 用户ID + * @return 结果 + */ + public static boolean isAdmin(Long userId) + { + return userId != null && 1L == userId; + } + + /** + * 验证用户是否具备某权限 + * + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + public static boolean hasPermi(String permission) + { + return hasPermi(getLoginUser().getPermissions(), permission); + } + + /** + * 判断是否包含权限 + * + * @param authorities 权限列表 + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + public static boolean hasPermi(Collection authorities, String permission) + { + return authorities.stream().filter(StringUtils::hasText) + .anyMatch(x -> Constants.ALL_PERMISSION.equals(x) || PatternMatchUtils.simpleMatch(x, permission)); + } + + /** + * 验证用户是否拥有某个角色 + * + * @param role 角色标识 + * @return 用户是否具备某角色 + */ + public static boolean hasRole(String role) + { + List roleList = getLoginUser().getUser().getRoles(); + Collection roles = roleList.stream().map(SysRole::getRoleKey).collect(Collectors.toSet()); + return hasRole(roles, role); + } + + /** + * 判断是否包含角色 + * + * @param roles 角色列表 + * @param role 角色 + * @return 用户是否具备某角色权限 + */ + public static boolean hasRole(Collection roles, String role) + { + return roles.stream().filter(StringUtils::hasText) + .anyMatch(x -> Constants.SUPER_ADMIN.equals(x) || PatternMatchUtils.simpleMatch(x, role)); + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java index 5635db7..292b8da 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java @@ -1,218 +1,218 @@ -package com.ruoyi.common.utils; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -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 org.springframework.web.context.request.RequestAttributes; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; -import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.core.text.Convert; - -/** - * 客户端工具类 - * - * @author ruoyi - */ -public class ServletUtils -{ - /** - * 获取String参数 - */ - public static String getParameter(String name) - { - return getRequest().getParameter(name); - } - - /** - * 获取String参数 - */ - public static String getParameter(String name, String defaultValue) - { - return Convert.toStr(getRequest().getParameter(name), defaultValue); - } - - /** - * 获取Integer参数 - */ - public static Integer getParameterToInt(String name) - { - return Convert.toInt(getRequest().getParameter(name)); - } - - /** - * 获取Integer参数 - */ - public static Integer getParameterToInt(String name, Integer defaultValue) - { - return Convert.toInt(getRequest().getParameter(name), defaultValue); - } - - /** - * 获取Boolean参数 - */ - public static Boolean getParameterToBool(String name) - { - return Convert.toBool(getRequest().getParameter(name)); - } - - /** - * 获取Boolean参数 - */ - public static Boolean getParameterToBool(String name, Boolean defaultValue) - { - return Convert.toBool(getRequest().getParameter(name), defaultValue); - } - - /** - * 获得所有请求参数 - * - * @param request 请求对象{@link ServletRequest} - * @return Map - */ - public static Map getParams(ServletRequest request) - { - final Map map = request.getParameterMap(); - return Collections.unmodifiableMap(map); - } - - /** - * 获得所有请求参数 - * - * @param request 请求对象{@link ServletRequest} - * @return Map - */ - public static Map getParamMap(ServletRequest request) - { - Map params = new HashMap<>(); - for (Map.Entry entry : getParams(request).entrySet()) - { - params.put(entry.getKey(), StringUtils.join(entry.getValue(), ",")); - } - return params; - } - - /** - * 获取request - */ - public static HttpServletRequest getRequest() - { - return getRequestAttributes().getRequest(); - } - - /** - * 获取response - */ - public static HttpServletResponse getResponse() - { - return getRequestAttributes().getResponse(); - } - - /** - * 获取session - */ - public static HttpSession getSession() - { - return getRequest().getSession(); - } - - public static ServletRequestAttributes getRequestAttributes() - { - RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); - return (ServletRequestAttributes) attributes; - } - - /** - * 将字符串渲染到客户端 - * - * @param response 渲染对象 - * @param string 待渲染的字符串 - */ - public static void renderString(HttpServletResponse response, String string) - { - try - { - response.setStatus(200); - response.setContentType("application/json"); - response.setCharacterEncoding("utf-8"); - response.getWriter().print(string); - } - catch (IOException e) - { - e.printStackTrace(); - } - } - - /** - * 是否是Ajax异步请求 - * - * @param request - */ - public static boolean isAjaxRequest(HttpServletRequest request) - { - String accept = request.getHeader("accept"); - if (accept != null && accept.contains("application/json")) - { - return true; - } - - String xRequestedWith = request.getHeader("X-Requested-With"); - if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) - { - return true; - } - - String uri = request.getRequestURI(); - if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) - { - return true; - } - - String ajax = request.getParameter("__ajax"); - return StringUtils.inStringIgnoreCase(ajax, "json", "xml"); - } - - /** - * 内容编码 - * - * @param str 内容 - * @return 编码后的内容 - */ - public static String urlEncode(String str) - { - try - { - return URLEncoder.encode(str, Constants.UTF8); - } - catch (UnsupportedEncodingException e) - { - return StringUtils.EMPTY; - } - } - - /** - * 内容解码 - * - * @param str 内容 - * @return 解码后的内容 - */ - public static String urlDecode(String str) - { - try - { - return URLDecoder.decode(str, Constants.UTF8); - } - catch (UnsupportedEncodingException e) - { - return StringUtils.EMPTY; - } - } -} +package com.ruoyi.common.utils; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +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; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.text.Convert; + +/** + * 客户端工具类 + * + * @author ruoyi + */ +public class ServletUtils +{ + /** + * 获取String参数 + */ + public static String getParameter(String name) + { + return getRequest().getParameter(name); + } + + /** + * 获取String参数 + */ + public static String getParameter(String name, String defaultValue) + { + return Convert.toStr(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name) + { + return Convert.toInt(getRequest().getParameter(name)); + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name, Integer defaultValue) + { + return Convert.toInt(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取Boolean参数 + */ + public static Boolean getParameterToBool(String name) + { + return Convert.toBool(getRequest().getParameter(name)); + } + + /** + * 获取Boolean参数 + */ + public static Boolean getParameterToBool(String name, Boolean defaultValue) + { + return Convert.toBool(getRequest().getParameter(name), defaultValue); + } + + /** + * 获得所有请求参数 + * + * @param request 请求对象{@link ServletRequest} + * @return Map + */ + public static Map getParams(ServletRequest request) + { + final Map map = request.getParameterMap(); + return Collections.unmodifiableMap(map); + } + + /** + * 获得所有请求参数 + * + * @param request 请求对象{@link ServletRequest} + * @return Map + */ + public static Map getParamMap(ServletRequest request) + { + Map params = new HashMap<>(); + for (Map.Entry entry : getParams(request).entrySet()) + { + params.put(entry.getKey(), StringUtils.join(entry.getValue(), ",")); + } + return params; + } + + /** + * 获取request + */ + public static HttpServletRequest getRequest() + { + return getRequestAttributes().getRequest(); + } + + /** + * 获取response + */ + public static HttpServletResponse getResponse() + { + return getRequestAttributes().getResponse(); + } + + /** + * 获取session + */ + public static HttpSession getSession() + { + return getRequest().getSession(); + } + + public static ServletRequestAttributes getRequestAttributes() + { + RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); + return (ServletRequestAttributes) attributes; + } + + /** + * 将字符串渲染到客户端 + * + * @param response 渲染对象 + * @param string 待渲染的字符串 + */ + public static void renderString(HttpServletResponse response, String string) + { + try + { + response.setStatus(200); + response.setContentType("application/json"); + response.setCharacterEncoding("utf-8"); + response.getWriter().print(string); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + + /** + * 是否是Ajax异步请求 + * + * @param request + */ + public static boolean isAjaxRequest(HttpServletRequest request) + { + String accept = request.getHeader("accept"); + if (accept != null && accept.contains("application/json")) + { + return true; + } + + String xRequestedWith = request.getHeader("X-Requested-With"); + if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) + { + return true; + } + + String uri = request.getRequestURI(); + if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) + { + return true; + } + + String ajax = request.getParameter("__ajax"); + return StringUtils.inStringIgnoreCase(ajax, "json", "xml"); + } + + /** + * 内容编码 + * + * @param str 内容 + * @return 编码后的内容 + */ + public static String urlEncode(String str) + { + try + { + return URLEncoder.encode(str, Constants.UTF8); + } + catch (UnsupportedEncodingException e) + { + return StringUtils.EMPTY; + } + } + + /** + * 内容解码 + * + * @param str 内容 + * @return 解码后的内容 + */ + public static String urlDecode(String str) + { + try + { + return URLDecoder.decode(str, Constants.UTF8); + } + catch (UnsupportedEncodingException e) + { + return StringUtils.EMPTY; + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java index 076a1ac..74eb532 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java @@ -1,723 +1,722 @@ -package com.ruoyi.common.utils; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import org.springframework.util.AntPathMatcher; -import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.core.text.StrFormatter; - -/** - * 字符串工具类 - * - * @author ruoyi - */ -@SuppressWarnings("deprecation") -public class StringUtils extends org.apache.commons.lang3.StringUtils -{ - /** 空字符串 */ - private static final String NULLSTR = ""; - - /** 下划线 */ - private static final char SEPARATOR = '_'; - - /** 星号 */ - private static final char ASTERISK = '*'; - - /** - * 获取参数不为空值 - * - * @param value defaultValue 要判断的value - * @return value 返回值 - */ - public static T nvl(T value, T defaultValue) - { - return value != null ? value : defaultValue; - } - - /** - * * 判断一个Collection是否为空, 包含List,Set,Queue - * - * @param coll 要判断的Collection - * @return true:为空 false:非空 - */ - public static boolean isEmpty(Collection coll) - { - return isNull(coll) || coll.isEmpty(); - } - - /** - * * 判断一个Collection是否非空,包含List,Set,Queue - * - * @param coll 要判断的Collection - * @return true:非空 false:空 - */ - public static boolean isNotEmpty(Collection coll) - { - return !isEmpty(coll); - } - - /** - * * 判断一个对象数组是否为空 - * - * @param objects 要判断的对象数组 - ** @return true:为空 false:非空 - */ - public static boolean isEmpty(Object[] objects) - { - return isNull(objects) || (objects.length == 0); - } - - /** - * * 判断一个对象数组是否非空 - * - * @param objects 要判断的对象数组 - * @return true:非空 false:空 - */ - public static boolean isNotEmpty(Object[] objects) - { - return !isEmpty(objects); - } - - /** - * * 判断一个Map是否为空 - * - * @param map 要判断的Map - * @return true:为空 false:非空 - */ - public static boolean isEmpty(Map map) - { - return isNull(map) || map.isEmpty(); - } - - /** - * * 判断一个Map是否为空 - * - * @param map 要判断的Map - * @return true:非空 false:空 - */ - public static boolean isNotEmpty(Map map) - { - return !isEmpty(map); - } - - /** - * * 判断一个字符串是否为空串 - * - * @param str String - * @return true:为空 false:非空 - */ - public static boolean isEmpty(String str) - { - return isNull(str) || NULLSTR.equals(str.trim()); - } - - /** - * * 判断一个字符串是否为非空串 - * - * @param str String - * @return true:非空串 false:空串 - */ - public static boolean isNotEmpty(String str) - { - return !isEmpty(str); - } - - /** - * * 判断一个对象是否为空 - * - * @param object Object - * @return true:为空 false:非空 - */ - public static boolean isNull(Object object) - { - return object == null; - } - - /** - * * 判断一个对象是否非空 - * - * @param object Object - * @return true:非空 false:空 - */ - public static boolean isNotNull(Object object) - { - return !isNull(object); - } - - /** - * * 判断一个对象是否是数组类型(Java基本型别的数组) - * - * @param object 对象 - * @return true:是数组 false:不是数组 - */ - public static boolean isArray(Object object) - { - return isNotNull(object) && object.getClass().isArray(); - } - - /** - * 去空格 - */ - public static String trim(String str) - { - return (str == null ? "" : str.trim()); - } - - /** - * 替换指定字符串的指定区间内字符为"*" - * - * @param str 字符串 - * @param startInclude 开始位置(包含) - * @param endExclude 结束位置(不包含) - * @return 替换后的字符串 - */ - public static String hide(CharSequence str, int startInclude, int endExclude) - { - if (isEmpty(str)) - { - return NULLSTR; - } - final int strLength = str.length(); - if (startInclude > strLength) - { - return NULLSTR; - } - if (endExclude > strLength) - { - endExclude = strLength; - } - if (startInclude > endExclude) - { - // 如果起始位置大于结束位置,不替换 - return NULLSTR; - } - final char[] chars = new char[strLength]; - for (int i = 0; i < strLength; i++) - { - if (i >= startInclude && i < endExclude) - { - chars[i] = ASTERISK; - } - else - { - chars[i] = str.charAt(i); - } - } - return new String(chars); - } - - /** - * 截取字符串 - * - * @param str 字符串 - * @param start 开始 - * @return 结果 - */ - public static String substring(final String str, int start) - { - if (str == null) - { - return NULLSTR; - } - - if (start < 0) - { - start = str.length() + start; - } - - if (start < 0) - { - start = 0; - } - if (start > str.length()) - { - return NULLSTR; - } - - return str.substring(start); - } - - /** - * 截取字符串 - * - * @param str 字符串 - * @param start 开始 - * @param end 结束 - * @return 结果 - */ - public static String substring(final String str, int start, int end) - { - if (str == null) - { - return NULLSTR; - } - - if (end < 0) - { - end = str.length() + end; - } - if (start < 0) - { - start = str.length() + start; - } - - if (end > str.length()) - { - end = str.length(); - } - - if (start > end) - { - return NULLSTR; - } - - if (start < 0) - { - start = 0; - } - if (end < 0) - { - end = 0; - } - - return str.substring(start, end); - } - - /** - * 在字符串中查找第一个出现的 `open` 和最后一个出现的 `close` 之间的子字符串 - * - * @param str 要截取的字符串 - * @param open 起始字符串 - * @param close 结束字符串 - * @return 截取结果 - */ - public static String substringBetweenLast(final String str, final String open, final String close) - { - if (isEmpty(str) || isEmpty(open) || isEmpty(close)) - { - return NULLSTR; - } - final int start = str.indexOf(open); - if (start != INDEX_NOT_FOUND) - { - final int end = str.lastIndexOf(close); - if (end != INDEX_NOT_FOUND) - { - return str.substring(start + open.length(), end); - } - } - return NULLSTR; - } - - /** - * 判断是否为空,并且不是空白字符 - * - * @param str 要判断的value - * @return 结果 - */ - public static boolean hasText(String str) - { - return (str != null && !str.isEmpty() && containsText(str)); - } - - private static boolean containsText(CharSequence str) - { - int strLen = str.length(); - for (int i = 0; i < strLen; i++) - { - if (!Character.isWhitespace(str.charAt(i))) - { - return true; - } - } - return false; - } - - /** - * 格式化文本, {} 表示占位符
- * 此方法只是简单将占位符 {} 按照顺序替换为参数
- * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
- * 例:
- * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
- * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
- * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
- * - * @param template 文本模板,被替换的部分用 {} 表示 - * @param params 参数值 - * @return 格式化后的文本 - */ - public static String format(String template, Object... params) - { - if (isEmpty(params) || isEmpty(template)) - { - return template; - } - return StrFormatter.format(template, params); - } - - /** - * 是否为http(s)://开头 - * - * @param link 链接 - * @return 结果 - */ - public static boolean ishttp(String link) - { - return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS); - } - - /** - * 字符串转set - * - * @param str 字符串 - * @param sep 分隔符 - * @return set集合 - */ - public static final Set str2Set(String str, String sep) - { - return new HashSet(str2List(str, sep, true, false)); - } - - /** - * 字符串转list - * - * @param str 字符串 - * @param sep 分隔符 - * @return list集合 - */ - public static final List str2List(String str, String sep) - { - return str2List(str, sep, true, false); - } - - /** - * 字符串转list - * - * @param str 字符串 - * @param sep 分隔符 - * @param filterBlank 过滤纯空白 - * @param trim 去掉首尾空白 - * @return list集合 - */ - public static final List str2List(String str, String sep, boolean filterBlank, boolean trim) - { - List list = new ArrayList(); - if (StringUtils.isEmpty(str)) - { - return list; - } - - // 过滤空白字符串 - if (filterBlank && StringUtils.isBlank(str)) - { - return list; - } - String[] split = str.split(sep); - for (String string : split) - { - if (filterBlank && StringUtils.isBlank(string)) - { - continue; - } - if (trim) - { - string = string.trim(); - } - list.add(string); - } - - return list; - } - - /** - * 判断给定的collection列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value - * - * @param collection 给定的集合 - * @param array 给定的数组 - * @return boolean 结果 - */ - public static boolean containsAny(Collection collection, String... array) - { - if (isEmpty(collection) || isEmpty(array)) - { - return false; - } - else - { - for (String str : array) - { - if (collection.contains(str)) - { - return true; - } - } - return false; - } - } - - /** - * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写 - * - * @param cs 指定字符串 - * @param searchCharSequences 需要检查的字符串数组 - * @return 是否包含任意一个字符串 - */ - public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) - { - if (isEmpty(cs) || isEmpty(searchCharSequences)) - { - return false; - } - for (CharSequence testStr : searchCharSequences) - { - if (containsIgnoreCase(cs, testStr)) - { - return true; - } - } - return false; - } - - /** - * 驼峰转下划线命名 - */ - public static String toUnderScoreCase(String str) - { - if (str == null) - { - return null; - } - StringBuilder sb = new StringBuilder(); - // 前置字符是否大写 - boolean preCharIsUpperCase = true; - // 当前字符是否大写 - boolean curreCharIsUpperCase = true; - // 下一字符是否大写 - boolean nexteCharIsUpperCase = true; - for (int i = 0; i < str.length(); i++) - { - char c = str.charAt(i); - if (i > 0) - { - preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1)); - } - else - { - preCharIsUpperCase = false; - } - - curreCharIsUpperCase = Character.isUpperCase(c); - - if (i < (str.length() - 1)) - { - nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1)); - } - - if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) - { - sb.append(SEPARATOR); - } - else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) - { - sb.append(SEPARATOR); - } - sb.append(Character.toLowerCase(c)); - } - - return sb.toString(); - } - - /** - * 是否包含字符串 - * - * @param str 验证字符串 - * @param strs 字符串组 - * @return 包含返回true - */ - public static boolean inStringIgnoreCase(String str, String... strs) - { - if (str != null && strs != null) - { - for (String s : strs) - { - if (str.equalsIgnoreCase(trim(s))) - { - return true; - } - } - } - return false; - } - - /** - * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld - * - * @param name 转换前的下划线大写方式命名的字符串 - * @return 转换后的驼峰式命名的字符串 - */ - public static String convertToCamelCase(String name) - { - StringBuilder result = new StringBuilder(); - // 快速检查 - if (name == null || name.isEmpty()) - { - // 没必要转换 - return ""; - } - else if (!name.contains("_")) - { - // 不含下划线,仅将首字母大写 - return name.substring(0, 1).toUpperCase() + name.substring(1); - } - // 用下划线将原始字符串分割 - String[] camels = name.split("_"); - for (String camel : camels) - { - // 跳过原始字符串中开头、结尾的下换线或双重下划线 - if (camel.isEmpty()) - { - continue; - } - // 首字母大写 - result.append(camel.substring(0, 1).toUpperCase()); - result.append(camel.substring(1).toLowerCase()); - } - return result.toString(); - } - - /** - * 驼峰式命名法 - * 例如:user_name->userName - */ - public static String toCamelCase(String s) - { - if (s == null) - { - return null; - } - if (s.indexOf(SEPARATOR) == -1) - { - return s; - } - s = s.toLowerCase(); - StringBuilder sb = new StringBuilder(s.length()); - boolean upperCase = false; - for (int i = 0; i < s.length(); i++) - { - char c = s.charAt(i); - - if (c == SEPARATOR) - { - upperCase = true; - } - else if (upperCase) - { - sb.append(Character.toUpperCase(c)); - upperCase = false; - } - else - { - sb.append(c); - } - } - return sb.toString(); - } - - /** - * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串 - * - * @param str 指定字符串 - * @param strs 需要检查的字符串数组 - * @return 是否匹配 - */ - public static boolean matches(String str, List strs) - { - if (isEmpty(str) || isEmpty(strs)) - { - return false; - } - for (String pattern : strs) - { - if (isMatch(pattern, str)) - { - return true; - } - } - return false; - } - - /** - * 判断url是否与规则配置: - * ? 表示单个字符; - * * 表示一层路径内的任意字符串,不可跨层级; - * ** 表示任意层路径; - * - * @param pattern 匹配规则 - * @param url 需要匹配的url - * @return - */ - public static boolean isMatch(String pattern, String url) - { - AntPathMatcher matcher = new AntPathMatcher(); - return matcher.match(pattern, url); - } - - @SuppressWarnings("unchecked") - public static T cast(Object obj) - { - return (T) obj; - } - - /** - * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。 - * - * @param num 数字对象 - * @param size 字符串指定长度 - * @return 返回数字的字符串格式,该字符串为指定长度。 - */ - public static final String padl(final Number num, final int size) - { - return padl(num.toString(), size, '0'); - } - - /** - * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。 - * - * @param s 原始字符串 - * @param size 字符串指定长度 - * @param c 用于补齐的字符 - * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。 - */ - public static final String padl(final String s, final int size, final char c) - { - final StringBuilder sb = new StringBuilder(size); - if (s != null) - { - final int len = s.length(); - if (s.length() <= size) - { - for (int i = size - len; i > 0; i--) - { - sb.append(c); - } - sb.append(s); - } - else - { - return s.substring(len - size, len); - } - } - else - { - for (int i = size; i > 0; i--) - { - sb.append(c); - } - } - return sb.toString(); - } +package com.ruoyi.common.utils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.springframework.util.AntPathMatcher; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.text.StrFormatter; + +/** + * 字符串工具类 + * + * @author ruoyi + */ +public class StringUtils extends org.apache.commons.lang3.StringUtils +{ + /** 空字符串 */ + private static final String NULLSTR = ""; + + /** 下划线 */ + private static final char SEPARATOR = '_'; + + /** 星号 */ + private static final char ASTERISK = '*'; + + /** + * 获取参数不为空值 + * + * @param value defaultValue 要判断的value + * @return value 返回值 + */ + public static T nvl(T value, T defaultValue) + { + return value != null ? value : defaultValue; + } + + /** + * * 判断一个Collection是否为空, 包含List,Set,Queue + * + * @param coll 要判断的Collection + * @return true:为空 false:非空 + */ + public static boolean isEmpty(Collection coll) + { + return isNull(coll) || coll.isEmpty(); + } + + /** + * * 判断一个Collection是否非空,包含List,Set,Queue + * + * @param coll 要判断的Collection + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Collection coll) + { + return !isEmpty(coll); + } + + /** + * * 判断一个对象数组是否为空 + * + * @param objects 要判断的对象数组 + ** @return true:为空 false:非空 + */ + public static boolean isEmpty(Object[] objects) + { + return isNull(objects) || (objects.length == 0); + } + + /** + * * 判断一个对象数组是否非空 + * + * @param objects 要判断的对象数组 + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Object[] objects) + { + return !isEmpty(objects); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * @return true:为空 false:非空 + */ + public static boolean isEmpty(Map map) + { + return isNull(map) || map.isEmpty(); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Map map) + { + return !isEmpty(map); + } + + /** + * * 判断一个字符串是否为空串 + * + * @param str String + * @return true:为空 false:非空 + */ + public static boolean isEmpty(String str) + { + return isNull(str) || NULLSTR.equals(str.trim()); + } + + /** + * * 判断一个字符串是否为非空串 + * + * @param str String + * @return true:非空串 false:空串 + */ + public static boolean isNotEmpty(String str) + { + return !isEmpty(str); + } + + /** + * * 判断一个对象是否为空 + * + * @param object Object + * @return true:为空 false:非空 + */ + public static boolean isNull(Object object) + { + return object == null; + } + + /** + * * 判断一个对象是否非空 + * + * @param object Object + * @return true:非空 false:空 + */ + public static boolean isNotNull(Object object) + { + return !isNull(object); + } + + /** + * * 判断一个对象是否是数组类型(Java基本型别的数组) + * + * @param object 对象 + * @return true:是数组 false:不是数组 + */ + public static boolean isArray(Object object) + { + return isNotNull(object) && object.getClass().isArray(); + } + + /** + * 去空格 + */ + public static String trim(String str) + { + return (str == null ? "" : str.trim()); + } + + /** + * 替换指定字符串的指定区间内字符为"*" + * + * @param str 字符串 + * @param startInclude 开始位置(包含) + * @param endExclude 结束位置(不包含) + * @return 替换后的字符串 + */ + public static String hide(CharSequence str, int startInclude, int endExclude) + { + if (isEmpty(str)) + { + return NULLSTR; + } + final int strLength = str.length(); + if (startInclude > strLength) + { + return NULLSTR; + } + if (endExclude > strLength) + { + endExclude = strLength; + } + if (startInclude > endExclude) + { + // 如果起始位置大于结束位置,不替换 + return NULLSTR; + } + final char[] chars = new char[strLength]; + for (int i = 0; i < strLength; i++) + { + if (i >= startInclude && i < endExclude) + { + chars[i] = ASTERISK; + } + else + { + chars[i] = str.charAt(i); + } + } + return new String(chars); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @return 结果 + */ + public static String substring(final String str, int start) + { + if (str == null) + { + return NULLSTR; + } + + if (start < 0) + { + start = str.length() + start; + } + + if (start < 0) + { + start = 0; + } + if (start > str.length()) + { + return NULLSTR; + } + + return str.substring(start); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @param end 结束 + * @return 结果 + */ + public static String substring(final String str, int start, int end) + { + if (str == null) + { + return NULLSTR; + } + + if (end < 0) + { + end = str.length() + end; + } + if (start < 0) + { + start = str.length() + start; + } + + if (end > str.length()) + { + end = str.length(); + } + + if (start > end) + { + return NULLSTR; + } + + if (start < 0) + { + start = 0; + } + if (end < 0) + { + end = 0; + } + + return str.substring(start, end); + } + + /** + * 在字符串中查找第一个出现的 `open` 和最后一个出现的 `close` 之间的子字符串 + * + * @param str 要截取的字符串 + * @param open 起始字符串 + * @param close 结束字符串 + * @return 截取结果 + */ + public static String substringBetweenLast(final String str, final String open, final String close) + { + if (isEmpty(str) || isEmpty(open) || isEmpty(close)) + { + return NULLSTR; + } + final int start = str.indexOf(open); + if (start != INDEX_NOT_FOUND) + { + final int end = str.lastIndexOf(close); + if (end != INDEX_NOT_FOUND) + { + return str.substring(start + open.length(), end); + } + } + return NULLSTR; + } + + /** + * 判断是否为空,并且不是空白字符 + * + * @param str 要判断的value + * @return 结果 + */ + public static boolean hasText(String str) + { + return (str != null && !str.isEmpty() && containsText(str)); + } + + private static boolean containsText(CharSequence str) + { + int strLen = str.length(); + for (int i = 0; i < strLen; i++) + { + if (!Character.isWhitespace(str.charAt(i))) + { + return true; + } + } + return false; + } + + /** + * 格式化文本, {} 表示占位符
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ * + * @param template 文本模板,被替换的部分用 {} 表示 + * @param params 参数值 + * @return 格式化后的文本 + */ + public static String format(String template, Object... params) + { + if (isEmpty(params) || isEmpty(template)) + { + return template; + } + return StrFormatter.format(template, params); + } + + /** + * 是否为http(s)://开头 + * + * @param link 链接 + * @return 结果 + */ + public static boolean ishttp(String link) + { + return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS); + } + + /** + * 字符串转set + * + * @param str 字符串 + * @param sep 分隔符 + * @return set集合 + */ + public static final Set str2Set(String str, String sep) + { + return new HashSet(str2List(str, sep, true, false)); + } + + /** + * 字符串转list + * + * @param str 字符串 + * @param sep 分隔符 + * @return list集合 + */ + public static final List str2List(String str, String sep) + { + return str2List(str, sep, true, false); + } + + /** + * 字符串转list + * + * @param str 字符串 + * @param sep 分隔符 + * @param filterBlank 过滤纯空白 + * @param trim 去掉首尾空白 + * @return list集合 + */ + public static final List str2List(String str, String sep, boolean filterBlank, boolean trim) + { + List list = new ArrayList(); + if (StringUtils.isEmpty(str)) + { + return list; + } + + // 过滤空白字符串 + if (filterBlank && StringUtils.isBlank(str)) + { + return list; + } + String[] split = str.split(sep); + for (String string : split) + { + if (filterBlank && StringUtils.isBlank(string)) + { + continue; + } + if (trim) + { + string = string.trim(); + } + list.add(string); + } + + return list; + } + + /** + * 判断给定的collection列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value + * + * @param collection 给定的集合 + * @param array 给定的数组 + * @return boolean 结果 + */ + public static boolean containsAny(Collection collection, String... array) + { + if (isEmpty(collection) || isEmpty(array)) + { + return false; + } + else + { + for (String str : array) + { + if (collection.contains(str)) + { + return true; + } + } + return false; + } + } + + /** + * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写 + * + * @param cs 指定字符串 + * @param searchCharSequences 需要检查的字符串数组 + * @return 是否包含任意一个字符串 + */ + public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) + { + if (isEmpty(cs) || isEmpty(searchCharSequences)) + { + return false; + } + for (CharSequence testStr : searchCharSequences) + { + if (containsIgnoreCase(cs, testStr)) + { + return true; + } + } + return false; + } + + /** + * 驼峰转下划线命名 + */ + public static String toUnderScoreCase(String str) + { + if (str == null) + { + return null; + } + StringBuilder sb = new StringBuilder(); + // 前置字符是否大写 + boolean preCharIsUpperCase = true; + // 当前字符是否大写 + boolean curreCharIsUpperCase = true; + // 下一字符是否大写 + boolean nexteCharIsUpperCase = true; + for (int i = 0; i < str.length(); i++) + { + char c = str.charAt(i); + if (i > 0) + { + preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1)); + } + else + { + preCharIsUpperCase = false; + } + + curreCharIsUpperCase = Character.isUpperCase(c); + + if (i < (str.length() - 1)) + { + nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1)); + } + + if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) + { + sb.append(SEPARATOR); + } + else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) + { + sb.append(SEPARATOR); + } + sb.append(Character.toLowerCase(c)); + } + + return sb.toString(); + } + + /** + * 是否包含字符串 + * + * @param str 验证字符串 + * @param strs 字符串组 + * @return 包含返回true + */ + public static boolean inStringIgnoreCase(String str, String... strs) + { + if (str != null && strs != null) + { + for (String s : strs) + { + if (str.equalsIgnoreCase(trim(s))) + { + return true; + } + } + } + return false; + } + + /** + * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld + * + * @param name 转换前的下划线大写方式命名的字符串 + * @return 转换后的驼峰式命名的字符串 + */ + public static String convertToCamelCase(String name) + { + StringBuilder result = new StringBuilder(); + // 快速检查 + if (name == null || name.isEmpty()) + { + // 没必要转换 + return ""; + } + else if (!name.contains("_")) + { + // 不含下划线,仅将首字母大写 + return name.substring(0, 1).toUpperCase() + name.substring(1); + } + // 用下划线将原始字符串分割 + String[] camels = name.split("_"); + for (String camel : camels) + { + // 跳过原始字符串中开头、结尾的下换线或双重下划线 + if (camel.isEmpty()) + { + continue; + } + // 首字母大写 + result.append(camel.substring(0, 1).toUpperCase()); + result.append(camel.substring(1).toLowerCase()); + } + return result.toString(); + } + + /** + * 驼峰式命名法 + * 例如:user_name->userName + */ + public static String toCamelCase(String s) + { + if (s == null) + { + return null; + } + if (s.indexOf(SEPARATOR) == -1) + { + return s; + } + s = s.toLowerCase(); + StringBuilder sb = new StringBuilder(s.length()); + boolean upperCase = false; + for (int i = 0; i < s.length(); i++) + { + char c = s.charAt(i); + + if (c == SEPARATOR) + { + upperCase = true; + } + else if (upperCase) + { + sb.append(Character.toUpperCase(c)); + upperCase = false; + } + else + { + sb.append(c); + } + } + return sb.toString(); + } + + /** + * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串 + * + * @param str 指定字符串 + * @param strs 需要检查的字符串数组 + * @return 是否匹配 + */ + public static boolean matches(String str, List strs) + { + if (isEmpty(str) || isEmpty(strs)) + { + return false; + } + for (String pattern : strs) + { + if (isMatch(pattern, str)) + { + return true; + } + } + return false; + } + + /** + * 判断url是否与规则配置: + * ? 表示单个字符; + * * 表示一层路径内的任意字符串,不可跨层级; + * ** 表示任意层路径; + * + * @param pattern 匹配规则 + * @param url 需要匹配的url + * @return + */ + public static boolean isMatch(String pattern, String url) + { + AntPathMatcher matcher = new AntPathMatcher(); + return matcher.match(pattern, url); + } + + @SuppressWarnings("unchecked") + public static T cast(Object obj) + { + return (T) obj; + } + + /** + * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。 + * + * @param num 数字对象 + * @param size 字符串指定长度 + * @return 返回数字的字符串格式,该字符串为指定长度。 + */ + public static final String padl(final Number num, final int size) + { + return padl(num.toString(), size, '0'); + } + + /** + * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。 + * + * @param s 原始字符串 + * @param size 字符串指定长度 + * @param c 用于补齐的字符 + * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。 + */ + public static final String padl(final String s, final int size, final char c) + { + final StringBuilder sb = new StringBuilder(size); + if (s != null) + { + final int len = s.length(); + if (s.length() <= size) + { + for (int i = size - len; i > 0; i--) + { + sb.append(c); + } + sb.append(s); + } + else + { + return s.substring(len - size, len); + } + } + else + { + for (int i = size; i > 0; i--) + { + sb.append(c); + } + } + return sb.toString(); + } } \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/Threads.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/Threads.java index 71fe6d5..f349352 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/Threads.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/Threads.java @@ -1,99 +1,99 @@ -package com.ruoyi.common.utils; - -import java.util.concurrent.CancellationException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * 线程相关工具类. - * - * @author ruoyi - */ -public class Threads -{ - private static final Logger logger = LoggerFactory.getLogger(Threads.class); - - /** - * sleep等待,单位为毫秒 - */ - public static void sleep(long milliseconds) - { - try - { - Thread.sleep(milliseconds); - } - catch (InterruptedException e) - { - return; - } - } - - /** - * 停止线程池 - * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务. - * 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数. - * 如果仍然超時,則強制退出. - * 另对在shutdown时线程本身被调用中断做了处理. - */ - public static void shutdownAndAwaitTermination(ExecutorService pool) - { - if (pool != null && !pool.isShutdown()) - { - pool.shutdown(); - try - { - if (!pool.awaitTermination(120, TimeUnit.SECONDS)) - { - pool.shutdownNow(); - if (!pool.awaitTermination(120, TimeUnit.SECONDS)) - { - logger.info("Pool did not terminate"); - } - } - } - catch (InterruptedException ie) - { - pool.shutdownNow(); - Thread.currentThread().interrupt(); - } - } - } - - /** - * 打印线程异常信息 - */ - public static void printException(Runnable r, Throwable t) - { - if (t == null && r instanceof Future) - { - try - { - Future future = (Future) r; - if (future.isDone()) - { - future.get(); - } - } - catch (CancellationException ce) - { - t = ce; - } - catch (ExecutionException ee) - { - t = ee.getCause(); - } - catch (InterruptedException ie) - { - Thread.currentThread().interrupt(); - } - } - if (t != null) - { - logger.error(t.getMessage(), t); - } - } -} +package com.ruoyi.common.utils; + +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 线程相关工具类. + * + * @author ruoyi + */ +public class Threads +{ + private static final Logger logger = LoggerFactory.getLogger(Threads.class); + + /** + * sleep等待,单位为毫秒 + */ + public static void sleep(long milliseconds) + { + try + { + Thread.sleep(milliseconds); + } + catch (InterruptedException e) + { + return; + } + } + + /** + * 停止线程池 + * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务. + * 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数. + * 如果仍然超時,則強制退出. + * 另对在shutdown时线程本身被调用中断做了处理. + */ + public static void shutdownAndAwaitTermination(ExecutorService pool) + { + if (pool != null && !pool.isShutdown()) + { + pool.shutdown(); + try + { + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) + { + pool.shutdownNow(); + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) + { + logger.info("Pool did not terminate"); + } + } + } + catch (InterruptedException ie) + { + pool.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + } + + /** + * 打印线程异常信息 + */ + public static void printException(Runnable r, Throwable t) + { + if (t == null && r instanceof Future) + { + try + { + Future future = (Future) r; + if (future.isDone()) + { + future.get(); + } + } + catch (CancellationException ce) + { + t = ce; + } + catch (ExecutionException ee) + { + t = ee.getCause(); + } + catch (InterruptedException ie) + { + Thread.currentThread().interrupt(); + } + } + if (t != null) + { + logger.error(t.getMessage(), t); + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java index 4463662..51cdc2f 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java @@ -1,110 +1,110 @@ -package com.ruoyi.common.utils.bean; - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Bean 工具类 - * - * @author ruoyi - */ -public class BeanUtils extends org.springframework.beans.BeanUtils -{ - /** Bean方法名中属性名开始的下标 */ - private static final int BEAN_METHOD_PROP_INDEX = 3; - - /** * 匹配getter方法的正则表达式 */ - private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)"); - - /** * 匹配setter方法的正则表达式 */ - private static final Pattern SET_PATTERN = Pattern.compile("set(\\p{javaUpperCase}\\w*)"); - - /** - * Bean属性复制工具方法。 - * - * @param dest 目标对象 - * @param src 源对象 - */ - public static void copyBeanProp(Object dest, Object src) - { - try - { - copyProperties(src, dest); - } - catch (Exception e) - { - e.printStackTrace(); - } - } - - /** - * 获取对象的setter方法。 - * - * @param obj 对象 - * @return 对象的setter方法列表 - */ - public static List getSetterMethods(Object obj) - { - // setter方法列表 - List setterMethods = new ArrayList(); - - // 获取所有方法 - Method[] methods = obj.getClass().getMethods(); - - // 查找setter方法 - - for (Method method : methods) - { - Matcher m = SET_PATTERN.matcher(method.getName()); - if (m.matches() && (method.getParameterTypes().length == 1)) - { - setterMethods.add(method); - } - } - // 返回setter方法列表 - return setterMethods; - } - - /** - * 获取对象的getter方法。 - * - * @param obj 对象 - * @return 对象的getter方法列表 - */ - - public static List getGetterMethods(Object obj) - { - // getter方法列表 - List getterMethods = new ArrayList(); - // 获取所有方法 - Method[] methods = obj.getClass().getMethods(); - // 查找getter方法 - for (Method method : methods) - { - Matcher m = GET_PATTERN.matcher(method.getName()); - if (m.matches() && (method.getParameterTypes().length == 0)) - { - getterMethods.add(method); - } - } - // 返回getter方法列表 - return getterMethods; - } - - /** - * 检查Bean方法名中的属性名是否相等。
- * 如getName()和setName()属性名一样,getName()和setAge()属性名不一样。 - * - * @param m1 方法名1 - * @param m2 方法名2 - * @return 属性名一样返回true,否则返回false - */ - - public static boolean isMethodPropEquals(String m1, String m2) - { - return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX)); - } -} +package com.ruoyi.common.utils.bean; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Bean 工具类 + * + * @author ruoyi + */ +public class BeanUtils extends org.springframework.beans.BeanUtils +{ + /** Bean方法名中属性名开始的下标 */ + private static final int BEAN_METHOD_PROP_INDEX = 3; + + /** * 匹配getter方法的正则表达式 */ + private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)"); + + /** * 匹配setter方法的正则表达式 */ + private static final Pattern SET_PATTERN = Pattern.compile("set(\\p{javaUpperCase}\\w*)"); + + /** + * Bean属性复制工具方法。 + * + * @param dest 目标对象 + * @param src 源对象 + */ + public static void copyBeanProp(Object dest, Object src) + { + try + { + copyProperties(src, dest); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + /** + * 获取对象的setter方法。 + * + * @param obj 对象 + * @return 对象的setter方法列表 + */ + public static List getSetterMethods(Object obj) + { + // setter方法列表 + List setterMethods = new ArrayList(); + + // 获取所有方法 + Method[] methods = obj.getClass().getMethods(); + + // 查找setter方法 + + for (Method method : methods) + { + Matcher m = SET_PATTERN.matcher(method.getName()); + if (m.matches() && (method.getParameterTypes().length == 1)) + { + setterMethods.add(method); + } + } + // 返回setter方法列表 + return setterMethods; + } + + /** + * 获取对象的getter方法。 + * + * @param obj 对象 + * @return 对象的getter方法列表 + */ + + public static List getGetterMethods(Object obj) + { + // getter方法列表 + List getterMethods = new ArrayList(); + // 获取所有方法 + Method[] methods = obj.getClass().getMethods(); + // 查找getter方法 + for (Method method : methods) + { + Matcher m = GET_PATTERN.matcher(method.getName()); + if (m.matches() && (method.getParameterTypes().length == 0)) + { + getterMethods.add(method); + } + } + // 返回getter方法列表 + return getterMethods; + } + + /** + * 检查Bean方法名中的属性名是否相等。
+ * 如getName()和setName()属性名一样,getName()和setAge()属性名不一样。 + * + * @param m1 方法名1 + * @param m2 方法名2 + * @return 属性名一样返回true,否则返回false + */ + + public static boolean isMethodPropEquals(String m1, String m2) + { + return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX)); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java index 898aca5..d9821e0 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java @@ -1,24 +1,24 @@ -package com.ruoyi.common.utils.bean; - -import java.util.Set; -import jakarta.validation.ConstraintViolation; -import jakarta.validation.ConstraintViolationException; -import jakarta.validation.Validator; - -/** - * bean对象属性验证 - * - * @author ruoyi - */ -public class BeanValidators -{ - public static void validateWithException(Validator validator, Object object, Class... groups) - throws ConstraintViolationException - { - Set> constraintViolations = validator.validate(object, groups); - if (!constraintViolations.isEmpty()) - { - throw new ConstraintViolationException(constraintViolations); - } - } -} +package com.ruoyi.common.utils.bean; + +import java.util.Set; +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import javax.validation.Validator; + +/** + * bean对象属性验证 + * + * @author ruoyi + */ +public class BeanValidators +{ + public static void validateWithException(Validator validator, Object object, Class... groups) + throws ConstraintViolationException + { + Set> constraintViolations = validator.validate(object, groups); + if (!constraintViolations.isEmpty()) + { + throw new ConstraintViolationException(constraintViolations); + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java index 68130b9..25f5306 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java @@ -1,76 +1,76 @@ -package com.ruoyi.common.utils.file; - -import java.io.File; -import org.apache.commons.lang3.StringUtils; - -/** - * 文件类型工具类 - * - * @author ruoyi - */ -public class FileTypeUtils -{ - /** - * 获取文件类型 - *

- * 例如: ruoyi.txt, 返回: txt - * - * @param file 文件名 - * @return 后缀(不含".") - */ - public static String getFileType(File file) - { - if (null == file) - { - return StringUtils.EMPTY; - } - return getFileType(file.getName()); - } - - /** - * 获取文件类型 - *

- * 例如: ruoyi.txt, 返回: txt - * - * @param fileName 文件名 - * @return 后缀(不含".") - */ - public static String getFileType(String fileName) - { - int separatorIndex = fileName.lastIndexOf("."); - if (separatorIndex < 0) - { - return ""; - } - return fileName.substring(separatorIndex + 1).toLowerCase(); - } - - /** - * 获取文件类型 - * - * @param photoByte 文件字节码 - * @return 后缀(不含".") - */ - public static String getFileExtendName(byte[] photoByte) - { - String strFileExtendName = "JPG"; - if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56) - && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) - { - strFileExtendName = "GIF"; - } - else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) - { - strFileExtendName = "JPG"; - } - else if ((photoByte[0] == 66) && (photoByte[1] == 77)) - { - strFileExtendName = "BMP"; - } - else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) - { - strFileExtendName = "PNG"; - } - return strFileExtendName; - } +package com.ruoyi.common.utils.file; + +import java.io.File; +import org.apache.commons.lang3.StringUtils; + +/** + * 文件类型工具类 + * + * @author ruoyi + */ +public class FileTypeUtils +{ + /** + * 获取文件类型 + *

+ * 例如: ruoyi.txt, 返回: txt + * + * @param file 文件名 + * @return 后缀(不含".") + */ + public static String getFileType(File file) + { + if (null == file) + { + return StringUtils.EMPTY; + } + return getFileType(file.getName()); + } + + /** + * 获取文件类型 + *

+ * 例如: ruoyi.txt, 返回: txt + * + * @param fileName 文件名 + * @return 后缀(不含".") + */ + public static String getFileType(String fileName) + { + int separatorIndex = fileName.lastIndexOf("."); + if (separatorIndex < 0) + { + return ""; + } + return fileName.substring(separatorIndex + 1).toLowerCase(); + } + + /** + * 获取文件类型 + * + * @param photoByte 文件字节码 + * @return 后缀(不含".") + */ + public static String getFileExtendName(byte[] photoByte) + { + String strFileExtendName = "JPG"; + if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56) + && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) + { + strFileExtendName = "GIF"; + } + else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) + { + strFileExtendName = "JPG"; + } + else if ((photoByte[0] == 66) && (photoByte[1] == 77)) + { + strFileExtendName = "BMP"; + } + else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) + { + strFileExtendName = "PNG"; + } + return strFileExtendName; + } } \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java index cc1b6f4..75bbacd 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java @@ -1,260 +1,260 @@ -package com.ruoyi.common.utils.file; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Paths; -import java.util.Objects; -import org.apache.commons.io.FilenameUtils; -import org.springframework.web.multipart.MultipartFile; -import com.ruoyi.common.config.RuoYiConfig; -import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.exception.file.FileNameLengthLimitExceededException; -import com.ruoyi.common.exception.file.FileSizeLimitExceededException; -import com.ruoyi.common.exception.file.InvalidExtensionException; -import com.ruoyi.common.utils.DateUtils; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.common.utils.uuid.IdUtils; -import com.ruoyi.common.utils.uuid.Seq; - -/** - * 文件上传工具类 - * - * @author ruoyi - */ -public class FileUploadUtils -{ - /** - * 默认大小 50M - */ - public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024L; - - /** - * 默认的文件名最大长度 100 - */ - public static final int DEFAULT_FILE_NAME_LENGTH = 100; - - /** - * 默认上传的地址 - */ - private static String defaultBaseDir = RuoYiConfig.getProfile(); - - public static void setDefaultBaseDir(String defaultBaseDir) - { - FileUploadUtils.defaultBaseDir = defaultBaseDir; - } - - public static String getDefaultBaseDir() - { - return defaultBaseDir; - } - - /** - * 以默认配置进行文件上传 - * - * @param file 上传的文件 - * @return 文件名称 - * @throws Exception - */ - public static final String upload(MultipartFile file) throws IOException - { - try - { - return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); - } - catch (Exception e) - { - throw new IOException(e.getMessage(), e); - } - } - - /** - * 根据文件路径上传 - * - * @param baseDir 相对应用的基目录 - * @param file 上传的文件 - * @return 文件名称 - * @throws IOException - */ - public static final String upload(String baseDir, MultipartFile file) throws IOException - { - try - { - return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); - } - catch (Exception e) - { - throw new IOException(e.getMessage(), e); - } - } - - /** - * 文件上传 - * - * @param baseDir 相对应用的基目录 - * @param file 上传的文件 - * @param allowedExtension 上传文件类型 - * @return 返回上传成功的文件名 - * @throws FileSizeLimitExceededException 如果超出最大大小 - * @throws FileNameLengthLimitExceededException 文件名太长 - * @throws IOException 比如读写文件出错时 - * @throws InvalidExtensionException 文件校验异常 - */ - public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension) - throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException, - InvalidExtensionException - { - return upload(baseDir, file, allowedExtension, false); - } - - /** - * 文件上传 - * - * @param baseDir 相对应用的基目录 - * @param file 上传的文件 - * @param useCustomNaming 系统自定义文件名 - * @param allowedExtension 上传文件类型 - * @return 返回上传成功的文件名 - * @throws FileSizeLimitExceededException 如果超出最大大小 - * @throws FileNameLengthLimitExceededException 文件名太长 - * @throws IOException 比如读写文件出错时 - * @throws InvalidExtensionException 文件校验异常 - */ - public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension, boolean useCustomNaming) - throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException, - InvalidExtensionException - { - int fileNameLength = Objects.requireNonNull(file.getOriginalFilename()).length(); - if (fileNameLength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) - { - throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH); - } - - assertAllowed(file, allowedExtension); - - String fileName = useCustomNaming ? uuidFilename(file) : extractFilename(file); - - String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath(); - file.transferTo(Paths.get(absPath)); - return getPathFileName(baseDir, fileName); - } - - /** - * 编码文件名(日期格式目录 + 原文件名 + 序列值 + 后缀) - */ - public static final String extractFilename(MultipartFile file) - { - return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(), FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file)); - } - - /** - * 编编码文件名(日期格式目录 + UUID + 后缀) - */ - public static final String uuidFilename(MultipartFile file) - { - return StringUtils.format("{}/{}.{}", DateUtils.datePath(), IdUtils.fastSimpleUUID(), getExtension(file)); - } - - public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException - { - File desc = new File(uploadDir + File.separator + fileName); - - if (!desc.exists()) - { - if (!desc.getParentFile().exists()) - { - desc.getParentFile().mkdirs(); - } - } - return desc; - } - - public static final String getPathFileName(String uploadDir, String fileName) throws IOException - { - int dirLastIndex = RuoYiConfig.getProfile().length() + 1; - String currentDir = StringUtils.substring(uploadDir, dirLastIndex); - return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName; - } - - /** - * 文件大小校验 - * - * @param file 上传的文件 - * @return - * @throws FileSizeLimitExceededException 如果超出最大大小 - * @throws InvalidExtensionException - */ - public static final void assertAllowed(MultipartFile file, String[] allowedExtension) - throws FileSizeLimitExceededException, InvalidExtensionException - { - long size = file.getSize(); - if (size > DEFAULT_MAX_SIZE) - { - throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024); - } - - String fileName = file.getOriginalFilename(); - String extension = getExtension(file); - if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) - { - if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) - { - throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension, - fileName); - } - else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) - { - throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension, - fileName); - } - else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) - { - throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension, - fileName); - } - else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) - { - throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension, - fileName); - } - else - { - throw new InvalidExtensionException(allowedExtension, extension, fileName); - } - } - } - - /** - * 判断MIME类型是否是允许的MIME类型 - * - * @param extension - * @param allowedExtension - * @return - */ - public static final boolean isAllowedExtension(String extension, String[] allowedExtension) - { - for (String str : allowedExtension) - { - if (str.equalsIgnoreCase(extension)) - { - return true; - } - } - return false; - } - - /** - * 获取文件名的后缀 - * - * @param file 表单文件 - * @return 后缀名 - */ - public static final String getExtension(MultipartFile file) - { - String extension = FilenameUtils.getExtension(file.getOriginalFilename()); - if (StringUtils.isEmpty(extension)) - { - extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType())); - } - return extension; - } -} +package com.ruoyi.common.utils.file; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.Objects; +import org.apache.commons.io.FilenameUtils; +import org.springframework.web.multipart.MultipartFile; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.exception.file.FileNameLengthLimitExceededException; +import com.ruoyi.common.exception.file.FileSizeLimitExceededException; +import com.ruoyi.common.exception.file.InvalidExtensionException; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.uuid.IdUtils; +import com.ruoyi.common.utils.uuid.Seq; + +/** + * 文件上传工具类 + * + * @author ruoyi + */ +public class FileUploadUtils +{ + /** + * 默认大小 50M + */ + public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024L; + + /** + * 默认的文件名最大长度 100 + */ + public static final int DEFAULT_FILE_NAME_LENGTH = 100; + + /** + * 默认上传的地址 + */ + private static String defaultBaseDir = RuoYiConfig.getProfile(); + + public static void setDefaultBaseDir(String defaultBaseDir) + { + FileUploadUtils.defaultBaseDir = defaultBaseDir; + } + + public static String getDefaultBaseDir() + { + return defaultBaseDir; + } + + /** + * 以默认配置进行文件上传 + * + * @param file 上传的文件 + * @return 文件名称 + * @throws Exception + */ + public static final String upload(MultipartFile file) throws IOException + { + try + { + return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); + } + catch (Exception e) + { + throw new IOException(e.getMessage(), e); + } + } + + /** + * 根据文件路径上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @return 文件名称 + * @throws IOException + */ + public static final String upload(String baseDir, MultipartFile file) throws IOException + { + try + { + return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); + } + catch (Exception e) + { + throw new IOException(e.getMessage(), e); + } + } + + /** + * 文件上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @param allowedExtension 上传文件类型 + * @return 返回上传成功的文件名 + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws FileNameLengthLimitExceededException 文件名太长 + * @throws IOException 比如读写文件出错时 + * @throws InvalidExtensionException 文件校验异常 + */ + public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException, + InvalidExtensionException + { + return upload(baseDir, file, allowedExtension, false); + } + + /** + * 文件上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @param useCustomNaming 系统自定义文件名 + * @param allowedExtension 上传文件类型 + * @return 返回上传成功的文件名 + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws FileNameLengthLimitExceededException 文件名太长 + * @throws IOException 比如读写文件出错时 + * @throws InvalidExtensionException 文件校验异常 + */ + public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension, boolean useCustomNaming) + throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException, + InvalidExtensionException + { + int fileNameLength = Objects.requireNonNull(file.getOriginalFilename()).length(); + if (fileNameLength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) + { + throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH); + } + + assertAllowed(file, allowedExtension); + + String fileName = useCustomNaming ? uuidFilename(file) : extractFilename(file); + + String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath(); + file.transferTo(Paths.get(absPath)); + return getPathFileName(baseDir, fileName); + } + + /** + * 编码文件名(日期格式目录 + 原文件名 + 序列值 + 后缀) + */ + public static final String extractFilename(MultipartFile file) + { + return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(), FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file)); + } + + /** + * 编编码文件名(日期格式目录 + UUID + 后缀) + */ + public static final String uuidFilename(MultipartFile file) + { + return StringUtils.format("{}/{}.{}", DateUtils.datePath(), IdUtils.fastSimpleUUID(), getExtension(file)); + } + + public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException + { + File desc = new File(uploadDir + File.separator + fileName); + + if (!desc.exists()) + { + if (!desc.getParentFile().exists()) + { + desc.getParentFile().mkdirs(); + } + } + return desc; + } + + public static final String getPathFileName(String uploadDir, String fileName) throws IOException + { + int dirLastIndex = RuoYiConfig.getProfile().length() + 1; + String currentDir = StringUtils.substring(uploadDir, dirLastIndex); + return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName; + } + + /** + * 文件大小校验 + * + * @param file 上传的文件 + * @return + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws InvalidExtensionException + */ + public static final void assertAllowed(MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, InvalidExtensionException + { + long size = file.getSize(); + if (size > DEFAULT_MAX_SIZE) + { + throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024); + } + + String fileName = file.getOriginalFilename(); + String extension = getExtension(file); + if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) + { + if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) + { + throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) + { + throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) + { + throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) + { + throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension, + fileName); + } + else + { + throw new InvalidExtensionException(allowedExtension, extension, fileName); + } + } + } + + /** + * 判断MIME类型是否是允许的MIME类型 + * + * @param extension + * @param allowedExtension + * @return + */ + public static final boolean isAllowedExtension(String extension, String[] allowedExtension) + { + for (String str : allowedExtension) + { + if (str.equalsIgnoreCase(extension)) + { + return true; + } + } + return false; + } + + /** + * 获取文件名的后缀 + * + * @param file 表单文件 + * @return 后缀名 + */ + public static final String getExtension(MultipartFile file) + { + String extension = FilenameUtils.getExtension(file.getOriginalFilename()); + if (StringUtils.isEmpty(extension)) + { + extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType())); + } + return extension; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java index 9f16a3d..fc17274 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java @@ -1,303 +1,303 @@ -package com.ruoyi.common.utils.file; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -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 org.apache.commons.io.FilenameUtils; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.ArrayUtils; -import com.ruoyi.common.config.RuoYiConfig; -import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.utils.DateUtils; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.common.utils.uuid.IdUtils; - -/** - * 文件处理工具类 - * - * @author ruoyi - */ -public class FileUtils -{ - public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+"; - - /** - * 输出指定文件的byte数组 - * - * @param filePath 文件路径 - * @param os 输出流 - * @return - */ - public static void writeBytes(String filePath, OutputStream os) throws IOException - { - FileInputStream fis = null; - try - { - File file = new File(filePath); - if (!file.exists()) - { - throw new FileNotFoundException(filePath); - } - fis = new FileInputStream(file); - byte[] b = new byte[1024]; - int length; - while ((length = fis.read(b)) > 0) - { - os.write(b, 0, length); - } - } - catch (IOException e) - { - throw e; - } - finally - { - IOUtils.close(os); - IOUtils.close(fis); - } - } - - /** - * 写数据到文件中 - * - * @param data 数据 - * @return 目标文件 - * @throws IOException IO异常 - */ - public static String writeImportBytes(byte[] data) throws IOException - { - return writeBytes(data, RuoYiConfig.getImportPath()); - } - - /** - * 写数据到文件中 - * - * @param data 数据 - * @param uploadDir 目标文件 - * @return 目标文件 - * @throws IOException IO异常 - */ - public static String writeBytes(byte[] data, String uploadDir) throws IOException - { - FileOutputStream fos = null; - String pathName = ""; - try - { - String extension = getFileExtendName(data); - pathName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension; - File file = FileUploadUtils.getAbsoluteFile(uploadDir, pathName); - fos = new FileOutputStream(file); - fos.write(data); - } - finally - { - IOUtils.close(fos); - } - return FileUploadUtils.getPathFileName(uploadDir, pathName); - } - - /** - * 移除路径中的请求前缀片段 - * - * @param filePath 文件路径 - * @return 移除后的文件路径 - */ - public static String stripPrefix(String filePath) - { - return StringUtils.substringAfter(filePath, Constants.RESOURCE_PREFIX); - } - - /** - * 删除文件 - * - * @param filePath 文件 - * @return - */ - public static boolean deleteFile(String filePath) - { - boolean flag = false; - File file = new File(filePath); - // 路径为文件且不为空则进行删除 - if (file.isFile() && file.exists()) - { - flag = file.delete(); - } - return flag; - } - - /** - * 文件名称验证 - * - * @param filename 文件名称 - * @return true 正常 false 非法 - */ - public static boolean isValidFilename(String filename) - { - return filename.matches(FILENAME_PATTERN); - } - - /** - * 检查文件是否可下载 - * - * @param resource 需要下载的文件 - * @return true 正常 false 非法 - */ - public static boolean checkAllowDownload(String resource) - { - // 禁止目录上跳级别 - if (StringUtils.contains(resource, "..")) - { - return false; - } - - // 检查允许下载的文件规则 - if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource))) - { - return true; - } - - // 不在允许下载的文件规则 - return false; - } - - /** - * 下载文件名重新编码 - * - * @param request 请求对象 - * @param fileName 文件名 - * @return 编码后的文件名 - */ - public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException - { - final String agent = request.getHeader("USER-AGENT"); - String filename = fileName; - if (agent.contains("MSIE")) - { - // IE浏览器 - filename = URLEncoder.encode(filename, "utf-8"); - filename = filename.replace("+", " "); - } - else if (agent.contains("Firefox")) - { - // 火狐浏览器 - filename = new String(fileName.getBytes(), "ISO8859-1"); - } - else if (agent.contains("Chrome")) - { - // google浏览器 - filename = URLEncoder.encode(filename, "utf-8"); - } - else - { - // 其它浏览器 - filename = URLEncoder.encode(filename, "utf-8"); - } - return filename; - } - - /** - * 下载文件名重新编码 - * - * @param response 响应对象 - * @param realFileName 真实文件名 - */ - public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException - { - String percentEncodedFileName = percentEncode(realFileName); - - StringBuilder contentDispositionValue = new StringBuilder(); - contentDispositionValue.append("attachment; filename=") - .append(percentEncodedFileName) - .append(";") - .append("filename*=") - .append("utf-8''") - .append(percentEncodedFileName); - - response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename"); - response.setHeader("Content-disposition", contentDispositionValue.toString()); - response.setHeader("download-filename", percentEncodedFileName); - } - - /** - * 百分号编码工具方法 - * - * @param s 需要百分号编码的字符串 - * @return 百分号编码后的字符串 - */ - public static String percentEncode(String s) throws UnsupportedEncodingException - { - String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString()); - return encode.replaceAll("\\+", "%20"); - } - - /** - * 获取图像后缀 - * - * @param photoByte 图像数据 - * @return 后缀名 - */ - public static String getFileExtendName(byte[] photoByte) - { - String strFileExtendName = "jpg"; - if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56) - && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) - { - strFileExtendName = "gif"; - } - else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) - { - strFileExtendName = "jpg"; - } - else if ((photoByte[0] == 66) && (photoByte[1] == 77)) - { - strFileExtendName = "bmp"; - } - else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) - { - strFileExtendName = "png"; - } - return strFileExtendName; - } - - /** - * 获取文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi.png - * - * @param fileName 路径名称 - * @return 没有文件路径的名称 - */ - public static String getName(String fileName) - { - if (fileName == null) - { - return null; - } - int lastUnixPos = fileName.lastIndexOf('/'); - int lastWindowsPos = fileName.lastIndexOf('\\'); - int index = Math.max(lastUnixPos, lastWindowsPos); - return fileName.substring(index + 1); - } - - /** - * 获取不带后缀文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi - * - * @param fileName 路径名称 - * @return 没有文件路径和后缀的名称 - */ - public static String getNameNotSuffix(String fileName) - { - if (fileName == null) - { - return null; - } - String baseName = FilenameUtils.getBaseName(fileName); - return baseName; - } -} +package com.ruoyi.common.utils.file; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +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; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.uuid.IdUtils; + +/** + * 文件处理工具类 + * + * @author ruoyi + */ +public class FileUtils +{ + public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+"; + + /** + * 输出指定文件的byte数组 + * + * @param filePath 文件路径 + * @param os 输出流 + * @return + */ + public static void writeBytes(String filePath, OutputStream os) throws IOException + { + FileInputStream fis = null; + try + { + File file = new File(filePath); + if (!file.exists()) + { + throw new FileNotFoundException(filePath); + } + fis = new FileInputStream(file); + byte[] b = new byte[1024]; + int length; + while ((length = fis.read(b)) > 0) + { + os.write(b, 0, length); + } + } + catch (IOException e) + { + throw e; + } + finally + { + IOUtils.close(os); + IOUtils.close(fis); + } + } + + /** + * 写数据到文件中 + * + * @param data 数据 + * @return 目标文件 + * @throws IOException IO异常 + */ + public static String writeImportBytes(byte[] data) throws IOException + { + return writeBytes(data, RuoYiConfig.getImportPath()); + } + + /** + * 写数据到文件中 + * + * @param data 数据 + * @param uploadDir 目标文件 + * @return 目标文件 + * @throws IOException IO异常 + */ + public static String writeBytes(byte[] data, String uploadDir) throws IOException + { + FileOutputStream fos = null; + String pathName = ""; + try + { + String extension = getFileExtendName(data); + pathName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension; + File file = FileUploadUtils.getAbsoluteFile(uploadDir, pathName); + fos = new FileOutputStream(file); + fos.write(data); + } + finally + { + IOUtils.close(fos); + } + return FileUploadUtils.getPathFileName(uploadDir, pathName); + } + + /** + * 移除路径中的请求前缀片段 + * + * @param filePath 文件路径 + * @return 移除后的文件路径 + */ + public static String stripPrefix(String filePath) + { + return StringUtils.substringAfter(filePath, Constants.RESOURCE_PREFIX); + } + + /** + * 删除文件 + * + * @param filePath 文件 + * @return + */ + public static boolean deleteFile(String filePath) + { + boolean flag = false; + File file = new File(filePath); + // 路径为文件且不为空则进行删除 + if (file.isFile() && file.exists()) + { + flag = file.delete(); + } + return flag; + } + + /** + * 文件名称验证 + * + * @param filename 文件名称 + * @return true 正常 false 非法 + */ + public static boolean isValidFilename(String filename) + { + return filename.matches(FILENAME_PATTERN); + } + + /** + * 检查文件是否可下载 + * + * @param resource 需要下载的文件 + * @return true 正常 false 非法 + */ + public static boolean checkAllowDownload(String resource) + { + // 禁止目录上跳级别 + if (StringUtils.contains(resource, "..")) + { + return false; + } + + // 检查允许下载的文件规则 + if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource))) + { + return true; + } + + // 不在允许下载的文件规则 + return false; + } + + /** + * 下载文件名重新编码 + * + * @param request 请求对象 + * @param fileName 文件名 + * @return 编码后的文件名 + */ + public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException + { + final String agent = request.getHeader("USER-AGENT"); + String filename = fileName; + if (agent.contains("MSIE")) + { + // IE浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + filename = filename.replace("+", " "); + } + else if (agent.contains("Firefox")) + { + // 火狐浏览器 + filename = new String(fileName.getBytes(), "ISO8859-1"); + } + else if (agent.contains("Chrome")) + { + // google浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + } + else + { + // 其它浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + } + return filename; + } + + /** + * 下载文件名重新编码 + * + * @param response 响应对象 + * @param realFileName 真实文件名 + */ + public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException + { + String percentEncodedFileName = percentEncode(realFileName); + + StringBuilder contentDispositionValue = new StringBuilder(); + contentDispositionValue.append("attachment; filename=") + .append(percentEncodedFileName) + .append(";") + .append("filename*=") + .append("utf-8''") + .append(percentEncodedFileName); + + response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename"); + response.setHeader("Content-disposition", contentDispositionValue.toString()); + response.setHeader("download-filename", percentEncodedFileName); + } + + /** + * 百分号编码工具方法 + * + * @param s 需要百分号编码的字符串 + * @return 百分号编码后的字符串 + */ + public static String percentEncode(String s) throws UnsupportedEncodingException + { + String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString()); + return encode.replaceAll("\\+", "%20"); + } + + /** + * 获取图像后缀 + * + * @param photoByte 图像数据 + * @return 后缀名 + */ + public static String getFileExtendName(byte[] photoByte) + { + String strFileExtendName = "jpg"; + if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56) + && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) + { + strFileExtendName = "gif"; + } + else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) + { + strFileExtendName = "jpg"; + } + else if ((photoByte[0] == 66) && (photoByte[1] == 77)) + { + strFileExtendName = "bmp"; + } + else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) + { + strFileExtendName = "png"; + } + return strFileExtendName; + } + + /** + * 获取文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi.png + * + * @param fileName 路径名称 + * @return 没有文件路径的名称 + */ + public static String getName(String fileName) + { + if (fileName == null) + { + return null; + } + int lastUnixPos = fileName.lastIndexOf('/'); + int lastWindowsPos = fileName.lastIndexOf('\\'); + int index = Math.max(lastUnixPos, lastWindowsPos); + return fileName.substring(index + 1); + } + + /** + * 获取不带后缀文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi + * + * @param fileName 路径名称 + * @return 没有文件路径和后缀的名称 + */ + public static String getNameNotSuffix(String fileName) + { + if (fileName == null) + { + return null; + } + String baseName = FilenameUtils.getBaseName(fileName); + return baseName; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java index 432dfda..9687bf3 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java @@ -1,98 +1,98 @@ -package com.ruoyi.common.utils.file; - -import java.io.ByteArrayInputStream; -import java.io.FileInputStream; -import java.io.InputStream; -import java.net.URL; -import java.net.URLConnection; -import java.util.Arrays; -import org.apache.poi.util.IOUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import com.ruoyi.common.config.RuoYiConfig; -import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.utils.StringUtils; - -/** - * 图片处理工具类 - * - * @author ruoyi - */ -public class ImageUtils -{ - private static final Logger log = LoggerFactory.getLogger(ImageUtils.class); - - public static byte[] getImage(String imagePath) - { - InputStream is = getFile(imagePath); - try - { - return IOUtils.toByteArray(is); - } - catch (Exception e) - { - log.error("图片加载异常 {}", e); - return null; - } - finally - { - IOUtils.closeQuietly(is); - } - } - - public static InputStream getFile(String imagePath) - { - try - { - byte[] result = readFile(imagePath); - result = Arrays.copyOf(result, result.length); - return new ByteArrayInputStream(result); - } - catch (Exception e) - { - log.error("获取图片异常 {}", e); - } - return null; - } - - /** - * 读取文件为字节数据 - * - * @param url 地址 - * @return 字节数据 - */ - public static byte[] readFile(String url) - { - InputStream in = null; - try - { - if (url.startsWith("http")) - { - // 网络地址 - URL urlObj = new URL(url); - URLConnection urlConnection = urlObj.openConnection(); - urlConnection.setConnectTimeout(30 * 1000); - urlConnection.setReadTimeout(60 * 1000); - urlConnection.setDoInput(true); - in = urlConnection.getInputStream(); - } - else - { - // 本机地址 - String localPath = RuoYiConfig.getProfile(); - String downloadPath = localPath + StringUtils.substringAfter(url, Constants.RESOURCE_PREFIX); - in = new FileInputStream(downloadPath); - } - return IOUtils.toByteArray(in); - } - catch (Exception e) - { - log.error("获取文件路径异常 {}", e); - return null; - } - finally - { - IOUtils.closeQuietly(in); - } - } -} +package com.ruoyi.common.utils.file; + +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.util.Arrays; +import org.apache.poi.util.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.StringUtils; + +/** + * 图片处理工具类 + * + * @author ruoyi + */ +public class ImageUtils +{ + private static final Logger log = LoggerFactory.getLogger(ImageUtils.class); + + public static byte[] getImage(String imagePath) + { + InputStream is = getFile(imagePath); + try + { + return IOUtils.toByteArray(is); + } + catch (Exception e) + { + log.error("图片加载异常 {}", e); + return null; + } + finally + { + IOUtils.closeQuietly(is); + } + } + + public static InputStream getFile(String imagePath) + { + try + { + byte[] result = readFile(imagePath); + result = Arrays.copyOf(result, result.length); + return new ByteArrayInputStream(result); + } + catch (Exception e) + { + log.error("获取图片异常 {}", e); + } + return null; + } + + /** + * 读取文件为字节数据 + * + * @param url 地址 + * @return 字节数据 + */ + public static byte[] readFile(String url) + { + InputStream in = null; + try + { + if (url.startsWith("http")) + { + // 网络地址 + URL urlObj = new URL(url); + URLConnection urlConnection = urlObj.openConnection(); + urlConnection.setConnectTimeout(30 * 1000); + urlConnection.setReadTimeout(60 * 1000); + urlConnection.setDoInput(true); + in = urlConnection.getInputStream(); + } + else + { + // 本机地址 + String localPath = RuoYiConfig.getProfile(); + String downloadPath = localPath + StringUtils.substringAfter(url, Constants.RESOURCE_PREFIX); + in = new FileInputStream(downloadPath); + } + return IOUtils.toByteArray(in); + } + catch (Exception e) + { + log.error("获取文件路径异常 {}", e); + return null; + } + finally + { + IOUtils.closeQuietly(in); + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java index f968f1a..5935815 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java @@ -1,59 +1,59 @@ -package com.ruoyi.common.utils.file; - -/** - * 媒体类型工具类 - * - * @author ruoyi - */ -public class MimeTypeUtils -{ - public static final String IMAGE_PNG = "image/png"; - - public static final String IMAGE_JPG = "image/jpg"; - - public static final String IMAGE_JPEG = "image/jpeg"; - - public static final String IMAGE_BMP = "image/bmp"; - - public static final String IMAGE_GIF = "image/gif"; - - public static final String[] IMAGE_EXTENSION = { "bmp", "gif", "jpg", "jpeg", "png" }; - - public static final String[] FLASH_EXTENSION = { "swf", "flv" }; - - public static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg", - "asf", "rm", "rmvb" }; - - public static final String[] VIDEO_EXTENSION = { "mp4", "avi", "rmvb" }; - - public static final String[] DEFAULT_ALLOWED_EXTENSION = { - // 图片 - "bmp", "gif", "jpg", "jpeg", "png", - // word excel powerpoint - "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt", - // 压缩文件 - "rar", "zip", "gz", "bz2", - // 视频格式 - "mp4", "avi", "rmvb", - // pdf - "pdf" }; - - public static String getExtension(String prefix) - { - switch (prefix) - { - case IMAGE_PNG: - return "png"; - case IMAGE_JPG: - return "jpg"; - case IMAGE_JPEG: - return "jpeg"; - case IMAGE_BMP: - return "bmp"; - case IMAGE_GIF: - return "gif"; - default: - return ""; - } - } -} +package com.ruoyi.common.utils.file; + +/** + * 媒体类型工具类 + * + * @author ruoyi + */ +public class MimeTypeUtils +{ + public static final String IMAGE_PNG = "image/png"; + + public static final String IMAGE_JPG = "image/jpg"; + + public static final String IMAGE_JPEG = "image/jpeg"; + + public static final String IMAGE_BMP = "image/bmp"; + + public static final String IMAGE_GIF = "image/gif"; + + public static final String[] IMAGE_EXTENSION = { "bmp", "gif", "jpg", "jpeg", "png" }; + + public static final String[] FLASH_EXTENSION = { "swf", "flv" }; + + public static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg", + "asf", "rm", "rmvb" }; + + public static final String[] VIDEO_EXTENSION = { "mp4", "avi", "rmvb" }; + + public static final String[] DEFAULT_ALLOWED_EXTENSION = { + // 图片 + "bmp", "gif", "jpg", "jpeg", "png", + // word excel powerpoint + "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt", + // 压缩文件 + "rar", "zip", "gz", "bz2", + // 视频格式 + "mp4", "avi", "rmvb", + // pdf + "pdf" }; + + public static String getExtension(String prefix) + { + switch (prefix) + { + case IMAGE_PNG: + return "png"; + case IMAGE_JPG: + return "jpg"; + case IMAGE_JPEG: + return "jpeg"; + case IMAGE_BMP: + return "bmp"; + case IMAGE_GIF: + return "gif"; + default: + return ""; + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java index f52e83e..dda96c3 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java @@ -1,167 +1,167 @@ -package com.ruoyi.common.utils.html; - -import com.ruoyi.common.utils.StringUtils; - -/** - * 转义和反转义工具类 - * - * @author ruoyi - */ -public class EscapeUtil -{ - public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)"; - - private static final char[][] TEXT = new char[64][]; - - static - { - for (int i = 0; i < 64; i++) - { - TEXT[i] = new char[] { (char) i }; - } - - // special HTML characters - TEXT['\''] = "'".toCharArray(); // 单引号 - TEXT['"'] = """.toCharArray(); // 双引号 - TEXT['&'] = "&".toCharArray(); // &符 - TEXT['<'] = "<".toCharArray(); // 小于号 - TEXT['>'] = ">".toCharArray(); // 大于号 - } - - /** - * 转义文本中的HTML字符为安全的字符 - * - * @param text 被转义的文本 - * @return 转义后的文本 - */ - public static String escape(String text) - { - return encode(text); - } - - /** - * 还原被转义的HTML特殊字符 - * - * @param content 包含转义符的HTML内容 - * @return 转换后的字符串 - */ - public static String unescape(String content) - { - return decode(content); - } - - /** - * 清除所有HTML标签,但是不删除标签内的内容 - * - * @param content 文本 - * @return 清除标签后的文本 - */ - public static String clean(String content) - { - return new HTMLFilter().filter(content); - } - - /** - * Escape编码 - * - * @param text 被编码的文本 - * @return 编码后的字符 - */ - private static String encode(String text) - { - if (StringUtils.isEmpty(text)) - { - return StringUtils.EMPTY; - } - - final StringBuilder tmp = new StringBuilder(text.length() * 6); - char c; - for (int i = 0; i < text.length(); i++) - { - c = text.charAt(i); - if (c < 256) - { - tmp.append("%"); - if (c < 16) - { - tmp.append("0"); - } - tmp.append(Integer.toString(c, 16)); - } - else - { - tmp.append("%u"); - if (c <= 0xfff) - { - // issue#I49JU8@Gitee - tmp.append("0"); - } - tmp.append(Integer.toString(c, 16)); - } - } - return tmp.toString(); - } - - /** - * Escape解码 - * - * @param content 被转义的内容 - * @return 解码后的字符串 - */ - public static String decode(String content) - { - if (StringUtils.isEmpty(content)) - { - return content; - } - - StringBuilder tmp = new StringBuilder(content.length()); - int lastPos = 0, pos = 0; - char ch; - while (lastPos < content.length()) - { - pos = content.indexOf("%", lastPos); - if (pos == lastPos) - { - if (content.charAt(pos + 1) == 'u') - { - ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16); - tmp.append(ch); - lastPos = pos + 6; - } - else - { - ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16); - tmp.append(ch); - lastPos = pos + 3; - } - } - else - { - if (pos == -1) - { - tmp.append(content.substring(lastPos)); - lastPos = content.length(); - } - else - { - tmp.append(content.substring(lastPos, pos)); - lastPos = pos; - } - } - } - return tmp.toString(); - } - - public static void main(String[] args) - { - String html = ""; - String escape = EscapeUtil.escape(html); - // String html = "ipt>alert(\"XSS\")ipt>"; - // String html = "<123"; - // String html = "123>"; - System.out.println("clean: " + EscapeUtil.clean(html)); - System.out.println("escape: " + escape); - System.out.println("unescape: " + EscapeUtil.unescape(escape)); - } -} +package com.ruoyi.common.utils.html; + +import com.ruoyi.common.utils.StringUtils; + +/** + * 转义和反转义工具类 + * + * @author ruoyi + */ +public class EscapeUtil +{ + public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)"; + + private static final char[][] TEXT = new char[64][]; + + static + { + for (int i = 0; i < 64; i++) + { + TEXT[i] = new char[] { (char) i }; + } + + // special HTML characters + TEXT['\''] = "'".toCharArray(); // 单引号 + TEXT['"'] = """.toCharArray(); // 双引号 + TEXT['&'] = "&".toCharArray(); // &符 + TEXT['<'] = "<".toCharArray(); // 小于号 + TEXT['>'] = ">".toCharArray(); // 大于号 + } + + /** + * 转义文本中的HTML字符为安全的字符 + * + * @param text 被转义的文本 + * @return 转义后的文本 + */ + public static String escape(String text) + { + return encode(text); + } + + /** + * 还原被转义的HTML特殊字符 + * + * @param content 包含转义符的HTML内容 + * @return 转换后的字符串 + */ + public static String unescape(String content) + { + return decode(content); + } + + /** + * 清除所有HTML标签,但是不删除标签内的内容 + * + * @param content 文本 + * @return 清除标签后的文本 + */ + public static String clean(String content) + { + return new HTMLFilter().filter(content); + } + + /** + * Escape编码 + * + * @param text 被编码的文本 + * @return 编码后的字符 + */ + private static String encode(String text) + { + if (StringUtils.isEmpty(text)) + { + return StringUtils.EMPTY; + } + + final StringBuilder tmp = new StringBuilder(text.length() * 6); + char c; + for (int i = 0; i < text.length(); i++) + { + c = text.charAt(i); + if (c < 256) + { + tmp.append("%"); + if (c < 16) + { + tmp.append("0"); + } + tmp.append(Integer.toString(c, 16)); + } + else + { + tmp.append("%u"); + if (c <= 0xfff) + { + // issue#I49JU8@Gitee + tmp.append("0"); + } + tmp.append(Integer.toString(c, 16)); + } + } + return tmp.toString(); + } + + /** + * Escape解码 + * + * @param content 被转义的内容 + * @return 解码后的字符串 + */ + public static String decode(String content) + { + if (StringUtils.isEmpty(content)) + { + return content; + } + + StringBuilder tmp = new StringBuilder(content.length()); + int lastPos = 0, pos = 0; + char ch; + while (lastPos < content.length()) + { + pos = content.indexOf("%", lastPos); + if (pos == lastPos) + { + if (content.charAt(pos + 1) == 'u') + { + ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16); + tmp.append(ch); + lastPos = pos + 6; + } + else + { + ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16); + tmp.append(ch); + lastPos = pos + 3; + } + } + else + { + if (pos == -1) + { + tmp.append(content.substring(lastPos)); + lastPos = content.length(); + } + else + { + tmp.append(content.substring(lastPos, pos)); + lastPos = pos; + } + } + } + return tmp.toString(); + } + + public static void main(String[] args) + { + String html = ""; + String escape = EscapeUtil.escape(html); + // String html = "ipt>alert(\"XSS\")ipt>"; + // String html = "<123"; + // String html = "123>"; + System.out.println("clean: " + EscapeUtil.clean(html)); + System.out.println("escape: " + escape); + System.out.println("unescape: " + EscapeUtil.unescape(escape)); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java index ebff3fd..587ee6f 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java @@ -1,570 +1,570 @@ -package com.ruoyi.common.utils.html; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * HTML过滤器,用于去除XSS漏洞隐患。 - * - * @author ruoyi - */ -public final class HTMLFilter -{ - /** - * regex flag union representing /si modifiers in php - **/ - private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL; - private static final Pattern P_COMMENTS = Pattern.compile("", Pattern.DOTALL); - private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI); - private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL); - private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI); - private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI); - private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI); - private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI); - private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI); - private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?"); - private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?"); - private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?"); - private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))"); - private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL); - private static final Pattern P_END_ARROW = Pattern.compile("^>"); - private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)"); - private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)"); - private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)"); - private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)"); - private static final Pattern P_AMP = Pattern.compile("&"); - private static final Pattern P_QUOTE = Pattern.compile("\""); - private static final Pattern P_LEFT_ARROW = Pattern.compile("<"); - private static final Pattern P_RIGHT_ARROW = Pattern.compile(">"); - private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>"); - - // @xxx could grow large... maybe use sesat's ReferenceMap - private static final ConcurrentMap P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>(); - private static final ConcurrentMap P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>(); - - /** - * set of allowed html elements, along with allowed attributes for each element - **/ - private final Map> vAllowed; - /** - * counts of open tags for each (allowable) html element - **/ - private final Map vTagCounts = new HashMap<>(); - - /** - * html elements which must always be self-closing (e.g. "") - **/ - private final String[] vSelfClosingTags; - /** - * html elements which must always have separate opening and closing tags (e.g. "") - **/ - private final String[] vNeedClosingTags; - /** - * set of disallowed html elements - **/ - private final String[] vDisallowed; - /** - * attributes which should be checked for valid protocols - **/ - private final String[] vProtocolAtts; - /** - * allowed protocols - **/ - private final String[] vAllowedProtocols; - /** - * tags which should be removed if they contain no content (e.g. "" or "") - **/ - private final String[] vRemoveBlanks; - /** - * entities allowed within html markup - **/ - private final String[] vAllowedEntities; - /** - * flag determining whether comments are allowed in input String. - */ - private final boolean stripComment; - private final boolean encodeQuotes; - /** - * flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "" - * becomes " text "). If set to false, unbalanced angle brackets will be html escaped. - */ - private final boolean alwaysMakeTags; - - /** - * Default constructor. - */ - public HTMLFilter() - { - vAllowed = new HashMap<>(); - - final ArrayList a_atts = new ArrayList<>(); - a_atts.add("href"); - a_atts.add("target"); - vAllowed.put("a", a_atts); - - final ArrayList img_atts = new ArrayList<>(); - img_atts.add("src"); - img_atts.add("width"); - img_atts.add("height"); - img_atts.add("alt"); - vAllowed.put("img", img_atts); - - final ArrayList no_atts = new ArrayList<>(); - vAllowed.put("b", no_atts); - vAllowed.put("strong", no_atts); - vAllowed.put("i", no_atts); - vAllowed.put("em", no_atts); - - vSelfClosingTags = new String[] { "img" }; - vNeedClosingTags = new String[] { "a", "b", "strong", "i", "em" }; - vDisallowed = new String[] {}; - vAllowedProtocols = new String[] { "http", "mailto", "https" }; // no ftp. - vProtocolAtts = new String[] { "src", "href" }; - vRemoveBlanks = new String[] { "a", "b", "strong", "i", "em" }; - vAllowedEntities = new String[] { "amp", "gt", "lt", "quot" }; - stripComment = true; - encodeQuotes = true; - alwaysMakeTags = false; - } - - /** - * Map-parameter configurable constructor. - * - * @param conf map containing configuration. keys match field names. - */ - @SuppressWarnings("unchecked") - public HTMLFilter(final Map conf) - { - - assert conf.containsKey("vAllowed") : "configuration requires vAllowed"; - assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags"; - assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags"; - assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed"; - assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols"; - assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts"; - assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks"; - assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities"; - - vAllowed = Collections.unmodifiableMap((HashMap>) conf.get("vAllowed")); - vSelfClosingTags = (String[]) conf.get("vSelfClosingTags"); - vNeedClosingTags = (String[]) conf.get("vNeedClosingTags"); - vDisallowed = (String[]) conf.get("vDisallowed"); - vAllowedProtocols = (String[]) conf.get("vAllowedProtocols"); - vProtocolAtts = (String[]) conf.get("vProtocolAtts"); - vRemoveBlanks = (String[]) conf.get("vRemoveBlanks"); - vAllowedEntities = (String[]) conf.get("vAllowedEntities"); - stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true; - encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true; - alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true; - } - - private void reset() - { - vTagCounts.clear(); - } - - // --------------------------------------------------------------- - // my versions of some PHP library functions - public static String chr(final int decimal) - { - return String.valueOf((char) decimal); - } - - public static String htmlSpecialChars(final String s) - { - String result = s; - result = regexReplace(P_AMP, "&", result); - result = regexReplace(P_QUOTE, """, result); - result = regexReplace(P_LEFT_ARROW, "<", result); - result = regexReplace(P_RIGHT_ARROW, ">", result); - return result; - } - - // --------------------------------------------------------------- - - /** - * given a user submitted input String, filter out any invalid or restricted html. - * - * @param input text (i.e. submitted by a user) than may contain html - * @return "clean" version of input, with only valid, whitelisted html elements allowed - */ - public String filter(final String input) - { - reset(); - String s = input; - - s = escapeComments(s); - - s = balanceHTML(s); - - s = checkTags(s); - - s = processRemoveBlanks(s); - - // s = validateEntities(s); - - return s; - } - - public boolean isAlwaysMakeTags() - { - return alwaysMakeTags; - } - - public boolean isStripComments() - { - return stripComment; - } - - private String escapeComments(final String s) - { - final Matcher m = P_COMMENTS.matcher(s); - final StringBuffer buf = new StringBuffer(); - if (m.find()) - { - final String match = m.group(1); // (.*?) - m.appendReplacement(buf, Matcher.quoteReplacement("")); - } - m.appendTail(buf); - - return buf.toString(); - } - - private String balanceHTML(String s) - { - if (alwaysMakeTags) - { - // - // try and form html - // - s = regexReplace(P_END_ARROW, "", s); - // 不追加结束标签 - s = regexReplace(P_BODY_TO_END, "<$1>", s); - s = regexReplace(P_XML_CONTENT, "$1<$2", s); - - } - else - { - // - // escape stray brackets - // - s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s); - s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s); - - // - // the last regexp causes '<>' entities to appear - // (we need to do a lookahead assertion so that the last bracket can - // be used in the next pass of the regexp) - // - s = regexReplace(P_BOTH_ARROWS, "", s); - } - - return s; - } - - private String checkTags(String s) - { - Matcher m = P_TAGS.matcher(s); - - final StringBuffer buf = new StringBuffer(); - while (m.find()) - { - String replaceStr = m.group(1); - replaceStr = processTag(replaceStr); - m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr)); - } - m.appendTail(buf); - - // these get tallied in processTag - // (remember to reset before subsequent calls to filter method) - final StringBuilder sBuilder = new StringBuilder(buf.toString()); - for (String key : vTagCounts.keySet()) - { - for (int ii = 0; ii < vTagCounts.get(key); ii++) - { - sBuilder.append(""); - } - } - s = sBuilder.toString(); - - return s; - } - - private String processRemoveBlanks(final String s) - { - String result = s; - for (String tag : vRemoveBlanks) - { - if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) - { - P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?>")); - } - result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result); - if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) - { - P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>")); - } - result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result); - } - - return result; - } - - private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) - { - Matcher m = regex_pattern.matcher(s); - return m.replaceAll(replacement); - } - - private String processTag(final String s) - { - // ending tags - Matcher m = P_END_TAG.matcher(s); - if (m.find()) - { - final String name = m.group(1).toLowerCase(); - if (allowed(name)) - { - if (!inArray(name, vSelfClosingTags)) - { - if (vTagCounts.containsKey(name)) - { - vTagCounts.put(name, vTagCounts.get(name) - 1); - return ""; - } - } - } - } - - // starting tags - m = P_START_TAG.matcher(s); - if (m.find()) - { - final String name = m.group(1).toLowerCase(); - final String body = m.group(2); - String ending = m.group(3); - - // debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" ); - if (allowed(name)) - { - final StringBuilder params = new StringBuilder(); - - final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body); - final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body); - final List paramNames = new ArrayList<>(); - final List paramValues = new ArrayList<>(); - while (m2.find()) - { - paramNames.add(m2.group(1)); // ([a-z0-9]+) - paramValues.add(m2.group(3)); // (.*?) - } - while (m3.find()) - { - paramNames.add(m3.group(1)); // ([a-z0-9]+) - paramValues.add(m3.group(3)); // ([^\"\\s']+) - } - - String paramName, paramValue; - for (int ii = 0; ii < paramNames.size(); ii++) - { - paramName = paramNames.get(ii).toLowerCase(); - paramValue = paramValues.get(ii); - - // debug( "paramName='" + paramName + "'" ); - // debug( "paramValue='" + paramValue + "'" ); - // debug( "allowed? " + vAllowed.get( name ).contains( paramName ) ); - - if (allowedAttribute(name, paramName)) - { - if (inArray(paramName, vProtocolAtts)) - { - paramValue = processParamProtocol(paramValue); - } - params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\\\""); - } - } - - if (inArray(name, vSelfClosingTags)) - { - ending = " /"; - } - - if (inArray(name, vNeedClosingTags)) - { - ending = ""; - } - - if (ending == null || ending.length() < 1) - { - if (vTagCounts.containsKey(name)) - { - vTagCounts.put(name, vTagCounts.get(name) + 1); - } - else - { - vTagCounts.put(name, 1); - } - } - else - { - ending = " /"; - } - return "<" + name + params + ending + ">"; - } - else - { - return ""; - } - } - - // comments - m = P_COMMENT.matcher(s); - if (!stripComment && m.find()) - { - return "<" + m.group() + ">"; - } - - return ""; - } - - private String processParamProtocol(String s) - { - s = decodeEntities(s); - final Matcher m = P_PROTOCOL.matcher(s); - if (m.find()) - { - final String protocol = m.group(1); - if (!inArray(protocol, vAllowedProtocols)) - { - // bad protocol, turn into local anchor link instead - s = "#" + s.substring(protocol.length() + 1); - if (s.startsWith("#//")) - { - s = "#" + s.substring(3); - } - } - } - - return s; - } - - private String decodeEntities(String s) - { - StringBuffer buf = new StringBuffer(); - - Matcher m = P_ENTITY.matcher(s); - while (m.find()) - { - final String match = m.group(1); - final int decimal = Integer.decode(match).intValue(); - m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); - } - m.appendTail(buf); - s = buf.toString(); - - buf = new StringBuffer(); - m = P_ENTITY_UNICODE.matcher(s); - while (m.find()) - { - final String match = m.group(1); - final int decimal = Integer.valueOf(match, 16).intValue(); - m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); - } - m.appendTail(buf); - s = buf.toString(); - - buf = new StringBuffer(); - m = P_ENCODE.matcher(s); - while (m.find()) - { - final String match = m.group(1); - final int decimal = Integer.valueOf(match, 16).intValue(); - m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); - } - m.appendTail(buf); - s = buf.toString(); - - s = validateEntities(s); - return s; - } - - private String validateEntities(final String s) - { - StringBuffer buf = new StringBuffer(); - - // validate entities throughout the string - Matcher m = P_VALID_ENTITIES.matcher(s); - while (m.find()) - { - final String one = m.group(1); // ([^&;]*) - final String two = m.group(2); // (?=(;|&|$)) - m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two))); - } - m.appendTail(buf); - - return encodeQuotes(buf.toString()); - } - - private String encodeQuotes(final String s) - { - if (encodeQuotes) - { - StringBuffer buf = new StringBuffer(); - Matcher m = P_VALID_QUOTES.matcher(s); - while (m.find()) - { - final String one = m.group(1); // (>|^) - final String two = m.group(2); // ([^<]+?) - final String three = m.group(3); // (<|$) - // 不替换双引号为",防止json格式无效 regexReplace(P_QUOTE, """, two) - m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three)); - } - m.appendTail(buf); - return buf.toString(); - } - else - { - return s; - } - } - - private String checkEntity(final String preamble, final String term) - { - - return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&" + preamble; - } - - private boolean isValidEntity(final String entity) - { - return inArray(entity, vAllowedEntities); - } - - private static boolean inArray(final String s, final String[] array) - { - for (String item : array) - { - if (item != null && item.equals(s)) - { - return true; - } - } - return false; - } - - private boolean allowed(final String name) - { - return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed); - } - - private boolean allowedAttribute(final String name, final String paramName) - { - return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName)); - } +package com.ruoyi.common.utils.html; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * HTML过滤器,用于去除XSS漏洞隐患。 + * + * @author ruoyi + */ +public final class HTMLFilter +{ + /** + * regex flag union representing /si modifiers in php + **/ + private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL; + private static final Pattern P_COMMENTS = Pattern.compile("", Pattern.DOTALL); + private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI); + private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL); + private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI); + private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI); + private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI); + private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI); + private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI); + private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?"); + private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?"); + private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?"); + private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))"); + private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL); + private static final Pattern P_END_ARROW = Pattern.compile("^>"); + private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_AMP = Pattern.compile("&"); + private static final Pattern P_QUOTE = Pattern.compile("\""); + private static final Pattern P_LEFT_ARROW = Pattern.compile("<"); + private static final Pattern P_RIGHT_ARROW = Pattern.compile(">"); + private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>"); + + // @xxx could grow large... maybe use sesat's ReferenceMap + private static final ConcurrentMap P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>(); + private static final ConcurrentMap P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>(); + + /** + * set of allowed html elements, along with allowed attributes for each element + **/ + private final Map> vAllowed; + /** + * counts of open tags for each (allowable) html element + **/ + private final Map vTagCounts = new HashMap<>(); + + /** + * html elements which must always be self-closing (e.g. "") + **/ + private final String[] vSelfClosingTags; + /** + * html elements which must always have separate opening and closing tags (e.g. "") + **/ + private final String[] vNeedClosingTags; + /** + * set of disallowed html elements + **/ + private final String[] vDisallowed; + /** + * attributes which should be checked for valid protocols + **/ + private final String[] vProtocolAtts; + /** + * allowed protocols + **/ + private final String[] vAllowedProtocols; + /** + * tags which should be removed if they contain no content (e.g. "" or "") + **/ + private final String[] vRemoveBlanks; + /** + * entities allowed within html markup + **/ + private final String[] vAllowedEntities; + /** + * flag determining whether comments are allowed in input String. + */ + private final boolean stripComment; + private final boolean encodeQuotes; + /** + * flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "" + * becomes " text "). If set to false, unbalanced angle brackets will be html escaped. + */ + private final boolean alwaysMakeTags; + + /** + * Default constructor. + */ + public HTMLFilter() + { + vAllowed = new HashMap<>(); + + final ArrayList a_atts = new ArrayList<>(); + a_atts.add("href"); + a_atts.add("target"); + vAllowed.put("a", a_atts); + + final ArrayList img_atts = new ArrayList<>(); + img_atts.add("src"); + img_atts.add("width"); + img_atts.add("height"); + img_atts.add("alt"); + vAllowed.put("img", img_atts); + + final ArrayList no_atts = new ArrayList<>(); + vAllowed.put("b", no_atts); + vAllowed.put("strong", no_atts); + vAllowed.put("i", no_atts); + vAllowed.put("em", no_atts); + + vSelfClosingTags = new String[] { "img" }; + vNeedClosingTags = new String[] { "a", "b", "strong", "i", "em" }; + vDisallowed = new String[] {}; + vAllowedProtocols = new String[] { "http", "mailto", "https" }; // no ftp. + vProtocolAtts = new String[] { "src", "href" }; + vRemoveBlanks = new String[] { "a", "b", "strong", "i", "em" }; + vAllowedEntities = new String[] { "amp", "gt", "lt", "quot" }; + stripComment = true; + encodeQuotes = true; + alwaysMakeTags = false; + } + + /** + * Map-parameter configurable constructor. + * + * @param conf map containing configuration. keys match field names. + */ + @SuppressWarnings("unchecked") + public HTMLFilter(final Map conf) + { + + assert conf.containsKey("vAllowed") : "configuration requires vAllowed"; + assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags"; + assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags"; + assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed"; + assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols"; + assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts"; + assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks"; + assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities"; + + vAllowed = Collections.unmodifiableMap((HashMap>) conf.get("vAllowed")); + vSelfClosingTags = (String[]) conf.get("vSelfClosingTags"); + vNeedClosingTags = (String[]) conf.get("vNeedClosingTags"); + vDisallowed = (String[]) conf.get("vDisallowed"); + vAllowedProtocols = (String[]) conf.get("vAllowedProtocols"); + vProtocolAtts = (String[]) conf.get("vProtocolAtts"); + vRemoveBlanks = (String[]) conf.get("vRemoveBlanks"); + vAllowedEntities = (String[]) conf.get("vAllowedEntities"); + stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true; + encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true; + alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true; + } + + private void reset() + { + vTagCounts.clear(); + } + + // --------------------------------------------------------------- + // my versions of some PHP library functions + public static String chr(final int decimal) + { + return String.valueOf((char) decimal); + } + + public static String htmlSpecialChars(final String s) + { + String result = s; + result = regexReplace(P_AMP, "&", result); + result = regexReplace(P_QUOTE, """, result); + result = regexReplace(P_LEFT_ARROW, "<", result); + result = regexReplace(P_RIGHT_ARROW, ">", result); + return result; + } + + // --------------------------------------------------------------- + + /** + * given a user submitted input String, filter out any invalid or restricted html. + * + * @param input text (i.e. submitted by a user) than may contain html + * @return "clean" version of input, with only valid, whitelisted html elements allowed + */ + public String filter(final String input) + { + reset(); + String s = input; + + s = escapeComments(s); + + s = balanceHTML(s); + + s = checkTags(s); + + s = processRemoveBlanks(s); + + // s = validateEntities(s); + + return s; + } + + public boolean isAlwaysMakeTags() + { + return alwaysMakeTags; + } + + public boolean isStripComments() + { + return stripComment; + } + + private String escapeComments(final String s) + { + final Matcher m = P_COMMENTS.matcher(s); + final StringBuffer buf = new StringBuffer(); + if (m.find()) + { + final String match = m.group(1); // (.*?) + m.appendReplacement(buf, Matcher.quoteReplacement("")); + } + m.appendTail(buf); + + return buf.toString(); + } + + private String balanceHTML(String s) + { + if (alwaysMakeTags) + { + // + // try and form html + // + s = regexReplace(P_END_ARROW, "", s); + // 不追加结束标签 + s = regexReplace(P_BODY_TO_END, "<$1>", s); + s = regexReplace(P_XML_CONTENT, "$1<$2", s); + + } + else + { + // + // escape stray brackets + // + s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s); + s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s); + + // + // the last regexp causes '<>' entities to appear + // (we need to do a lookahead assertion so that the last bracket can + // be used in the next pass of the regexp) + // + s = regexReplace(P_BOTH_ARROWS, "", s); + } + + return s; + } + + private String checkTags(String s) + { + Matcher m = P_TAGS.matcher(s); + + final StringBuffer buf = new StringBuffer(); + while (m.find()) + { + String replaceStr = m.group(1); + replaceStr = processTag(replaceStr); + m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr)); + } + m.appendTail(buf); + + // these get tallied in processTag + // (remember to reset before subsequent calls to filter method) + final StringBuilder sBuilder = new StringBuilder(buf.toString()); + for (String key : vTagCounts.keySet()) + { + for (int ii = 0; ii < vTagCounts.get(key); ii++) + { + sBuilder.append(""); + } + } + s = sBuilder.toString(); + + return s; + } + + private String processRemoveBlanks(final String s) + { + String result = s; + for (String tag : vRemoveBlanks) + { + if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) + { + P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?>")); + } + result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result); + if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) + { + P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>")); + } + result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result); + } + + return result; + } + + private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) + { + Matcher m = regex_pattern.matcher(s); + return m.replaceAll(replacement); + } + + private String processTag(final String s) + { + // ending tags + Matcher m = P_END_TAG.matcher(s); + if (m.find()) + { + final String name = m.group(1).toLowerCase(); + if (allowed(name)) + { + if (!inArray(name, vSelfClosingTags)) + { + if (vTagCounts.containsKey(name)) + { + vTagCounts.put(name, vTagCounts.get(name) - 1); + return ""; + } + } + } + } + + // starting tags + m = P_START_TAG.matcher(s); + if (m.find()) + { + final String name = m.group(1).toLowerCase(); + final String body = m.group(2); + String ending = m.group(3); + + // debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" ); + if (allowed(name)) + { + final StringBuilder params = new StringBuilder(); + + final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body); + final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body); + final List paramNames = new ArrayList<>(); + final List paramValues = new ArrayList<>(); + while (m2.find()) + { + paramNames.add(m2.group(1)); // ([a-z0-9]+) + paramValues.add(m2.group(3)); // (.*?) + } + while (m3.find()) + { + paramNames.add(m3.group(1)); // ([a-z0-9]+) + paramValues.add(m3.group(3)); // ([^\"\\s']+) + } + + String paramName, paramValue; + for (int ii = 0; ii < paramNames.size(); ii++) + { + paramName = paramNames.get(ii).toLowerCase(); + paramValue = paramValues.get(ii); + + // debug( "paramName='" + paramName + "'" ); + // debug( "paramValue='" + paramValue + "'" ); + // debug( "allowed? " + vAllowed.get( name ).contains( paramName ) ); + + if (allowedAttribute(name, paramName)) + { + if (inArray(paramName, vProtocolAtts)) + { + paramValue = processParamProtocol(paramValue); + } + params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\\\""); + } + } + + if (inArray(name, vSelfClosingTags)) + { + ending = " /"; + } + + if (inArray(name, vNeedClosingTags)) + { + ending = ""; + } + + if (ending == null || ending.length() < 1) + { + if (vTagCounts.containsKey(name)) + { + vTagCounts.put(name, vTagCounts.get(name) + 1); + } + else + { + vTagCounts.put(name, 1); + } + } + else + { + ending = " /"; + } + return "<" + name + params + ending + ">"; + } + else + { + return ""; + } + } + + // comments + m = P_COMMENT.matcher(s); + if (!stripComment && m.find()) + { + return "<" + m.group() + ">"; + } + + return ""; + } + + private String processParamProtocol(String s) + { + s = decodeEntities(s); + final Matcher m = P_PROTOCOL.matcher(s); + if (m.find()) + { + final String protocol = m.group(1); + if (!inArray(protocol, vAllowedProtocols)) + { + // bad protocol, turn into local anchor link instead + s = "#" + s.substring(protocol.length() + 1); + if (s.startsWith("#//")) + { + s = "#" + s.substring(3); + } + } + } + + return s; + } + + private String decodeEntities(String s) + { + StringBuffer buf = new StringBuffer(); + + Matcher m = P_ENTITY.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.decode(match).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENTITY_UNICODE.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENCODE.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + s = validateEntities(s); + return s; + } + + private String validateEntities(final String s) + { + StringBuffer buf = new StringBuffer(); + + // validate entities throughout the string + Matcher m = P_VALID_ENTITIES.matcher(s); + while (m.find()) + { + final String one = m.group(1); // ([^&;]*) + final String two = m.group(2); // (?=(;|&|$)) + m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two))); + } + m.appendTail(buf); + + return encodeQuotes(buf.toString()); + } + + private String encodeQuotes(final String s) + { + if (encodeQuotes) + { + StringBuffer buf = new StringBuffer(); + Matcher m = P_VALID_QUOTES.matcher(s); + while (m.find()) + { + final String one = m.group(1); // (>|^) + final String two = m.group(2); // ([^<]+?) + final String three = m.group(3); // (<|$) + // 不替换双引号为",防止json格式无效 regexReplace(P_QUOTE, """, two) + m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three)); + } + m.appendTail(buf); + return buf.toString(); + } + else + { + return s; + } + } + + private String checkEntity(final String preamble, final String term) + { + + return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&" + preamble; + } + + private boolean isValidEntity(final String entity) + { + return inArray(entity, vAllowedEntities); + } + + private static boolean inArray(final String s, final String[] array) + { + for (String item : array) + { + if (item != null && item.equals(s)) + { + return true; + } + } + return false; + } + + private boolean allowed(final String name) + { + return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed); + } + + private boolean allowedAttribute(final String name, final String paramName) + { + return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName)); + } } \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java index 401f25a..8baad38 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java @@ -1,55 +1,55 @@ -package com.ruoyi.common.utils.http; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import jakarta.servlet.ServletRequest; -import org.apache.commons.lang3.exception.ExceptionUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * 通用http工具封装 - * - * @author ruoyi - */ -public class HttpHelper -{ - private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelper.class); - - public static String getBodyString(ServletRequest request) - { - StringBuilder sb = new StringBuilder(); - BufferedReader reader = null; - try (InputStream inputStream = request.getInputStream()) - { - reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); - String line = ""; - while ((line = reader.readLine()) != null) - { - sb.append(line); - } - } - catch (IOException e) - { - LOGGER.warn("getBodyString出现问题!"); - } - finally - { - if (reader != null) - { - try - { - reader.close(); - } - catch (IOException e) - { - LOGGER.error(ExceptionUtils.getMessage(e)); - } - } - } - return sb.toString(); - } -} +package com.ruoyi.common.utils.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import javax.servlet.ServletRequest; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 通用http工具封装 + * + * @author ruoyi + */ +public class HttpHelper +{ + private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelper.class); + + public static String getBodyString(ServletRequest request) + { + StringBuilder sb = new StringBuilder(); + BufferedReader reader = null; + try (InputStream inputStream = request.getInputStream()) + { + reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + String line = ""; + while ((line = reader.readLine()) != null) + { + sb.append(line); + } + } + catch (IOException e) + { + LOGGER.warn("getBodyString出现问题!"); + } + finally + { + if (reader != null) + { + try + { + reader.close(); + } + catch (IOException e) + { + LOGGER.error(ExceptionUtils.getMessage(e)); + } + } + } + return sb.toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java index fe8bb5c..d505789 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java @@ -1,373 +1,293 @@ -package com.ruoyi.common.utils.http; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.PrintWriter; -import java.net.ConnectException; -import java.net.SocketTimeoutException; -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; -import javax.net.ssl.SSLSession; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; -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; - -/** - * 通用http发送方法 - * - * @author ruoyi - */ -public class HttpUtils -{ - private static final Logger log = LoggerFactory.getLogger(HttpUtils.class); - - /** - * 向指定 URL 发送GET方法的请求 - * - * @param url 发送请求的 URL - * @return 所代表远程资源的响应结果 - */ - public static String sendGet(String url) - { - return sendGet(url, StringUtils.EMPTY); - } - - /** - * 向指定 URL 发送GET方法的请求 - * - * @param url 发送请求的 URL - * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 - * @return 所代表远程资源的响应结果 - */ - public static String sendGet(String url, String param) - { - return sendGet(url, param, Constants.UTF8); - } - - /** - * 向指定 URL 发送GET方法的请求 - * - * @param url 发送请求的 URL - * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 - * @param contentType 编码类型 - * @return 所代表远程资源的响应结果 - */ - public static String sendGet(String url, String param, String contentType) - { - StringBuilder result = new StringBuilder(); - BufferedReader in = null; - try - { - String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url; - log.info("sendGet - {}", urlNameString); - URL realUrl = new URL(urlNameString); - URLConnection connection = realUrl.openConnection(); - connection.setRequestProperty("accept", "*/*"); - connection.setRequestProperty("connection", "Keep-Alive"); - connection.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"); - connection.connect(); - in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType)); - String line; - while ((line = in.readLine()) != null) - { - result.append(line); - } - log.info("recv - {}", result); - } - catch (ConnectException e) - { - log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e); - } - catch (SocketTimeoutException e) - { - log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e); - } - catch (IOException e) - { - log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e); - } - catch (Exception e) - { - log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e); - } - finally - { - try - { - if (in != null) - { - in.close(); - } - } - catch (Exception ex) - { - log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); - } - } - return result.toString(); - } - - /** - * 向指定 URL 发送POST方法的请求 - * - * @param url 发送请求的 URL - * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 - * @return 所代表远程资源的响应结果 - */ - public static String sendPost(String url, String param) - { - return sendPost(url, param, MediaType.APPLICATION_FORM_URLENCODED_VALUE); - } - - /** - * 向指定 URL 发送POST方法的请求 - * - * @param url 发送请求的 URL - * @param param 请求参数 - * @param contentType 内容类型 - * @return 所代表远程资源的响应结果 - */ - public static String sendPost(String url, String param, String contentType) - { - PrintWriter out = null; - BufferedReader in = null; - StringBuilder result = new StringBuilder(); - try - { - log.info("sendPost - {}", url); - URL realUrl = new URL(url); - URLConnection conn = realUrl.openConnection(); - conn.setRequestProperty("accept", "*/*"); - conn.setRequestProperty("connection", "Keep-Alive"); - conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"); - conn.setRequestProperty("Accept-Charset", "utf-8"); - conn.setRequestProperty("Content-Type", contentType); - conn.setDoOutput(true); - conn.setDoInput(true); - out = new PrintWriter(conn.getOutputStream()); - out.print(param); - out.flush(); - in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)); - String line; - while ((line = in.readLine()) != null) - { - result.append(line); - } - log.info("recv - {}", result); - } - catch (ConnectException e) - { - log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e); - } - catch (SocketTimeoutException e) - { - log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e); - } - catch (IOException e) - { - log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e); - } - catch (Exception e) - { - log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e); - } - finally - { - try - { - if (out != null) - { - out.close(); - } - if (in != null) - { - in.close(); - } - } - catch (IOException ex) - { - log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); - } - } - return result.toString(); - } - - public static String sendSSLPost(String url, String param) - { - return sendSSLPost(url, param, MediaType.APPLICATION_FORM_URLENCODED_VALUE); - } - - public static String sendSSLPost(String url, String param, String contentType) - { - StringBuilder result = new StringBuilder(); - String urlNameString = url + "?" + param; - try - { - log.info("sendSSLPost - {}", urlNameString); - SSLContext sc = SSLContext.getInstance("SSL"); - sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom()); - URL console = new URL(urlNameString); - HttpsURLConnection conn = (HttpsURLConnection) console.openConnection(); - conn.setRequestProperty("accept", "*/*"); - conn.setRequestProperty("connection", "Keep-Alive"); - conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"); - conn.setRequestProperty("Accept-Charset", "utf-8"); - conn.setRequestProperty("Content-Type", contentType); - conn.setDoOutput(true); - conn.setDoInput(true); - - conn.setSSLSocketFactory(sc.getSocketFactory()); - conn.setHostnameVerifier(new TrustAnyHostnameVerifier()); - conn.connect(); - InputStream is = conn.getInputStream(); - BufferedReader br = new BufferedReader(new InputStreamReader(is)); - String ret = ""; - while ((ret = br.readLine()) != null) - { - if (ret != null && !"".equals(ret.trim())) - { - result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8)); - } - } - log.info("recv - {}", result); - conn.disconnect(); - br.close(); - } - catch (ConnectException e) - { - log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e); - } - catch (SocketTimeoutException e) - { - log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e); - } - catch (IOException e) - { - log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e); - } - catch (Exception e) - { - log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e); - } - return result.toString(); - } - - private static class TrustAnyTrustManager implements X509TrustManager - { - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) - { - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) - { - } - - @Override - public X509Certificate[] getAcceptedIssuers() - { - return new X509Certificate[] {}; - } - } - - private static class TrustAnyHostnameVerifier implements HostnameVerifier - { - @Override - public boolean verify(String hostname, SSLSession session) - { - return true; - } - } - - /** - * 发送 POST 请求(application/x-www-form-urlencoded 格式) - * @param url 请求地址 - * @param params 表单参数 - * @param headers 请求头(可为 null) - * @param responseType 响应类型 - * @param 泛型返回值 - * @return 响应结果 - */ - public static T doPostFormUrlEncoded(String url, Map params, HttpHeaders headers, Class responseType) { - // 构建表单参数 - MultiValueMap 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> requestEntity = new HttpEntity<>(formParams, requestHeaders); - RestTemplate restTemplate = new RestTemplate(); - // 发送请求 - try { - ResponseEntity 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 响应泛型类型 - * @param 请求体类型 - * @return 响应结果 - */ - public static T doPostJson(String url, R requestBody, HttpHeaders headers, Class 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 requestEntity = new HttpEntity<>(requestBody, requestHeaders); - RestTemplate restTemplate = new RestTemplate(); - // 发送 JSON POST 请求 - try { - ResponseEntity 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); - } - } +package com.ruoyi.common.utils.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.ConnectException; +import java.net.SocketTimeoutException; +import java.net.URL; +import java.net.URLConnection; +import java.nio.charset.StandardCharsets; +import java.security.cert.X509Certificate; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.StringUtils; +import org.springframework.http.MediaType; + +/** + * 通用http发送方法 + * + * @author ruoyi + */ +public class HttpUtils +{ + private static final Logger log = LoggerFactory.getLogger(HttpUtils.class); + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url) + { + return sendGet(url, StringUtils.EMPTY); + } + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url, String param) + { + return sendGet(url, param, Constants.UTF8); + } + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @param contentType 编码类型 + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url, String param, String contentType) + { + StringBuilder result = new StringBuilder(); + BufferedReader in = null; + try + { + String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url; + log.info("sendGet - {}", urlNameString); + URL realUrl = new URL(urlNameString); + URLConnection connection = realUrl.openConnection(); + connection.setRequestProperty("accept", "*/*"); + connection.setRequestProperty("connection", "Keep-Alive"); + connection.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"); + connection.connect(); + in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType)); + String line; + while ((line = in.readLine()) != null) + { + result.append(line); + } + log.info("recv - {}", result); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e); + } + finally + { + try + { + if (in != null) + { + in.close(); + } + } + catch (Exception ex) + { + log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); + } + } + return result.toString(); + } + + /** + * 向指定 URL 发送POST方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String sendPost(String url, String param) + { + return sendPost(url, param, MediaType.APPLICATION_FORM_URLENCODED_VALUE); + } + + /** + * 向指定 URL 发送POST方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数 + * @param contentType 内容类型 + * @return 所代表远程资源的响应结果 + */ + public static String sendPost(String url, String param, String contentType) + { + PrintWriter out = null; + BufferedReader in = null; + StringBuilder result = new StringBuilder(); + try + { + log.info("sendPost - {}", url); + URL realUrl = new URL(url); + URLConnection conn = realUrl.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("Content-Type", contentType); + conn.setDoOutput(true); + conn.setDoInput(true); + out = new PrintWriter(conn.getOutputStream()); + out.print(param); + out.flush(); + in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)); + String line; + while ((line = in.readLine()) != null) + { + result.append(line); + } + log.info("recv - {}", result); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e); + } + finally + { + try + { + if (out != null) + { + out.close(); + } + if (in != null) + { + in.close(); + } + } + catch (IOException ex) + { + log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); + } + } + return result.toString(); + } + + public static String sendSSLPost(String url, String param) + { + return sendSSLPost(url, param, MediaType.APPLICATION_FORM_URLENCODED_VALUE); + } + + public static String sendSSLPost(String url, String param, String contentType) + { + StringBuilder result = new StringBuilder(); + String urlNameString = url + "?" + param; + try + { + log.info("sendSSLPost - {}", urlNameString); + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom()); + URL console = new URL(urlNameString); + HttpsURLConnection conn = (HttpsURLConnection) console.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("Content-Type", contentType); + conn.setDoOutput(true); + conn.setDoInput(true); + + conn.setSSLSocketFactory(sc.getSocketFactory()); + conn.setHostnameVerifier(new TrustAnyHostnameVerifier()); + conn.connect(); + InputStream is = conn.getInputStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + String ret = ""; + while ((ret = br.readLine()) != null) + { + if (ret != null && !"".equals(ret.trim())) + { + result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8)); + } + } + log.info("recv - {}", result); + conn.disconnect(); + br.close(); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e); + } + return result.toString(); + } + + private static class TrustAnyTrustManager implements X509TrustManager + { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) + { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) + { + } + + @Override + public X509Certificate[] getAcceptedIssuers() + { + return new X509Certificate[] {}; + } + } + + private static class TrustAnyHostnameVerifier implements HostnameVerifier + { + @Override + public boolean verify(String hostname, SSLSession session) + { + return true; + } + } } \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java index edfe419..62e10a4 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java @@ -1,56 +1,56 @@ -package com.ruoyi.common.utils.ip; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import com.alibaba.fastjson2.JSON; -import com.alibaba.fastjson2.JSONObject; -import com.ruoyi.common.config.RuoYiConfig; -import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.common.utils.http.HttpUtils; - -/** - * 获取地址类 - * - * @author ruoyi - */ -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 UNKNOWN = "XX XX"; - - public static String getRealAddressByIP(String ip) - { - // 内网不查询 - if (IpUtils.internalIp(ip)) - { - return "内网IP"; - } - if (RuoYiConfig.isAddressEnabled()) - { - try - { - String rspStr = HttpUtils.sendGet(IP_URL, "ip=" + ip + "&json=true", Constants.GBK); - if (StringUtils.isEmpty(rspStr)) - { - log.error("获取地理位置异常 {}", ip); - return UNKNOWN; - } - JSONObject obj = JSON.parseObject(rspStr); - String region = obj.getString("pro"); - String city = obj.getString("city"); - return String.format("%s %s", region, city); - } - catch (Exception e) - { - log.error("获取地理位置异常 {}", ip); - } - } - return UNKNOWN; - } -} +package com.ruoyi.common.utils.ip; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.http.HttpUtils; + +/** + * 获取地址类 + * + * @author ruoyi + */ +public class AddressUtils +{ + private static final Logger log = LoggerFactory.getLogger(AddressUtils.class); + + // IP地址查询 + public static final String IP_URL = "https://whois.pconline.com.cn/ipJson.jsp"; + + // 未知地址 + public static final String UNKNOWN = "XX XX"; + + public static String getRealAddressByIP(String ip) + { + // 内网不查询 + if (IpUtils.internalIp(ip)) + { + return "内网IP"; + } + if (RuoYiConfig.isAddressEnabled()) + { + try + { + String rspStr = HttpUtils.sendGet(IP_URL, "ip=" + ip + "&json=true", Constants.GBK); + if (StringUtils.isEmpty(rspStr)) + { + log.error("获取地理位置异常 {}", ip); + return UNKNOWN; + } + JSONObject obj = JSON.parseObject(rspStr); + String region = obj.getString("pro"); + String city = obj.getString("city"); + return String.format("%s %s", region, city); + } + catch (Exception e) + { + log.error("获取地理位置异常 {}", ip); + } + } + return UNKNOWN; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java index a376416..3316240 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java @@ -1,382 +1,382 @@ -package com.ruoyi.common.utils.ip; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import jakarta.servlet.http.HttpServletRequest; -import com.ruoyi.common.utils.ServletUtils; -import com.ruoyi.common.utils.StringUtils; - -/** - * 获取IP方法 - * - * @author ruoyi - */ -public class IpUtils -{ - public final static String REGX_0_255 = "(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)"; - // 匹配 ip - public final static String REGX_IP = "((" + REGX_0_255 + "\\.){3}" + REGX_0_255 + ")"; - public final static String REGX_IP_WILDCARD = "(((\\*\\.){3}\\*)|(" + REGX_0_255 + "(\\.\\*){3})|(" + REGX_0_255 + "\\." + REGX_0_255 + ")(\\.\\*){2}" + "|((" + REGX_0_255 + "\\.){3}\\*))"; - // 匹配网段 - public final static String REGX_IP_SEG = "(" + REGX_IP + "\\-" + REGX_IP + ")"; - - /** - * 获取客户端IP - * - * @return IP地址 - */ - public static String getIpAddr() - { - return getIpAddr(ServletUtils.getRequest()); - } - - /** - * 获取客户端IP - * - * @param request 请求对象 - * @return IP地址 - */ - public static String getIpAddr(HttpServletRequest request) - { - if (request == null) - { - return "unknown"; - } - String ip = request.getHeader("x-forwarded-for"); - if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) - { - ip = request.getHeader("Proxy-Client-IP"); - } - if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) - { - ip = request.getHeader("X-Forwarded-For"); - } - if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) - { - ip = request.getHeader("WL-Proxy-Client-IP"); - } - if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) - { - ip = request.getHeader("X-Real-IP"); - } - - if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) - { - ip = request.getRemoteAddr(); - } - - return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip); - } - - /** - * 检查是否为内部IP地址 - * - * @param ip IP地址 - * @return 结果 - */ - public static boolean internalIp(String ip) - { - byte[] addr = textToNumericFormatV4(ip); - return internalIp(addr) || "127.0.0.1".equals(ip); - } - - /** - * 检查是否为内部IP地址 - * - * @param addr byte地址 - * @return 结果 - */ - private static boolean internalIp(byte[] addr) - { - if (StringUtils.isNull(addr) || addr.length < 2) - { - return true; - } - final byte b0 = addr[0]; - final byte b1 = addr[1]; - // 10.x.x.x/8 - final byte SECTION_1 = 0x0A; - // 172.16.x.x/12 - final byte SECTION_2 = (byte) 0xAC; - final byte SECTION_3 = (byte) 0x10; - final byte SECTION_4 = (byte) 0x1F; - // 192.168.x.x/16 - final byte SECTION_5 = (byte) 0xC0; - final byte SECTION_6 = (byte) 0xA8; - switch (b0) - { - case SECTION_1: - return true; - case SECTION_2: - if (b1 >= SECTION_3 && b1 <= SECTION_4) - { - return true; - } - case SECTION_5: - switch (b1) - { - case SECTION_6: - return true; - } - default: - return false; - } - } - - /** - * 将IPv4地址转换成字节 - * - * @param text IPv4地址 - * @return byte 字节 - */ - public static byte[] textToNumericFormatV4(String text) - { - if (text.length() == 0) - { - return null; - } - - byte[] bytes = new byte[4]; - String[] elements = text.split("\\.", -1); - try - { - long l; - int i; - switch (elements.length) - { - case 1: - l = Long.parseLong(elements[0]); - if ((l < 0L) || (l > 4294967295L)) - { - return null; - } - bytes[0] = (byte) (int) (l >> 24 & 0xFF); - bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF); - bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); - bytes[3] = (byte) (int) (l & 0xFF); - break; - case 2: - l = Integer.parseInt(elements[0]); - if ((l < 0L) || (l > 255L)) - { - return null; - } - bytes[0] = (byte) (int) (l & 0xFF); - l = Integer.parseInt(elements[1]); - if ((l < 0L) || (l > 16777215L)) - { - return null; - } - bytes[1] = (byte) (int) (l >> 16 & 0xFF); - bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); - bytes[3] = (byte) (int) (l & 0xFF); - break; - case 3: - for (i = 0; i < 2; ++i) - { - l = Integer.parseInt(elements[i]); - if ((l < 0L) || (l > 255L)) - { - return null; - } - bytes[i] = (byte) (int) (l & 0xFF); - } - l = Integer.parseInt(elements[2]); - if ((l < 0L) || (l > 65535L)) - { - return null; - } - bytes[2] = (byte) (int) (l >> 8 & 0xFF); - bytes[3] = (byte) (int) (l & 0xFF); - break; - case 4: - for (i = 0; i < 4; ++i) - { - l = Integer.parseInt(elements[i]); - if ((l < 0L) || (l > 255L)) - { - return null; - } - bytes[i] = (byte) (int) (l & 0xFF); - } - break; - default: - return null; - } - } - catch (NumberFormatException e) - { - return null; - } - return bytes; - } - - /** - * 获取IP地址 - * - * @return 本地IP地址 - */ - public static String getHostIp() - { - try - { - return InetAddress.getLocalHost().getHostAddress(); - } - catch (UnknownHostException e) - { - } - return "127.0.0.1"; - } - - /** - * 获取主机名 - * - * @return 本地主机名 - */ - public static String getHostName() - { - try - { - return InetAddress.getLocalHost().getHostName(); - } - catch (UnknownHostException e) - { - } - return "未知"; - } - - /** - * 从多级反向代理中获得第一个非unknown IP地址 - * - * @param ip 获得的IP地址 - * @return 第一个非unknown IP地址 - */ - public static String getMultistageReverseProxyIp(String ip) - { - // 多级反向代理检测 - if (ip != null && ip.indexOf(",") > 0) - { - final String[] ips = ip.trim().split(","); - for (String subIp : ips) - { - if (false == isUnknown(subIp)) - { - ip = subIp; - break; - } - } - } - return StringUtils.substring(ip, 0, 255); - } - - /** - * 检测给定字符串是否为未知,多用于检测HTTP请求相关 - * - * @param checkString 被检测的字符串 - * @return 是否未知 - */ - public static boolean isUnknown(String checkString) - { - return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString); - } - - /** - * 是否为IP - */ - public static boolean isIP(String ip) - { - return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP); - } - - /** - * 是否为IP,或 *为间隔的通配符地址 - */ - public static boolean isIpWildCard(String ip) - { - return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP_WILDCARD); - } - - /** - * 检测参数是否在ip通配符里 - */ - public static boolean ipIsInWildCardNoCheck(String ipWildCard, String ip) - { - String[] s1 = ipWildCard.split("\\."); - String[] s2 = ip.split("\\."); - boolean isMatchedSeg = true; - for (int i = 0; i < s1.length && !s1[i].equals("*"); i++) - { - if (!s1[i].equals(s2[i])) - { - isMatchedSeg = false; - break; - } - } - return isMatchedSeg; - } - - /** - * 是否为特定格式如:“10.10.10.1-10.10.10.99”的ip段字符串 - */ - public static boolean isIPSegment(String ipSeg) - { - return StringUtils.isNotBlank(ipSeg) && ipSeg.matches(REGX_IP_SEG); - } - - /** - * 判断ip是否在指定网段中 - */ - public static boolean ipIsInNetNoCheck(String iparea, String ip) - { - int idx = iparea.indexOf('-'); - String[] sips = iparea.substring(0, idx).split("\\."); - String[] sipe = iparea.substring(idx + 1).split("\\."); - String[] sipt = ip.split("\\."); - long ips = 0L, ipe = 0L, ipt = 0L; - for (int i = 0; i < 4; ++i) - { - ips = ips << 8 | Integer.parseInt(sips[i]); - ipe = ipe << 8 | Integer.parseInt(sipe[i]); - ipt = ipt << 8 | Integer.parseInt(sipt[i]); - } - if (ips > ipe) - { - long t = ips; - ips = ipe; - ipe = t; - } - return ips <= ipt && ipt <= ipe; - } - - /** - * 校验ip是否符合过滤串规则 - * - * @param filter 过滤IP列表,支持后缀'*'通配,支持网段如:`10.10.10.1-10.10.10.99` - * @param ip 校验IP地址 - * @return boolean 结果 - */ - public static boolean isMatchedIp(String filter, String ip) - { - if (StringUtils.isEmpty(filter) || StringUtils.isEmpty(ip)) - { - return false; - } - String[] ips = filter.split(";"); - for (String iStr : ips) - { - if (isIP(iStr) && iStr.equals(ip)) - { - return true; - } - else if (isIpWildCard(iStr) && ipIsInWildCardNoCheck(iStr, ip)) - { - return true; - } - else if (isIPSegment(iStr) && ipIsInNetNoCheck(iStr, ip)) - { - return true; - } - } - return false; - } +package com.ruoyi.common.utils.ip; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import javax.servlet.http.HttpServletRequest; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; + +/** + * 获取IP方法 + * + * @author ruoyi + */ +public class IpUtils +{ + public final static String REGX_0_255 = "(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)"; + // 匹配 ip + public final static String REGX_IP = "((" + REGX_0_255 + "\\.){3}" + REGX_0_255 + ")"; + public final static String REGX_IP_WILDCARD = "(((\\*\\.){3}\\*)|(" + REGX_0_255 + "(\\.\\*){3})|(" + REGX_0_255 + "\\." + REGX_0_255 + ")(\\.\\*){2}" + "|((" + REGX_0_255 + "\\.){3}\\*))"; + // 匹配网段 + public final static String REGX_IP_SEG = "(" + REGX_IP + "\\-" + REGX_IP + ")"; + + /** + * 获取客户端IP + * + * @return IP地址 + */ + public static String getIpAddr() + { + return getIpAddr(ServletUtils.getRequest()); + } + + /** + * 获取客户端IP + * + * @param request 请求对象 + * @return IP地址 + */ + public static String getIpAddr(HttpServletRequest request) + { + if (request == null) + { + return "unknown"; + } + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("X-Forwarded-For"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("X-Real-IP"); + } + + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getRemoteAddr(); + } + + return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip); + } + + /** + * 检查是否为内部IP地址 + * + * @param ip IP地址 + * @return 结果 + */ + public static boolean internalIp(String ip) + { + byte[] addr = textToNumericFormatV4(ip); + return internalIp(addr) || "127.0.0.1".equals(ip); + } + + /** + * 检查是否为内部IP地址 + * + * @param addr byte地址 + * @return 结果 + */ + private static boolean internalIp(byte[] addr) + { + if (StringUtils.isNull(addr) || addr.length < 2) + { + return true; + } + final byte b0 = addr[0]; + final byte b1 = addr[1]; + // 10.x.x.x/8 + final byte SECTION_1 = 0x0A; + // 172.16.x.x/12 + final byte SECTION_2 = (byte) 0xAC; + final byte SECTION_3 = (byte) 0x10; + final byte SECTION_4 = (byte) 0x1F; + // 192.168.x.x/16 + final byte SECTION_5 = (byte) 0xC0; + final byte SECTION_6 = (byte) 0xA8; + switch (b0) + { + case SECTION_1: + return true; + case SECTION_2: + if (b1 >= SECTION_3 && b1 <= SECTION_4) + { + return true; + } + case SECTION_5: + switch (b1) + { + case SECTION_6: + return true; + } + default: + return false; + } + } + + /** + * 将IPv4地址转换成字节 + * + * @param text IPv4地址 + * @return byte 字节 + */ + public static byte[] textToNumericFormatV4(String text) + { + if (text.length() == 0) + { + return null; + } + + byte[] bytes = new byte[4]; + String[] elements = text.split("\\.", -1); + try + { + long l; + int i; + switch (elements.length) + { + case 1: + l = Long.parseLong(elements[0]); + if ((l < 0L) || (l > 4294967295L)) + { + return null; + } + bytes[0] = (byte) (int) (l >> 24 & 0xFF); + bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 2: + l = Integer.parseInt(elements[0]); + if ((l < 0L) || (l > 255L)) + { + return null; + } + bytes[0] = (byte) (int) (l & 0xFF); + l = Integer.parseInt(elements[1]); + if ((l < 0L) || (l > 16777215L)) + { + return null; + } + bytes[1] = (byte) (int) (l >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 3: + for (i = 0; i < 2; ++i) + { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) + { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + l = Integer.parseInt(elements[2]); + if ((l < 0L) || (l > 65535L)) + { + return null; + } + bytes[2] = (byte) (int) (l >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 4: + for (i = 0; i < 4; ++i) + { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) + { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + break; + default: + return null; + } + } + catch (NumberFormatException e) + { + return null; + } + return bytes; + } + + /** + * 获取IP地址 + * + * @return 本地IP地址 + */ + public static String getHostIp() + { + try + { + return InetAddress.getLocalHost().getHostAddress(); + } + catch (UnknownHostException e) + { + } + return "127.0.0.1"; + } + + /** + * 获取主机名 + * + * @return 本地主机名 + */ + public static String getHostName() + { + try + { + return InetAddress.getLocalHost().getHostName(); + } + catch (UnknownHostException e) + { + } + return "未知"; + } + + /** + * 从多级反向代理中获得第一个非unknown IP地址 + * + * @param ip 获得的IP地址 + * @return 第一个非unknown IP地址 + */ + public static String getMultistageReverseProxyIp(String ip) + { + // 多级反向代理检测 + if (ip != null && ip.indexOf(",") > 0) + { + final String[] ips = ip.trim().split(","); + for (String subIp : ips) + { + if (false == isUnknown(subIp)) + { + ip = subIp; + break; + } + } + } + return StringUtils.substring(ip, 0, 255); + } + + /** + * 检测给定字符串是否为未知,多用于检测HTTP请求相关 + * + * @param checkString 被检测的字符串 + * @return 是否未知 + */ + public static boolean isUnknown(String checkString) + { + return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString); + } + + /** + * 是否为IP + */ + public static boolean isIP(String ip) + { + return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP); + } + + /** + * 是否为IP,或 *为间隔的通配符地址 + */ + public static boolean isIpWildCard(String ip) + { + return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP_WILDCARD); + } + + /** + * 检测参数是否在ip通配符里 + */ + public static boolean ipIsInWildCardNoCheck(String ipWildCard, String ip) + { + String[] s1 = ipWildCard.split("\\."); + String[] s2 = ip.split("\\."); + boolean isMatchedSeg = true; + for (int i = 0; i < s1.length && !s1[i].equals("*"); i++) + { + if (!s1[i].equals(s2[i])) + { + isMatchedSeg = false; + break; + } + } + return isMatchedSeg; + } + + /** + * 是否为特定格式如:“10.10.10.1-10.10.10.99”的ip段字符串 + */ + public static boolean isIPSegment(String ipSeg) + { + return StringUtils.isNotBlank(ipSeg) && ipSeg.matches(REGX_IP_SEG); + } + + /** + * 判断ip是否在指定网段中 + */ + public static boolean ipIsInNetNoCheck(String iparea, String ip) + { + int idx = iparea.indexOf('-'); + String[] sips = iparea.substring(0, idx).split("\\."); + String[] sipe = iparea.substring(idx + 1).split("\\."); + String[] sipt = ip.split("\\."); + long ips = 0L, ipe = 0L, ipt = 0L; + for (int i = 0; i < 4; ++i) + { + ips = ips << 8 | Integer.parseInt(sips[i]); + ipe = ipe << 8 | Integer.parseInt(sipe[i]); + ipt = ipt << 8 | Integer.parseInt(sipt[i]); + } + if (ips > ipe) + { + long t = ips; + ips = ipe; + ipe = t; + } + return ips <= ipt && ipt <= ipe; + } + + /** + * 校验ip是否符合过滤串规则 + * + * @param filter 过滤IP列表,支持后缀'*'通配,支持网段如:`10.10.10.1-10.10.10.99` + * @param ip 校验IP地址 + * @return boolean 结果 + */ + public static boolean isMatchedIp(String filter, String ip) + { + if (StringUtils.isEmpty(filter) || StringUtils.isEmpty(ip)) + { + return false; + } + String[] ips = filter.split(";"); + for (String iStr : ips) + { + if (isIP(iStr) && iStr.equals(ip)) + { + return true; + } + else if (isIpWildCard(iStr) && ipIsInWildCardNoCheck(iStr, ip)) + { + return true; + } + else if (isIPSegment(iStr) && ipIsInNetNoCheck(iStr, ip)) + { + return true; + } + } + return false; + } } \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.java index ccab288..c36c9f1 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.java @@ -1,24 +1,24 @@ -package com.ruoyi.common.utils.poi; - -import org.apache.poi.ss.usermodel.Cell; -import org.apache.poi.ss.usermodel.Workbook; - -/** - * Excel数据格式处理适配器 - * - * @author ruoyi - */ -public interface ExcelHandlerAdapter -{ - /** - * 格式化 - * - * @param value 单元格数据值 - * @param args excel注解args参数组 - * @param cell 单元格对象 - * @param wb 工作簿对象 - * - * @return 处理后的值 - */ - Object format(Object value, String[] args, Cell cell, Workbook wb); -} +package com.ruoyi.common.utils.poi; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Workbook; + +/** + * Excel数据格式处理适配器 + * + * @author ruoyi + */ +public interface ExcelHandlerAdapter +{ + /** + * 格式化 + * + * @param value 单元格数据值 + * @param args excel注解args参数组 + * @param cell 单元格对象 + * @param wb 工作簿对象 + * + * @return 处理后的值 + */ + Object format(Object value, String[] args, Cell cell, Workbook wb); +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelSheet.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelSheet.java new file mode 100644 index 0000000..20ca22c --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelSheet.java @@ -0,0 +1,85 @@ +package com.ruoyi.common.utils.poi; + +import java.util.ArrayList; +import java.util.List; + +/** + * 多 Sheet 导出时的数据信息 + * + * 使用示例: + *

+ *   List> sheets = new ArrayList<>();
+ *   sheets.add(new ExcelSheet<>("参数数据", configList, Config.class, "参数信息"));
+ *   sheets.add(new ExcelSheet<>("岗位数据", postList, Post.class, "岗位信息"));
+ *   return ExcelUtil.exportMultiSheet(sheets);
+ * 
+ * + * @author ruoyi + */ +public class ExcelSheet +{ + /** Sheet 名称 */ + private String sheetName; + + /** 导出数据集合 */ + private List list; + + /** 数据对应的实体 Class */ + private Class clazz; + + /** Sheet 顶部大标题(可为空) */ + private String title; + + public ExcelSheet(String sheetName, List list, Class clazz) + { + this(sheetName, list, clazz, ""); + } + + public ExcelSheet(String sheetName, List list, Class 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 getList() + { + return list; + } + + public Class getClazz() + { + return clazz; + } + + public String getTitle() + { + return title; + } + + public void setSheetName(String sheetName) + { + this.sheetName = sheetName; + } + + public void setList(List list) + { + this.list = list; + } + + public void setClazz(Class clazz) + { + this.clazz = clazz; + } + + public void setTitle(String title) + { + this.title = title; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java index 5005bd3..c737270 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java @@ -1,1944 +1,2066 @@ -package com.ruoyi.common.utils.poi; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.math.BigDecimal; -import java.text.DecimalFormat; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Comparator; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.stream.Collectors; -import jakarta.servlet.http.HttpServletResponse; -import org.apache.commons.lang3.ArrayUtils; -import org.apache.commons.lang3.RegExUtils; -import org.apache.commons.lang3.reflect.FieldUtils; -import org.apache.poi.hssf.usermodel.HSSFClientAnchor; -import org.apache.poi.hssf.usermodel.HSSFPicture; -import org.apache.poi.hssf.usermodel.HSSFPictureData; -import org.apache.poi.hssf.usermodel.HSSFShape; -import org.apache.poi.hssf.usermodel.HSSFSheet; -import org.apache.poi.hssf.usermodel.HSSFWorkbook; -import org.apache.poi.ooxml.POIXMLDocumentPart; -import org.apache.poi.ss.usermodel.BorderStyle; -import org.apache.poi.ss.usermodel.Cell; -import org.apache.poi.ss.usermodel.CellStyle; -import org.apache.poi.ss.usermodel.CellType; -import org.apache.poi.ss.usermodel.ClientAnchor; -import org.apache.poi.ss.usermodel.DataFormat; -import org.apache.poi.ss.usermodel.DataValidation; -import org.apache.poi.ss.usermodel.DataValidationConstraint; -import org.apache.poi.ss.usermodel.DataValidationHelper; -import org.apache.poi.ss.usermodel.DateUtil; -import org.apache.poi.ss.usermodel.Drawing; -import org.apache.poi.ss.usermodel.FillPatternType; -import org.apache.poi.ss.usermodel.Font; -import org.apache.poi.ss.usermodel.HorizontalAlignment; -import org.apache.poi.ss.usermodel.IndexedColors; -import org.apache.poi.ss.usermodel.Name; -import org.apache.poi.ss.usermodel.PictureData; -import org.apache.poi.ss.usermodel.Row; -import org.apache.poi.ss.usermodel.Sheet; -import org.apache.poi.ss.usermodel.VerticalAlignment; -import org.apache.poi.ss.usermodel.Workbook; -import org.apache.poi.ss.usermodel.WorkbookFactory; -import org.apache.poi.ss.util.CellRangeAddress; -import org.apache.poi.ss.util.CellRangeAddressList; -import org.apache.poi.util.IOUtils; -import org.apache.poi.xssf.streaming.SXSSFWorkbook; -import org.apache.poi.xssf.usermodel.XSSFClientAnchor; -import org.apache.poi.xssf.usermodel.XSSFDataValidation; -import org.apache.poi.xssf.usermodel.XSSFDrawing; -import org.apache.poi.xssf.usermodel.XSSFPicture; -import org.apache.poi.xssf.usermodel.XSSFShape; -import org.apache.poi.xssf.usermodel.XSSFSheet; -import org.apache.poi.xssf.usermodel.XSSFWorkbook; -import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import com.ruoyi.common.annotation.Excel; -import com.ruoyi.common.annotation.Excel.ColumnType; -import com.ruoyi.common.annotation.Excel.Type; -import com.ruoyi.common.annotation.Excels; -import com.ruoyi.common.config.RuoYiConfig; -import com.ruoyi.common.core.domain.AjaxResult; -import com.ruoyi.common.core.text.Convert; -import com.ruoyi.common.exception.UtilException; -import com.ruoyi.common.utils.DateUtils; -import com.ruoyi.common.utils.DictUtils; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.common.utils.file.FileTypeUtils; -import com.ruoyi.common.utils.file.FileUtils; -import com.ruoyi.common.utils.file.ImageUtils; -import com.ruoyi.common.utils.reflect.ReflectUtils; - -/** - * Excel相关处理 - * - * @author ruoyi - */ -public class ExcelUtil -{ - private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class); - - public static final String SEPARATOR = ","; - - public static final String FORMULA_REGEX_STR = "=|-|\\+|@"; - - public static final String[] FORMULA_STR = { "=", "-", "+", "@" }; - - /** - * 用于dictType属性数据存储,避免重复查缓存 - */ - public Map sysDictMap = new HashMap(); - - /** - * Excel sheet最大行数,默认65536 - */ - public static final int sheetSize = 65536; - - /** - * 工作表名称 - */ - private String sheetName; - - /** - * 导出类型(EXPORT:导出数据;IMPORT:导入模板) - */ - private Type type; - - /** - * 工作薄对象 - */ - private Workbook wb; - - /** - * 工作表对象 - */ - private Sheet sheet; - - /** - * 样式列表 - */ - private Map styles; - - /** - * 导入导出数据列表 - */ - private List list; - - /** - * 注解列表 - */ - private List fields; - - /** - * 当前行号 - */ - private int rownum; - - /** - * 标题 - */ - private String title; - - /** - * 最大高度 - */ - private short maxHeight; - - /** - * 合并后最后行数 - */ - private int subMergedLastRowNum = 0; - - /** - * 合并后开始行数 - */ - private int subMergedFirstRowNum = 1; - - /** - * 对象的子列表方法 - */ - private Map subMethods; - - /** - * 对象的子列表属性 - */ - private Map> subFieldsMap; - - /** - * 统计列表 - */ - private Map statistics = new HashMap(); - - /** - * 实体对象 - */ - public Class clazz; - - /** - * 需要显示列属性 - */ - public String[] includeFields; - - /** - * 需要排除列属性 - */ - public String[] excludeFields; - - public ExcelUtil(Class clazz) - { - this.clazz = clazz; - } - - /** - * 仅在Excel中显示列属性 - * - * @param fields 列属性名 示例[单个"name"/多个"id","name"] - */ - public void showColumn(String... fields) - { - this.includeFields = fields; - } - - /** - * 隐藏Excel中列属性 - * - * @param fields 列属性名 示例[单个"name"/多个"id","name"] - */ - public void hideColumn(String... fields) - { - this.excludeFields = fields; - } - - public void init(List list, String sheetName, String title, Type type) - { - if (list == null) - { - list = new ArrayList(); - } - this.list = list; - this.sheetName = sheetName; - this.type = type; - this.title = title; - createExcelField(); - createWorkbook(); - createTitle(); - createSubHead(); - } - - /** - * 创建excel第一行标题 - */ - public void createTitle() - { - if (StringUtils.isNotEmpty(title)) - { - int titleLastCol = this.fields.size() - 1; - if (isSubList()) - { - for (List currentSubFields : subFieldsMap.values()) - { - titleLastCol = titleLastCol + currentSubFields.size() - 1; - } - } - Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0); - titleRow.setHeightInPoints(30); - Cell titleCell = titleRow.createCell(0); - titleCell.setCellStyle(styles.get("title")); - titleCell.setCellValue(title); - sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), 0, titleLastCol)); - } - } - - /** - * 创建对象的子列表名称 - */ - public void createSubHead() - { - if (isSubList()) - { - Row subRow = sheet.createRow(rownum); - int column = 0; - for (Object[] objects : fields) - { - Field field = (Field) objects[0]; - Excel attr = (Excel) objects[1]; - CellStyle cellStyle = styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())); - if (Collection.class.isAssignableFrom(field.getType())) - { - Cell cell = subRow.createCell(column); - cell.setCellValue(attr.name()); - cell.setCellStyle(cellStyle); - int subFieldSize = subFieldsMap != null ? subFieldsMap.get(field.getName()).size() : 0; - if (subFieldSize > 1) - { - CellRangeAddress cellAddress = new CellRangeAddress(rownum, rownum, column, column + subFieldSize - 1); - sheet.addMergedRegion(cellAddress); - } - column += subFieldSize; - } - else - { - Cell cell = subRow.createCell(column++); - cell.setCellValue(attr.name()); - cell.setCellStyle(cellStyle); - } - } - rownum++; - } - } - - /** - * 对excel表单默认第一个索引名转换成list - * - * @param is 输入流 - * @return 转换后集合 - */ - public List importExcel(InputStream is) - { - return importExcel(is, 0); - } - - /** - * 对excel表单默认第一个索引名转换成list - * - * @param is 输入流 - * @param titleNum 标题占用行数 - * @return 转换后集合 - */ - public List importExcel(InputStream is, int titleNum) - { - List list = null; - try - { - list = importExcel(StringUtils.EMPTY, is, titleNum); - } - catch (Exception e) - { - log.error("导入Excel异常{}", e.getMessage()); - throw new UtilException(e.getMessage()); - } - finally - { - IOUtils.closeQuietly(is); - } - return list; - } - - /** - * 对excel表单指定表格索引名转换成list - * - * @param sheetName 表格索引名 - * @param titleNum 标题占用行数 - * @param is 输入流 - * @return 转换后集合 - */ - public List importExcel(String sheetName, InputStream is, int titleNum) throws Exception - { - this.type = Type.IMPORT; - this.wb = WorkbookFactory.create(is); - List list = new ArrayList(); - // 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheet - Sheet sheet = StringUtils.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0); - if (sheet == null) - { - throw new IOException("文件sheet不存在"); - } - boolean isXSSFWorkbook = !(wb instanceof HSSFWorkbook); - Map> pictures = null; - if (isXSSFWorkbook) - { - pictures = getSheetPictures07((XSSFSheet) sheet, (XSSFWorkbook) wb); - } - else - { - pictures = getSheetPictures03((HSSFSheet) sheet, (HSSFWorkbook) wb); - } - // 获取最后一个非空行的行下标,比如总行数为n,则返回的为n-1 - int rows = sheet.getLastRowNum(); - if (rows > 0) - { - // 定义一个map用于存放excel列的序号和field. - Map cellMap = new HashMap(); - // 获取表头 - Row heard = sheet.getRow(titleNum); - if (heard == null) - { - throw new UtilException("文件标题行为空,请检查Excel文件格式"); - } - for (int i = 0; i < heard.getLastCellNum(); i++) - { - Cell cell = heard.getCell(i); - if (StringUtils.isNotNull(cell)) - { - String value = this.getCellValue(heard, i).toString(); - cellMap.put(value, i); - } - } - // 有数据时才处理 得到类的所有field. - List fields = this.getFields(); - Map fieldsMap = new HashMap(); - for (Object[] objects : fields) - { - Excel attr = (Excel) objects[1]; - Integer column = cellMap.get(attr.name()); - if (column != null) - { - fieldsMap.put(column, objects); - } - } - for (int i = titleNum + 1; i <= rows; i++) - { - // 从第2行开始取数据,默认第一行是表头. - Row row = sheet.getRow(i); - // 判断当前行是否是空行 - if (isRowEmpty(row)) - { - continue; - } - T entity = null; - for (Map.Entry entry : fieldsMap.entrySet()) - { - Object val = this.getCellValue(row, entry.getKey()); - - // 如果不存在实例则新建. - entity = (entity == null ? clazz.getDeclaredConstructor().newInstance() : entity); - // 从map中得到对应列的field. - Field field = (Field) entry.getValue()[0]; - Excel attr = (Excel) entry.getValue()[1]; - // 取得类型,并根据对象类型设置值. - Class fieldType = field.getType(); - if (String.class == fieldType) - { - String s = Convert.toStr(val); - if (s.matches("^\\d+\\.0$")) - { - val = StringUtils.substringBefore(s, ".0"); - } - else - { - String dateFormat = field.getAnnotation(Excel.class).dateFormat(); - if (StringUtils.isNotEmpty(dateFormat)) - { - val = parseDateToStr(dateFormat, val); - } - else - { - val = Convert.toStr(val); - } - } - } - else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) - { - val = Convert.toInt(val); - } - else if ((Long.TYPE == fieldType || Long.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) - { - val = Convert.toLong(val); - } - else if (Double.TYPE == fieldType || Double.class == fieldType) - { - val = Convert.toDouble(val); - } - else if (Float.TYPE == fieldType || Float.class == fieldType) - { - val = Convert.toFloat(val); - } - else if (BigDecimal.class == fieldType) - { - val = Convert.toBigDecimal(val); - } - else if (Date.class == fieldType) - { - if (val instanceof String) - { - val = DateUtils.parseDate(val); - } - else if (val instanceof Double) - { - val = DateUtil.getJavaDate((Double) val); - } - } - else if (Boolean.TYPE == fieldType || Boolean.class == fieldType) - { - val = Convert.toBool(val, false); - } - if (StringUtils.isNotNull(fieldType)) - { - String propertyName = field.getName(); - if (StringUtils.isNotEmpty(attr.targetAttr())) - { - propertyName = field.getName() + "." + attr.targetAttr(); - } - if (StringUtils.isNotEmpty(attr.readConverterExp())) - { - val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator()); - } - else if (StringUtils.isNotEmpty(attr.dictType())) - { - if (!sysDictMap.containsKey(attr.dictType() + val)) - { - String dictValue = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator()); - sysDictMap.put(attr.dictType() + val, dictValue); - } - val = sysDictMap.get(attr.dictType() + val); - } - else if (!attr.handler().equals(ExcelHandlerAdapter.class)) - { - val = dataFormatHandlerAdapter(val, attr, null); - } - else if (ColumnType.IMAGE == attr.cellType() && StringUtils.isNotEmpty(pictures)) - { - StringBuilder propertyString = new StringBuilder(); - List images = pictures.get(row.getRowNum() + "_" + entry.getKey()); - for (PictureData picture : images) - { - byte[] data = picture.getData(); - String fileName = FileUtils.writeImportBytes(data); - propertyString.append(fileName).append(SEPARATOR); - } - val = StringUtils.stripEnd(propertyString.toString(), SEPARATOR); - } - ReflectUtils.invokeSetter(entity, propertyName, val); - } - } - list.add(entity); - } - } - return list; - } - - /** - * 对list数据源将其里面的数据导入到excel表单 - * - * @param list 导出数据集合 - * @param sheetName 工作表的名称 - * @return 结果 - */ - public AjaxResult exportExcel(List list, String sheetName) - { - return exportExcel(list, sheetName, StringUtils.EMPTY); - } - - /** - * 对list数据源将其里面的数据导入到excel表单 - * - * @param list 导出数据集合 - * @param sheetName 工作表的名称 - * @param title 标题 - * @return 结果 - */ - public AjaxResult exportExcel(List list, String sheetName, String title) - { - this.init(list, sheetName, title, Type.EXPORT); - return exportExcel(); - } - - /** - * 对list数据源将其里面的数据导入到excel表单 - * - * @param response 返回数据 - * @param list 导出数据集合 - * @param sheetName 工作表的名称 - * @return 结果 - */ - public void exportExcel(HttpServletResponse response, List list, String sheetName) - { - exportExcel(response, list, sheetName, StringUtils.EMPTY); - } - - /** - * 对list数据源将其里面的数据导入到excel表单 - * - * @param response 返回数据 - * @param list 导出数据集合 - * @param sheetName 工作表的名称 - * @param title 标题 - * @return 结果 - */ - public void exportExcel(HttpServletResponse response, List list, String sheetName, String title) - { - response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); - response.setCharacterEncoding("utf-8"); - this.init(list, sheetName, title, Type.EXPORT); - exportExcel(response); - } - - /** - * 对list数据源将其里面的数据导入到excel表单 - * - * @param sheetName 工作表的名称 - * @return 结果 - */ - public AjaxResult importTemplateExcel(String sheetName) - { - return importTemplateExcel(sheetName, StringUtils.EMPTY); - } - - /** - * 对list数据源将其里面的数据导入到excel表单 - * - * @param sheetName 工作表的名称 - * @param title 标题 - * @return 结果 - */ - public AjaxResult importTemplateExcel(String sheetName, String title) - { - this.init(null, sheetName, title, Type.IMPORT); - return exportExcel(); - } - - /** - * 对list数据源将其里面的数据导入到excel表单 - * - * @param sheetName 工作表的名称 - * @return 结果 - */ - public void importTemplateExcel(HttpServletResponse response, String sheetName) - { - importTemplateExcel(response, sheetName, StringUtils.EMPTY); - } - - /** - * 对list数据源将其里面的数据导入到excel表单 - * - * @param sheetName 工作表的名称 - * @param title 标题 - * @return 结果 - */ - public void importTemplateExcel(HttpServletResponse response, String sheetName, String title) - { - response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); - response.setCharacterEncoding("utf-8"); - this.init(null, sheetName, title, Type.IMPORT); - exportExcel(response); - } - - /** - * 对list数据源将其里面的数据导入到excel表单 - * - * @return 结果 - */ - public void exportExcel(HttpServletResponse response) - { - try - { - writeSheet(); - wb.write(response.getOutputStream()); - } - catch (Exception e) - { - log.error("导出Excel异常{}", e.getMessage()); - } - finally - { - IOUtils.closeQuietly(wb); - } - } - - /** - * 对list数据源将其里面的数据导入到excel表单 - * - * @return 结果 - */ - public AjaxResult exportExcel() - { - OutputStream out = null; - try - { - writeSheet(); - String filename = encodingFilename(sheetName); - out = new FileOutputStream(getAbsoluteFile(filename)); - wb.write(out); - return AjaxResult.success(filename); - } - catch (Exception e) - { - log.error("导出Excel异常{}", e.getMessage()); - throw new UtilException("导出Excel失败,请联系网站管理员!"); - } - finally - { - IOUtils.closeQuietly(wb); - IOUtils.closeQuietly(out); - } - } - - /** - * 创建写入数据到Sheet - */ - public void writeSheet() - { - // 取出一共有多少个sheet. - int sheetNo = Math.max(1, (int) Math.ceil(list.size() * 1.0 / sheetSize)); - for (int index = 0; index < sheetNo; index++) - { - createSheet(sheetNo, index); - - // 产生一行 - Row row = sheet.createRow(rownum); - int column = 0; - // 写入各个字段的列头名称 - for (Object[] os : fields) - { - Field field = (Field) os[0]; - Excel excel = (Excel) os[1]; - if (Collection.class.isAssignableFrom(field.getType())) - { - List currentSubFields = subFieldsMap.get(field.getName()); - for (Field subField : currentSubFields) - { - Excel subExcel = subField.getAnnotation(Excel.class); - this.createHeadCell(subExcel, row, column++); - } - } - else - { - this.createHeadCell(excel, row, column++); - } - } - if (Type.EXPORT.equals(type)) - { - fillExcelData(index); - addStatisticsRow(); - } - } - } - - /** - * 填充excel数据 - * - * @param index 序号 - */ - @SuppressWarnings("unchecked") - public void fillExcelData(int index) - { - int startNo = index * sheetSize; - int endNo = Math.min(startNo + sheetSize, list.size()); - int currentRowNum = rownum + 1; // 从标题行后开始 - - for (int i = startNo; i < endNo; i++) - { - Row row = sheet.createRow(currentRowNum); - T vo = (T) list.get(i); - int column = 0; - int maxSubListSize = getCurrentMaxSubListSize(vo); - for (Object[] os : fields) - { - Field field = (Field) os[0]; - Excel excel = (Excel) os[1]; - if (Collection.class.isAssignableFrom(field.getType())) - { - try - { - Collection subList = (Collection) getTargetValue(vo, field, excel); - List currentSubFields = subFieldsMap.get(field.getName()); - if (subList != null && !subList.isEmpty()) - { - int subIndex = 0; - for (Object subVo : subList) - { - Row subRow = sheet.getRow(currentRowNum + subIndex); - if (subRow == null) - { - subRow = sheet.createRow(currentRowNum + subIndex); - } - - int subColumn = column; - for (Field subField : currentSubFields) - { - Excel subExcel = subField.getAnnotation(Excel.class); - addCell(subExcel, subRow, (T) subVo, subField, subColumn++); - } - subIndex++; - } - } - column += currentSubFields.size(); - } - catch (Exception e) - { - log.error("填充集合数据失败", e); - } - } - else - { - // 创建单元格并设置值 - addCell(excel, row, vo, field, column); - if (maxSubListSize > 1 && excel.needMerge()) - { - sheet.addMergedRegion(new CellRangeAddress(currentRowNum, currentRowNum + maxSubListSize - 1, column, column)); - } - column++; - } - } - currentRowNum += maxSubListSize; - } - } - - /** - * 获取子列表最大数 - */ - private int getCurrentMaxSubListSize(T vo) - { - int maxSubListSize = 1; - for (Object[] os : fields) - { - Field field = (Field) os[0]; - if (Collection.class.isAssignableFrom(field.getType())) - { - try - { - Collection subList = (Collection) getTargetValue(vo, field, (Excel) os[1]); - if (subList != null && !subList.isEmpty()) - { - maxSubListSize = Math.max(maxSubListSize, subList.size()); - } - } - catch (Exception e) - { - log.error("获取集合大小失败", e); - } - } - } - return maxSubListSize; - } - - /** - * 创建表格样式 - * - * @param wb 工作薄对象 - * @return 样式列表 - */ - private Map createStyles(Workbook wb) - { - // 写入各条记录,每条记录对应excel表中的一行 - Map styles = new HashMap(); - CellStyle style = wb.createCellStyle(); - style.setAlignment(HorizontalAlignment.CENTER); - style.setVerticalAlignment(VerticalAlignment.CENTER); - Font titleFont = wb.createFont(); - titleFont.setFontName("Arial"); - titleFont.setFontHeightInPoints((short) 16); - titleFont.setBold(true); - style.setFont(titleFont); - DataFormat dataFormat = wb.createDataFormat(); - style.setDataFormat(dataFormat.getFormat("@")); - styles.put("title", style); - - style = wb.createCellStyle(); - style.setAlignment(HorizontalAlignment.CENTER); - style.setVerticalAlignment(VerticalAlignment.CENTER); - style.setBorderRight(BorderStyle.THIN); - style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); - style.setBorderLeft(BorderStyle.THIN); - style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); - style.setBorderTop(BorderStyle.THIN); - style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); - style.setBorderBottom(BorderStyle.THIN); - style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); - Font dataFont = wb.createFont(); - dataFont.setFontName("Arial"); - dataFont.setFontHeightInPoints((short) 10); - style.setFont(dataFont); - styles.put("data", style); - - style = wb.createCellStyle(); - style.setAlignment(HorizontalAlignment.CENTER); - style.setVerticalAlignment(VerticalAlignment.CENTER); - style.setDataFormat(dataFormat.getFormat("######0.00")); - Font totalFont = wb.createFont(); - totalFont.setFontName("Arial"); - totalFont.setFontHeightInPoints((short) 10); - style.setFont(totalFont); - styles.put("total", style); - - styles.putAll(annotationHeaderStyles(wb, styles)); - - styles.putAll(annotationDataStyles(wb)); - - return styles; - } - - /** - * 根据Excel注解创建表格头样式 - * - * @param wb 工作薄对象 - * @return 自定义样式列表 - */ - private Map annotationHeaderStyles(Workbook wb, Map styles) - { - Map headerStyles = new HashMap(); - for (Object[] os : fields) - { - Excel excel = (Excel) os[1]; - String key = StringUtils.format("header_{}_{}", excel.headerColor(), excel.headerBackgroundColor()); - if (!headerStyles.containsKey(key)) - { - CellStyle style = wb.createCellStyle(); - style.cloneStyleFrom(styles.get("data")); - style.setAlignment(HorizontalAlignment.CENTER); - style.setVerticalAlignment(VerticalAlignment.CENTER); - style.setFillForegroundColor(excel.headerBackgroundColor().index); - style.setFillPattern(FillPatternType.SOLID_FOREGROUND); - Font headerFont = wb.createFont(); - headerFont.setFontName("Arial"); - headerFont.setFontHeightInPoints((short) 10); - headerFont.setBold(true); - headerFont.setColor(excel.headerColor().index); - style.setFont(headerFont); - // 设置表格头单元格文本形式 - DataFormat dataFormat = wb.createDataFormat(); - style.setDataFormat(dataFormat.getFormat("@")); - headerStyles.put(key, style); - } - } - return headerStyles; - } - - /** - * 根据Excel注解创建表格列样式 - * - * @param wb 工作薄对象 - * @return 自定义样式列表 - */ - private Map annotationDataStyles(Workbook wb) - { - Map styles = new HashMap(); - for (Object[] os : fields) - { - Field field = (Field) os[0]; - Excel excel = (Excel) os[1]; - if (Collection.class.isAssignableFrom(field.getType())) - { - ParameterizedType pt = (ParameterizedType) field.getGenericType(); - Class subClass = (Class) pt.getActualTypeArguments()[0]; - List subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class); - for (Field subField : subFields) - { - Excel subExcel = subField.getAnnotation(Excel.class); - annotationDataStyles(styles, subField, subExcel); - } - } - else - { - annotationDataStyles(styles, field, excel); - } - } - return styles; - } - - /** - * 根据Excel注解创建表格列样式 - * - * @param styles 自定义样式列表 - * @param field 属性列信息 - * @param excel 注解信息 - */ - public void annotationDataStyles(Map styles, Field field, Excel excel) - { - String key = StringUtils.format("data_{}_{}_{}_{}_{}", excel.align(), excel.color(), excel.backgroundColor(), excel.cellType(), excel.wrapText()); - if (!styles.containsKey(key)) - { - CellStyle style = wb.createCellStyle(); - style.setAlignment(excel.align()); - style.setVerticalAlignment(VerticalAlignment.CENTER); - style.setBorderRight(BorderStyle.THIN); - style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); - style.setBorderLeft(BorderStyle.THIN); - style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); - style.setBorderTop(BorderStyle.THIN); - style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); - style.setBorderBottom(BorderStyle.THIN); - style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); - style.setFillPattern(FillPatternType.SOLID_FOREGROUND); - style.setFillForegroundColor(excel.backgroundColor().getIndex()); - style.setWrapText(excel.wrapText()); - Font dataFont = wb.createFont(); - dataFont.setFontName("Arial"); - dataFont.setFontHeightInPoints((short) 10); - dataFont.setColor(excel.color().index); - style.setFont(dataFont); - if (ColumnType.TEXT == excel.cellType()) - { - DataFormat dataFormat = wb.createDataFormat(); - style.setDataFormat(dataFormat.getFormat("@")); - } - styles.put(key, style); - } - } - - /** - * 创建单元格 - */ - public Cell createHeadCell(Excel attr, Row row, int column) - { - // 创建列 - Cell cell = row.createCell(column); - // 写入列信息 - cell.setCellValue(attr.name()); - setDataValidation(attr, row, column); - cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); - if (isSubList()) - { - // 填充默认样式,防止合并单元格样式失效 - sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType(), attr.wrapText()))); - if (attr.needMerge()) - { - sheet.addMergedRegion(new CellRangeAddress(rownum - 1, rownum, column, column)); - } - } - return cell; - } - - /** - * 设置单元格信息 - * - * @param value 单元格值 - * @param attr 注解相关 - * @param cell 单元格信息 - */ - public void setCellVo(Object value, Excel attr, Cell cell) - { - if (ColumnType.STRING == attr.cellType() || ColumnType.TEXT == attr.cellType()) - { - String cellValue = Convert.toStr(value); - // 对于任何以表达式触发字符 =-+@开头的单元格,直接使用tab字符作为前缀,防止CSV注入。 - if (StringUtils.startsWithAny(cellValue, FORMULA_STR)) - { - cellValue = RegExUtils.replaceFirst(cellValue, FORMULA_REGEX_STR, "\t$0"); - } - if (value instanceof Collection && StringUtils.equals("[]", cellValue)) - { - cellValue = StringUtils.EMPTY; - } - cell.setCellValue(StringUtils.isNull(cellValue) ? attr.defaultValue() : cellValue + attr.suffix()); - } - else if (ColumnType.NUMERIC == attr.cellType()) - { - if (StringUtils.isNotNull(value)) - { - cell.setCellValue(StringUtils.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value)); - } - } - else if (ColumnType.IMAGE == attr.cellType()) - { - ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1), cell.getRow().getRowNum() + 1); - String propertyValue = Convert.toStr(value); - if (StringUtils.isNotEmpty(propertyValue)) - { - List imagePaths = StringUtils.str2List(propertyValue, SEPARATOR); - for (String imagePath : imagePaths) - { - byte[] data = ImageUtils.getImage(imagePath); - getDrawingPatriarch(cell.getSheet()).createPicture(anchor, cell.getSheet().getWorkbook().addPicture(data, getImageType(data))); - } - } - } - } - - /** - * 获取画布 - */ - public static Drawing getDrawingPatriarch(Sheet sheet) - { - if (sheet.getDrawingPatriarch() == null) - { - sheet.createDrawingPatriarch(); - } - return sheet.getDrawingPatriarch(); - } - - /** - * 获取图片类型,设置图片插入类型 - */ - public int getImageType(byte[] value) - { - String type = FileTypeUtils.getFileExtendName(value); - if ("JPG".equalsIgnoreCase(type)) - { - return Workbook.PICTURE_TYPE_JPEG; - } - else if ("PNG".equalsIgnoreCase(type)) - { - return Workbook.PICTURE_TYPE_PNG; - } - return Workbook.PICTURE_TYPE_JPEG; - } - - /** - * 创建表格样式 - */ - public void setDataValidation(Excel attr, Row row, int column) - { - if (attr.name().indexOf("注:") >= 0) - { - sheet.setColumnWidth(column, 6000); - } - else - { - // 设置列宽 - sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256)); - } - if (StringUtils.isNotEmpty(attr.prompt()) || attr.combo().length > 0 || attr.comboReadDict()) - { - String[] comboArray = attr.combo(); - if (attr.comboReadDict()) - { - if (!sysDictMap.containsKey("combo_" + attr.dictType())) - { - String labels = DictUtils.getDictLabels(attr.dictType()); - sysDictMap.put("combo_" + attr.dictType(), labels); - } - String val = sysDictMap.get("combo_" + attr.dictType()); - comboArray = StringUtils.split(val, DictUtils.SEPARATOR); - } - if (comboArray.length > 15 || StringUtils.join(comboArray).length() > 255) - { - // 如果下拉数大于15或字符串长度大于255,则使用一个新sheet存储,避免生成的模板下拉值获取不到 - setXSSFValidationWithHidden(sheet, comboArray, attr.prompt(), 1, 100, column, column); - } - else - { - // 提示信息或只能选择不能输入的列内容. - setPromptOrValidation(sheet, comboArray, attr.prompt(), 1, 100, column, column); - } - } - } - - /** - * 添加单元格 - */ - @SuppressWarnings("deprecation") - public Cell addCell(Excel attr, Row row, T vo, Field field, int column) - { - Cell cell = null; - try - { - // 设置行高 - row.setHeight(maxHeight); - // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列. - if (attr.isExport()) - { - // 创建cell - cell = row.createCell(column); - if (isSubListValue(vo) && getListCellValue(vo) > 1 && attr.needMerge()) - { - if (subMergedLastRowNum >= subMergedFirstRowNum) - { - sheet.addMergedRegion(new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column)); - } - } - cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType(), attr.wrapText()))); - - // 用于读取对象中的属性 - Object value = getTargetValue(vo, field, attr); - String dateFormat = attr.dateFormat(); - String readConverterExp = attr.readConverterExp(); - String separator = attr.separator(); - String dictType = attr.dictType(); - if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value)) - { - cell.setCellStyle(createCellStyle(cell.getCellStyle(), dateFormat)); - cell.setCellValue(parseDateToStr(dateFormat, value)); - } - else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value)) - { - cell.setCellValue(convertByExp(Convert.toStr(value), readConverterExp, separator)); - } - else if (StringUtils.isNotEmpty(dictType) && StringUtils.isNotNull(value)) - { - if (!sysDictMap.containsKey(dictType + value)) - { - String lable = convertDictByExp(Convert.toStr(value), dictType, separator); - sysDictMap.put(dictType + value, lable); - } - cell.setCellValue(sysDictMap.get(dictType + value)); - } - else if (value instanceof BigDecimal && -1 != attr.scale()) - { - cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).doubleValue()); - } - else if (!attr.handler().equals(ExcelHandlerAdapter.class)) - { - cell.setCellValue(dataFormatHandlerAdapter(value, attr, cell)); - } - else - { - // 设置列类型 - setCellVo(value, attr, cell); - } - addStatisticsData(column, Convert.toStr(value), attr); - } - } - catch (Exception e) - { - log.error("导出Excel失败{}", e); - } - return cell; - } - - /** - * 使用自定义格式,同时避免样式污染 - * - * @param cellStyle 从此样式复制 - * @param format 格式匹配的字符串 - * @return 格式化后CellStyle对象 - */ - private CellStyle createCellStyle(CellStyle cellStyle, String format) - { - CellStyle style = wb.createCellStyle(); - style.cloneStyleFrom(cellStyle); - style.setDataFormat(wb.getCreationHelper().createDataFormat().getFormat(format)); - return style; - } - - /** - * 设置 POI XSSFSheet 单元格提示或选择框 - * - * @param sheet 表单 - * @param textlist 下拉框显示的内容 - * @param promptContent 提示内容 - * @param firstRow 开始行 - * @param endRow 结束行 - * @param firstCol 开始列 - * @param endCol 结束列 - */ - public void setPromptOrValidation(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, - int firstCol, int endCol) - { - DataValidationHelper helper = sheet.getDataValidationHelper(); - DataValidationConstraint constraint = textlist.length > 0 ? helper.createExplicitListConstraint(textlist) : helper.createCustomConstraint("DD1"); - CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); - DataValidation dataValidation = helper.createValidation(constraint, regions); - if (StringUtils.isNotEmpty(promptContent)) - { - // 如果设置了提示信息则鼠标放上去提示 - dataValidation.createPromptBox("", promptContent); - dataValidation.setShowPromptBox(true); - } - // 处理Excel兼容性问题 - if (dataValidation instanceof XSSFDataValidation) - { - dataValidation.setSuppressDropDownArrow(true); - dataValidation.setShowErrorBox(true); - } - else - { - dataValidation.setSuppressDropDownArrow(false); - } - sheet.addValidationData(dataValidation); - } - - /** - * 设置某些列的值只能输入预制的数据,显示下拉框(兼容超出一定数量的下拉框). - * - * @param sheet 要设置的sheet. - * @param textlist 下拉框显示的内容 - * @param promptContent 提示内容 - * @param firstRow 开始行 - * @param endRow 结束行 - * @param firstCol 开始列 - * @param endCol 结束列 - */ - public void setXSSFValidationWithHidden(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, int firstCol, int endCol) - { - String hideSheetName = "combo_" + firstCol + "_" + endCol; - Sheet hideSheet = null; - String hideSheetDataName = hideSheetName + "_data"; - Name name = wb.getName(hideSheetDataName); - if (name != null) - { - // 名称已存在,尝试从名称的引用中找到sheet名称 - String refersToFormula = name.getRefersToFormula(); - if (StringUtils.isNotEmpty(refersToFormula) && refersToFormula.contains("!")) - { - String sheetNameFromFormula = refersToFormula.substring(0, refersToFormula.indexOf("!")); - hideSheet = wb.getSheet(sheetNameFromFormula); - } - } - - if (hideSheet == null) - { - hideSheet = wb.createSheet(hideSheetName); // 用于存储 下拉菜单数据 - for (int i = 0; i < textlist.length; i++) - { - hideSheet.createRow(i).createCell(0).setCellValue(textlist[i]); - } - // 创建名称,可被其他单元格引用 - name = wb.createName(); - name.setNameName(hideSheetDataName); - name.setRefersToFormula(hideSheetName + "!$A$1:$A$" + textlist.length); - } - - DataValidationHelper helper = sheet.getDataValidationHelper(); - // 加载下拉列表内容 - DataValidationConstraint constraint = helper.createFormulaListConstraint(hideSheetDataName); - // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列 - CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); - // 数据有效性对象 - DataValidation dataValidation = helper.createValidation(constraint, regions); - if (StringUtils.isNotEmpty(promptContent)) - { - // 如果设置了提示信息则鼠标放上去提示 - dataValidation.createPromptBox("", promptContent); - dataValidation.setShowPromptBox(true); - } - // 处理Excel兼容性问题 - if (dataValidation instanceof XSSFDataValidation) - { - dataValidation.setSuppressDropDownArrow(true); - dataValidation.setShowErrorBox(true); - } - else - { - dataValidation.setSuppressDropDownArrow(false); - } - - sheet.addValidationData(dataValidation); - // 设置hiddenSheet隐藏 - wb.setSheetHidden(wb.getSheetIndex(hideSheet), true); - } - - /** - * 解析导出值 0=男,1=女,2=未知 - * - * @param propertyValue 参数值 - * @param converterExp 翻译注解 - * @param separator 分隔符 - * @return 解析后值 - */ - public static String convertByExp(String propertyValue, String converterExp, String separator) - { - StringBuilder propertyString = new StringBuilder(); - String[] convertSource = converterExp.split(SEPARATOR); - for (String item : convertSource) - { - String[] itemArray = item.split("="); - if (StringUtils.containsAny(propertyValue, separator)) - { - for (String value : propertyValue.split(separator)) - { - if (itemArray[0].equals(value)) - { - propertyString.append(itemArray[1] + separator); - break; - } - } - } - else - { - if (itemArray[0].equals(propertyValue)) - { - return itemArray[1]; - } - } - } - return StringUtils.stripEnd(propertyString.toString(), separator); - } - - /** - * 反向解析值 男=0,女=1,未知=2 - * - * @param propertyValue 参数值 - * @param converterExp 翻译注解 - * @param separator 分隔符 - * @return 解析后值 - */ - public static String reverseByExp(String propertyValue, String converterExp, String separator) - { - StringBuilder propertyString = new StringBuilder(); - String[] convertSource = converterExp.split(SEPARATOR); - for (String item : convertSource) - { - String[] itemArray = item.split("="); - if (StringUtils.containsAny(propertyValue, separator)) - { - for (String value : propertyValue.split(separator)) - { - if (itemArray[1].equals(value)) - { - propertyString.append(itemArray[0] + separator); - break; - } - } - } - else - { - if (itemArray[1].equals(propertyValue)) - { - return itemArray[0]; - } - } - } - return StringUtils.stripEnd(propertyString.toString(), separator); - } - - /** - * 解析字典值 - * - * @param dictValue 字典值 - * @param dictType 字典类型 - * @param separator 分隔符 - * @return 字典标签 - */ - public static String convertDictByExp(String dictValue, String dictType, String separator) - { - return DictUtils.getDictLabel(dictType, dictValue, separator); - } - - /** - * 反向解析值字典值 - * - * @param dictLabel 字典标签 - * @param dictType 字典类型 - * @param separator 分隔符 - * @return 字典值 - */ - public static String reverseDictByExp(String dictLabel, String dictType, String separator) - { - return DictUtils.getDictValue(dictType, dictLabel, separator); - } - - /** - * 数据处理器 - * - * @param value 数据值 - * @param excel 数据注解 - * @return - */ - public String dataFormatHandlerAdapter(Object value, Excel excel, Cell cell) - { - try - { - Object instance = excel.handler().getDeclaredConstructor().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); - } - catch (Exception e) - { - log.error("不能格式化数据 " + excel.handler(), e.getMessage()); - } - return Convert.toStr(value); - } - - /** - * 合计统计信息 - */ - private void addStatisticsData(Integer index, String text, Excel entity) - { - if (entity != null && entity.isStatistics()) - { - Double temp = 0D; - if (!statistics.containsKey(index)) - { - statistics.put(index, temp); - } - try - { - temp = Double.valueOf(text); - } - catch (NumberFormatException e) - { - } - statistics.put(index, statistics.get(index) + temp); - } - } - - /** - * 创建统计行 - */ - public void addStatisticsRow() - { - if (statistics.size() > 0) - { - Row row = sheet.createRow(sheet.getLastRowNum() + 1); - Set keys = statistics.keySet(); - Cell cell = row.createCell(0); - cell.setCellStyle(styles.get("total")); - cell.setCellValue("合计"); - - for (Integer key : keys) - { - cell = row.createCell(key); - cell.setCellStyle(styles.get("total")); - cell.setCellValue(statistics.get(key)); - } - statistics.clear(); - } - } - - /** - * 编码文件名 - */ - public String encodingFilename(String filename) - { - return UUID.randomUUID() + "_" + filename + ".xlsx"; - } - - /** - * 获取下载路径 - * - * @param filename 文件名称 - */ - public String getAbsoluteFile(String filename) - { - String downloadPath = RuoYiConfig.getDownloadPath() + filename; - File desc = new File(downloadPath); - if (!desc.getParentFile().exists()) - { - desc.getParentFile().mkdirs(); - } - return downloadPath; - } - - /** - * 获取bean中的属性值 - * - * @param vo 实体对象 - * @param field 字段 - * @param excel 注解 - * @return 最终的属性值 - * @throws Exception - */ - private Object getTargetValue(T vo, Field field, Excel excel) throws Exception - { - field.setAccessible(true); - Object o = field.get(vo); - if (StringUtils.isNotEmpty(excel.targetAttr())) - { - String target = excel.targetAttr(); - if (target.contains(".")) - { - String[] targets = target.split("[.]"); - for (String name : targets) - { - o = getValue(o, name); - } - } - else - { - o = getValue(o, target); - } - } - return o; - } - - /** - * 以类的属性的get方法方法形式获取值 - * - * @param o - * @param name - * @return value - * @throws Exception - */ - private Object getValue(Object o, String name) throws Exception - { - if (StringUtils.isNotNull(o) && StringUtils.isNotEmpty(name)) - { - Class clazz = o.getClass(); - Field field = clazz.getDeclaredField(name); - field.setAccessible(true); - o = field.get(o); - } - return o; - } - - /** - * 得到所有定义字段 - */ - private void createExcelField() - { - this.fields = getFields(); - this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList()); - this.maxHeight = getRowHeight(); - } - - /** - * 获取字段注解信息 - */ - public List getFields() - { - List fields = new ArrayList(); - List tempFields = new ArrayList<>(); - subFieldsMap = new HashMap<>(); - subMethods = new HashMap<>(); - tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields())); - tempFields.addAll(Arrays.asList(clazz.getDeclaredFields())); - if (StringUtils.isNotEmpty(includeFields)) - { - for (Field field : tempFields) - { - if (ArrayUtils.contains(this.includeFields, field.getName()) || field.isAnnotationPresent(Excels.class)) - { - addField(fields, field); - } - } - } - else if (StringUtils.isNotEmpty(excludeFields)) - { - for (Field field : tempFields) - { - if (!ArrayUtils.contains(this.excludeFields, field.getName())) - { - addField(fields, field); - } - } - } - else - { - for (Field field : tempFields) - { - addField(fields, field); - } - } - return fields; - } - - /** - * 添加字段信息 - */ - public void addField(List fields, Field field) - { - // 单注解 - if (field.isAnnotationPresent(Excel.class)) - { - Excel attr = field.getAnnotation(Excel.class); - if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) - { - fields.add(new Object[] { field, attr }); - } - if (Collection.class.isAssignableFrom(field.getType())) - { - String fieldName = field.getName(); - subMethods.put(fieldName, getSubMethod(fieldName, clazz)); - ParameterizedType pt = (ParameterizedType) field.getGenericType(); - Class subClass = (Class) pt.getActualTypeArguments()[0]; - subFieldsMap.put(fieldName, FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class)); - } - } - - // 多注解 - if (field.isAnnotationPresent(Excels.class)) - { - Excels attrs = field.getAnnotation(Excels.class); - Excel[] excels = attrs.value(); - for (Excel attr : excels) - { - if (StringUtils.isNotEmpty(includeFields)) - { - if (ArrayUtils.contains(this.includeFields, field.getName() + "." + attr.targetAttr()) - && (attr != null && (attr.type() == Type.ALL || attr.type() == type))) - { - fields.add(new Object[] { field, attr }); - } - } - else - { - if (!ArrayUtils.contains(this.excludeFields, field.getName() + "." + attr.targetAttr()) - && (attr != null && (attr.type() == Type.ALL || attr.type() == type))) - { - fields.add(new Object[] { field, attr }); - } - } - } - } - } - - /** - * 根据注解获取最大行高 - */ - public short getRowHeight() - { - double maxHeight = 0; - for (Object[] os : this.fields) - { - Excel excel = (Excel) os[1]; - maxHeight = Math.max(maxHeight, excel.height()); - } - return (short) (maxHeight * 20); - } - - /** - * 创建一个工作簿 - */ - public void createWorkbook() - { - this.wb = new SXSSFWorkbook(500); - this.sheet = wb.createSheet(); - wb.setSheetName(0, sheetName); - this.styles = createStyles(wb); - } - - /** - * 创建工作表 - * - * @param sheetNo sheet数量 - * @param index 序号 - */ - public void createSheet(int sheetNo, int index) - { - // 设置工作表的名称. - if (sheetNo > 1 && index > 0) - { - this.sheet = wb.createSheet(); - this.createTitle(); - int actualIndex = wb.getSheetIndex(this.sheet); - wb.setSheetName(actualIndex, sheetName + index); - } - } - - /** - * 获取单元格值 - * - * @param row 获取的行 - * @param column 获取单元格列号 - * @return 单元格值 - */ - public Object getCellValue(Row row, int column) - { - if (row == null) - { - return row; - } - Object val = ""; - try - { - Cell cell = row.getCell(column); - if (StringUtils.isNotNull(cell)) - { - if (cell.getCellType() == CellType.NUMERIC || cell.getCellType() == CellType.FORMULA) - { - val = cell.getNumericCellValue(); - if (DateUtil.isCellDateFormatted(cell)) - { - val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换 - } - else - { - if ((Double) val % 1 != 0) - { - val = new BigDecimal(val.toString()); - } - else - { - val = new DecimalFormat("0").format(val); - } - } - } - else if (cell.getCellType() == CellType.STRING) - { - val = cell.getStringCellValue(); - } - else if (cell.getCellType() == CellType.BOOLEAN) - { - val = cell.getBooleanCellValue(); - } - else if (cell.getCellType() == CellType.ERROR) - { - val = cell.getErrorCellValue(); - } - - } - } - catch (Exception e) - { - return val; - } - return val; - } - - /** - * 判断是否是空行 - * - * @param row 判断的行 - * @return - */ - private boolean isRowEmpty(Row row) - { - if (row == null) - { - return true; - } - for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) - { - Cell cell = row.getCell(i); - if (cell != null && cell.getCellType() != CellType.BLANK) - { - return false; - } - } - return true; - } - - /** - * 获取Excel2003图片 - * - * @param sheet 当前sheet对象 - * @param workbook 工作簿对象 - * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData - */ - public static Map> getSheetPictures03(HSSFSheet sheet, HSSFWorkbook workbook) - { - Map> sheetIndexPicMap = new HashMap<>(); - List pictures = workbook.getAllPictures(); - if (!pictures.isEmpty() && sheet.getDrawingPatriarch() != null) - { - for (HSSFShape shape : sheet.getDrawingPatriarch().getChildren()) - { - if (shape instanceof HSSFPicture) - { - HSSFPicture pic = (HSSFPicture) shape; - HSSFClientAnchor anchor = (HSSFClientAnchor) pic.getAnchor(); - String picIndex = anchor.getRow1() + "_" + anchor.getCol1(); - sheetIndexPicMap.computeIfAbsent(picIndex, k -> new ArrayList<>()).add(pic.getPictureData()); - } - } - } - return sheetIndexPicMap; - } - - /** - * 获取Excel2007图片 - * - * @param sheet 当前sheet对象 - * @param workbook 工作簿对象 - * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData - */ - public static Map> getSheetPictures07(XSSFSheet sheet, XSSFWorkbook workbook) - { - Map> sheetIndexPicMap = new HashMap<>(); - for (POIXMLDocumentPart dr : sheet.getRelations()) - { - if (dr instanceof XSSFDrawing) - { - XSSFDrawing drawing = (XSSFDrawing) dr; - for (XSSFShape shape : drawing.getShapes()) - { - if (shape instanceof XSSFPicture) - { - XSSFPicture pic = (XSSFPicture) shape; - XSSFClientAnchor anchor = pic.getPreferredSize(); - CTMarker ctMarker = anchor.getFrom(); - String picIndex = ctMarker.getRow() + "_" + ctMarker.getCol(); - sheetIndexPicMap.computeIfAbsent(picIndex, k -> new ArrayList<>()).add(pic.getPictureData()); - } - } - } - } - return sheetIndexPicMap; - } - - /** - * 格式化不同类型的日期对象 - * - * @param dateFormat 日期格式 - * @param val 被格式化的日期对象 - * @return 格式化后的日期字符 - */ - public String parseDateToStr(String dateFormat, Object val) - { - if (val == null) - { - return ""; - } - String str; - if (val instanceof Date) - { - str = DateUtils.parseDateToStr(dateFormat, (Date) val); - } - else if (val instanceof LocalDateTime) - { - str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDateTime) val)); - } - else if (val instanceof LocalDate) - { - str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDate) val)); - } - else - { - str = val.toString(); - } - return str; - } - - /** - * 是否有对象的子列表 - */ - public boolean isSubList() - { - return !StringUtils.isEmpty(subFieldsMap); - } - - /** - * 是否有对象的子列表,集合不为空 - */ - public boolean isSubListValue(T vo) - { - return !StringUtils.isEmpty(subFieldsMap) && getListCellValue(vo) > 0; - } - - /** - * 获取集合的值 - */ - public int getListCellValue(Object obj) - { - Collection value; - int max = 0; - try - { - for (String s : subMethods.keySet()) - { - value = (Collection) subMethods.get(s).invoke(obj); - if (value.size() > max) - { - max = value.size(); - } - } - } - catch (Exception e) - { - return 0; - } - return max; - } - - /** - * 获取对象的子列表方法 - * - * @param name 名称 - * @param pojoClass 类对象 - * @return 子列表方法 - */ - public Method getSubMethod(String name, Class pojoClass) - { - StringBuffer getMethodName = new StringBuffer("get"); - getMethodName.append(name.substring(0, 1).toUpperCase()); - getMethodName.append(name.substring(1)); - Method method = null; - try - { - method = pojoClass.getMethod(getMethodName.toString(), new Class[] {}); - } - catch (Exception e) - { - log.error("获取对象异常{}", e.getMessage()); - } - return method; - } -} +package com.ruoyi.common.utils.poi; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.math.BigDecimal; +import java.text.DecimalFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; +import javax.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.RegExUtils; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.apache.poi.hssf.usermodel.HSSFClientAnchor; +import org.apache.poi.hssf.usermodel.HSSFPicture; +import org.apache.poi.hssf.usermodel.HSSFPictureData; +import org.apache.poi.hssf.usermodel.HSSFShape; +import org.apache.poi.hssf.usermodel.HSSFSheet; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ooxml.POIXMLDocumentPart; +import org.apache.poi.ss.usermodel.BorderStyle; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.ClientAnchor; +import org.apache.poi.ss.usermodel.DataFormat; +import org.apache.poi.ss.usermodel.DataValidation; +import org.apache.poi.ss.usermodel.DataValidationConstraint; +import org.apache.poi.ss.usermodel.DataValidationHelper; +import org.apache.poi.ss.usermodel.DateUtil; +import org.apache.poi.ss.usermodel.Drawing; +import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.ss.usermodel.Name; +import org.apache.poi.ss.usermodel.PictureData; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.VerticalAlignment; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.usermodel.WorkbookFactory; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.CellRangeAddressList; +import org.apache.poi.util.IOUtils; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFDataValidation; +import org.apache.poi.xssf.usermodel.XSSFDrawing; +import org.apache.poi.xssf.usermodel.XSSFPicture; +import org.apache.poi.xssf.usermodel.XSSFShape; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.annotation.Excel.Type; +import com.ruoyi.common.annotation.Excels; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.exception.UtilException; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.DictUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.file.FileTypeUtils; +import com.ruoyi.common.utils.file.FileUtils; +import com.ruoyi.common.utils.file.ImageUtils; +import com.ruoyi.common.utils.reflect.ReflectUtils; + +/** + * Excel相关处理 + * + * @author ruoyi + */ +public class ExcelUtil +{ + private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class); + + public static final String SEPARATOR = ","; + + public static final String FORMULA_REGEX_STR = "=|-|\\+|@"; + + public static final String[] FORMULA_STR = { "=", "-", "+", "@" }; + + /** + * 用于dictType属性数据存储,避免重复查缓存 + */ + public Map sysDictMap = new HashMap(); + + /** + * 单元格样式缓存 + */ + private Map cellStyleCache = new HashMap(); + + /** + * Excel sheet最大行数,默认65536 + */ + public static final int sheetSize = 65536; + + /** + * 工作表名称 + */ + private String sheetName; + + /** + * 导出类型(EXPORT:导出数据;IMPORT:导入模板) + */ + private Type type; + + /** + * 工作薄对象 + */ + private Workbook wb; + + /** + * 工作表对象 + */ + private Sheet sheet; + + /** + * 样式列表 + */ + private Map styles; + + /** + * 导入导出数据列表 + */ + private List list; + + /** + * 注解列表 + */ + private List fields; + + /** + * 当前行号 + */ + private int rownum; + + /** + * 标题 + */ + private String title; + + /** + * 最大高度 + */ + private short maxHeight; + + /** + * 合并后最后行数 + */ + private int subMergedLastRowNum = 0; + + /** + * 合并后开始行数 + */ + private int subMergedFirstRowNum = 1; + + /** + * 对象的子列表方法 + */ + private Map subMethods; + + /** + * 对象的子列表属性 + */ + private Map> subFieldsMap; + + /** + * 统计列表 + */ + private Map statistics = new HashMap(); + + /** + * 实体对象 + */ + public Class clazz; + + /** + * 需要显示列属性 + */ + public String[] includeFields; + + /** + * 需要排除列属性 + */ + public String[] excludeFields; + + public ExcelUtil(Class clazz) + { + this.clazz = clazz; + } + + /** + * 仅在Excel中显示列属性 + * + * @param fields 列属性名 示例[单个"name"/多个"id","name"] + */ + public void showColumn(String... fields) + { + this.includeFields = fields; + } + + /** + * 隐藏Excel中列属性 + * + * @param fields 列属性名 示例[单个"name"/多个"id","name"] + */ + public void hideColumn(String... fields) + { + this.excludeFields = fields; + } + + public void init(List list, String sheetName, String title, Type type) + { + if (list == null) + { + list = new ArrayList(); + } + this.list = list; + this.sheetName = sheetName; + this.type = type; + this.title = title; + createExcelField(); + createWorkbook(); + createTitle(); + createSubHead(); + } + + /** + * 创建excel第一行标题 + */ + public void createTitle() + { + if (StringUtils.isNotEmpty(title)) + { + int titleLastCol = this.fields.size() - 1; + if (isSubList()) + { + for (List currentSubFields : subFieldsMap.values()) + { + titleLastCol = titleLastCol + currentSubFields.size() - 1; + } + } + Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0); + titleRow.setHeightInPoints(30); + Cell titleCell = titleRow.createCell(0); + titleCell.setCellStyle(styles.get("title")); + titleCell.setCellValue(title); + sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), 0, titleLastCol)); + } + } + + /** + * 创建对象的子列表名称 + */ + public void createSubHead() + { + if (isSubList()) + { + Row subRow = sheet.createRow(rownum); + int column = 0; + for (Object[] objects : fields) + { + Field field = (Field) objects[0]; + Excel attr = (Excel) objects[1]; + CellStyle cellStyle = styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())); + if (Collection.class.isAssignableFrom(field.getType())) + { + Cell cell = subRow.createCell(column); + cell.setCellValue(attr.name()); + cell.setCellStyle(cellStyle); + int subFieldSize = subFieldsMap != null ? subFieldsMap.get(field.getName()).size() : 0; + if (subFieldSize > 1) + { + CellRangeAddress cellAddress = new CellRangeAddress(rownum, rownum, column, column + subFieldSize - 1); + sheet.addMergedRegion(cellAddress); + } + column += subFieldSize; + } + else + { + Cell cell = subRow.createCell(column++); + cell.setCellValue(attr.name()); + cell.setCellStyle(cellStyle); + } + } + rownum++; + } + } + + /** + * 对excel表单默认第一个索引名转换成list + * + * @param is 输入流 + * @return 转换后集合 + */ + public List importExcel(InputStream is) + { + return importExcel(is, 0); + } + + /** + * 对excel表单默认第一个索引名转换成list + * + * @param is 输入流 + * @param titleNum 标题占用行数 + * @return 转换后集合 + */ + public List importExcel(InputStream is, int titleNum) + { + List list = null; + try + { + list = importExcel(StringUtils.EMPTY, is, titleNum); + } + catch (Exception e) + { + log.error("导入Excel异常{}", e.getMessage()); + throw new UtilException(e.getMessage()); + } + finally + { + IOUtils.closeQuietly(is); + } + return list; + } + + /** + * 对excel表单指定表格索引名转换成list + * + * @param sheetName 表格索引名 + * @param titleNum 标题占用行数 + * @param is 输入流 + * @return 转换后集合 + */ + public List importExcel(String sheetName, InputStream is, int titleNum) throws Exception + { + this.type = Type.IMPORT; + this.wb = WorkbookFactory.create(is); + List list = new ArrayList(); + // 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheet + Sheet sheet = StringUtils.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0); + if (sheet == null) + { + throw new IOException("文件sheet不存在"); + } + boolean isXSSFWorkbook = !(wb instanceof HSSFWorkbook); + Map> pictures = null; + if (isXSSFWorkbook) + { + pictures = getSheetPictures07((XSSFSheet) sheet, (XSSFWorkbook) wb); + } + else + { + pictures = getSheetPictures03((HSSFSheet) sheet, (HSSFWorkbook) wb); + } + // 获取最后一个非空行的行下标,比如总行数为n,则返回的为n-1 + int rows = sheet.getLastRowNum(); + if (rows > 0) + { + // 定义一个map用于存放excel列的序号和field. + Map cellMap = new HashMap(); + // 获取表头 + Row heard = sheet.getRow(titleNum); + if (heard == null) + { + throw new UtilException("文件标题行为空,请检查Excel文件格式"); + } + for (int i = 0; i < heard.getLastCellNum(); i++) + { + Cell cell = heard.getCell(i); + if (StringUtils.isNotNull(cell)) + { + String value = this.getCellValue(heard, i).toString(); + cellMap.put(value, i); + } + } + // 有数据时才处理 得到类的所有field. + List fields = this.getFields(); + Map fieldsMap = new HashMap(); + for (Object[] objects : fields) + { + Excel attr = (Excel) objects[1]; + Integer column = cellMap.get(attr.name()); + if (column != null) + { + fieldsMap.put(column, objects); + } + } + for (int i = titleNum + 1; i <= rows; i++) + { + // 从第2行开始取数据,默认第一行是表头. + Row row = sheet.getRow(i); + // 判断当前行是否是空行 + if (isRowEmpty(row)) + { + continue; + } + T entity = null; + for (Map.Entry entry : fieldsMap.entrySet()) + { + Object val = this.getCellValue(row, entry.getKey()); + + // 如果不存在实例则新建. + entity = (entity == null ? clazz.newInstance() : entity); + // 从map中得到对应列的field. + Field field = (Field) entry.getValue()[0]; + Excel attr = (Excel) entry.getValue()[1]; + // 取得类型,并根据对象类型设置值. + Class fieldType = field.getType(); + if (String.class == fieldType) + { + String s = Convert.toStr(val); + if (s.matches("^\\d+\\.0$")) + { + val = StringUtils.substringBefore(s, ".0"); + } + else + { + String dateFormat = field.getAnnotation(Excel.class).dateFormat(); + if (StringUtils.isNotEmpty(dateFormat)) + { + val = parseDateToStr(dateFormat, val); + } + else + { + val = Convert.toStr(val); + } + } + } + else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) + { + val = Convert.toInt(val); + } + else if ((Long.TYPE == fieldType || Long.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) + { + val = Convert.toLong(val); + } + else if (Double.TYPE == fieldType || Double.class == fieldType) + { + val = Convert.toDouble(val); + } + else if (Float.TYPE == fieldType || Float.class == fieldType) + { + val = Convert.toFloat(val); + } + else if (BigDecimal.class == fieldType) + { + val = Convert.toBigDecimal(val); + } + else if (Date.class == fieldType) + { + if (val instanceof String) + { + val = DateUtils.parseDate(val); + } + else if (val instanceof Double) + { + val = DateUtil.getJavaDate((Double) val); + } + } + else if (Boolean.TYPE == fieldType || Boolean.class == fieldType) + { + val = Convert.toBool(val, false); + } + if (StringUtils.isNotNull(fieldType)) + { + String propertyName = field.getName(); + if (StringUtils.isNotEmpty(attr.targetAttr())) + { + propertyName = field.getName() + "." + attr.targetAttr(); + } + if (StringUtils.isNotEmpty(attr.readConverterExp())) + { + val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator()); + } + else if (StringUtils.isNotEmpty(attr.dictType())) + { + if (!sysDictMap.containsKey(attr.dictType() + val)) + { + String dictValue = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator()); + sysDictMap.put(attr.dictType() + val, dictValue); + } + val = sysDictMap.get(attr.dictType() + val); + } + else if (!attr.handler().equals(ExcelHandlerAdapter.class)) + { + val = dataFormatHandlerAdapter(val, attr, null); + } + else if (ColumnType.IMAGE == attr.cellType() && StringUtils.isNotEmpty(pictures)) + { + StringBuilder propertyString = new StringBuilder(); + List images = pictures.get(row.getRowNum() + "_" + entry.getKey()); + for (PictureData picture : images) + { + byte[] data = picture.getData(); + String fileName = FileUtils.writeImportBytes(data); + propertyString.append(fileName).append(SEPARATOR); + } + val = StringUtils.stripEnd(propertyString.toString(), SEPARATOR); + } + ReflectUtils.invokeSetter(entity, propertyName, val); + } + } + list.add(entity); + } + } + return list; + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @return 结果 + */ + public AjaxResult exportExcel(List list, String sheetName) + { + return exportExcel(list, sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param title 标题 + * @return 结果 + */ + public AjaxResult exportExcel(List list, String sheetName, String title) + { + this.init(list, sheetName, title, Type.EXPORT); + return exportExcel(); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param response 返回数据 + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @return 结果 + */ + public void exportExcel(HttpServletResponse response, List list, String sheetName) + { + exportExcel(response, list, sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param response 返回数据 + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param title 标题 + * @return 结果 + */ + public void exportExcel(HttpServletResponse response, List list, String sheetName, String title) + { + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + this.init(list, sheetName, title, Type.EXPORT); + exportExcel(response); + } + + /** + * 多 Sheet 导出 —— 将多个不同类型的数据集合写入同一 Excel,直接输出到 HttpServletResponse + * + * @param response HTTP 响应 + * @param sheets Sheet 描述列表 + */ + public static void exportMultiSheet(HttpServletResponse response, List> 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> 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> 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 list, String sheetName, String title) + { + if (list == null) + { + list = new ArrayList(); + } + 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表单 + * + * @param sheetName 工作表的名称 + * @return 结果 + */ + public AjaxResult importTemplateExcel(String sheetName) + { + return importTemplateExcel(sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @param title 标题 + * @return 结果 + */ + public AjaxResult importTemplateExcel(String sheetName, String title) + { + this.init(null, sheetName, title, Type.IMPORT); + return exportExcel(); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @return 结果 + */ + public void importTemplateExcel(HttpServletResponse response, String sheetName) + { + importTemplateExcel(response, sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @param title 标题 + * @return 结果 + */ + public void importTemplateExcel(HttpServletResponse response, String sheetName, String title) + { + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + this.init(null, sheetName, title, Type.IMPORT); + exportExcel(response); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @return 结果 + */ + public void exportExcel(HttpServletResponse response) + { + try + { + writeSheet(); + wb.write(response.getOutputStream()); + } + catch (Exception e) + { + log.error("导出Excel异常{}", e.getMessage()); + } + finally + { + IOUtils.closeQuietly(wb); + } + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @return 结果 + */ + public AjaxResult exportExcel() + { + OutputStream out = null; + try + { + writeSheet(); + String filename = encodingFilename(sheetName); + out = new FileOutputStream(getAbsoluteFile(filename)); + wb.write(out); + return AjaxResult.success(filename); + } + catch (Exception e) + { + log.error("导出Excel异常{}", e.getMessage()); + throw new UtilException("导出Excel失败,请联系网站管理员!"); + } + finally + { + IOUtils.closeQuietly(wb); + IOUtils.closeQuietly(out); + } + } + + /** + * 创建写入数据到Sheet + */ + public void writeSheet() + { + // 取出一共有多少个sheet. + int sheetNo = Math.max(1, (int) Math.ceil(list.size() * 1.0 / sheetSize)); + for (int index = 0; index < sheetNo; index++) + { + createSheet(sheetNo, index); + + // 产生一行 + Row row = sheet.createRow(rownum); + int column = 0; + // 写入各个字段的列头名称 + for (Object[] os : fields) + { + Field field = (Field) os[0]; + Excel excel = (Excel) os[1]; + if (Collection.class.isAssignableFrom(field.getType())) + { + List currentSubFields = subFieldsMap.get(field.getName()); + for (Field subField : currentSubFields) + { + Excel subExcel = subField.getAnnotation(Excel.class); + this.createHeadCell(subExcel, row, column++); + } + } + else + { + this.createHeadCell(excel, row, column++); + } + } + if (Type.EXPORT.equals(type)) + { + fillExcelData(index); + addStatisticsRow(); + } + } + } + + /** + * 填充excel数据 + * + * @param index 序号 + */ + @SuppressWarnings("unchecked") + public void fillExcelData(int index) + { + int startNo = index * sheetSize; + int endNo = Math.min(startNo + sheetSize, list.size()); + int currentRowNum = rownum + 1; // 从标题行后开始 + + for (int i = startNo; i < endNo; i++) + { + Row row = sheet.createRow(currentRowNum); + T vo = (T) list.get(i); + int column = 0; + int maxSubListSize = getCurrentMaxSubListSize(vo); + for (Object[] os : fields) + { + Field field = (Field) os[0]; + Excel excel = (Excel) os[1]; + if (Collection.class.isAssignableFrom(field.getType())) + { + try + { + Collection subList = (Collection) getTargetValue(vo, field, excel); + List currentSubFields = subFieldsMap.get(field.getName()); + if (subList != null && !subList.isEmpty()) + { + int subIndex = 0; + for (Object subVo : subList) + { + Row subRow = sheet.getRow(currentRowNum + subIndex); + if (subRow == null) + { + subRow = sheet.createRow(currentRowNum + subIndex); + } + + int subColumn = column; + for (Field subField : currentSubFields) + { + Excel subExcel = subField.getAnnotation(Excel.class); + addCell(subExcel, subRow, (T) subVo, subField, subColumn++); + } + subIndex++; + } + } + column += currentSubFields.size(); + } + catch (Exception e) + { + log.error("填充集合数据失败", e); + } + } + else + { + // 创建单元格并设置值 + addCell(excel, row, vo, field, column); + if (maxSubListSize > 1 && excel.needMerge()) + { + sheet.addMergedRegion(new CellRangeAddress(currentRowNum, currentRowNum + maxSubListSize - 1, column, column)); + } + column++; + } + } + currentRowNum += maxSubListSize; + } + } + + /** + * 获取子列表最大数 + */ + private int getCurrentMaxSubListSize(T vo) + { + int maxSubListSize = 1; + for (Object[] os : fields) + { + Field field = (Field) os[0]; + if (Collection.class.isAssignableFrom(field.getType())) + { + try + { + Collection subList = (Collection) getTargetValue(vo, field, (Excel) os[1]); + if (subList != null && !subList.isEmpty()) + { + maxSubListSize = Math.max(maxSubListSize, subList.size()); + } + } + catch (Exception e) + { + log.error("获取集合大小失败", e); + } + } + } + return maxSubListSize; + } + + /** + * 创建表格样式 + * + * @param wb 工作薄对象 + * @return 样式列表 + */ + private Map createStyles(Workbook wb) + { + // 写入各条记录,每条记录对应excel表中的一行 + Map styles = new HashMap(); + CellStyle style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + Font titleFont = wb.createFont(); + titleFont.setFontName("Arial"); + titleFont.setFontHeightInPoints((short) 16); + titleFont.setBold(true); + style.setFont(titleFont); + DataFormat dataFormat = wb.createDataFormat(); + style.setDataFormat(dataFormat.getFormat("@")); + styles.put("title", style); + + style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setBorderRight(BorderStyle.THIN); + style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderLeft(BorderStyle.THIN); + style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderTop(BorderStyle.THIN); + style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderBottom(BorderStyle.THIN); + style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + Font dataFont = wb.createFont(); + dataFont.setFontName("Arial"); + dataFont.setFontHeightInPoints((short) 10); + style.setFont(dataFont); + styles.put("data", style); + + style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setDataFormat(dataFormat.getFormat("######0.00")); + Font totalFont = wb.createFont(); + totalFont.setFontName("Arial"); + totalFont.setFontHeightInPoints((short) 10); + style.setFont(totalFont); + styles.put("total", style); + + styles.putAll(annotationHeaderStyles(wb, styles)); + + styles.putAll(annotationDataStyles(wb)); + + return styles; + } + + /** + * 根据Excel注解创建表格头样式 + * + * @param wb 工作薄对象 + * @return 自定义样式列表 + */ + private Map annotationHeaderStyles(Workbook wb, Map styles) + { + Map headerStyles = new HashMap(); + for (Object[] os : fields) + { + Excel excel = (Excel) os[1]; + String key = StringUtils.format("header_{}_{}", excel.headerColor(), excel.headerBackgroundColor()); + if (!headerStyles.containsKey(key)) + { + CellStyle style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setFillForegroundColor(excel.headerBackgroundColor().index); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + Font headerFont = wb.createFont(); + headerFont.setFontName("Arial"); + headerFont.setFontHeightInPoints((short) 10); + headerFont.setBold(true); + headerFont.setColor(excel.headerColor().index); + style.setFont(headerFont); + // 设置表格头单元格文本形式 + DataFormat dataFormat = wb.createDataFormat(); + style.setDataFormat(dataFormat.getFormat("@")); + headerStyles.put(key, style); + } + } + return headerStyles; + } + + /** + * 根据Excel注解创建表格列样式 + * + * @param wb 工作薄对象 + * @return 自定义样式列表 + */ + private Map annotationDataStyles(Workbook wb) + { + Map styles = new HashMap(); + for (Object[] os : fields) + { + Field field = (Field) os[0]; + Excel excel = (Excel) os[1]; + if (Collection.class.isAssignableFrom(field.getType())) + { + ParameterizedType pt = (ParameterizedType) field.getGenericType(); + Class subClass = (Class) pt.getActualTypeArguments()[0]; + List subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class); + for (Field subField : subFields) + { + Excel subExcel = subField.getAnnotation(Excel.class); + annotationDataStyles(styles, subField, subExcel); + } + } + else + { + annotationDataStyles(styles, field, excel); + } + } + return styles; + } + + /** + * 根据Excel注解创建表格列样式 + * + * @param styles 自定义样式列表 + * @param field 属性列信息 + * @param excel 注解信息 + */ + public void annotationDataStyles(Map styles, Field field, Excel excel) + { + String key = StringUtils.format("data_{}_{}_{}_{}_{}", excel.align(), excel.color(), excel.backgroundColor(), excel.cellType(), excel.wrapText()); + if (!styles.containsKey(key)) + { + CellStyle style = wb.createCellStyle(); + style.setAlignment(excel.align()); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setBorderRight(BorderStyle.THIN); + style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderLeft(BorderStyle.THIN); + style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderTop(BorderStyle.THIN); + style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderBottom(BorderStyle.THIN); + style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + style.setFillForegroundColor(excel.backgroundColor().getIndex()); + style.setWrapText(excel.wrapText()); + Font dataFont = wb.createFont(); + dataFont.setFontName("Arial"); + dataFont.setFontHeightInPoints((short) 10); + dataFont.setColor(excel.color().index); + style.setFont(dataFont); + if (ColumnType.TEXT == excel.cellType()) + { + DataFormat dataFormat = wb.createDataFormat(); + style.setDataFormat(dataFormat.getFormat("@")); + } + styles.put(key, style); + } + } + + /** + * 创建单元格 + */ + public Cell createHeadCell(Excel attr, Row row, int column) + { + // 创建列 + Cell cell = row.createCell(column); + // 写入列信息 + cell.setCellValue(attr.name()); + setDataValidation(attr, row, column); + cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); + if (isSubList()) + { + // 填充默认样式,防止合并单元格样式失效 + sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType(), attr.wrapText()))); + if (attr.needMerge()) + { + sheet.addMergedRegion(new CellRangeAddress(rownum - 1, rownum, column, column)); + } + } + return cell; + } + + /** + * 设置单元格信息 + * + * @param value 单元格值 + * @param attr 注解相关 + * @param cell 单元格信息 + */ + public void setCellVo(Object value, Excel attr, Cell cell) + { + if (ColumnType.STRING == attr.cellType() || ColumnType.TEXT == attr.cellType()) + { + String cellValue = Convert.toStr(value); + // 对于任何以表达式触发字符 =-+@开头的单元格,直接使用tab字符作为前缀,防止CSV注入。 + if (StringUtils.startsWithAny(cellValue, FORMULA_STR)) + { + cellValue = RegExUtils.replaceFirst(cellValue, FORMULA_REGEX_STR, "\t$0"); + } + if (value instanceof Collection && StringUtils.equals("[]", cellValue)) + { + cellValue = StringUtils.EMPTY; + } + cell.setCellValue(StringUtils.isNull(cellValue) ? attr.defaultValue() : cellValue + attr.suffix()); + } + else if (ColumnType.NUMERIC == attr.cellType()) + { + if (StringUtils.isNotNull(value)) + { + cell.setCellValue(StringUtils.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value)); + } + } + else if (ColumnType.IMAGE == attr.cellType()) + { + ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1), cell.getRow().getRowNum() + 1); + String propertyValue = Convert.toStr(value); + if (StringUtils.isNotEmpty(propertyValue)) + { + List imagePaths = StringUtils.str2List(propertyValue, SEPARATOR); + for (String imagePath : imagePaths) + { + byte[] data = ImageUtils.getImage(imagePath); + getDrawingPatriarch(cell.getSheet()).createPicture(anchor, cell.getSheet().getWorkbook().addPicture(data, getImageType(data))); + } + } + } + } + + /** + * 获取画布 + */ + public static Drawing getDrawingPatriarch(Sheet sheet) + { + if (sheet.getDrawingPatriarch() == null) + { + sheet.createDrawingPatriarch(); + } + return sheet.getDrawingPatriarch(); + } + + /** + * 获取图片类型,设置图片插入类型 + */ + public int getImageType(byte[] value) + { + String type = FileTypeUtils.getFileExtendName(value); + if ("JPG".equalsIgnoreCase(type)) + { + return Workbook.PICTURE_TYPE_JPEG; + } + else if ("PNG".equalsIgnoreCase(type)) + { + return Workbook.PICTURE_TYPE_PNG; + } + return Workbook.PICTURE_TYPE_JPEG; + } + + /** + * 创建表格样式 + */ + public void setDataValidation(Excel attr, Row row, int column) + { + if (attr.name().indexOf("注:") >= 0) + { + sheet.setColumnWidth(column, 6000); + } + else + { + // 设置列宽 + sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256)); + } + if (StringUtils.isNotEmpty(attr.prompt()) || attr.combo().length > 0 || attr.comboReadDict()) + { + String[] comboArray = attr.combo(); + if (attr.comboReadDict()) + { + if (!sysDictMap.containsKey("combo_" + attr.dictType())) + { + String labels = DictUtils.getDictLabels(attr.dictType()); + sysDictMap.put("combo_" + attr.dictType(), labels); + } + String val = sysDictMap.get("combo_" + attr.dictType()); + comboArray = StringUtils.split(val, DictUtils.SEPARATOR); + } + if (comboArray.length > 15 || StringUtils.join(comboArray).length() > 255) + { + // 如果下拉数大于15或字符串长度大于255,则使用一个新sheet存储,避免生成的模板下拉值获取不到 + setXSSFValidationWithHidden(sheet, comboArray, attr.prompt(), 1, 100, column, column); + } + else + { + // 提示信息或只能选择不能输入的列内容. + setPromptOrValidation(sheet, comboArray, attr.prompt(), 1, 100, column, column); + } + } + } + + /** + * 添加单元格 + */ + public Cell addCell(Excel attr, Row row, T vo, Field field, int column) + { + Cell cell = null; + try + { + // 设置行高 + row.setHeight(maxHeight); + // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列. + if (attr.isExport()) + { + // 创建cell + cell = row.createCell(column); + if (isSubListValue(vo) && getListCellValue(vo) > 1 && attr.needMerge()) + { + if (subMergedLastRowNum >= subMergedFirstRowNum) + { + sheet.addMergedRegion(new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column)); + } + } + cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType(), attr.wrapText()))); + + // 用于读取对象中的属性 + Object value = getTargetValue(vo, field, attr); + String dateFormat = attr.dateFormat(); + String readConverterExp = attr.readConverterExp(); + String separator = attr.separator(); + String dictType = attr.dictType(); + if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value)) + { + cell.setCellStyle(createCellStyle(cell.getCellStyle(), dateFormat)); + cell.setCellValue(parseDateToStr(dateFormat, value)); + } + else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value)) + { + cell.setCellValue(convertByExp(Convert.toStr(value), readConverterExp, separator)); + } + else if (StringUtils.isNotEmpty(dictType) && StringUtils.isNotNull(value)) + { + if (!sysDictMap.containsKey(dictType + value)) + { + String lable = convertDictByExp(Convert.toStr(value), dictType, separator); + sysDictMap.put(dictType + value, lable); + } + cell.setCellValue(sysDictMap.get(dictType + value)); + } + else if (value instanceof BigDecimal && -1 != attr.scale()) + { + cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).doubleValue()); + } + else if (!attr.handler().equals(ExcelHandlerAdapter.class)) + { + cell.setCellValue(dataFormatHandlerAdapter(value, attr, cell)); + } + else + { + // 设置列类型 + setCellVo(value, attr, cell); + } + addStatisticsData(column, Convert.toStr(value), attr); + } + } + catch (Exception e) + { + log.error("导出Excel失败{}", e); + } + return cell; + } + + /** + * 使用自定义格式,同时避免样式污染 + * + * @param cellStyle 从此样式复制 + * @param format 格式匹配的字符串 + * @return 格式化后CellStyle对象 + */ + 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; + } + + /** + * 设置 POI XSSFSheet 单元格提示或选择框 + * + * @param sheet 表单 + * @param textlist 下拉框显示的内容 + * @param promptContent 提示内容 + * @param firstRow 开始行 + * @param endRow 结束行 + * @param firstCol 开始列 + * @param endCol 结束列 + */ + public void setPromptOrValidation(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, + int firstCol, int endCol) + { + DataValidationHelper helper = sheet.getDataValidationHelper(); + DataValidationConstraint constraint = textlist.length > 0 ? helper.createExplicitListConstraint(textlist) : helper.createCustomConstraint("DD1"); + CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); + DataValidation dataValidation = helper.createValidation(constraint, regions); + if (StringUtils.isNotEmpty(promptContent)) + { + // 如果设置了提示信息则鼠标放上去提示 + dataValidation.createPromptBox("", promptContent); + dataValidation.setShowPromptBox(true); + } + // 处理Excel兼容性问题 + if (dataValidation instanceof XSSFDataValidation) + { + dataValidation.setSuppressDropDownArrow(true); + dataValidation.setShowErrorBox(true); + } + else + { + dataValidation.setSuppressDropDownArrow(false); + } + sheet.addValidationData(dataValidation); + } + + /** + * 设置某些列的值只能输入预制的数据,显示下拉框(兼容超出一定数量的下拉框). + * + * @param sheet 要设置的sheet. + * @param textlist 下拉框显示的内容 + * @param promptContent 提示内容 + * @param firstRow 开始行 + * @param endRow 结束行 + * @param firstCol 开始列 + * @param endCol 结束列 + */ + public void setXSSFValidationWithHidden(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, int firstCol, int endCol) + { + String hideSheetName = "combo_" + firstCol + "_" + endCol; + Sheet hideSheet = null; + String hideSheetDataName = hideSheetName + "_data"; + Name name = wb.getName(hideSheetDataName); + if (name != null) + { + // 名称已存在,尝试从名称的引用中找到sheet名称 + String refersToFormula = name.getRefersToFormula(); + if (StringUtils.isNotEmpty(refersToFormula) && refersToFormula.contains("!")) + { + String sheetNameFromFormula = refersToFormula.substring(0, refersToFormula.indexOf("!")); + hideSheet = wb.getSheet(sheetNameFromFormula); + } + } + + if (hideSheet == null) + { + hideSheet = wb.createSheet(hideSheetName); // 用于存储 下拉菜单数据 + for (int i = 0; i < textlist.length; i++) + { + hideSheet.createRow(i).createCell(0).setCellValue(textlist[i]); + } + // 创建名称,可被其他单元格引用 + name = wb.createName(); + name.setNameName(hideSheetDataName); + name.setRefersToFormula(hideSheetName + "!$A$1:$A$" + textlist.length); + } + + DataValidationHelper helper = sheet.getDataValidationHelper(); + // 加载下拉列表内容 + DataValidationConstraint constraint = helper.createFormulaListConstraint(hideSheetDataName); + // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列 + CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); + // 数据有效性对象 + DataValidation dataValidation = helper.createValidation(constraint, regions); + if (StringUtils.isNotEmpty(promptContent)) + { + // 如果设置了提示信息则鼠标放上去提示 + dataValidation.createPromptBox("", promptContent); + dataValidation.setShowPromptBox(true); + } + // 处理Excel兼容性问题 + if (dataValidation instanceof XSSFDataValidation) + { + dataValidation.setSuppressDropDownArrow(true); + dataValidation.setShowErrorBox(true); + } + else + { + dataValidation.setSuppressDropDownArrow(false); + } + + sheet.addValidationData(dataValidation); + // 设置hiddenSheet隐藏 + wb.setSheetHidden(wb.getSheetIndex(hideSheet), true); + } + + /** + * 解析导出值 0=男,1=女,2=未知 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @param separator 分隔符 + * @return 解析后值 + */ + public static String convertByExp(String propertyValue, String converterExp, String separator) + { + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(SEPARATOR); + for (String item : convertSource) + { + String[] itemArray = item.split("="); + if (StringUtils.containsAny(propertyValue, separator)) + { + for (String value : propertyValue.split(separator)) + { + if (itemArray[0].equals(value)) + { + propertyString.append(itemArray[1] + separator); + break; + } + } + } + else + { + if (itemArray[0].equals(propertyValue)) + { + return itemArray[1]; + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 反向解析值 男=0,女=1,未知=2 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @param separator 分隔符 + * @return 解析后值 + */ + public static String reverseByExp(String propertyValue, String converterExp, String separator) + { + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(SEPARATOR); + for (String item : convertSource) + { + String[] itemArray = item.split("="); + if (StringUtils.containsAny(propertyValue, separator)) + { + for (String value : propertyValue.split(separator)) + { + if (itemArray[1].equals(value)) + { + propertyString.append(itemArray[0] + separator); + break; + } + } + } + else + { + if (itemArray[1].equals(propertyValue)) + { + return itemArray[0]; + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 解析字典值 + * + * @param dictValue 字典值 + * @param dictType 字典类型 + * @param separator 分隔符 + * @return 字典标签 + */ + public static String convertDictByExp(String dictValue, String dictType, String separator) + { + return DictUtils.getDictLabel(dictType, dictValue, separator); + } + + /** + * 反向解析值字典值 + * + * @param dictLabel 字典标签 + * @param dictType 字典类型 + * @param separator 分隔符 + * @return 字典值 + */ + public static String reverseDictByExp(String dictLabel, String dictType, String separator) + { + return DictUtils.getDictValue(dictType, dictLabel, separator); + } + + /** + * 数据处理器 + * + * @param value 数据值 + * @param excel 数据注解 + * @return + */ + public String dataFormatHandlerAdapter(Object value, Excel excel, Cell cell) + { + try + { + 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); + } + catch (Exception e) + { + log.error("不能格式化数据 " + excel.handler(), e.getMessage()); + } + return Convert.toStr(value); + } + + /** + * 合计统计信息 + */ + private void addStatisticsData(Integer index, String text, Excel entity) + { + if (entity != null && entity.isStatistics()) + { + Double temp = 0D; + if (!statistics.containsKey(index)) + { + statistics.put(index, temp); + } + try + { + temp = Double.valueOf(text); + } + catch (NumberFormatException e) + { + } + statistics.put(index, statistics.get(index) + temp); + } + } + + /** + * 创建统计行 + */ + public void addStatisticsRow() + { + if (statistics.size() > 0) + { + Row row = sheet.createRow(sheet.getLastRowNum() + 1); + Set keys = statistics.keySet(); + Cell cell = row.createCell(0); + cell.setCellStyle(styles.get("total")); + cell.setCellValue("合计"); + + for (Integer key : keys) + { + cell = row.createCell(key); + cell.setCellStyle(styles.get("total")); + cell.setCellValue(statistics.get(key)); + } + statistics.clear(); + } + } + + /** + * 编码文件名 + */ + public String encodingFilename(String filename) + { + return UUID.randomUUID() + "_" + filename + ".xlsx"; + } + + /** + * 获取下载路径 + * + * @param filename 文件名称 + */ + public String getAbsoluteFile(String filename) + { + String downloadPath = RuoYiConfig.getDownloadPath() + filename; + File desc = new File(downloadPath); + if (!desc.getParentFile().exists()) + { + desc.getParentFile().mkdirs(); + } + return downloadPath; + } + + /** + * 获取bean中的属性值 + * + * @param vo 实体对象 + * @param field 字段 + * @param excel 注解 + * @return 最终的属性值 + * @throws Exception + */ + private Object getTargetValue(T vo, Field field, Excel excel) throws Exception + { + field.setAccessible(true); + Object o = field.get(vo); + if (StringUtils.isNotEmpty(excel.targetAttr())) + { + String target = excel.targetAttr(); + if (target.contains(".")) + { + String[] targets = target.split("[.]"); + for (String name : targets) + { + o = getValue(o, name); + } + } + else + { + o = getValue(o, target); + } + } + return o; + } + + /** + * 以类的属性的get方法方法形式获取值 + * + * @param o + * @param name + * @return value + * @throws Exception + */ + private Object getValue(Object o, String name) throws Exception + { + if (StringUtils.isNotNull(o) && StringUtils.isNotEmpty(name)) + { + Class clazz = o.getClass(); + Field field = clazz.getDeclaredField(name); + field.setAccessible(true); + o = field.get(o); + } + return o; + } + + /** + * 得到所有定义字段 + */ + private void createExcelField() + { + this.fields = getFields(); + this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList()); + this.maxHeight = getRowHeight(); + } + + /** + * 获取字段注解信息 + */ + public List getFields() + { + List fields = new ArrayList(); + List tempFields = new ArrayList<>(); + subFieldsMap = new HashMap<>(); + subMethods = new HashMap<>(); + tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields())); + tempFields.addAll(Arrays.asList(clazz.getDeclaredFields())); + if (StringUtils.isNotEmpty(includeFields)) + { + for (Field field : tempFields) + { + if (ArrayUtils.contains(this.includeFields, field.getName()) || field.isAnnotationPresent(Excels.class)) + { + addField(fields, field); + } + } + } + else if (StringUtils.isNotEmpty(excludeFields)) + { + for (Field field : tempFields) + { + if (!ArrayUtils.contains(this.excludeFields, field.getName())) + { + addField(fields, field); + } + } + } + else + { + for (Field field : tempFields) + { + addField(fields, field); + } + } + return fields; + } + + /** + * 添加字段信息 + */ + public void addField(List fields, Field field) + { + // 单注解 + if (field.isAnnotationPresent(Excel.class)) + { + Excel attr = field.getAnnotation(Excel.class); + if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) + { + fields.add(new Object[] { field, attr }); + } + if (Collection.class.isAssignableFrom(field.getType())) + { + String fieldName = field.getName(); + subMethods.put(fieldName, getSubMethod(fieldName, clazz)); + ParameterizedType pt = (ParameterizedType) field.getGenericType(); + Class subClass = (Class) pt.getActualTypeArguments()[0]; + subFieldsMap.put(fieldName, FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class)); + } + } + + // 多注解 + if (field.isAnnotationPresent(Excels.class)) + { + Excels attrs = field.getAnnotation(Excels.class); + Excel[] excels = attrs.value(); + for (Excel attr : excels) + { + if (StringUtils.isNotEmpty(includeFields)) + { + if (ArrayUtils.contains(this.includeFields, field.getName() + "." + attr.targetAttr()) + && (attr != null && (attr.type() == Type.ALL || attr.type() == type))) + { + fields.add(new Object[] { field, attr }); + } + } + else + { + if (!ArrayUtils.contains(this.excludeFields, field.getName() + "." + attr.targetAttr()) + && (attr != null && (attr.type() == Type.ALL || attr.type() == type))) + { + fields.add(new Object[] { field, attr }); + } + } + } + } + } + + /** + * 根据注解获取最大行高 + */ + public short getRowHeight() + { + double maxHeight = 0; + for (Object[] os : this.fields) + { + Excel excel = (Excel) os[1]; + maxHeight = Math.max(maxHeight, excel.height()); + } + return (short) (maxHeight * 20); + } + + /** + * 创建一个工作簿 + */ + public void createWorkbook() + { + this.wb = new SXSSFWorkbook(500); + this.sheet = wb.createSheet(); + wb.setSheetName(0, sheetName); + this.styles = createStyles(wb); + } + + /** + * 创建工作表 + * + * @param sheetNo sheet数量 + * @param index 序号 + */ + public void createSheet(int sheetNo, int index) + { + // 设置工作表的名称. + if (sheetNo > 1 && index > 0) + { + this.sheet = wb.createSheet(); + this.createTitle(); + int actualIndex = wb.getSheetIndex(this.sheet); + wb.setSheetName(actualIndex, sheetName + index); + } + } + + /** + * 获取单元格值 + * + * @param row 获取的行 + * @param column 获取单元格列号 + * @return 单元格值 + */ + public Object getCellValue(Row row, int column) + { + if (row == null) + { + return row; + } + Object val = ""; + try + { + Cell cell = row.getCell(column); + if (StringUtils.isNotNull(cell)) + { + if (cell.getCellType() == CellType.NUMERIC || cell.getCellType() == CellType.FORMULA) + { + val = cell.getNumericCellValue(); + if (DateUtil.isCellDateFormatted(cell)) + { + val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换 + } + else + { + if ((Double) val % 1 != 0) + { + val = new BigDecimal(val.toString()); + } + else + { + val = new DecimalFormat("0").format(val); + } + } + } + else if (cell.getCellType() == CellType.STRING) + { + val = cell.getStringCellValue(); + } + else if (cell.getCellType() == CellType.BOOLEAN) + { + val = cell.getBooleanCellValue(); + } + else if (cell.getCellType() == CellType.ERROR) + { + val = cell.getErrorCellValue(); + } + + } + } + catch (Exception e) + { + return val; + } + return val; + } + + /** + * 判断是否是空行 + * + * @param row 判断的行 + * @return + */ + private boolean isRowEmpty(Row row) + { + if (row == null) + { + return true; + } + for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) + { + Cell cell = row.getCell(i); + if (cell != null && cell.getCellType() != CellType.BLANK) + { + return false; + } + } + return true; + } + + /** + * 获取Excel2003图片 + * + * @param sheet 当前sheet对象 + * @param workbook 工作簿对象 + * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData + */ + public static Map> getSheetPictures03(HSSFSheet sheet, HSSFWorkbook workbook) + { + Map> sheetIndexPicMap = new HashMap<>(); + List pictures = workbook.getAllPictures(); + if (!pictures.isEmpty() && sheet.getDrawingPatriarch() != null) + { + for (HSSFShape shape : sheet.getDrawingPatriarch().getChildren()) + { + if (shape instanceof HSSFPicture) + { + HSSFPicture pic = (HSSFPicture) shape; + HSSFClientAnchor anchor = (HSSFClientAnchor) pic.getAnchor(); + String picIndex = anchor.getRow1() + "_" + anchor.getCol1(); + sheetIndexPicMap.computeIfAbsent(picIndex, k -> new ArrayList<>()).add(pic.getPictureData()); + } + } + } + return sheetIndexPicMap; + } + + /** + * 获取Excel2007图片 + * + * @param sheet 当前sheet对象 + * @param workbook 工作簿对象 + * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData + */ + public static Map> getSheetPictures07(XSSFSheet sheet, XSSFWorkbook workbook) + { + Map> sheetIndexPicMap = new HashMap<>(); + for (POIXMLDocumentPart dr : sheet.getRelations()) + { + if (dr instanceof XSSFDrawing) + { + XSSFDrawing drawing = (XSSFDrawing) dr; + for (XSSFShape shape : drawing.getShapes()) + { + if (shape instanceof XSSFPicture) + { + XSSFPicture pic = (XSSFPicture) shape; + XSSFClientAnchor anchor = pic.getPreferredSize(); + CTMarker ctMarker = anchor.getFrom(); + String picIndex = ctMarker.getRow() + "_" + ctMarker.getCol(); + sheetIndexPicMap.computeIfAbsent(picIndex, k -> new ArrayList<>()).add(pic.getPictureData()); + } + } + } + } + return sheetIndexPicMap; + } + + /** + * 格式化不同类型的日期对象 + * + * @param dateFormat 日期格式 + * @param val 被格式化的日期对象 + * @return 格式化后的日期字符 + */ + public String parseDateToStr(String dateFormat, Object val) + { + if (val == null) + { + return ""; + } + String str; + if (val instanceof Date) + { + str = DateUtils.parseDateToStr(dateFormat, (Date) val); + } + else if (val instanceof LocalDateTime) + { + str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDateTime) val)); + } + else if (val instanceof LocalDate) + { + str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDate) val)); + } + else + { + str = val.toString(); + } + return str; + } + + /** + * 是否有对象的子列表 + */ + public boolean isSubList() + { + return !StringUtils.isEmpty(subFieldsMap); + } + + /** + * 是否有对象的子列表,集合不为空 + */ + public boolean isSubListValue(T vo) + { + return !StringUtils.isEmpty(subFieldsMap) && getListCellValue(vo) > 0; + } + + /** + * 获取集合的值 + */ + public int getListCellValue(Object obj) + { + Collection value; + int max = 0; + try + { + for (String s : subMethods.keySet()) + { + value = (Collection) subMethods.get(s).invoke(obj); + if (value.size() > max) + { + max = value.size(); + } + } + } + catch (Exception e) + { + return 0; + } + return max; + } + + /** + * 获取对象的子列表方法 + * + * @param name 名称 + * @param pojoClass 类对象 + * @return 子列表方法 + */ + public Method getSubMethod(String name, Class pojoClass) + { + StringBuffer getMethodName = new StringBuffer("get"); + getMethodName.append(name.substring(0, 1).toUpperCase()); + getMethodName.append(name.substring(1)); + Method method = null; + try + { + method = pojoClass.getMethod(getMethodName.toString(), new Class[] {}); + } + catch (Exception e) + { + log.error("获取对象异常{}", e.getMessage()); + } + return method; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java index a4fdc7c..9befe41 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java @@ -1,412 +1,410 @@ -package com.ruoyi.common.utils.reflect; - -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.Date; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.Validate; -import org.apache.poi.ss.usermodel.DateUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import com.ruoyi.common.core.text.Convert; -import com.ruoyi.common.utils.DateUtils; - -/** - * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数. - * - * @author ruoyi - */ -@SuppressWarnings("rawtypes") -public class ReflectUtils -{ - private static final String SETTER_PREFIX = "set"; - - private static final String GETTER_PREFIX = "get"; - - private static final String CGLIB_CLASS_SEPARATOR = "$$"; - - private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class); - - /** - * 调用Getter方法. - * 支持多级,如:对象名.对象名.方法 - */ - @SuppressWarnings("unchecked") - public static E invokeGetter(Object obj, String propertyName) - { - Object object = obj; - for (String name : StringUtils.split(propertyName, ".")) - { - String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name); - object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); - } - return (E) object; - } - - /** - * 调用Setter方法, 仅匹配方法名。 - * 支持多级,如:对象名.对象名.方法 - */ - public static void invokeSetter(Object obj, String propertyName, E value) - { - Object object = obj; - String[] names = StringUtils.split(propertyName, "."); - for (int i = 0; i < names.length; i++) - { - if (i < names.length - 1) - { - String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]); - object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); - } - else - { - String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]); - invokeMethodByName(object, setterMethodName, new Object[] { value }); - } - } - } - - /** - * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数. - */ - @SuppressWarnings("unchecked") - public static E getFieldValue(final Object obj, final String fieldName) - { - Field field = getAccessibleField(obj, fieldName); - if (field == null) - { - logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); - return null; - } - E result = null; - try - { - result = (E) field.get(obj); - } - catch (IllegalAccessException e) - { - logger.error("不可能抛出的异常{}", e.getMessage()); - } - return result; - } - - /** - * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数. - */ - public static void setFieldValue(final Object obj, final String fieldName, final E value) - { - Field field = getAccessibleField(obj, fieldName); - if (field == null) - { - // throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); - logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); - return; - } - try - { - field.set(obj, value); - } - catch (IllegalAccessException e) - { - logger.error("不可能抛出的异常: {}", e.getMessage()); - } - } - - /** - * 直接调用对象方法, 无视private/protected修饰符. - * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用. - * 同时匹配方法名+参数类型, - */ - @SuppressWarnings("unchecked") - public static E invokeMethod(final Object obj, final String methodName, final Class[] parameterTypes, - final Object[] args) - { - if (obj == null || methodName == null) - { - return null; - } - Method method = getAccessibleMethod(obj, methodName, parameterTypes); - if (method == null) - { - logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); - return null; - } - try - { - return (E) method.invoke(obj, args); - } - catch (Exception e) - { - String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; - throw convertReflectionExceptionToUnchecked(msg, e); - } - } - - /** - * 直接调用对象方法, 无视private/protected修饰符, - * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用. - * 只匹配函数名,如果有多个同名函数调用第一个。 - */ - @SuppressWarnings("unchecked") - public static E invokeMethodByName(final Object obj, final String methodName, final Object[] args) - { - Method method = getAccessibleMethodByName(obj, methodName, args.length); - if (method == null) - { - // 如果为空不报错,直接返回空。 - logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); - return null; - } - try - { - // 类型转换(将参数数据类型转换为目标方法参数类型) - Class[] cs = method.getParameterTypes(); - for (int i = 0; i < cs.length; i++) - { - if (args[i] != null && !args[i].getClass().equals(cs[i])) - { - if (cs[i] == String.class) - { - args[i] = Convert.toStr(args[i]); - if (StringUtils.endsWith((String) args[i], ".0")) - { - args[i] = StringUtils.substringBefore((String) args[i], ".0"); - } - } - else if (cs[i] == Integer.class) - { - args[i] = Convert.toInt(args[i]); - } - else if (cs[i] == Long.class) - { - args[i] = Convert.toLong(args[i]); - } - else if (cs[i] == Double.class) - { - args[i] = Convert.toDouble(args[i]); - } - else if (cs[i] == Float.class) - { - args[i] = Convert.toFloat(args[i]); - } - else if (cs[i] == Date.class) - { - if (args[i] instanceof String) - { - args[i] = DateUtils.parseDate(args[i]); - } - else - { - args[i] = DateUtil.getJavaDate((Double) args[i]); - } - } - else if (cs[i] == boolean.class || cs[i] == Boolean.class) - { - args[i] = Convert.toBool(args[i]); - } - } - } - return (E) method.invoke(obj, args); - } - catch (Exception e) - { - String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; - throw convertReflectionExceptionToUnchecked(msg, e); - } - } - - /** - * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问. - * 如向上转型到Object仍无法找到, 返回null. - */ - public static Field getAccessibleField(final Object obj, final String fieldName) - { - // 为空不报错。直接返回 null - if (obj == null) - { - return null; - } - Validate.notBlank(fieldName, "fieldName can't be blank"); - for (Class superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) - { - try - { - Field field = superClass.getDeclaredField(fieldName); - makeAccessible(field); - return field; - } - catch (NoSuchFieldException e) - { - continue; - } - } - return null; - } - - /** - * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. - * 如向上转型到Object仍无法找到, 返回null. - * 匹配函数名+参数类型。 - * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) - */ - public static Method getAccessibleMethod(final Object obj, final String methodName, - final Class... parameterTypes) - { - // 为空不报错。直接返回 null - if (obj == null) - { - return null; - } - Validate.notBlank(methodName, "methodName can't be blank"); - for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) - { - try - { - Method method = searchType.getDeclaredMethod(methodName, parameterTypes); - makeAccessible(method); - return method; - } - catch (NoSuchMethodException e) - { - continue; - } - } - return null; - } - - /** - * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. - * 如向上转型到Object仍无法找到, 返回null. - * 只匹配函数名。 - * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) - */ - public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum) - { - // 为空不报错。直接返回 null - if (obj == null) - { - return null; - } - Validate.notBlank(methodName, "methodName can't be blank"); - for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) - { - Method[] methods = searchType.getDeclaredMethods(); - for (Method method : methods) - { - if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum) - { - makeAccessible(method); - return method; - } - } - } - return null; - } - - /** - * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 - */ - @SuppressWarnings("deprecation") - public static void makeAccessible(Method method) - { - if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) - && !method.isAccessible()) - { - method.setAccessible(true); - } - } - - /** - * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 - */ - @SuppressWarnings("deprecation") - public static void makeAccessible(Field field) - { - if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) - || Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) - { - field.setAccessible(true); - } - } - - /** - * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处 - * 如无法找到, 返回Object.class. - */ - @SuppressWarnings("unchecked") - public static Class getClassGenricType(final Class clazz) - { - return getClassGenricType(clazz, 0); - } - - /** - * 通过反射, 获得Class定义中声明的父类的泛型参数的类型. - * 如无法找到, 返回Object.class. - */ - public static Class getClassGenricType(final Class clazz, final int index) - { - Type genType = clazz.getGenericSuperclass(); - - if (!(genType instanceof ParameterizedType)) - { - logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType"); - return Object.class; - } - - Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); - - if (index >= params.length || index < 0) - { - logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: " - + params.length); - return Object.class; - } - if (!(params[index] instanceof Class)) - { - logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter"); - return Object.class; - } - - return (Class) params[index]; - } - - public static Class getUserClass(Object instance) - { - if (instance == null) - { - throw new RuntimeException("Instance must not be null"); - } - Class clazz = instance.getClass(); - if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) - { - Class superClass = clazz.getSuperclass(); - if (superClass != null && !Object.class.equals(superClass)) - { - return superClass; - } - } - return clazz; - - } - - /** - * 将反射时的checked exception转换为unchecked exception. - */ - public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e) - { - if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException - || e instanceof NoSuchMethodException) - { - return new IllegalArgumentException(msg, e); - } - else if (e instanceof InvocationTargetException) - { - return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException()); - } - return new RuntimeException(msg, e); - } -} +package com.ruoyi.common.utils.reflect; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Date; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; +import org.apache.poi.ss.usermodel.DateUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.utils.DateUtils; + +/** + * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数. + * + * @author ruoyi + */ +@SuppressWarnings("rawtypes") +public class ReflectUtils +{ + private static final String SETTER_PREFIX = "set"; + + private static final String GETTER_PREFIX = "get"; + + private static final String CGLIB_CLASS_SEPARATOR = "$$"; + + private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class); + + /** + * 调用Getter方法. + * 支持多级,如:对象名.对象名.方法 + */ + @SuppressWarnings("unchecked") + public static E invokeGetter(Object obj, String propertyName) + { + Object object = obj; + for (String name : StringUtils.split(propertyName, ".")) + { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name); + object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); + } + return (E) object; + } + + /** + * 调用Setter方法, 仅匹配方法名。 + * 支持多级,如:对象名.对象名.方法 + */ + public static void invokeSetter(Object obj, String propertyName, E value) + { + Object object = obj; + String[] names = StringUtils.split(propertyName, "."); + for (int i = 0; i < names.length; i++) + { + if (i < names.length - 1) + { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]); + object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); + } + else + { + String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]); + invokeMethodByName(object, setterMethodName, new Object[] { value }); + } + } + } + + /** + * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数. + */ + @SuppressWarnings("unchecked") + public static E getFieldValue(final Object obj, final String fieldName) + { + Field field = getAccessibleField(obj, fieldName); + if (field == null) + { + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + return null; + } + E result = null; + try + { + result = (E) field.get(obj); + } + catch (IllegalAccessException e) + { + logger.error("不可能抛出的异常{}", e.getMessage()); + } + return result; + } + + /** + * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数. + */ + public static void setFieldValue(final Object obj, final String fieldName, final E value) + { + Field field = getAccessibleField(obj, fieldName); + if (field == null) + { + // throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + return; + } + try + { + field.set(obj, value); + } + catch (IllegalAccessException e) + { + logger.error("不可能抛出的异常: {}", e.getMessage()); + } + } + + /** + * 直接调用对象方法, 无视private/protected修饰符. + * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用. + * 同时匹配方法名+参数类型, + */ + @SuppressWarnings("unchecked") + public static E invokeMethod(final Object obj, final String methodName, final Class[] parameterTypes, + final Object[] args) + { + if (obj == null || methodName == null) + { + return null; + } + Method method = getAccessibleMethod(obj, methodName, parameterTypes); + if (method == null) + { + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); + return null; + } + try + { + return (E) method.invoke(obj, args); + } + catch (Exception e) + { + String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; + throw convertReflectionExceptionToUnchecked(msg, e); + } + } + + /** + * 直接调用对象方法, 无视private/protected修饰符, + * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用. + * 只匹配函数名,如果有多个同名函数调用第一个。 + */ + @SuppressWarnings("unchecked") + public static E invokeMethodByName(final Object obj, final String methodName, final Object[] args) + { + Method method = getAccessibleMethodByName(obj, methodName, args.length); + if (method == null) + { + // 如果为空不报错,直接返回空。 + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); + return null; + } + try + { + // 类型转换(将参数数据类型转换为目标方法参数类型) + Class[] cs = method.getParameterTypes(); + for (int i = 0; i < cs.length; i++) + { + if (args[i] != null && !args[i].getClass().equals(cs[i])) + { + if (cs[i] == String.class) + { + args[i] = Convert.toStr(args[i]); + if (StringUtils.endsWith((String) args[i], ".0")) + { + args[i] = StringUtils.substringBefore((String) args[i], ".0"); + } + } + else if (cs[i] == Integer.class) + { + args[i] = Convert.toInt(args[i]); + } + else if (cs[i] == Long.class) + { + args[i] = Convert.toLong(args[i]); + } + else if (cs[i] == Double.class) + { + args[i] = Convert.toDouble(args[i]); + } + else if (cs[i] == Float.class) + { + args[i] = Convert.toFloat(args[i]); + } + else if (cs[i] == Date.class) + { + if (args[i] instanceof String) + { + args[i] = DateUtils.parseDate(args[i]); + } + else + { + args[i] = DateUtil.getJavaDate((Double) args[i]); + } + } + else if (cs[i] == boolean.class || cs[i] == Boolean.class) + { + args[i] = Convert.toBool(args[i]); + } + } + } + return (E) method.invoke(obj, args); + } + catch (Exception e) + { + String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; + throw convertReflectionExceptionToUnchecked(msg, e); + } + } + + /** + * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + */ + public static Field getAccessibleField(final Object obj, final String fieldName) + { + // 为空不报错。直接返回 null + if (obj == null) + { + return null; + } + Validate.notBlank(fieldName, "fieldName can't be blank"); + for (Class superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) + { + try + { + Field field = superClass.getDeclaredField(fieldName); + makeAccessible(field); + return field; + } + catch (NoSuchFieldException e) + { + continue; + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * 匹配函数名+参数类型。 + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + */ + public static Method getAccessibleMethod(final Object obj, final String methodName, + final Class... parameterTypes) + { + // 为空不报错。直接返回 null + if (obj == null) + { + return null; + } + Validate.notBlank(methodName, "methodName can't be blank"); + for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) + { + try + { + Method method = searchType.getDeclaredMethod(methodName, parameterTypes); + makeAccessible(method); + return method; + } + catch (NoSuchMethodException e) + { + continue; + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * 只匹配函数名。 + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + */ + public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum) + { + // 为空不报错。直接返回 null + if (obj == null) + { + return null; + } + Validate.notBlank(methodName, "methodName can't be blank"); + for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) + { + Method[] methods = searchType.getDeclaredMethods(); + for (Method method : methods) + { + if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum) + { + makeAccessible(method); + return method; + } + } + } + return null; + } + + /** + * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + */ + public static void makeAccessible(Method method) + { + if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) + && !method.isAccessible()) + { + method.setAccessible(true); + } + } + + /** + * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + */ + public static void makeAccessible(Field field) + { + if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) + || Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) + { + field.setAccessible(true); + } + } + + /** + * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处 + * 如无法找到, 返回Object.class. + */ + @SuppressWarnings("unchecked") + public static Class getClassGenricType(final Class clazz) + { + return getClassGenricType(clazz, 0); + } + + /** + * 通过反射, 获得Class定义中声明的父类的泛型参数的类型. + * 如无法找到, 返回Object.class. + */ + public static Class getClassGenricType(final Class clazz, final int index) + { + Type genType = clazz.getGenericSuperclass(); + + if (!(genType instanceof ParameterizedType)) + { + logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType"); + return Object.class; + } + + Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); + + if (index >= params.length || index < 0) + { + logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: " + + params.length); + return Object.class; + } + if (!(params[index] instanceof Class)) + { + logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter"); + return Object.class; + } + + return (Class) params[index]; + } + + public static Class getUserClass(Object instance) + { + if (instance == null) + { + throw new RuntimeException("Instance must not be null"); + } + Class clazz = instance.getClass(); + if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) + { + Class superClass = clazz.getSuperclass(); + if (superClass != null && !Object.class.equals(superClass)) + { + return superClass; + } + } + return clazz; + + } + + /** + * 将反射时的checked exception转换为unchecked exception. + */ + public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e) + { + if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException + || e instanceof NoSuchMethodException) + { + return new IllegalArgumentException(msg, e); + } + else if (e instanceof InvocationTargetException) + { + return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException()); + } + return new RuntimeException(msg, e); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Base64.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Base64.java index ca1cd92..f2c1bfe 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Base64.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Base64.java @@ -1,291 +1,291 @@ -package com.ruoyi.common.utils.sign; - -/** - * Base64工具类 - * - * @author ruoyi - */ -public final class Base64 -{ - static private final int BASELENGTH = 128; - static private final int LOOKUPLENGTH = 64; - static private final int TWENTYFOURBITGROUP = 24; - static private final int EIGHTBIT = 8; - static private final int SIXTEENBIT = 16; - static private final int FOURBYTE = 4; - static private final int SIGN = -128; - static private final char PAD = '='; - static final private byte[] base64Alphabet = new byte[BASELENGTH]; - static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH]; - - static - { - for (int i = 0; i < BASELENGTH; ++i) - { - base64Alphabet[i] = -1; - } - for (int i = 'Z'; i >= 'A'; i--) - { - base64Alphabet[i] = (byte) (i - 'A'); - } - for (int i = 'z'; i >= 'a'; i--) - { - base64Alphabet[i] = (byte) (i - 'a' + 26); - } - - for (int i = '9'; i >= '0'; i--) - { - base64Alphabet[i] = (byte) (i - '0' + 52); - } - - base64Alphabet['+'] = 62; - base64Alphabet['/'] = 63; - - for (int i = 0; i <= 25; i++) - { - lookUpBase64Alphabet[i] = (char) ('A' + i); - } - - for (int i = 26, j = 0; i <= 51; i++, j++) - { - lookUpBase64Alphabet[i] = (char) ('a' + j); - } - - for (int i = 52, j = 0; i <= 61; i++, j++) - { - lookUpBase64Alphabet[i] = (char) ('0' + j); - } - lookUpBase64Alphabet[62] = (char) '+'; - lookUpBase64Alphabet[63] = (char) '/'; - } - - private static boolean isWhiteSpace(char octect) - { - return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9); - } - - private static boolean isPad(char octect) - { - return (octect == PAD); - } - - private static boolean isData(char octect) - { - return (octect < BASELENGTH && base64Alphabet[octect] != -1); - } - - /** - * Encodes hex octects into Base64 - * - * @param binaryData Array containing binaryData - * @return Encoded Base64 array - */ - public static String encode(byte[] binaryData) - { - if (binaryData == null) - { - return null; - } - - int lengthDataBits = binaryData.length * EIGHTBIT; - if (lengthDataBits == 0) - { - return ""; - } - - int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP; - int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP; - int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets; - char encodedData[] = null; - - encodedData = new char[numberQuartet * 4]; - - byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0; - - int encodedIndex = 0; - int dataIndex = 0; - - for (int i = 0; i < numberTriplets; i++) - { - b1 = binaryData[dataIndex++]; - b2 = binaryData[dataIndex++]; - b3 = binaryData[dataIndex++]; - - l = (byte) (b2 & 0x0f); - k = (byte) (b1 & 0x03); - - byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); - byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); - byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc); - - encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; - encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; - encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3]; - encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f]; - } - - // form integral number of 6-bit groups - if (fewerThan24bits == EIGHTBIT) - { - b1 = binaryData[dataIndex]; - k = (byte) (b1 & 0x03); - byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); - encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; - encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4]; - encodedData[encodedIndex++] = PAD; - encodedData[encodedIndex++] = PAD; - } - else if (fewerThan24bits == SIXTEENBIT) - { - b1 = binaryData[dataIndex]; - b2 = binaryData[dataIndex + 1]; - l = (byte) (b2 & 0x0f); - k = (byte) (b1 & 0x03); - - byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); - byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); - - encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; - encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; - encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2]; - encodedData[encodedIndex++] = PAD; - } - return new String(encodedData); - } - - /** - * Decodes Base64 data into octects - * - * @param encoded string containing Base64 data - * @return Array containind decoded data. - */ - public static byte[] decode(String encoded) - { - if (encoded == null) - { - return null; - } - - char[] base64Data = encoded.toCharArray(); - // remove white spaces - int len = removeWhiteSpace(base64Data); - - if (len % FOURBYTE != 0) - { - return null;// should be divisible by four - } - - int numberQuadruple = (len / FOURBYTE); - - if (numberQuadruple == 0) - { - return new byte[0]; - } - - byte decodedData[] = null; - byte b1 = 0, b2 = 0, b3 = 0, b4 = 0; - char d1 = 0, d2 = 0, d3 = 0, d4 = 0; - - int i = 0; - int encodedIndex = 0; - int dataIndex = 0; - decodedData = new byte[(numberQuadruple) * 3]; - - for (; i < numberQuadruple - 1; i++) - { - - if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++])) - || !isData((d3 = base64Data[dataIndex++])) || !isData((d4 = base64Data[dataIndex++]))) - { - return null; - } // if found "no data" just return null - - b1 = base64Alphabet[d1]; - b2 = base64Alphabet[d2]; - b3 = base64Alphabet[d3]; - b4 = base64Alphabet[d4]; - - decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); - decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); - decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); - } - - if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) - { - return null;// if found "no data" just return null - } - - b1 = base64Alphabet[d1]; - b2 = base64Alphabet[d2]; - - d3 = base64Data[dataIndex++]; - d4 = base64Data[dataIndex++]; - if (!isData((d3)) || !isData((d4))) - {// Check if they are PAD characters - if (isPad(d3) && isPad(d4)) - { - if ((b2 & 0xf) != 0)// last 4 bits should be zero - { - return null; - } - byte[] tmp = new byte[i * 3 + 1]; - System.arraycopy(decodedData, 0, tmp, 0, i * 3); - tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); - return tmp; - } - else if (!isPad(d3) && isPad(d4)) - { - b3 = base64Alphabet[d3]; - if ((b3 & 0x3) != 0)// last 2 bits should be zero - { - return null; - } - byte[] tmp = new byte[i * 3 + 2]; - System.arraycopy(decodedData, 0, tmp, 0, i * 3); - tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); - tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); - return tmp; - } - else - { - return null; - } - } - else - { // No PAD e.g 3cQl - b3 = base64Alphabet[d3]; - b4 = base64Alphabet[d4]; - decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); - decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); - decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); - - } - return decodedData; - } - - /** - * remove WhiteSpace from MIME containing encoded Base64 data. - * - * @param data the byte array of base64 data (with WS) - * @return the new length - */ - private static int removeWhiteSpace(char[] data) - { - if (data == null) - { - return 0; - } - - // count characters that's not whitespace - int newSize = 0; - int len = data.length; - for (int i = 0; i < len; i++) - { - if (!isWhiteSpace(data[i])) - { - data[newSize++] = data[i]; - } - } - return newSize; - } -} +package com.ruoyi.common.utils.sign; + +/** + * Base64工具类 + * + * @author ruoyi + */ +public final class Base64 +{ + static private final int BASELENGTH = 128; + static private final int LOOKUPLENGTH = 64; + static private final int TWENTYFOURBITGROUP = 24; + static private final int EIGHTBIT = 8; + static private final int SIXTEENBIT = 16; + static private final int FOURBYTE = 4; + static private final int SIGN = -128; + static private final char PAD = '='; + static final private byte[] base64Alphabet = new byte[BASELENGTH]; + static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH]; + + static + { + for (int i = 0; i < BASELENGTH; ++i) + { + base64Alphabet[i] = -1; + } + for (int i = 'Z'; i >= 'A'; i--) + { + base64Alphabet[i] = (byte) (i - 'A'); + } + for (int i = 'z'; i >= 'a'; i--) + { + base64Alphabet[i] = (byte) (i - 'a' + 26); + } + + for (int i = '9'; i >= '0'; i--) + { + base64Alphabet[i] = (byte) (i - '0' + 52); + } + + base64Alphabet['+'] = 62; + base64Alphabet['/'] = 63; + + for (int i = 0; i <= 25; i++) + { + lookUpBase64Alphabet[i] = (char) ('A' + i); + } + + for (int i = 26, j = 0; i <= 51; i++, j++) + { + lookUpBase64Alphabet[i] = (char) ('a' + j); + } + + for (int i = 52, j = 0; i <= 61; i++, j++) + { + lookUpBase64Alphabet[i] = (char) ('0' + j); + } + lookUpBase64Alphabet[62] = (char) '+'; + lookUpBase64Alphabet[63] = (char) '/'; + } + + private static boolean isWhiteSpace(char octect) + { + return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9); + } + + private static boolean isPad(char octect) + { + return (octect == PAD); + } + + private static boolean isData(char octect) + { + return (octect < BASELENGTH && base64Alphabet[octect] != -1); + } + + /** + * Encodes hex octects into Base64 + * + * @param binaryData Array containing binaryData + * @return Encoded Base64 array + */ + public static String encode(byte[] binaryData) + { + if (binaryData == null) + { + return null; + } + + int lengthDataBits = binaryData.length * EIGHTBIT; + if (lengthDataBits == 0) + { + return ""; + } + + int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP; + int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP; + int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets; + char encodedData[] = null; + + encodedData = new char[numberQuartet * 4]; + + byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0; + + int encodedIndex = 0; + int dataIndex = 0; + + for (int i = 0; i < numberTriplets; i++) + { + b1 = binaryData[dataIndex++]; + b2 = binaryData[dataIndex++]; + b3 = binaryData[dataIndex++]; + + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f]; + } + + // form integral number of 6-bit groups + if (fewerThan24bits == EIGHTBIT) + { + b1 = binaryData[dataIndex]; + k = (byte) (b1 & 0x03); + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4]; + encodedData[encodedIndex++] = PAD; + encodedData[encodedIndex++] = PAD; + } + else if (fewerThan24bits == SIXTEENBIT) + { + b1 = binaryData[dataIndex]; + b2 = binaryData[dataIndex + 1]; + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2]; + encodedData[encodedIndex++] = PAD; + } + return new String(encodedData); + } + + /** + * Decodes Base64 data into octects + * + * @param encoded string containing Base64 data + * @return Array containind decoded data. + */ + public static byte[] decode(String encoded) + { + if (encoded == null) + { + return null; + } + + char[] base64Data = encoded.toCharArray(); + // remove white spaces + int len = removeWhiteSpace(base64Data); + + if (len % FOURBYTE != 0) + { + return null;// should be divisible by four + } + + int numberQuadruple = (len / FOURBYTE); + + if (numberQuadruple == 0) + { + return new byte[0]; + } + + byte decodedData[] = null; + byte b1 = 0, b2 = 0, b3 = 0, b4 = 0; + char d1 = 0, d2 = 0, d3 = 0, d4 = 0; + + int i = 0; + int encodedIndex = 0; + int dataIndex = 0; + decodedData = new byte[(numberQuadruple) * 3]; + + for (; i < numberQuadruple - 1; i++) + { + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++])) + || !isData((d3 = base64Data[dataIndex++])) || !isData((d4 = base64Data[dataIndex++]))) + { + return null; + } // if found "no data" just return null + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); + } + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) + { + return null;// if found "no data" just return null + } + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + + d3 = base64Data[dataIndex++]; + d4 = base64Data[dataIndex++]; + if (!isData((d3)) || !isData((d4))) + {// Check if they are PAD characters + if (isPad(d3) && isPad(d4)) + { + if ((b2 & 0xf) != 0)// last 4 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 1]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); + return tmp; + } + else if (!isPad(d3) && isPad(d4)) + { + b3 = base64Alphabet[d3]; + if ((b3 & 0x3) != 0)// last 2 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 2]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + return tmp; + } + else + { + return null; + } + } + else + { // No PAD e.g 3cQl + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); + + } + return decodedData; + } + + /** + * remove WhiteSpace from MIME containing encoded Base64 data. + * + * @param data the byte array of base64 data (with WS) + * @return the new length + */ + private static int removeWhiteSpace(char[] data) + { + if (data == null) + { + return 0; + } + + // count characters that's not whitespace + int newSize = 0; + int len = data.length; + for (int i = 0; i < len; i++) + { + if (!isWhiteSpace(data[i])) + { + data[newSize++] = data[i]; + } + } + return newSize; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java index c1c58db..8fb0fc5 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java @@ -1,67 +1,67 @@ -package com.ruoyi.common.utils.sign; - -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Md5加密方法 - * - * @author ruoyi - */ -public class Md5Utils -{ - private static final Logger log = LoggerFactory.getLogger(Md5Utils.class); - - private static byte[] md5(String s) - { - MessageDigest algorithm; - try - { - algorithm = MessageDigest.getInstance("MD5"); - algorithm.reset(); - algorithm.update(s.getBytes("UTF-8")); - byte[] messageDigest = algorithm.digest(); - return messageDigest; - } - catch (Exception e) - { - log.error("MD5 Error...", e); - } - return null; - } - - private static final String toHex(byte hash[]) - { - if (hash == null) - { - return null; - } - StringBuffer buf = new StringBuffer(hash.length * 2); - int i; - - for (i = 0; i < hash.length; i++) - { - if ((hash[i] & 0xff) < 0x10) - { - buf.append("0"); - } - buf.append(Long.toString(hash[i] & 0xff, 16)); - } - return buf.toString(); - } - - public static String hash(String s) - { - try - { - return new String(toHex(md5(s)).getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8); - } - catch (Exception e) - { - log.error("not supported charset...{}", e); - return s; - } - } -} +package com.ruoyi.common.utils.sign; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Md5加密方法 + * + * @author ruoyi + */ +public class Md5Utils +{ + private static final Logger log = LoggerFactory.getLogger(Md5Utils.class); + + private static byte[] md5(String s) + { + MessageDigest algorithm; + try + { + algorithm = MessageDigest.getInstance("MD5"); + algorithm.reset(); + algorithm.update(s.getBytes("UTF-8")); + byte[] messageDigest = algorithm.digest(); + return messageDigest; + } + catch (Exception e) + { + log.error("MD5 Error...", e); + } + return null; + } + + private static final String toHex(byte hash[]) + { + if (hash == null) + { + return null; + } + StringBuffer buf = new StringBuffer(hash.length * 2); + int i; + + for (i = 0; i < hash.length; i++) + { + if ((hash[i] & 0xff) < 0x10) + { + buf.append("0"); + } + buf.append(Long.toString(hash[i] & 0xff, 16)); + } + return buf.toString(); + } + + public static String hash(String s) + { + try + { + return new String(toHex(md5(s)).getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8); + } + catch (Exception e) + { + log.error("not supported charset...{}", e); + return s; + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java index 4e3f603..64a0249 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java @@ -1,164 +1,164 @@ -package com.ruoyi.common.utils.spring; - -import org.springframework.aop.framework.Advised; -import org.springframework.aop.framework.AopContext; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.stereotype.Component; -import com.ruoyi.common.utils.StringUtils; - -/** - * spring工具类 方便在非spring管理环境中获取bean - * - * @author ruoyi - */ -@Component -public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware -{ - /** Spring应用上下文环境 */ - private static ConfigurableListableBeanFactory beanFactory; - - private static ApplicationContext applicationContext; - - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException - { - SpringUtils.beanFactory = beanFactory; - } - - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException - { - SpringUtils.applicationContext = applicationContext; - } - - /** - * 获取对象 - * - * @param name - * @return Object 一个以所给名字注册的bean的实例 - * @throws org.springframework.beans.BeansException - * - */ - @SuppressWarnings("unchecked") - public static T getBean(String name) throws BeansException - { - return (T) beanFactory.getBean(name); - } - - /** - * 获取类型为requiredType的对象 - * - * @param clz - * @return - * @throws org.springframework.beans.BeansException - * - */ - public static T getBean(Class clz) throws BeansException - { - T result = (T) beanFactory.getBean(clz); - return result; - } - - /** - * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true - * - * @param name - * @return boolean - */ - public static boolean containsBean(String name) - { - return beanFactory.containsBean(name); - } - - /** - * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) - * - * @param name - * @return boolean - * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException - * - */ - public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException - { - return beanFactory.isSingleton(name); - } - - /** - * @param name - * @return Class 注册对象的类型 - * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException - * - */ - public static Class getType(String name) throws NoSuchBeanDefinitionException - { - return beanFactory.getType(name); - } - - /** - * 如果给定的bean名字在bean定义中有别名,则返回这些别名 - * - * @param name - * @return - * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException - * - */ - public static String[] getAliases(String name) throws NoSuchBeanDefinitionException - { - return beanFactory.getAliases(name); - } - - /** - * 获取aop代理对象 - * - * @param invoker - * @return - */ - @SuppressWarnings("unchecked") - public static T getAopProxy(T invoker) - { - Object proxy = AopContext.currentProxy(); - if (((Advised) proxy).getTargetSource().getTargetClass() == invoker.getClass()) - { - return (T) proxy; - } - return invoker; - } - - /** - * 获取当前的环境配置,无配置返回null - * - * @return 当前的环境配置 - */ - public static String[] getActiveProfiles() - { - return applicationContext.getEnvironment().getActiveProfiles(); - } - - /** - * 获取当前的环境配置,当有多个环境配置时,只获取第一个 - * - * @return 当前的环境配置 - */ - public static String getActiveProfile() - { - final String[] activeProfiles = getActiveProfiles(); - return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null; - } - - /** - * 获取配置文件中的值 - * - * @param key 配置文件的key - * @return 当前的配置文件的值 - * - */ - public static String getRequiredProperty(String key) - { - return applicationContext.getEnvironment().getRequiredProperty(key); - } -} +package com.ruoyi.common.utils.spring; + +import org.springframework.aop.framework.Advised; +import org.springframework.aop.framework.AopContext; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; +import com.ruoyi.common.utils.StringUtils; + +/** + * spring工具类 方便在非spring管理环境中获取bean + * + * @author ruoyi + */ +@Component +public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware +{ + /** Spring应用上下文环境 */ + private static ConfigurableListableBeanFactory beanFactory; + + private static ApplicationContext applicationContext; + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException + { + SpringUtils.beanFactory = beanFactory; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException + { + SpringUtils.applicationContext = applicationContext; + } + + /** + * 获取对象 + * + * @param name + * @return Object 一个以所给名字注册的bean的实例 + * @throws org.springframework.beans.BeansException + * + */ + @SuppressWarnings("unchecked") + public static T getBean(String name) throws BeansException + { + return (T) beanFactory.getBean(name); + } + + /** + * 获取类型为requiredType的对象 + * + * @param clz + * @return + * @throws org.springframework.beans.BeansException + * + */ + public static T getBean(Class clz) throws BeansException + { + T result = (T) beanFactory.getBean(clz); + return result; + } + + /** + * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true + * + * @param name + * @return boolean + */ + public static boolean containsBean(String name) + { + return beanFactory.containsBean(name); + } + + /** + * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) + * + * @param name + * @return boolean + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.isSingleton(name); + } + + /** + * @param name + * @return Class 注册对象的类型 + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static Class getType(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.getType(name); + } + + /** + * 如果给定的bean名字在bean定义中有别名,则返回这些别名 + * + * @param name + * @return + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static String[] getAliases(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.getAliases(name); + } + + /** + * 获取aop代理对象 + * + * @param invoker + * @return + */ + @SuppressWarnings("unchecked") + public static T getAopProxy(T invoker) + { + Object proxy = AopContext.currentProxy(); + if (((Advised) proxy).getTargetSource().getTargetClass() == invoker.getClass()) + { + return (T) proxy; + } + return invoker; + } + + /** + * 获取当前的环境配置,无配置返回null + * + * @return 当前的环境配置 + */ + public static String[] getActiveProfiles() + { + return applicationContext.getEnvironment().getActiveProfiles(); + } + + /** + * 获取当前的环境配置,当有多个环境配置时,只获取第一个 + * + * @return 当前的环境配置 + */ + public static String getActiveProfile() + { + final String[] activeProfiles = getActiveProfiles(); + return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null; + } + + /** + * 获取配置文件中的值 + * + * @param key 配置文件的key + * @return 当前的配置文件的值 + * + */ + public static String getRequiredProperty(String key) + { + return applicationContext.getEnvironment().getRequiredProperty(key); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java index e08683d..b11f8d5 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java @@ -1,70 +1,71 @@ -package com.ruoyi.common.utils.sql; - -import com.ruoyi.common.exception.UtilException; -import com.ruoyi.common.utils.StringUtils; - -/** - * sql操作工具类 - * - * @author ruoyi - */ -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_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+"; - - /** - * 限制orderBy最大长度 - */ - private static final int ORDER_BY_MAX_LENGTH = 500; - - /** - * 检查字符,防止注入绕过 - */ - public static String escapeOrderBySql(String value) - { - if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) - { - throw new UtilException("参数不符合规范,不能进行查询"); - } - if (StringUtils.length(value) > ORDER_BY_MAX_LENGTH) - { - throw new UtilException("参数已超过最大限制,不能进行查询"); - } - return value; - } - - /** - * 验证 order by 语法是否符合规范 - */ - public static boolean isValidOrderBySql(String value) - { - return value.matches(SQL_PATTERN); - } - - /** - * SQL关键字检查 - */ - public static void filterKeyword(String value) - { - if (StringUtils.isEmpty(value)) - { - return; - } - String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|"); - for (String sqlKeyword : sqlKeywords) - { - if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) - { - throw new UtilException("参数存在SQL注入风险"); - } - } - } -} +package com.ruoyi.common.utils.sql; + +import com.ruoyi.common.exception.UtilException; +import com.ruoyi.common.utils.StringUtils; + +/** + * sql操作工具类 + * + * @author ruoyi + */ +public class SqlUtil +{ + /** + * 定义常用的 sql关键字 + */ + 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()"; + + /** + * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) + */ + public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+"; + + /** + * 限制orderBy最大长度 + */ + private static final int ORDER_BY_MAX_LENGTH = 500; + + /** + * 检查字符,防止注入绕过 + */ + public static String escapeOrderBySql(String value) + { + if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) + { + throw new UtilException("参数不符合规范,不能进行查询"); + } + if (StringUtils.length(value) > ORDER_BY_MAX_LENGTH) + { + throw new UtilException("参数已超过最大限制,不能进行查询"); + } + return value; + } + + /** + * 验证 order by 语法是否符合规范 + */ + public static boolean isValidOrderBySql(String value) + { + return value.matches(SQL_PATTERN); + } + + /** + * SQL关键字检查 + */ + public static void filterKeyword(String value) + { + if (StringUtils.isEmpty(value)) + { + return; + } + String normalizedValue = value.replaceAll("\\p{Z}|\\s", ""); + String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|"); + for (String sqlKeyword : sqlKeywords) + { + if (StringUtils.indexOfIgnoreCase(normalizedValue, sqlKeyword) > -1) + { + throw new UtilException("请求参数包含敏感关键词'" + sqlKeyword + "',可能存在安全风险"); + } + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/IdUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/IdUtils.java index 2c84427..0256511 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/IdUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/IdUtils.java @@ -1,49 +1,49 @@ -package com.ruoyi.common.utils.uuid; - -/** - * ID生成器工具类 - * - * @author ruoyi - */ -public class IdUtils -{ - /** - * 获取随机UUID - * - * @return 随机UUID - */ - public static String randomUUID() - { - return UUID.randomUUID().toString(); - } - - /** - * 简化的UUID,去掉了横线 - * - * @return 简化的UUID,去掉了横线 - */ - public static String simpleUUID() - { - return UUID.randomUUID().toString(true); - } - - /** - * 获取随机UUID,使用性能更好的ThreadLocalRandom生成UUID - * - * @return 随机UUID - */ - public static String fastUUID() - { - return UUID.fastUUID().toString(); - } - - /** - * 简化的UUID,去掉了横线,使用性能更好的ThreadLocalRandom生成UUID - * - * @return 简化的UUID,去掉了横线 - */ - public static String fastSimpleUUID() - { - return UUID.fastUUID().toString(true); - } -} +package com.ruoyi.common.utils.uuid; + +/** + * ID生成器工具类 + * + * @author ruoyi + */ +public class IdUtils +{ + /** + * 获取随机UUID + * + * @return 随机UUID + */ + public static String randomUUID() + { + return UUID.randomUUID().toString(); + } + + /** + * 简化的UUID,去掉了横线 + * + * @return 简化的UUID,去掉了横线 + */ + public static String simpleUUID() + { + return UUID.randomUUID().toString(true); + } + + /** + * 获取随机UUID,使用性能更好的ThreadLocalRandom生成UUID + * + * @return 随机UUID + */ + public static String fastUUID() + { + return UUID.fastUUID().toString(); + } + + /** + * 简化的UUID,去掉了横线,使用性能更好的ThreadLocalRandom生成UUID + * + * @return 简化的UUID,去掉了横线 + */ + public static String fastSimpleUUID() + { + return UUID.fastUUID().toString(true); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/Seq.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/Seq.java index bf99611..3f621d5 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/Seq.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/Seq.java @@ -1,86 +1,86 @@ -package com.ruoyi.common.utils.uuid; - -import java.util.concurrent.atomic.AtomicInteger; -import com.ruoyi.common.utils.DateUtils; -import com.ruoyi.common.utils.StringUtils; - -/** - * @author ruoyi 序列生成类 - */ -public class Seq -{ - // 通用序列类型 - public static final String commSeqType = "COMMON"; - - // 上传序列类型 - public static final String uploadSeqType = "UPLOAD"; - - // 通用接口序列数 - private static AtomicInteger commSeq = new AtomicInteger(1); - - // 上传接口序列数 - private static AtomicInteger uploadSeq = new AtomicInteger(1); - - // 机器标识 - private static final String machineCode = "A"; - - /** - * 获取通用序列号 - * - * @return 序列值 - */ - public static String getId() - { - return getId(commSeqType); - } - - /** - * 默认16位序列号 yyMMddHHmmss + 一位机器标识 + 3长度循环递增字符串 - * - * @return 序列值 - */ - public static String getId(String type) - { - AtomicInteger atomicInt = commSeq; - if (uploadSeqType.equals(type)) - { - atomicInt = uploadSeq; - } - return getId(atomicInt, 3); - } - - /** - * 通用接口序列号 yyMMddHHmmss + 一位机器标识 + length长度循环递增字符串 - * - * @param atomicInt 序列数 - * @param length 数值长度 - * @return 序列值 - */ - public static String getId(AtomicInteger atomicInt, int length) - { - String result = DateUtils.dateTimeNow(); - result += machineCode; - result += getSeq(atomicInt, length); - return result; - } - - /** - * 序列循环递增字符串[1, 10 的 (length)幂次方), 用0左补齐length位数 - * - * @return 序列值 - */ - private synchronized static String getSeq(AtomicInteger atomicInt, int length) - { - // 先取值再+1 - int value = atomicInt.getAndIncrement(); - - // 如果更新后值>=10 的 (length)幂次方则重置为1 - int maxSeq = (int) Math.pow(10, length); - if (atomicInt.get() >= maxSeq) - { - atomicInt.set(1); - } - // 转字符串,用0左补齐 - return StringUtils.padl(value, length); - } -} +package com.ruoyi.common.utils.uuid; + +import java.util.concurrent.atomic.AtomicInteger; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; + +/** + * @author ruoyi 序列生成类 + */ +public class Seq +{ + // 通用序列类型 + public static final String commSeqType = "COMMON"; + + // 上传序列类型 + public static final String uploadSeqType = "UPLOAD"; + + // 通用接口序列数 + private static AtomicInteger commSeq = new AtomicInteger(1); + + // 上传接口序列数 + private static AtomicInteger uploadSeq = new AtomicInteger(1); + + // 机器标识 + private static final String machineCode = "A"; + + /** + * 获取通用序列号 + * + * @return 序列值 + */ + public static String getId() + { + return getId(commSeqType); + } + + /** + * 默认16位序列号 yyMMddHHmmss + 一位机器标识 + 3长度循环递增字符串 + * + * @return 序列值 + */ + public static String getId(String type) + { + AtomicInteger atomicInt = commSeq; + if (uploadSeqType.equals(type)) + { + atomicInt = uploadSeq; + } + return getId(atomicInt, 3); + } + + /** + * 通用接口序列号 yyMMddHHmmss + 一位机器标识 + length长度循环递增字符串 + * + * @param atomicInt 序列数 + * @param length 数值长度 + * @return 序列值 + */ + public static String getId(AtomicInteger atomicInt, int length) + { + String result = DateUtils.dateTimeNow(); + result += machineCode; + result += getSeq(atomicInt, length); + return result; + } + + /** + * 序列循环递增字符串[1, 10 的 (length)幂次方), 用0左补齐length位数 + * + * @return 序列值 + */ + private synchronized static String getSeq(AtomicInteger atomicInt, int length) + { + // 先取值再+1 + int value = atomicInt.getAndIncrement(); + + // 如果更新后值>=10 的 (length)幂次方则重置为1 + int maxSeq = (int) Math.pow(10, length); + if (atomicInt.get() >= maxSeq) + { + atomicInt.set(1); + } + // 转字符串,用0左补齐 + return StringUtils.padl(value, length); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/UUID.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/UUID.java index a5585d6..97497f2 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/UUID.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/UUID.java @@ -1,484 +1,484 @@ -package com.ruoyi.common.utils.uuid; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.util.Random; -import java.util.concurrent.ThreadLocalRandom; -import com.ruoyi.common.exception.UtilException; - -/** - * 提供通用唯一识别码(universally unique identifier)(UUID)实现 - * - * @author ruoyi - */ -public final class UUID implements java.io.Serializable, Comparable -{ - private static final long serialVersionUID = -1185015143654744140L; - - /** - * SecureRandom 的单例 - * - */ - private static class Holder - { - static final SecureRandom numberGenerator = getSecureRandom(); - } - - /** 此UUID的最高64有效位 */ - private final long mostSigBits; - - /** 此UUID的最低64有效位 */ - private final long leastSigBits; - - /** - * 私有构造 - * - * @param data 数据 - */ - private UUID(byte[] data) - { - long msb = 0; - long lsb = 0; - assert data.length == 16 : "data must be 16 bytes in length"; - for (int i = 0; i < 8; i++) - { - msb = (msb << 8) | (data[i] & 0xff); - } - for (int i = 8; i < 16; i++) - { - lsb = (lsb << 8) | (data[i] & 0xff); - } - this.mostSigBits = msb; - this.leastSigBits = lsb; - } - - /** - * 使用指定的数据构造新的 UUID。 - * - * @param mostSigBits 用于 {@code UUID} 的最高有效 64 位 - * @param leastSigBits 用于 {@code UUID} 的最低有效 64 位 - */ - public UUID(long mostSigBits, long leastSigBits) - { - this.mostSigBits = mostSigBits; - this.leastSigBits = leastSigBits; - } - - /** - * 获取类型 4(伪随机生成的)UUID 的静态工厂。 - * - * @return 随机生成的 {@code UUID} - */ - public static UUID fastUUID() - { - return randomUUID(false); - } - - /** - * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 - * - * @return 随机生成的 {@code UUID} - */ - public static UUID randomUUID() - { - return randomUUID(true); - } - - /** - * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 - * - * @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能 - * @return 随机生成的 {@code UUID} - */ - public static UUID randomUUID(boolean isSecure) - { - final Random ng = isSecure ? Holder.numberGenerator : getRandom(); - - byte[] randomBytes = new byte[16]; - ng.nextBytes(randomBytes); - randomBytes[6] &= 0x0f; /* clear version */ - randomBytes[6] |= 0x40; /* set to version 4 */ - randomBytes[8] &= 0x3f; /* clear variant */ - randomBytes[8] |= 0x80; /* set to IETF variant */ - return new UUID(randomBytes); - } - - /** - * 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。 - * - * @param name 用于构造 UUID 的字节数组。 - * - * @return 根据指定数组生成的 {@code UUID} - */ - public static UUID nameUUIDFromBytes(byte[] name) - { - MessageDigest md; - try - { - md = MessageDigest.getInstance("MD5"); - } - catch (NoSuchAlgorithmException nsae) - { - throw new InternalError("MD5 not supported"); - } - byte[] md5Bytes = md.digest(name); - md5Bytes[6] &= 0x0f; /* clear version */ - md5Bytes[6] |= 0x30; /* set to version 3 */ - md5Bytes[8] &= 0x3f; /* clear variant */ - md5Bytes[8] |= 0x80; /* set to IETF variant */ - return new UUID(md5Bytes); - } - - /** - * 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。 - * - * @param name 指定 {@code UUID} 字符串 - * @return 具有指定值的 {@code UUID} - * @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常 - * - */ - public static UUID fromString(String name) - { - String[] components = name.split("-"); - if (components.length != 5) - { - throw new IllegalArgumentException("Invalid UUID string: " + name); - } - for (int i = 0; i < 5; i++) - { - components[i] = "0x" + components[i]; - } - - long mostSigBits = Long.decode(components[0]).longValue(); - mostSigBits <<= 16; - mostSigBits |= Long.decode(components[1]).longValue(); - mostSigBits <<= 16; - mostSigBits |= Long.decode(components[2]).longValue(); - - long leastSigBits = Long.decode(components[3]).longValue(); - leastSigBits <<= 48; - leastSigBits |= Long.decode(components[4]).longValue(); - - return new UUID(mostSigBits, leastSigBits); - } - - /** - * 返回此 UUID 的 128 位值中的最低有效 64 位。 - * - * @return 此 UUID 的 128 位值中的最低有效 64 位。 - */ - public long getLeastSignificantBits() - { - return leastSigBits; - } - - /** - * 返回此 UUID 的 128 位值中的最高有效 64 位。 - * - * @return 此 UUID 的 128 位值中最高有效 64 位。 - */ - public long getMostSignificantBits() - { - return mostSigBits; - } - - /** - * 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。 - *

- * 版本号具有以下含意: - *

    - *
  • 1 基于时间的 UUID - *
  • 2 DCE 安全 UUID - *
  • 3 基于名称的 UUID - *
  • 4 随机生成的 UUID - *
- * - * @return 此 {@code UUID} 的版本号 - */ - public int version() - { - // Version is bits masked by 0x000000000000F000 in MS long - return (int) ((mostSigBits >> 12) & 0x0f); - } - - /** - * 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。 - *

- * 变体号具有以下含意: - *

    - *
  • 0 为 NCS 向后兼容保留 - *
  • 2 IETF RFC 4122(Leach-Salz), 用于此类 - *
  • 6 保留,微软向后兼容 - *
  • 7 保留供以后定义使用 - *
- * - * @return 此 {@code UUID} 相关联的变体号 - */ - public int variant() - { - // This field is composed of a varying number of bits. - // 0 - - Reserved for NCS backward compatibility - // 1 0 - The IETF aka Leach-Salz variant (used by this class) - // 1 1 0 Reserved, Microsoft backward compatibility - // 1 1 1 Reserved for future definition. - return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63)); - } - - /** - * 与此 UUID 相关联的时间戳值。 - * - *

- * 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。
- * 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。 - * - *

- * 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。
- * 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 - * - * @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。 - */ - public long timestamp() throws UnsupportedOperationException - { - checkTimeBase(); - return (mostSigBits & 0x0FFFL) << 48// - | ((mostSigBits >> 16) & 0x0FFFFL) << 32// - | mostSigBits >>> 32; - } - - /** - * 与此 UUID 相关联的时钟序列值。 - * - *

- * 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。 - *

- * {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出 - * UnsupportedOperationException。 - * - * @return 此 {@code UUID} 的时钟序列 - * - * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 - */ - public int clockSequence() throws UnsupportedOperationException - { - checkTimeBase(); - return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48); - } - - /** - * 与此 UUID 相关的节点值。 - * - *

- * 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。 - *

- * 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。
- * 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 - * - * @return 此 {@code UUID} 的节点值 - * - * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 - */ - public long node() throws UnsupportedOperationException - { - checkTimeBase(); - return leastSigBits & 0x0000FFFFFFFFFFFFL; - } - - /** - * 返回此{@code UUID} 的字符串表现形式。 - * - *

- * UUID 的字符串表示形式由此 BNF 描述: - * - *

-     * {@code
-     * UUID                   = ----
-     * time_low               = 4*
-     * time_mid               = 2*
-     * time_high_and_version  = 2*
-     * variant_and_sequence   = 2*
-     * node                   = 6*
-     * hexOctet               = 
-     * hexDigit               = [0-9a-fA-F]
-     * }
-     * 
- * - * - * - * @return 此{@code UUID} 的字符串表现形式 - * @see #toString(boolean) - */ - @Override - public String toString() - { - return toString(false); - } - - /** - * 返回此{@code UUID} 的字符串表现形式。 - * - *

- * UUID 的字符串表示形式由此 BNF 描述: - * - *

-     * {@code
-     * UUID                   = ----
-     * time_low               = 4*
-     * time_mid               = 2*
-     * time_high_and_version  = 2*
-     * variant_and_sequence   = 2*
-     * node                   = 6*
-     * hexOctet               = 
-     * hexDigit               = [0-9a-fA-F]
-     * }
-     * 
- * - * - * - * @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串 - * @return 此{@code UUID} 的字符串表现形式 - */ - public String toString(boolean isSimple) - { - final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36); - // time_low - builder.append(digits(mostSigBits >> 32, 8)); - if (!isSimple) - { - builder.append('-'); - } - // time_mid - builder.append(digits(mostSigBits >> 16, 4)); - if (!isSimple) - { - builder.append('-'); - } - // time_high_and_version - builder.append(digits(mostSigBits, 4)); - if (!isSimple) - { - builder.append('-'); - } - // variant_and_sequence - builder.append(digits(leastSigBits >> 48, 4)); - if (!isSimple) - { - builder.append('-'); - } - // node - builder.append(digits(leastSigBits, 12)); - - return builder.toString(); - } - - /** - * 返回此 UUID 的哈希码。 - * - * @return UUID 的哈希码值。 - */ - @Override - public int hashCode() - { - long hilo = mostSigBits ^ leastSigBits; - return ((int) (hilo >> 32)) ^ (int) hilo; - } - - /** - * 将此对象与指定对象比较。 - *

- * 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。 - * - * @param obj 要与之比较的对象 - * - * @return 如果对象相同,则返回 {@code true};否则返回 {@code false} - */ - @Override - public boolean equals(Object obj) - { - if ((null == obj) || (obj.getClass() != UUID.class)) - { - return false; - } - UUID id = (UUID) obj; - return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits); - } - - // Comparison Operations - - /** - * 将此 UUID 与指定的 UUID 比较。 - * - *

- * 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。 - * - * @param val 与此 UUID 比较的 UUID - * - * @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。 - * - */ - @Override - public int compareTo(UUID val) - { - // The ordering is intentionally set up so that the UUIDs - // can simply be numerically compared as two numbers - return (this.mostSigBits < val.mostSigBits ? -1 : // - (this.mostSigBits > val.mostSigBits ? 1 : // - (this.leastSigBits < val.leastSigBits ? -1 : // - (this.leastSigBits > val.leastSigBits ? 1 : // - 0)))); - } - - // ------------------------------------------------------------------------------------------------------------------- - // Private method start - /** - * 返回指定数字对应的hex值 - * - * @param val 值 - * @param digits 位 - * @return 值 - */ - private static String digits(long val, int digits) - { - long hi = 1L << (digits * 4); - return Long.toHexString(hi | (val & (hi - 1))).substring(1); - } - - /** - * 检查是否为time-based版本UUID - */ - private void checkTimeBase() - { - if (version() != 1) - { - throw new UnsupportedOperationException("Not a time-based UUID"); - } - } - - /** - * 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG) - * - * @return {@link SecureRandom} - */ - public static SecureRandom getSecureRandom() - { - try - { - return SecureRandom.getInstance("SHA1PRNG"); - } - catch (NoSuchAlgorithmException e) - { - throw new UtilException(e); - } - } - - /** - * 获取随机数生成器对象
- * ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。 - * - * @return {@link ThreadLocalRandom} - */ - public static ThreadLocalRandom getRandom() - { - return ThreadLocalRandom.current(); - } -} +package com.ruoyi.common.utils.uuid; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; +import com.ruoyi.common.exception.UtilException; + +/** + * 提供通用唯一识别码(universally unique identifier)(UUID)实现 + * + * @author ruoyi + */ +public final class UUID implements java.io.Serializable, Comparable +{ + private static final long serialVersionUID = -1185015143654744140L; + + /** + * SecureRandom 的单例 + * + */ + private static class Holder + { + static final SecureRandom numberGenerator = getSecureRandom(); + } + + /** 此UUID的最高64有效位 */ + private final long mostSigBits; + + /** 此UUID的最低64有效位 */ + private final long leastSigBits; + + /** + * 私有构造 + * + * @param data 数据 + */ + private UUID(byte[] data) + { + long msb = 0; + long lsb = 0; + assert data.length == 16 : "data must be 16 bytes in length"; + for (int i = 0; i < 8; i++) + { + msb = (msb << 8) | (data[i] & 0xff); + } + for (int i = 8; i < 16; i++) + { + lsb = (lsb << 8) | (data[i] & 0xff); + } + this.mostSigBits = msb; + this.leastSigBits = lsb; + } + + /** + * 使用指定的数据构造新的 UUID。 + * + * @param mostSigBits 用于 {@code UUID} 的最高有效 64 位 + * @param leastSigBits 用于 {@code UUID} 的最低有效 64 位 + */ + public UUID(long mostSigBits, long leastSigBits) + { + this.mostSigBits = mostSigBits; + this.leastSigBits = leastSigBits; + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID fastUUID() + { + return randomUUID(false); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID() + { + return randomUUID(true); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能 + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID(boolean isSecure) + { + final Random ng = isSecure ? Holder.numberGenerator : getRandom(); + + byte[] randomBytes = new byte[16]; + ng.nextBytes(randomBytes); + randomBytes[6] &= 0x0f; /* clear version */ + randomBytes[6] |= 0x40; /* set to version 4 */ + randomBytes[8] &= 0x3f; /* clear variant */ + randomBytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(randomBytes); + } + + /** + * 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。 + * + * @param name 用于构造 UUID 的字节数组。 + * + * @return 根据指定数组生成的 {@code UUID} + */ + public static UUID nameUUIDFromBytes(byte[] name) + { + MessageDigest md; + try + { + md = MessageDigest.getInstance("MD5"); + } + catch (NoSuchAlgorithmException nsae) + { + throw new InternalError("MD5 not supported"); + } + byte[] md5Bytes = md.digest(name); + md5Bytes[6] &= 0x0f; /* clear version */ + md5Bytes[6] |= 0x30; /* set to version 3 */ + md5Bytes[8] &= 0x3f; /* clear variant */ + md5Bytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(md5Bytes); + } + + /** + * 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。 + * + * @param name 指定 {@code UUID} 字符串 + * @return 具有指定值的 {@code UUID} + * @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常 + * + */ + public static UUID fromString(String name) + { + String[] components = name.split("-"); + if (components.length != 5) + { + throw new IllegalArgumentException("Invalid UUID string: " + name); + } + for (int i = 0; i < 5; i++) + { + components[i] = "0x" + components[i]; + } + + long mostSigBits = Long.decode(components[0]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[1]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[2]).longValue(); + + long leastSigBits = Long.decode(components[3]).longValue(); + leastSigBits <<= 48; + leastSigBits |= Long.decode(components[4]).longValue(); + + return new UUID(mostSigBits, leastSigBits); + } + + /** + * 返回此 UUID 的 128 位值中的最低有效 64 位。 + * + * @return 此 UUID 的 128 位值中的最低有效 64 位。 + */ + public long getLeastSignificantBits() + { + return leastSigBits; + } + + /** + * 返回此 UUID 的 128 位值中的最高有效 64 位。 + * + * @return 此 UUID 的 128 位值中最高有效 64 位。 + */ + public long getMostSignificantBits() + { + return mostSigBits; + } + + /** + * 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。 + *

+ * 版本号具有以下含意: + *

    + *
  • 1 基于时间的 UUID + *
  • 2 DCE 安全 UUID + *
  • 3 基于名称的 UUID + *
  • 4 随机生成的 UUID + *
+ * + * @return 此 {@code UUID} 的版本号 + */ + public int version() + { + // Version is bits masked by 0x000000000000F000 in MS long + return (int) ((mostSigBits >> 12) & 0x0f); + } + + /** + * 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。 + *

+ * 变体号具有以下含意: + *

    + *
  • 0 为 NCS 向后兼容保留 + *
  • 2 IETF RFC 4122(Leach-Salz), 用于此类 + *
  • 6 保留,微软向后兼容 + *
  • 7 保留供以后定义使用 + *
+ * + * @return 此 {@code UUID} 相关联的变体号 + */ + public int variant() + { + // This field is composed of a varying number of bits. + // 0 - - Reserved for NCS backward compatibility + // 1 0 - The IETF aka Leach-Salz variant (used by this class) + // 1 1 0 Reserved, Microsoft backward compatibility + // 1 1 1 Reserved for future definition. + return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63)); + } + + /** + * 与此 UUID 相关联的时间戳值。 + * + *

+ * 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。
+ * 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。 + * + *

+ * 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。
+ * 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 + * + * @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。 + */ + public long timestamp() throws UnsupportedOperationException + { + checkTimeBase(); + return (mostSigBits & 0x0FFFL) << 48// + | ((mostSigBits >> 16) & 0x0FFFFL) << 32// + | mostSigBits >>> 32; + } + + /** + * 与此 UUID 相关联的时钟序列值。 + * + *

+ * 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。 + *

+ * {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出 + * UnsupportedOperationException。 + * + * @return 此 {@code UUID} 的时钟序列 + * + * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 + */ + public int clockSequence() throws UnsupportedOperationException + { + checkTimeBase(); + return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48); + } + + /** + * 与此 UUID 相关的节点值。 + * + *

+ * 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。 + *

+ * 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。
+ * 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 + * + * @return 此 {@code UUID} 的节点值 + * + * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 + */ + public long node() throws UnsupportedOperationException + { + checkTimeBase(); + return leastSigBits & 0x0000FFFFFFFFFFFFL; + } + + /** + * 返回此{@code UUID} 的字符串表现形式。 + * + *

+ * UUID 的字符串表示形式由此 BNF 描述: + * + *

+     * {@code
+     * UUID                   = ----
+     * time_low               = 4*
+     * time_mid               = 2*
+     * time_high_and_version  = 2*
+     * variant_and_sequence   = 2*
+     * node                   = 6*
+     * hexOctet               = 
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * 
+ * + * + * + * @return 此{@code UUID} 的字符串表现形式 + * @see #toString(boolean) + */ + @Override + public String toString() + { + return toString(false); + } + + /** + * 返回此{@code UUID} 的字符串表现形式。 + * + *

+ * UUID 的字符串表示形式由此 BNF 描述: + * + *

+     * {@code
+     * UUID                   = ----
+     * time_low               = 4*
+     * time_mid               = 2*
+     * time_high_and_version  = 2*
+     * variant_and_sequence   = 2*
+     * node                   = 6*
+     * hexOctet               = 
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * 
+ * + * + * + * @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串 + * @return 此{@code UUID} 的字符串表现形式 + */ + public String toString(boolean isSimple) + { + final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36); + // time_low + builder.append(digits(mostSigBits >> 32, 8)); + if (!isSimple) + { + builder.append('-'); + } + // time_mid + builder.append(digits(mostSigBits >> 16, 4)); + if (!isSimple) + { + builder.append('-'); + } + // time_high_and_version + builder.append(digits(mostSigBits, 4)); + if (!isSimple) + { + builder.append('-'); + } + // variant_and_sequence + builder.append(digits(leastSigBits >> 48, 4)); + if (!isSimple) + { + builder.append('-'); + } + // node + builder.append(digits(leastSigBits, 12)); + + return builder.toString(); + } + + /** + * 返回此 UUID 的哈希码。 + * + * @return UUID 的哈希码值。 + */ + @Override + public int hashCode() + { + long hilo = mostSigBits ^ leastSigBits; + return ((int) (hilo >> 32)) ^ (int) hilo; + } + + /** + * 将此对象与指定对象比较。 + *

+ * 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。 + * + * @param obj 要与之比较的对象 + * + * @return 如果对象相同,则返回 {@code true};否则返回 {@code false} + */ + @Override + public boolean equals(Object obj) + { + if ((null == obj) || (obj.getClass() != UUID.class)) + { + return false; + } + UUID id = (UUID) obj; + return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits); + } + + // Comparison Operations + + /** + * 将此 UUID 与指定的 UUID 比较。 + * + *

+ * 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。 + * + * @param val 与此 UUID 比较的 UUID + * + * @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。 + * + */ + @Override + public int compareTo(UUID val) + { + // The ordering is intentionally set up so that the UUIDs + // can simply be numerically compared as two numbers + return (this.mostSigBits < val.mostSigBits ? -1 : // + (this.mostSigBits > val.mostSigBits ? 1 : // + (this.leastSigBits < val.leastSigBits ? -1 : // + (this.leastSigBits > val.leastSigBits ? 1 : // + 0)))); + } + + // ------------------------------------------------------------------------------------------------------------------- + // Private method start + /** + * 返回指定数字对应的hex值 + * + * @param val 值 + * @param digits 位 + * @return 值 + */ + private static String digits(long val, int digits) + { + long hi = 1L << (digits * 4); + return Long.toHexString(hi | (val & (hi - 1))).substring(1); + } + + /** + * 检查是否为time-based版本UUID + */ + private void checkTimeBase() + { + if (version() != 1) + { + throw new UnsupportedOperationException("Not a time-based UUID"); + } + } + + /** + * 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG) + * + * @return {@link SecureRandom} + */ + public static SecureRandom getSecureRandom() + { + try + { + return SecureRandom.getInstance("SHA1PRNG"); + } + catch (NoSuchAlgorithmException e) + { + throw new UtilException(e); + } + } + + /** + * 获取随机数生成器对象
+ * ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。 + * + * @return {@link ThreadLocalRandom} + */ + public static ThreadLocalRandom getRandom() + { + return ThreadLocalRandom.current(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/xss/Xss.java b/ruoyi-common/src/main/java/com/ruoyi/common/xss/Xss.java index cb4b408..14e43dc 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/xss/Xss.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/xss/Xss.java @@ -1,27 +1,27 @@ -package com.ruoyi.common.xss; - -import jakarta.validation.Constraint; -import jakarta.validation.Payload; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 自定义xss校验注解 - * - * @author ruoyi - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(value = { ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER }) -@Constraint(validatedBy = { XssValidator.class }) -public @interface Xss -{ - String message() - - default "不允许任何脚本运行"; - - Class[] groups() default {}; - - Class[] payload() default {}; -} +package com.ruoyi.common.xss; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义xss校验注解 + * + * @author ruoyi + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(value = { ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER }) +@Constraint(validatedBy = { XssValidator.class }) +public @interface Xss +{ + String message() + + default "不允许任何脚本运行"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/xss/XssValidator.java b/ruoyi-common/src/main/java/com/ruoyi/common/xss/XssValidator.java index 7c8e4c0..b859428 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/xss/XssValidator.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/xss/XssValidator.java @@ -1,39 +1,39 @@ -package com.ruoyi.common.xss; - -import com.ruoyi.common.utils.StringUtils; -import jakarta.validation.ConstraintValidator; -import jakarta.validation.ConstraintValidatorContext; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * 自定义xss校验注解实现 - * - * @author ruoyi - */ -public class XssValidator implements ConstraintValidator -{ - private static final String HTML_PATTERN = "<(\\S*?)[^>]*>.*?|<.*? />"; - - @Override - public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) - { - if (StringUtils.isBlank(value)) - { - return true; - } - return !containsHtml(value); - } - - public static boolean containsHtml(String value) - { - StringBuilder sHtml = new StringBuilder(); - Pattern pattern = Pattern.compile(HTML_PATTERN); - Matcher matcher = pattern.matcher(value); - while (matcher.find()) - { - sHtml.append(matcher.group()); - } - return pattern.matcher(sHtml).matches(); - } +package com.ruoyi.common.xss; + +import com.ruoyi.common.utils.StringUtils; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 自定义xss校验注解实现 + * + * @author ruoyi + */ +public class XssValidator implements ConstraintValidator +{ + private static final String HTML_PATTERN = "<(\\S*?)[^>]*>.*?|<.*? />"; + + @Override + public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) + { + if (StringUtils.isBlank(value)) + { + return true; + } + return !containsHtml(value); + } + + public static boolean containsHtml(String value) + { + StringBuilder sHtml = new StringBuilder(); + Pattern pattern = Pattern.compile(HTML_PATTERN); + Matcher matcher = pattern.matcher(value); + while (matcher.find()) + { + sHtml.append(matcher.group()); + } + return pattern.matcher(sHtml).matches(); + } } \ No newline at end of file diff --git a/ruoyi-common/src/test/java/com/ruoyi/common/core/cache/InMemoryCacheStoreTest.java b/ruoyi-common/src/test/java/com/ruoyi/common/core/cache/InMemoryCacheStoreTest.java deleted file mode 100644 index e6723bd..0000000 --- a/ruoyi-common/src/test/java/com/ruoyi/common/core/cache/InMemoryCacheStoreTest.java +++ /dev/null @@ -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()); - } -} diff --git a/ruoyi-common/src/test/java/com/ruoyi/common/core/redis/RedisCacheTest.java b/ruoyi-common/src/test/java/com/ruoyi/common/core/redis/RedisCacheTest.java deleted file mode 100644 index 472393a..0000000 --- a/ruoyi-common/src/test/java/com/ruoyi/common/core/redis/RedisCacheTest.java +++ /dev/null @@ -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)); - } -} diff --git a/ruoyi-common/src/test/java/com/ruoyi/common/utils/DictUtilsTest.java b/ruoyi-common/src/test/java/com/ruoyi/common/utils/DictUtilsTest.java deleted file mode 100644 index e2b4d97..0000000 --- a/ruoyi-common/src/test/java/com/ruoyi/common/utils/DictUtilsTest.java +++ /dev/null @@ -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 cached = DictUtils.getDictCache("sys_normal_disable"); - assertEquals(1, cached.size()); - assertEquals("正常", cached.get(0).getDictLabel()); - - DictUtils.clearDictCache(); - assertNull(DictUtils.getDictCache("sys_normal_disable")); - } -} diff --git a/ruoyi-framework/pom.xml b/ruoyi-framework/pom.xml index 51ced87..29c245b 100644 --- a/ruoyi-framework/pom.xml +++ b/ruoyi-framework/pom.xml @@ -1,70 +1,64 @@ - - - - ruoyi - com.ruoyi - 3.9.1 - - 4.0.0 - - ruoyi-framework - - - framework框架核心 - - - - - - - org.springframework.boot - spring-boot-starter-web - - - - - org.springframework.boot - spring-boot-starter-aop - - - - - com.alibaba - druid-spring-boot-3-starter - - - - - pro.fessional - kaptcha - - - servlet-api - javax.servlet - - - - - - - com.github.oshi - oshi-core - - - - - com.ruoyi - ruoyi-system - - - - org.springframework.boot - spring-boot-starter-test - test - - - - - + + + + ruoyi + com.ruoyi + 3.9.2 + + 4.0.0 + + ruoyi-framework + + + framework框架核心 + + + + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.boot + spring-boot-starter-aop + + + + + com.alibaba + druid-spring-boot-starter + + + + + pro.fessional + kaptcha + + + servlet-api + javax.servlet + + + + + + + com.github.oshi + oshi-core + + + + + com.ruoyi + ruoyi-system + + + + + \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java index 713efd9..d98db04 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java @@ -1,184 +1,160 @@ -package com.ruoyi.framework.aspectj; - -import java.util.ArrayList; -import java.util.List; -import org.aspectj.lang.JoinPoint; -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.UserConstants; -import com.ruoyi.common.core.domain.BaseEntity; -import com.ruoyi.common.core.domain.entity.SysRole; -import com.ruoyi.common.core.domain.entity.SysUser; -import com.ruoyi.common.core.domain.model.LoginUser; -import com.ruoyi.common.core.text.Convert; -import com.ruoyi.common.utils.SecurityUtils; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.framework.security.context.PermissionContextHolder; - -/** - * 数据过滤处理 - * - * @author ruoyi - */ -@Aspect -@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"; - - /** - * 数据权限过滤关键字 - */ - public static final String DATA_SCOPE = "dataScope"; - - @Before("@annotation(controllerDataScope)") - public void doBefore(JoinPoint point, DataScope controllerDataScope) throws Throwable - { - clearDataScope(point); - handleDataScope(point, controllerDataScope); - } - - protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope) - { - // 获取当前的用户 - LoginUser loginUser = SecurityUtils.getLoginUser(); - if (StringUtils.isNotNull(loginUser)) - { - SysUser currentUser = loginUser.getUser(); - // 如果是超级管理员,则不过滤数据 - if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()) - { - String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), PermissionContextHolder.getContext()); - dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(), controllerDataScope.userAlias(), permission); - } - } - } - - /** - * 数据范围过滤 - * - * @param joinPoint 切点 - * @param user 用户 - * @param deptAlias 部门别名 - * @param userAlias 用户别名 - * @param permission 权限字符 - */ - public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias, String permission) - { - StringBuilder sqlString = new StringBuilder(); - List conditions = new ArrayList(); - List scopeCustomIds = new ArrayList(); - 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)))) - { - scopeCustomIds.add(Convert.toStr(role.getRoleId())); - } - }); - - for (SysRole role : user.getRoles()) - { - String dataScope = role.getDataScope(); - if (conditions.contains(dataScope) || StringUtils.equals(role.getStatus(), UserConstants.ROLE_DISABLE)) - { - continue; - } - if (StringUtils.isNotEmpty(permission) && !StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission))) - { - continue; - } - if (DATA_SCOPE_ALL.equals(dataScope)) - { - sqlString = new StringBuilder(); - conditions.add(dataScope); - break; - } - else if (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))); - } - else - { - sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, role.getRoleId())); - } - } - else if (DATA_SCOPE_DEPT.equals(dataScope)) - { - sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId())); - } - else if (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())); - } - else if (DATA_SCOPE_SELF.equals(dataScope)) - { - if (StringUtils.isNotBlank(userAlias)) - { - sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId())); - } - else - { - // 数据权限为仅本人且没有userAlias别名不查询任何数据 - sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias)); - } - } - conditions.add(dataScope); - } - - // 角色都不包含传递过来的权限字符,这个时候sqlString也会为空,所以要限制一下,不查询任何数据 - if (StringUtils.isEmpty(conditions)) - { - sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias)); - } - - if (StringUtils.isNotBlank(sqlString.toString())) - { - Object params = joinPoint.getArgs()[0]; - if (StringUtils.isNotNull(params) && params instanceof BaseEntity) - { - BaseEntity baseEntity = (BaseEntity) params; - baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")"); - } - } - } - - /** - * 拼接权限sql前先清空params.dataScope参数防止注入 - */ - private void clearDataScope(final JoinPoint joinPoint) - { - Object params = joinPoint.getArgs()[0]; - if (StringUtils.isNotNull(params) && params instanceof BaseEntity) - { - BaseEntity baseEntity = (BaseEntity) params; - baseEntity.getParams().put(DATA_SCOPE, ""); - } - } -} +package com.ruoyi.framework.aspectj; + +import java.util.ArrayList; +import java.util.List; +import org.aspectj.lang.JoinPoint; +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; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.security.context.PermissionContextHolder; + +/** + * 数据过滤处理 + * + * @author ruoyi + */ +@Aspect +@Component +public class DataScopeAspect +{ + /** + * 数据权限过滤关键字 + */ + public static final String DATA_SCOPE = "dataScope"; + + @Before("@annotation(controllerDataScope)") + public void doBefore(JoinPoint point, DataScope controllerDataScope) throws Throwable + { + clearDataScope(point); + handleDataScope(point, controllerDataScope); + } + + protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope) + { + // 获取当前的用户 + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNotNull(loginUser)) + { + SysUser currentUser = loginUser.getUser(); + // 如果是超级管理员,则不过滤数据 + if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()) + { + String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), PermissionContextHolder.getContext()); + dataScopeFilter(joinPoint, currentUser, controllerDataScope.userAlias(), controllerDataScope.deptAlias(), controllerDataScope.userField(), controllerDataScope.deptField(), permission); + } + } + } + + /** + * 数据范围过滤 + * + * @param joinPoint 切点 + * @param user 用户 + * @param deptAlias 部门别名 + * @param userAlias 用户别名 + * @param permission 权限字符 + */ + public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String userAlias, String deptAlias, String userField, String deptField, String permission) + { + StringBuilder sqlString = new StringBuilder(); + List conditions = new ArrayList(); + List scopeCustomIds = new ArrayList(); + user.getRoles().forEach(role -> { + 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())); + } + }); + + for (SysRole role : user.getRoles()) + { + String dataScope = role.getDataScope(); + if (conditions.contains(dataScope) || StringUtils.equals(role.getStatus(), UserConstants.ROLE_DISABLE)) + { + continue; + } + if (StringUtils.isNotEmpty(permission) && !StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission))) + { + continue; + } + if (Constants.Dept.DATA_SCOPE_ALL.equals(dataScope)) + { + sqlString = new StringBuilder(); + conditions.add(dataScope); + break; + } + else if (Constants.Dept.DATA_SCOPE_CUSTOM.equals(dataScope)) + { + if (scopeCustomIds.size() > 1) + { + // 多个自定数据权限使用in查询,避免多次拼接。 + 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 {}.{} IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, deptField, role.getRoleId())); + } + } + else if (Constants.Dept.DATA_SCOPE_DEPT.equals(dataScope)) + { + sqlString.append(StringUtils.format(" OR {}.{} = {} ", deptAlias, deptField, user.getDeptId())); + } + else if (Constants.Dept.DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) + { + 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 (Constants.Dept.DATA_SCOPE_SELF.equals(dataScope)) + { + if (StringUtils.isNotBlank(userAlias)) + { + sqlString.append(StringUtils.format(" OR {}.{} = {} ", userAlias, userField, user.getUserId())); + } + else + { + // 数据权限为仅本人且没有userAlias别名不查询任何数据 + sqlString.append(StringUtils.format(" OR {}.{} = 0 ", deptAlias, deptField)); + } + } + conditions.add(dataScope); + } + + // 角色都不包含传递过来的权限字符,这个时候sqlString也会为空,所以要限制一下,不查询任何数据 + if (StringUtils.isEmpty(conditions)) + { + sqlString.append(StringUtils.format(" OR {}.{} = 0 ", deptAlias, deptField)); + } + + if (StringUtils.isNotBlank(sqlString.toString())) + { + Object params = joinPoint.getArgs()[0]; + if (StringUtils.isNotNull(params) && params instanceof BaseEntity) + { + BaseEntity baseEntity = (BaseEntity) params; + baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")"); + } + } + } + + /** + * 拼接权限sql前先清空params.dataScope参数防止注入 + */ + private void clearDataScope(final JoinPoint joinPoint) + { + Object params = joinPoint.getArgs()[0]; + if (StringUtils.isNotNull(params) && params instanceof BaseEntity) + { + BaseEntity baseEntity = (BaseEntity) params; + baseEntity.getParams().put(DATA_SCOPE, ""); + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java index 8c2c9f4..4648fcd 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java @@ -1,72 +1,72 @@ -package com.ruoyi.framework.aspectj; - -import java.util.Objects; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Pointcut; -import org.aspectj.lang.reflect.MethodSignature; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.core.annotation.AnnotationUtils; -import org.springframework.core.annotation.Order; -import org.springframework.stereotype.Component; -import com.ruoyi.common.annotation.DataSource; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.framework.datasource.DynamicDataSourceContextHolder; - -/** - * 多数据源处理 - * - * @author ruoyi - */ -@Aspect -@Order(1) -@Component -public class DataSourceAspect -{ - protected Logger logger = LoggerFactory.getLogger(getClass()); - - @Pointcut("@annotation(com.ruoyi.common.annotation.DataSource)" - + "|| @within(com.ruoyi.common.annotation.DataSource)") - public void dsPointCut() - { - - } - - @Around("dsPointCut()") - public Object around(ProceedingJoinPoint point) throws Throwable - { - DataSource dataSource = getDataSource(point); - - if (StringUtils.isNotNull(dataSource)) - { - DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name()); - } - - try - { - return point.proceed(); - } - finally - { - // 销毁数据源 在执行方法之后 - DynamicDataSourceContextHolder.clearDataSourceType(); - } - } - - /** - * 获取需要切换的数据源 - */ - public DataSource getDataSource(ProceedingJoinPoint point) - { - MethodSignature signature = (MethodSignature) point.getSignature(); - DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class); - if (Objects.nonNull(dataSource)) - { - return dataSource; - } - - return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class); - } -} +package com.ruoyi.framework.aspectj; + +import java.util.Objects; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import com.ruoyi.common.annotation.DataSource; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.datasource.DynamicDataSourceContextHolder; + +/** + * 多数据源处理 + * + * @author ruoyi + */ +@Aspect +@Order(1) +@Component +public class DataSourceAspect +{ + protected Logger logger = LoggerFactory.getLogger(getClass()); + + @Pointcut("@annotation(com.ruoyi.common.annotation.DataSource)" + + "|| @within(com.ruoyi.common.annotation.DataSource)") + public void dsPointCut() + { + + } + + @Around("dsPointCut()") + public Object around(ProceedingJoinPoint point) throws Throwable + { + DataSource dataSource = getDataSource(point); + + if (StringUtils.isNotNull(dataSource)) + { + DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name()); + } + + try + { + return point.proceed(); + } + finally + { + // 销毁数据源 在执行方法之后 + DynamicDataSourceContextHolder.clearDataSourceType(); + } + } + + /** + * 获取需要切换的数据源 + */ + public DataSource getDataSource(ProceedingJoinPoint point) + { + MethodSignature signature = (MethodSignature) point.getSignature(); + DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class); + if (Objects.nonNull(dataSource)) + { + return dataSource; + } + + return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java index c6204fd..ce8a0e3 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java @@ -1,264 +1,264 @@ -package com.ruoyi.framework.aspectj; - -import java.util.Collection; -import java.util.Map; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.apache.commons.lang3.ArrayUtils; -import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.annotation.AfterReturning; -import org.aspectj.lang.annotation.AfterThrowing; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Before; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.core.NamedThreadLocal; -import org.springframework.stereotype.Component; -import org.springframework.validation.BindingResult; -import org.springframework.web.multipart.MultipartFile; -import com.alibaba.fastjson2.JSON; -import com.ruoyi.common.annotation.Log; -import com.ruoyi.common.core.domain.entity.SysUser; -import com.ruoyi.common.core.domain.model.LoginUser; -import com.ruoyi.common.core.text.Convert; -import com.ruoyi.common.enums.BusinessStatus; -import com.ruoyi.common.enums.HttpMethod; -import com.ruoyi.common.filter.PropertyPreExcludeFilter; -import com.ruoyi.common.utils.ExceptionUtil; -import com.ruoyi.common.utils.SecurityUtils; -import com.ruoyi.common.utils.ServletUtils; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.common.utils.ip.IpUtils; -import com.ruoyi.framework.manager.AsyncManager; -import com.ruoyi.framework.manager.factory.AsyncFactory; -import com.ruoyi.system.domain.SysOperLog; - -/** - * 操作日志记录处理 - * - * @author ruoyi - */ -@Aspect -@Component -public class LogAspect -{ - private static final Logger log = LoggerFactory.getLogger(LogAspect.class); - - /** 排除敏感属性字段 */ - public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" }; - - /** 计算操作消耗时间 */ - private static final ThreadLocal TIME_THREADLOCAL = new NamedThreadLocal("Cost Time"); - - /** 参数最大长度限制 */ - private static final int PARAM_MAX_LENGTH = 2000; - - /** - * 处理请求前执行 - */ - @Before(value = "@annotation(controllerLog)") - public void doBefore(JoinPoint joinPoint, Log controllerLog) - { - TIME_THREADLOCAL.set(System.currentTimeMillis()); - } - - /** - * 处理完请求后执行 - * - * @param joinPoint 切点 - */ - @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult") - public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) - { - handleLog(joinPoint, controllerLog, null, jsonResult); - } - - /** - * 拦截异常操作 - * - * @param joinPoint 切点 - * @param e 异常 - */ - @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e") - public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) - { - handleLog(joinPoint, controllerLog, e, null); - } - - protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) - { - try - { - // 获取当前的用户 - LoginUser loginUser = SecurityUtils.getLoginUser(); - - // *========数据库日志=========*// - SysOperLog operLog = new SysOperLog(); - operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); - // 请求的地址 - String ip = IpUtils.getIpAddr(); - operLog.setOperIp(ip); - operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255)); - if (loginUser != null) - { - operLog.setOperName(loginUser.getUsername()); - SysUser currentUser = loginUser.getUser(); - if (StringUtils.isNotNull(currentUser) && StringUtils.isNotNull(currentUser.getDept())) - { - operLog.setDeptName(currentUser.getDept().getDeptName()); - } - } - - if (e != null) - { - operLog.setStatus(BusinessStatus.FAIL.ordinal()); - operLog.setErrorMsg(StringUtils.substring(Convert.toStr(e.getMessage(), ExceptionUtil.getExceptionMessage(e)), 0, 2000)); - } - // 设置方法名称 - String className = joinPoint.getTarget().getClass().getName(); - String methodName = joinPoint.getSignature().getName(); - operLog.setMethod(className + "." + methodName + "()"); - // 设置请求方式 - operLog.setRequestMethod(ServletUtils.getRequest().getMethod()); - // 处理设置注解上的参数 - getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult); - // 设置消耗时间 - operLog.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get()); - // 保存数据库 - AsyncManager.me().execute(AsyncFactory.recordOper(operLog)); - } - catch (Exception exp) - { - // 记录本地异常日志 - log.error("异常信息:{}", exp.getMessage()); - exp.printStackTrace(); - } - finally - { - TIME_THREADLOCAL.remove(); - } - } - - /** - * 获取注解中对方法的描述信息 用于Controller层注解 - * - * @param log 日志 - * @param operLog 操作日志 - * @throws Exception - */ - public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception - { - // 设置action动作 - operLog.setBusinessType(log.businessType().ordinal()); - // 设置标题 - operLog.setTitle(log.title()); - // 设置操作人类别 - operLog.setOperatorType(log.operatorType().ordinal()); - // 是否需要保存request,参数和值 - if (log.isSaveRequestData()) - { - // 获取参数的信息,传入到数据库中。 - setRequestValue(joinPoint, operLog, log.excludeParamNames()); - } - // 是否需要保存response,参数和值 - if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult)) - { - operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000)); - } - } - - /** - * 获取请求的参数,放到log中 - * - * @param operLog 操作日志 - * @throws Exception 异常 - */ - private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog, String[] excludeParamNames) throws Exception - { - String requestMethod = operLog.getRequestMethod(); - Map paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest()); - if (StringUtils.isEmpty(paramsMap) && StringUtils.equalsAny(requestMethod, HttpMethod.PUT.name(), HttpMethod.POST.name(), HttpMethod.DELETE.name())) - { - String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames); - operLog.setOperParam(params); - } - else - { - operLog.setOperParam(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter(excludeParamNames)), 0, PARAM_MAX_LENGTH)); - } - } - - /** - * 参数拼装 - */ - private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames) - { - StringBuilder params = new StringBuilder(); - if (paramsArray != null && paramsArray.length > 0) - { - for (Object o : paramsArray) - { - if (StringUtils.isNotNull(o) && !isFilterObject(o)) - { - try - { - String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter(excludeParamNames)); - params.append(jsonObj).append(" "); - if (params.length() >= PARAM_MAX_LENGTH) - { - return StringUtils.substring(params.toString(), 0, PARAM_MAX_LENGTH); - } - } - catch (Exception e) - { - log.error("请求参数拼装异常 msg:{}, 参数:{}", e.getMessage(), paramsArray, e); - } - } - } - } - return params.toString(); - } - - /** - * 忽略敏感属性 - */ - public PropertyPreExcludeFilter excludePropertyPreFilter(String[] excludeParamNames) - { - return new PropertyPreExcludeFilter().addExcludes(ArrayUtils.addAll(EXCLUDE_PROPERTIES, excludeParamNames)); - } - - /** - * 判断是否需要过滤的对象。 - * - * @param o 对象信息。 - * @return 如果是需要过滤的对象,则返回true;否则返回false。 - */ - @SuppressWarnings("rawtypes") - public boolean isFilterObject(final Object o) - { - Class clazz = o.getClass(); - if (clazz.isArray()) - { - return clazz.getComponentType().isAssignableFrom(MultipartFile.class); - } - else if (Collection.class.isAssignableFrom(clazz)) - { - Collection collection = (Collection) o; - for (Object value : collection) - { - return value instanceof MultipartFile; - } - } - else if (Map.class.isAssignableFrom(clazz)) - { - Map map = (Map) o; - for (Object value : map.entrySet()) - { - Map.Entry entry = (Map.Entry) value; - return entry.getValue() instanceof MultipartFile; - } - } - return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse - || o instanceof BindingResult; - } -} +package com.ruoyi.framework.aspectj; + +import java.util.Collection; +import java.util.Map; +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; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.NamedThreadLocal; +import org.springframework.stereotype.Component; +import org.springframework.validation.BindingResult; +import org.springframework.web.multipart.MultipartFile; +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.enums.BusinessStatus; +import com.ruoyi.common.enums.HttpMethod; +import com.ruoyi.common.filter.PropertyPreExcludeFilter; +import com.ruoyi.common.utils.ExceptionUtil; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.system.domain.SysOperLog; + +/** + * 操作日志记录处理 + * + * @author ruoyi + */ +@Aspect +@Component +public class LogAspect +{ + private static final Logger log = LoggerFactory.getLogger(LogAspect.class); + + /** 排除敏感属性字段 */ + public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" }; + + /** 计算操作消耗时间 */ + private static final ThreadLocal TIME_THREADLOCAL = new NamedThreadLocal("Cost Time"); + + /** 参数最大长度限制 */ + private static final int PARAM_MAX_LENGTH = 2000; + + /** + * 处理请求前执行 + */ + @Before(value = "@annotation(controllerLog)") + public void doBefore(JoinPoint joinPoint, Log controllerLog) + { + TIME_THREADLOCAL.set(System.currentTimeMillis()); + } + + /** + * 处理完请求后执行 + * + * @param joinPoint 切点 + */ + @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult") + public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) + { + handleLog(joinPoint, controllerLog, null, jsonResult); + } + + /** + * 拦截异常操作 + * + * @param joinPoint 切点 + * @param e 异常 + */ + @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e") + public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) + { + handleLog(joinPoint, controllerLog, e, null); + } + + protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) + { + try + { + // 获取当前的用户 + LoginUser loginUser = SecurityUtils.getLoginUser(); + + // *========数据库日志=========*// + SysOperLog operLog = new SysOperLog(); + operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); + // 请求的地址 + String ip = IpUtils.getIpAddr(); + operLog.setOperIp(ip); + operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255)); + if (loginUser != null) + { + operLog.setOperName(loginUser.getUsername()); + SysUser currentUser = loginUser.getUser(); + if (StringUtils.isNotNull(currentUser) && StringUtils.isNotNull(currentUser.getDept())) + { + operLog.setDeptName(currentUser.getDept().getDeptName()); + } + } + + if (e != null) + { + operLog.setStatus(BusinessStatus.FAIL.ordinal()); + operLog.setErrorMsg(StringUtils.substring(Convert.toStr(e.getMessage(), ExceptionUtil.getExceptionMessage(e)), 0, 2000)); + } + // 设置方法名称 + String className = joinPoint.getTarget().getClass().getName(); + String methodName = joinPoint.getSignature().getName(); + operLog.setMethod(className + "." + methodName + "()"); + // 设置请求方式 + operLog.setRequestMethod(ServletUtils.getRequest().getMethod()); + // 处理设置注解上的参数 + getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult); + // 设置消耗时间 + operLog.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get()); + // 保存数据库 + AsyncManager.me().execute(AsyncFactory.recordOper(operLog)); + } + catch (Exception exp) + { + // 记录本地异常日志 + log.error("异常信息:{}", exp.getMessage()); + exp.printStackTrace(); + } + finally + { + TIME_THREADLOCAL.remove(); + } + } + + /** + * 获取注解中对方法的描述信息 用于Controller层注解 + * + * @param log 日志 + * @param operLog 操作日志 + * @throws Exception + */ + public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception + { + // 设置action动作 + operLog.setBusinessType(log.businessType().ordinal()); + // 设置标题 + operLog.setTitle(log.title()); + // 设置操作人类别 + operLog.setOperatorType(log.operatorType().ordinal()); + // 是否需要保存request,参数和值 + if (log.isSaveRequestData()) + { + // 获取参数的信息,传入到数据库中。 + setRequestValue(joinPoint, operLog, log.excludeParamNames()); + } + // 是否需要保存response,参数和值 + if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult)) + { + operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000)); + } + } + + /** + * 获取请求的参数,放到log中 + * + * @param operLog 操作日志 + * @throws Exception 异常 + */ + private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog, String[] excludeParamNames) throws Exception + { + String requestMethod = operLog.getRequestMethod(); + Map paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest()); + if (StringUtils.isEmpty(paramsMap) && StringUtils.equalsAny(requestMethod, HttpMethod.PUT.name(), HttpMethod.POST.name(), HttpMethod.DELETE.name())) + { + String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames); + operLog.setOperParam(params); + } + else + { + operLog.setOperParam(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter(excludeParamNames)), 0, PARAM_MAX_LENGTH)); + } + } + + /** + * 参数拼装 + */ + private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames) + { + StringBuilder params = new StringBuilder(); + if (paramsArray != null && paramsArray.length > 0) + { + for (Object o : paramsArray) + { + if (StringUtils.isNotNull(o) && !isFilterObject(o)) + { + try + { + String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter(excludeParamNames)); + params.append(jsonObj).append(" "); + if (params.length() >= PARAM_MAX_LENGTH) + { + return StringUtils.substring(params.toString(), 0, PARAM_MAX_LENGTH); + } + } + catch (Exception e) + { + log.error("请求参数拼装异常 msg:{}, 参数:{}", e.getMessage(), paramsArray, e); + } + } + } + } + return params.toString(); + } + + /** + * 忽略敏感属性 + */ + public PropertyPreExcludeFilter excludePropertyPreFilter(String[] excludeParamNames) + { + return new PropertyPreExcludeFilter().addExcludes(ArrayUtils.addAll(EXCLUDE_PROPERTIES, excludeParamNames)); + } + + /** + * 判断是否需要过滤的对象。 + * + * @param o 对象信息。 + * @return 如果是需要过滤的对象,则返回true;否则返回false。 + */ + @SuppressWarnings("rawtypes") + public boolean isFilterObject(final Object o) + { + Class clazz = o.getClass(); + if (clazz.isArray()) + { + return clazz.getComponentType().isAssignableFrom(MultipartFile.class); + } + else if (Collection.class.isAssignableFrom(clazz)) + { + Collection collection = (Collection) o; + for (Object value : collection) + { + return value instanceof MultipartFile; + } + } + else if (Map.class.isAssignableFrom(clazz)) + { + Map map = (Map) o; + for (Object value : map.entrySet()) + { + Map.Entry entry = (Map.Entry) value; + return entry.getValue() instanceof MultipartFile; + } + } + return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse + || o instanceof BindingResult; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java index e06b7a7..a2015d7 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java @@ -1,75 +1,89 @@ -package com.ruoyi.framework.aspectj; - -import java.lang.reflect.Method; -import java.util.concurrent.TimeUnit; -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.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.ip.IpUtils; - -/** - * 限流处理 - * - * @author ruoyi - */ -@Aspect -@Component -public class RateLimiterAspect -{ - private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class); - - private final RedisCache redisCache; - - public RateLimiterAspect(RedisCache redisCache) - { - this.redisCache = redisCache; - } - - @Before("@annotation(rateLimiter)") - public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable - { - int time = rateLimiter.time(); - int count = rateLimiter.count(); - - String combineKey = getCombineKey(rateLimiter, point); - try - { - long number = redisCache.increment(combineKey, time, TimeUnit.SECONDS); - if (number > count) - { - throw new ServiceException("访问过于频繁,请稍候再试"); - } - log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number, combineKey); - } - catch (ServiceException e) - { - throw e; - } - catch (Exception e) - { - throw new RuntimeException("服务器限流异常,请稍候再试"); - } - } - - public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) - { - StringBuffer stringBuffer = new StringBuffer(rateLimiter.key()); - if (rateLimiter.limitType() == LimitType.IP) - { - stringBuffer.append(IpUtils.getIpAddr()).append("-"); - } - MethodSignature signature = (MethodSignature) point.getSignature(); - Method method = signature.getMethod(); - Class targetClass = method.getDeclaringClass(); - stringBuffer.append(targetClass.getName()).append("-").append(method.getName()); - return stringBuffer.toString(); - } -} +package com.ruoyi.framework.aspectj; + +import java.lang.reflect.Method; +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.enums.LimitType; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.IpUtils; + +/** + * 限流处理 + * + * @author ruoyi + */ +@Aspect +@Component +public class RateLimiterAspect +{ + private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class); + + private RedisTemplate redisTemplate; + + private RedisScript limitScript; + + @Autowired + public void setRedisTemplate1(RedisTemplate redisTemplate) + { + this.redisTemplate = redisTemplate; + } + + @Autowired + public void setLimitScript(RedisScript limitScript) + { + this.limitScript = limitScript; + } + + @Before("@annotation(rateLimiter)") + public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable + { + int time = rateLimiter.time(); + int count = rateLimiter.count(); + + String combineKey = getCombineKey(rateLimiter, point); + List keys = Collections.singletonList(combineKey); + try + { + Long number = redisTemplate.execute(limitScript, keys, count, time); + if (StringUtils.isNull(number) || number.intValue() > count) + { + throw new ServiceException("访问过于频繁,请稍候再试"); + } + log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), combineKey); + } + catch (ServiceException e) + { + throw e; + } + catch (Exception e) + { + throw new RuntimeException("服务器限流异常,请稍候再试"); + } + } + + public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) + { + StringBuffer stringBuffer = new StringBuffer(rateLimiter.key()); + if (rateLimiter.limitType() == LimitType.IP) + { + stringBuffer.append(IpUtils.getIpAddr()).append("-"); + } + MethodSignature signature = (MethodSignature) point.getSignature(); + Method method = signature.getMethod(); + Class targetClass = method.getDeclaringClass(); + stringBuffer.append(targetClass.getName()).append("-").append(method.getName()); + return stringBuffer.toString(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java index 1d4dc1f..66a00e7 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java @@ -1,30 +1,30 @@ -package com.ruoyi.framework.config; - -import java.util.TimeZone; -import org.mybatis.spring.annotation.MapperScan; -import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.EnableAspectJAutoProxy; - -/** - * 程序注解配置 - * - * @author ruoyi - */ -@Configuration -// 表示通过aop框架暴露该代理对象,AopContext能够访问 -@EnableAspectJAutoProxy(exposeProxy = true) -// 指定要扫描的Mapper类的包的路径 -@MapperScan("com.ruoyi.**.mapper") -public class ApplicationConfig -{ - /** - * 时区配置 - */ - @Bean - public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() - { - return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault()); - } -} +package com.ruoyi.framework.config; + +import java.util.TimeZone; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; + +/** + * 程序注解配置 + * + * @author ruoyi + */ +@Configuration +// 表示通过aop框架暴露该代理对象,AopContext能够访问 +@EnableAspectJAutoProxy(exposeProxy = true) +// 指定要扫描的Mapper类的包的路径 +@MapperScan("com.ruoyi.**.mapper") +public class ApplicationConfig +{ + /** + * 时区配置 + */ + @Bean + public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() + { + return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault()); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java index d9cb8c5..f671b3a 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java @@ -1,126 +1,126 @@ -package com.ruoyi.framework.config; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import javax.sql.DataSource; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.web.servlet.FilterRegistrationBean; -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.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 配置多数据源 - * - * @author ruoyi - */ -@Configuration -public class DruidConfig -{ - @Bean - @ConfigurationProperties("spring.datasource.druid.master") - public DataSource masterDataSource(DruidProperties druidProperties) - { - DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); - return druidProperties.dataSource(dataSource); - } - - @Bean - @ConfigurationProperties("spring.datasource.druid.slave") - @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true") - public DataSource slaveDataSource(DruidProperties druidProperties) - { - DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); - return druidProperties.dataSource(dataSource); - } - - @Bean(name = "dynamicDataSource") - @Primary - public DynamicDataSource dataSource(DataSource masterDataSource) - { - Map targetDataSources = new HashMap<>(); - targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource); - setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource"); - return new DynamicDataSource(masterDataSource, targetDataSources); - } - - /** - * 设置数据源 - * - * @param targetDataSources 备选数据源集合 - * @param sourceName 数据源名称 - * @param beanName bean名称 - */ - public void setDataSource(Map targetDataSources, String sourceName, String beanName) - { - try - { - DataSource dataSource = SpringUtils.getBean(beanName); - targetDataSources.put(sourceName, dataSource); - } - catch (Exception e) - { - } - } - - /** - * 去除监控页面底部的广告 - */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Bean - @ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true") - public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties) - { - // 获取web监控页面的参数 - DruidStatProperties.StatViewServlet config = properties.getStatViewServlet(); - // 提取common.js的配置路径 - String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*"; - String commonJsPattern = pattern.replaceAll("\\*", "js/common.js"); - final String filePath = "support/http/resources/js/common.js"; - // 创建filter进行过滤 - Filter filter = new Filter() - { - @Override - public void init(jakarta.servlet.FilterConfig filterConfig) throws ServletException - { - } - @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) - throws IOException, ServletException - { - chain.doFilter(request, response); - // 重置缓冲区,响应头不会被重置 - response.resetBuffer(); - // 获取common.js - String text = Utils.readFromResource(filePath); - // 正则替换banner, 除去底部的广告信息 - text = text.replaceAll("
", ""); - text = text.replaceAll("powered.*?shrek.wang", ""); - response.getWriter().write(text); - } - @Override - public void destroy() - { - } - }; - FilterRegistrationBean registrationBean = new FilterRegistrationBean(); - registrationBean.setFilter(filter); - registrationBean.addUrlPatterns(commonJsPattern); - return registrationBean; - } -} +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; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +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.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; + +/** + * druid 配置多数据源 + * + * @author ruoyi + */ +@Configuration +public class DruidConfig +{ + @Bean + @ConfigurationProperties("spring.datasource.druid.master") + public DataSource masterDataSource(DruidProperties druidProperties) + { + DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); + return druidProperties.dataSource(dataSource); + } + + @Bean + @ConfigurationProperties("spring.datasource.druid.slave") + @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true") + public DataSource slaveDataSource(DruidProperties druidProperties) + { + DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); + return druidProperties.dataSource(dataSource); + } + + @Bean(name = "dynamicDataSource") + @Primary + public DynamicDataSource dataSource(DataSource masterDataSource) + { + Map targetDataSources = new HashMap<>(); + targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource); + setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource"); + return new DynamicDataSource(masterDataSource, targetDataSources); + } + + /** + * 设置数据源 + * + * @param targetDataSources 备选数据源集合 + * @param sourceName 数据源名称 + * @param beanName bean名称 + */ + public void setDataSource(Map targetDataSources, String sourceName, String beanName) + { + try + { + DataSource dataSource = SpringUtils.getBean(beanName); + targetDataSources.put(sourceName, dataSource); + } + catch (Exception e) + { + } + } + + /** + * 去除监控页面底部的广告 + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + @ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true") + public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties) + { + // 获取web监控页面的参数 + DruidStatProperties.StatViewServlet config = properties.getStatViewServlet(); + // 提取common.js的配置路径 + String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*"; + String commonJsPattern = pattern.replaceAll("\\*", "js/common.js"); + final String filePath = "support/http/resources/js/common.js"; + // 创建filter进行过滤 + Filter filter = new Filter() + { + @Override + public void init(javax.servlet.FilterConfig filterConfig) throws ServletException + { + } + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + chain.doFilter(request, response); + // 重置缓冲区,响应头不会被重置 + response.resetBuffer(); + // 获取common.js + String text = Utils.readFromResource(filePath); + // 正则替换banner, 除去底部的广告信息 + text = text.replaceAll("
", ""); + text = text.replaceAll("powered.*?shrek.wang", ""); + response.getWriter().write(text); + } + @Override + public void destroy() + { + } + }; + FilterRegistrationBean registrationBean = new FilterRegistrationBean(); + registrationBean.setFilter(filter); + registrationBean.addUrlPatterns(commonJsPattern); + return registrationBean; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java new file mode 100644 index 0000000..bd369b4 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java @@ -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 implements RedisSerializer +{ + public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + + static final Filter AUTO_TYPE_FILTER = JSONReader.autoTypeFilter(Constants.JSON_WHITELIST_STR); + + private Class clazz; + + public FastJson2JsonRedisSerializer(Class 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); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java index e4189b3..2e6a0b9 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java @@ -1,80 +1,80 @@ -package com.ruoyi.framework.config; - -import java.util.HashMap; -import java.util.Map; -import jakarta.servlet.DispatcherType; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.web.servlet.FilterRegistrationBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.filter.RefererFilter; -import com.ruoyi.common.filter.RepeatableFilter; -import com.ruoyi.common.filter.XssFilter; -import com.ruoyi.common.utils.StringUtils; - -/** - * Filter配置 - * - * @author ruoyi - */ -@Configuration -public class FilterConfig -{ - @Value("${xss.excludes}") - private String excludes; - - @Value("${xss.urlPatterns}") - private String urlPatterns; - - @Value("${referer.allowed-domains}") - private String allowedDomains; - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Bean - @ConditionalOnProperty(value = "xss.enabled", havingValue = "true") - public FilterRegistrationBean xssFilterRegistration() - { - FilterRegistrationBean registration = new FilterRegistrationBean(); - registration.setDispatcherTypes(DispatcherType.REQUEST); - registration.setFilter(new XssFilter()); - registration.addUrlPatterns(StringUtils.split(urlPatterns, ",")); - registration.setName("xssFilter"); - registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE); - Map initParameters = new HashMap(); - initParameters.put("excludes", excludes); - registration.setInitParameters(initParameters); - return registration; - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Bean - @ConditionalOnProperty(value = "referer.enabled", havingValue = "true") - public FilterRegistrationBean refererFilterRegistration() - { - FilterRegistrationBean registration = new FilterRegistrationBean(); - registration.setDispatcherTypes(DispatcherType.REQUEST); - registration.setFilter(new RefererFilter()); - registration.addUrlPatterns(Constants.RESOURCE_PREFIX + "/*"); - registration.setName("refererFilter"); - registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE); - Map initParameters = new HashMap(); - initParameters.put("allowedDomains", allowedDomains); - registration.setInitParameters(initParameters); - return registration; - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Bean - public FilterRegistrationBean someFilterRegistration() - { - FilterRegistrationBean registration = new FilterRegistrationBean(); - registration.setFilter(new RepeatableFilter()); - registration.addUrlPatterns("/*"); - registration.setName("repeatableFilter"); - registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE); - return registration; - } - -} +package com.ruoyi.framework.config; + +import java.util.HashMap; +import java.util.Map; +import javax.servlet.DispatcherType; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.filter.RefererFilter; +import com.ruoyi.common.filter.RepeatableFilter; +import com.ruoyi.common.filter.XssFilter; +import com.ruoyi.common.utils.StringUtils; + +/** + * Filter配置 + * + * @author ruoyi + */ +@Configuration +public class FilterConfig +{ + @Value("${xss.excludes}") + private String excludes; + + @Value("${xss.urlPatterns}") + private String urlPatterns; + + @Value("${referer.allowed-domains}") + private String allowedDomains; + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + @ConditionalOnProperty(value = "xss.enabled", havingValue = "true") + public FilterRegistrationBean xssFilterRegistration() + { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setDispatcherTypes(DispatcherType.REQUEST); + registration.setFilter(new XssFilter()); + registration.addUrlPatterns(StringUtils.split(urlPatterns, ",")); + registration.setName("xssFilter"); + registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE); + Map initParameters = new HashMap(); + initParameters.put("excludes", excludes); + registration.setInitParameters(initParameters); + return registration; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + @ConditionalOnProperty(value = "referer.enabled", havingValue = "true") + public FilterRegistrationBean refererFilterRegistration() + { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setDispatcherTypes(DispatcherType.REQUEST); + registration.setFilter(new RefererFilter()); + registration.addUrlPatterns(Constants.RESOURCE_PREFIX + "/*"); + registration.setName("refererFilter"); + registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE); + Map initParameters = new HashMap(); + initParameters.put("allowedDomains", allowedDomains); + registration.setInitParameters(initParameters); + return registration; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + public FilterRegistrationBean someFilterRegistration() + { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setFilter(new RepeatableFilter()); + registration.addUrlPatterns("/*"); + registration.setName("repeatableFilter"); + registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE); + return registration; + } + +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java new file mode 100644 index 0000000..e30fe74 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java @@ -0,0 +1,132 @@ +package com.ruoyi.framework.config; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import javax.sql.DataSource; +import org.apache.ibatis.io.VFS; +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.SqlSessionFactoryBean; +import org.mybatis.spring.boot.autoconfigure.SpringBootVFS; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; +import org.springframework.core.type.classreading.CachingMetadataReaderFactory; +import org.springframework.core.type.classreading.MetadataReader; +import org.springframework.core.type.classreading.MetadataReaderFactory; +import org.springframework.util.ClassUtils; +import com.ruoyi.common.utils.StringUtils; + +/** + * Mybatis支持*匹配扫描包 + * + * @author ruoyi + */ +@Configuration +public class MyBatisConfig +{ + @Autowired + private Environment env; + + static final String DEFAULT_RESOURCE_PATTERN = "**/*.class"; + + public static String setTypeAliasesPackage(String typeAliasesPackage) + { + ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver(); + MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver); + List allResult = new ArrayList(); + try + { + for (String aliasesPackage : typeAliasesPackage.split(",")) + { + List result = new ArrayList(); + aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + + ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN; + Resource[] resources = resolver.getResources(aliasesPackage); + if (resources != null && resources.length > 0) + { + MetadataReader metadataReader = null; + for (Resource resource : resources) + { + if (resource.isReadable()) + { + metadataReader = metadataReaderFactory.getMetadataReader(resource); + try + { + result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName()); + } + catch (ClassNotFoundException e) + { + e.printStackTrace(); + } + } + } + } + if (result.size() > 0) + { + HashSet hashResult = new HashSet(result); + allResult.addAll(hashResult); + } + } + if (allResult.size() > 0) + { + typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0])); + } + else + { + throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包"); + } + } + catch (IOException e) + { + e.printStackTrace(); + } + return typeAliasesPackage; + } + + public Resource[] resolveMapperLocations(String[] mapperLocations) + { + ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver(); + List resources = new ArrayList(); + if (mapperLocations != null) + { + for (String mapperLocation : mapperLocations) + { + try + { + Resource[] mappers = resourceResolver.getResources(mapperLocation); + resources.addAll(Arrays.asList(mappers)); + } + catch (IOException e) + { + // ignore + } + } + } + return resources.toArray(new Resource[resources.size()]); + } + + @Bean + public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception + { + String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage"); + String mapperLocations = env.getProperty("mybatis.mapperLocations"); + String configLocation = env.getProperty("mybatis.configLocation"); + typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage); + VFS.addImplClass(SpringBootVFS.class); + + final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); + sessionFactory.setDataSource(dataSource); + sessionFactory.setTypeAliasesPackage(typeAliasesPackage); + sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ","))); + sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation)); + return sessionFactory.getObject(); + } +} \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java deleted file mode 100644 index 794da73..0000000 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.ruoyi.framework.config; - - -import com.baomidou.mybatisplus.annotation.DbType; -import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; -import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor; -import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; -import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.transaction.annotation.EnableTransactionManagement; - -/** - * Mybatis Plus 配置 - * - * @author ruoyi - */ -@EnableTransactionManagement(proxyTargetClass = true) -@Configuration -public class MybatisPlusConfig -{ - @Bean - public MybatisPlusInterceptor mybatisPlusInterceptor() - { - MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); - // 分页插件 - interceptor.addInnerInterceptor(paginationInnerInterceptor()); - // 乐观锁插件 - interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor()); - // 阻断插件 - interceptor.addInnerInterceptor(blockAttackInnerInterceptor()); - return interceptor; - } - - /** - * 分页插件,自动识别数据库类型 https://baomidou.com/guide/interceptor-pagination.html - */ - public PaginationInnerInterceptor paginationInnerInterceptor() - { - PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(); - // 设置数据库类型为mysql - paginationInnerInterceptor.setDbType(DbType.MYSQL); - // 设置最大单页限制数量,默认 500 条,-1 不受限制 - paginationInnerInterceptor.setMaxLimit(-1L); - return paginationInnerInterceptor; - } - - /** - * 乐观锁插件 https://baomidou.com/guide/interceptor-optimistic-locker.html - */ - public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() - { - return new OptimisticLockerInnerInterceptor(); - } - - /** - * 如果是对全表的删除或更新操作,就会终止该操作 https://baomidou.com/guide/interceptor-block-attack.html - */ - public BlockAttackInnerInterceptor blockAttackInnerInterceptor() - { - return new BlockAttackInnerInterceptor(); - } -} \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java new file mode 100644 index 0000000..b188ac2 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java @@ -0,0 +1,69 @@ +package com.ruoyi.framework.config; + +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +/** + * redis配置 + * + * @author ruoyi + */ +@Configuration +@EnableCaching +public class RedisConfig extends CachingConfigurerSupport +{ + @Bean + @SuppressWarnings(value = { "unchecked", "rawtypes" }) + public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) + { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + + FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); + + // 使用StringRedisSerializer来序列化和反序列化redis的key值 + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(serializer); + + // Hash的key也采用StringRedisSerializer的序列化方式 + template.setHashKeySerializer(new StringRedisSerializer()); + template.setHashValueSerializer(serializer); + + template.afterPropertiesSet(); + return template; + } + + @Bean + public DefaultRedisScript limitScript() + { + DefaultRedisScript redisScript = new DefaultRedisScript<>(); + redisScript.setScriptText(limitScriptText()); + redisScript.setResultType(Long.class); + return redisScript; + } + + /** + * 限流脚本 + */ + private String limitScriptText() + { + return "local key = KEYS[1]\n" + + "local count = tonumber(ARGV[1])\n" + + "local time = tonumber(ARGV[2])\n" + + "local current = redis.call('get', key);\n" + + "if current and tonumber(current) > count then\n" + + " return tonumber(current);\n" + + "end\n" + + "current = redis.call('incr', key)\n" + + "if tonumber(current) == 1 then\n" + + " redis.call('expire', key, time)\n" + + "end\n" + + "return tonumber(current);"; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java index 0f48b11..12d3549 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java @@ -1,72 +1,72 @@ -package com.ruoyi.framework.config; - -import java.util.concurrent.TimeUnit; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.CacheControl; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -import org.springframework.web.filter.CorsFilter; -import org.springframework.web.servlet.config.annotation.InterceptorRegistry; -import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import com.ruoyi.common.config.RuoYiConfig; -import com.ruoyi.common.constant.Constants; -import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor; - -/** - * 通用配置 - * - * @author ruoyi - */ -@Configuration -public class ResourcesConfig implements WebMvcConfigurer -{ - @Autowired - private RepeatSubmitInterceptor repeatSubmitInterceptor; - - @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) - { - /** 本地文件上传路径 */ - registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**") - .addResourceLocations("file:" + RuoYiConfig.getProfile() + "/"); - - /** swagger配置 */ - registry.addResourceHandler("/swagger-ui/**") - .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/") - .setCacheControl(CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic()); - } - - /** - * 自定义拦截规则 - */ - @Override - public void addInterceptors(InterceptorRegistry registry) - { - registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**"); - } - - /** - * 跨域配置 - */ - @Bean - public CorsFilter corsFilter() - { - CorsConfiguration config = new CorsConfiguration(); - // 设置访问源地址 - config.addAllowedOriginPattern("*"); - // 设置访问源请求头 - config.addAllowedHeader("*"); - // 设置访问源请求方法 - config.addAllowedMethod("*"); - // 有效期 1800秒 - config.setMaxAge(1800L); - // 添加映射路径,拦截一切请求 - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - source.registerCorsConfiguration("/**", config); - // 返回新的CorsFilter - return new CorsFilter(source); - } +package com.ruoyi.framework.config; + +import java.util.concurrent.TimeUnit; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.CacheControl; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor; + +/** + * 通用配置 + * + * @author ruoyi + */ +@Configuration +public class ResourcesConfig implements WebMvcConfigurer +{ + @Autowired + private RepeatSubmitInterceptor repeatSubmitInterceptor; + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) + { + /** 本地文件上传路径 */ + registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**") + .addResourceLocations("file:" + RuoYiConfig.getProfile() + "/"); + + /** swagger配置 */ + registry.addResourceHandler("/swagger-ui/**") + .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/") + .setCacheControl(CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic()); + } + + /** + * 自定义拦截规则 + */ + @Override + public void addInterceptors(InterceptorRegistry registry) + { + registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**"); + } + + /** + * 跨域配置 + */ + @Bean + public CorsFilter corsFilter() + { + CorsConfiguration config = new CorsConfiguration(); + // 设置访问源地址 + config.addAllowedOriginPattern("*"); + // 设置访问源请求头 + config.addAllowedHeader("*"); + // 设置访问源请求方法 + config.addAllowedMethod("*"); + // 有效期 1800秒 + config.setMaxAge(1800L); + // 添加映射路径,拦截一切请求 + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", config); + // 返回新的CorsFilter + return new CorsFilter(source); + } } \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java index 51b0ff5..b04beff 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java @@ -1,128 +1,139 @@ -package com.ruoyi.framework.config; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpMethod; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; -import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import org.springframework.security.web.authentication.logout.LogoutFilter; -import org.springframework.web.filter.CorsFilter; -import com.ruoyi.framework.config.properties.PermitAllUrlProperties; -import com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter; -import com.ruoyi.framework.security.handle.AuthenticationEntryPointImpl; -import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl; - -/** - * spring security配置 - * - * @author ruoyi - */ -@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true) -@Configuration -public class SecurityConfig -{ - /** - * 认证失败处理类 - */ - @Autowired - private AuthenticationEntryPointImpl unauthorizedHandler; - - /** - * 退出处理类 - */ - @Autowired - private LogoutSuccessHandlerImpl logoutSuccessHandler; - - /** - * token认证过滤器 - */ - @Autowired - private JwtAuthenticationTokenFilter authenticationTokenFilter; - - /** - * 跨域过滤器 - */ - @Autowired - private CorsFilter corsFilter; - - /** - * 允许匿名访问的地址 - */ - @Autowired - private PermitAllUrlProperties permitAllUrl; - - /** - * 身份验证实现 - */ - @Bean - public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception - { - return authenticationConfiguration.getAuthenticationManager(); - } - - /** - * anyRequest | 匹配所有请求路径 - * access | SpringEl表达式结果为true时可以访问 - * anonymous | 匿名可以访问 - * denyAll | 用户不能访问 - * fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录) - * hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问 - * hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问 - * hasAuthority | 如果有参数,参数表示权限,则其权限可以访问 - * hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问 - * hasRole | 如果有参数,参数表示角色,则其角色可以访问 - * permitAll | 用户可以任意访问 - * rememberMe | 允许通过remember-me登录的用户访问 - * authenticated | 用户登录后可访问 - */ - @Bean - protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception - { - return httpSecurity - // CSRF禁用,因为不使用session - .csrf(csrf -> csrf.disable()) - // 禁用HTTP响应标头 - .headers((headersCustomizer) -> { - headersCustomizer.cacheControl(cache -> cache.disable()).frameOptions(options -> options.sameOrigin()); - }) - // 认证失败处理类 - .exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler)) - // 基于token,所以不需要session - .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) - // 注解标记允许匿名访问的url - .authorizeHttpRequests((requests) -> { - permitAllUrl.getUrls().forEach(url -> requests.requestMatchers(url).permitAll()); - // 对于登录login 注册register 验证码captchaImage 允许匿名访问 - requests.requestMatchers("/login", "/login/test", "/register", "/captchaImage").permitAll() - // 静态资源,可匿名访问 - .requestMatchers(HttpMethod.GET, "/", "/*.html", "/**.html", "/**.css", "/**.js", "/profile/**").permitAll() - .requestMatchers("/swagger-ui.html", "/v3/api-docs/**", "/swagger-ui/**", "/druid/**").permitAll() - // 除上面外的所有请求全部需要鉴权认证 - .anyRequest().authenticated(); - }) - // 添加Logout filter - .logout(logout -> logout.logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler)) - // 添加JWT filter - .addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class) - // 添加CORS filter - .addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class) - .addFilterBefore(corsFilter, LogoutFilter.class) - .build(); - } - - /** - * 强散列哈希加密实现 - */ - @Bean - public BCryptPasswordEncoder bCryptPasswordEncoder() - { - return new BCryptPasswordEncoder(); - } -} +package com.ruoyi.framework.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.ProviderManager; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.authentication.logout.LogoutFilter; +import org.springframework.web.filter.CorsFilter; +import com.ruoyi.framework.config.properties.PermitAllUrlProperties; +import com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter; +import com.ruoyi.framework.security.handle.AuthenticationEntryPointImpl; +import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl; + +/** + * spring security配置 + * + * @author ruoyi + */ +@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true) +@Configuration +public class SecurityConfig +{ + /** + * 自定义用户认证逻辑 + */ + @Autowired + private UserDetailsService userDetailsService; + + /** + * 认证失败处理类 + */ + @Autowired + private AuthenticationEntryPointImpl unauthorizedHandler; + + /** + * 退出处理类 + */ + @Autowired + private LogoutSuccessHandlerImpl logoutSuccessHandler; + + /** + * token认证过滤器 + */ + @Autowired + private JwtAuthenticationTokenFilter authenticationTokenFilter; + + /** + * 跨域过滤器 + */ + @Autowired + private CorsFilter corsFilter; + + /** + * 允许匿名访问的地址 + */ + @Autowired + private PermitAllUrlProperties permitAllUrl; + + /** + * 身份验证实现 + */ + @Bean + public AuthenticationManager authenticationManager() + { + DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider(); + daoAuthenticationProvider.setUserDetailsService(userDetailsService); + daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder()); + return new ProviderManager(daoAuthenticationProvider); + } + + /** + * anyRequest | 匹配所有请求路径 + * access | SpringEl表达式结果为true时可以访问 + * anonymous | 匿名可以访问 + * denyAll | 用户不能访问 + * fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录) + * hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问 + * hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问 + * hasAuthority | 如果有参数,参数表示权限,则其权限可以访问 + * hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问 + * hasRole | 如果有参数,参数表示角色,则其角色可以访问 + * permitAll | 用户可以任意访问 + * rememberMe | 允许通过remember-me登录的用户访问 + * authenticated | 用户登录后可访问 + */ + @Bean + protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception + { + return httpSecurity + // CSRF禁用,因为不使用session + .csrf(csrf -> csrf.disable()) + // 禁用HTTP响应标头 + .headers((headersCustomizer) -> { + headersCustomizer.cacheControl(cache -> cache.disable()).frameOptions(options -> options.sameOrigin()); + }) + // 认证失败处理类 + .exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler)) + // 基于token,所以不需要session + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + // 注解标记允许匿名访问的url + .authorizeHttpRequests((requests) -> { + permitAllUrl.getUrls().forEach(url -> requests.antMatchers(url).permitAll()); + // 对于登录login 注册register 验证码captchaImage 允许匿名访问 + requests.antMatchers("/login", "/register", "/captchaImage").permitAll() + // 静态资源,可匿名访问 + .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll() + .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll() + // 除上面外的所有请求全部需要鉴权认证 + .anyRequest().authenticated(); + }) + // 添加Logout filter + .logout(logout -> logout.logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler)) + // 添加JWT filter + .addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class) + // 添加CORS filter + .addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class) + .addFilterBefore(corsFilter, LogoutFilter.class) + .build(); + } + + /** + * 强散列哈希加密实现 + */ + @Bean + public BCryptPasswordEncoder bCryptPasswordEncoder() + { + return new BCryptPasswordEncoder(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ServerConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ServerConfig.java index cd8fb09..157e9f4 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ServerConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ServerConfig.java @@ -1,32 +1,32 @@ -package com.ruoyi.framework.config; - -import jakarta.servlet.http.HttpServletRequest; -import org.springframework.stereotype.Component; -import com.ruoyi.common.utils.ServletUtils; - -/** - * 服务相关配置 - * - * @author ruoyi - */ -@Component -public class ServerConfig -{ - /** - * 获取完整的请求路径,包括:域名,端口,上下文访问路径 - * - * @return 服务地址 - */ - public String getUrl() - { - HttpServletRequest request = ServletUtils.getRequest(); - return getDomain(request); - } - - public static String getDomain(HttpServletRequest request) - { - StringBuffer url = request.getRequestURL(); - String contextPath = request.getServletContext().getContextPath(); - return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString(); - } -} +package com.ruoyi.framework.config; + +import javax.servlet.http.HttpServletRequest; +import org.springframework.stereotype.Component; +import com.ruoyi.common.utils.ServletUtils; + +/** + * 服务相关配置 + * + * @author ruoyi + */ +@Component +public class ServerConfig +{ + /** + * 获取完整的请求路径,包括:域名,端口,上下文访问路径 + * + * @return 服务地址 + */ + public String getUrl() + { + HttpServletRequest request = ServletUtils.getRequest(); + return getDomain(request); + } + + public static String getDomain(HttpServletRequest request) + { + StringBuffer url = request.getRequestURL(); + String contextPath = request.getServletContext().getContextPath(); + return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java index 7840141..63739f9 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java @@ -1,63 +1,63 @@ -package com.ruoyi.framework.config; - -import com.ruoyi.common.utils.Threads; -import org.apache.commons.lang3.concurrent.BasicThreadFactory; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.ThreadPoolExecutor; - -/** - * 线程池配置 - * - * @author ruoyi - **/ -@Configuration -public class ThreadPoolConfig -{ - // 核心线程池大小 - private int corePoolSize = 50; - - // 最大可创建的线程数 - private int maxPoolSize = 200; - - // 队列最大长度 - private int queueCapacity = 1000; - - // 线程池维护线程所允许的空闲时间 - private int keepAliveSeconds = 300; - - @Bean(name = "threadPoolTaskExecutor") - public ThreadPoolTaskExecutor threadPoolTaskExecutor() - { - ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); - executor.setMaxPoolSize(maxPoolSize); - executor.setCorePoolSize(corePoolSize); - executor.setQueueCapacity(queueCapacity); - executor.setKeepAliveSeconds(keepAliveSeconds); - // 线程池对拒绝任务(无线程可用)的处理策略 - executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); - return executor; - } - - /** - * 执行周期性或定时任务 - */ - @Bean(name = "scheduledExecutorService") - protected ScheduledExecutorService scheduledExecutorService() - { - return new ScheduledThreadPoolExecutor(corePoolSize, - new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(), - new ThreadPoolExecutor.CallerRunsPolicy()) - { - @Override - protected void afterExecute(Runnable r, Throwable t) - { - super.afterExecute(r, t); - Threads.printException(r, t); - } - }; - } -} +package com.ruoyi.framework.config; + +import com.ruoyi.common.utils.Threads; +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * 线程池配置 + * + * @author ruoyi + **/ +@Configuration +public class ThreadPoolConfig +{ + // 核心线程池大小 + private int corePoolSize = 50; + + // 最大可创建的线程数 + private int maxPoolSize = 200; + + // 队列最大长度 + private int queueCapacity = 1000; + + // 线程池维护线程所允许的空闲时间 + private int keepAliveSeconds = 300; + + @Bean(name = "threadPoolTaskExecutor") + public ThreadPoolTaskExecutor threadPoolTaskExecutor() + { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setMaxPoolSize(maxPoolSize); + executor.setCorePoolSize(corePoolSize); + executor.setQueueCapacity(queueCapacity); + executor.setKeepAliveSeconds(keepAliveSeconds); + // 线程池对拒绝任务(无线程可用)的处理策略 + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + return executor; + } + + /** + * 执行周期性或定时任务 + */ + @Bean(name = "scheduledExecutorService") + protected ScheduledExecutorService scheduledExecutorService() + { + return new ScheduledThreadPoolExecutor(corePoolSize, + new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(), + new ThreadPoolExecutor.CallerRunsPolicy()) + { + @Override + protected void afterExecute(Runnable r, Throwable t) + { + super.afterExecute(r, t); + Threads.printException(r, t); + } + }; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/handler/MyMetaHandler.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/handler/MyMetaHandler.java deleted file mode 100644 index 657ea34..0000000 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/handler/MyMetaHandler.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.ruoyi.framework.config.handler; - -import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; -import com.ruoyi.common.utils.SecurityUtils; -import org.apache.ibatis.reflection.MetaObject; -import org.springframework.stereotype.Component; - -import java.util.Date; - -/** - * 实体类自动配置创建日期和更新日期 - * - * @author 600477 - * @date 2023/6/6 - */ -@Component -public class MyMetaHandler implements MetaObjectHandler { - - @Override - public void insertFill(MetaObject metaObject) { - this.setFieldValByName("createBy", SecurityUtils.getLoginUser().getUser().getNickName() + '-'+ SecurityUtils.getUsername(), metaObject); - this.setFieldValByName("createTime", new Date(), metaObject); - this.setFieldValByName("updateBy", SecurityUtils.getLoginUser().getUser().getNickName() + '-'+ SecurityUtils.getUsername(), metaObject); - this.setFieldValByName("updateTime", new Date(), metaObject); - - } - - @Override - public void updateFill(MetaObject metaObject) { - this.setFieldValByName("updateBy", SecurityUtils.getLoginUser().getUser().getNickName() + '-'+ SecurityUtils.getUsername(), metaObject); - this.setFieldValByName("updateTime", new Date(), metaObject); - } -} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java index eddd81c..2dd49ab 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java @@ -1,90 +1,89 @@ -package com.ruoyi.framework.config.properties; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Configuration; -import com.alibaba.druid.pool.DruidDataSource; - -/** - * druid 配置属性 - * - * @author ruoyi - */ -@Configuration -public class DruidProperties -{ - @Value("${spring.datasource.druid.initialSize}") - private int initialSize; - - @Value("${spring.datasource.druid.minIdle}") - private int minIdle; - - @Value("${spring.datasource.druid.maxActive}") - private int maxActive; - - @Value("${spring.datasource.druid.maxWait}") - private int maxWait; - - @Value("${spring.datasource.druid.connectTimeout}") - private int connectTimeout; - - @Value("${spring.datasource.druid.socketTimeout}") - private int socketTimeout; - - @Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}") - private int timeBetweenEvictionRunsMillis; - - @Value("${spring.datasource.druid.minEvictableIdleTimeMillis}") - private int minEvictableIdleTimeMillis; - - @Value("${spring.datasource.druid.maxEvictableIdleTimeMillis}") - private int maxEvictableIdleTimeMillis; - - @Value("${spring.datasource.druid.validationQuery}") - private String validationQuery; - - @Value("${spring.datasource.druid.testWhileIdle}") - private boolean testWhileIdle; - - @Value("${spring.datasource.druid.testOnBorrow}") - private boolean testOnBorrow; - - @Value("${spring.datasource.druid.testOnReturn}") - private boolean testOnReturn; - - public DruidDataSource dataSource(DruidDataSource datasource) - { - /** 配置初始化大小、最小、最大 */ - datasource.setInitialSize(initialSize); - datasource.setMaxActive(maxActive); - datasource.setMinIdle(minIdle); - - /** 配置获取连接等待超时的时间 */ - datasource.setMaxWait(maxWait); - - /** 配置驱动连接超时时间,检测数据库建立连接的超时时间,单位是毫秒 */ - datasource.setConnectTimeout(connectTimeout); - - /** 配置网络超时时间,等待数据库操作完成的网络超时时间,单位是毫秒 */ - datasource.setSocketTimeout(socketTimeout); - - /** 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 */ - datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); - - /** 配置一个连接在池中最小、最大生存的时间,单位是毫秒 */ - datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); - datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis); - - /** - * 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。 - */ - datasource.setValidationQuery(validationQuery); - /** 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 */ - datasource.setTestWhileIdle(testWhileIdle); - /** 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */ - datasource.setTestOnBorrow(testOnBorrow); - /** 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */ - datasource.setTestOnReturn(testOnReturn); - - return datasource; - } -} +package com.ruoyi.framework.config.properties; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import com.alibaba.druid.pool.DruidDataSource; + +/** + * druid 配置属性 + * + * @author ruoyi + */ +@Configuration +public class DruidProperties +{ + @Value("${spring.datasource.druid.initialSize}") + private int initialSize; + + @Value("${spring.datasource.druid.minIdle}") + private int minIdle; + + @Value("${spring.datasource.druid.maxActive}") + private int maxActive; + + @Value("${spring.datasource.druid.maxWait}") + private int maxWait; + + @Value("${spring.datasource.druid.connectTimeout}") + private int connectTimeout; + + @Value("${spring.datasource.druid.socketTimeout}") + private int socketTimeout; + + @Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}") + private int timeBetweenEvictionRunsMillis; + + @Value("${spring.datasource.druid.minEvictableIdleTimeMillis}") + private int minEvictableIdleTimeMillis; + + @Value("${spring.datasource.druid.maxEvictableIdleTimeMillis}") + private int maxEvictableIdleTimeMillis; + + @Value("${spring.datasource.druid.validationQuery}") + private String validationQuery; + + @Value("${spring.datasource.druid.testWhileIdle}") + private boolean testWhileIdle; + + @Value("${spring.datasource.druid.testOnBorrow}") + private boolean testOnBorrow; + + @Value("${spring.datasource.druid.testOnReturn}") + private boolean testOnReturn; + + public DruidDataSource dataSource(DruidDataSource datasource) + { + /** 配置初始化大小、最小、最大 */ + datasource.setInitialSize(initialSize); + datasource.setMaxActive(maxActive); + datasource.setMinIdle(minIdle); + + /** 配置获取连接等待超时的时间 */ + datasource.setMaxWait(maxWait); + + /** 配置驱动连接超时时间,检测数据库建立连接的超时时间,单位是毫秒 */ + datasource.setConnectTimeout(connectTimeout); + + /** 配置网络超时时间,等待数据库操作完成的网络超时时间,单位是毫秒 */ + datasource.setSocketTimeout(socketTimeout); + + /** 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 */ + datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); + + /** 配置一个连接在池中最小、最大生存的时间,单位是毫秒 */ + datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); + datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis); + + /** + * 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。 + */ + datasource.setValidationQuery(validationQuery); + /** 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 */ + datasource.setTestWhileIdle(testWhileIdle); + /** 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */ + datasource.setTestOnBorrow(testOnBorrow); + /** 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */ + datasource.setTestOnReturn(testOnReturn); + return datasource; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java index d76932a..f1dcbfc 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java @@ -1,73 +1,73 @@ -package com.ruoyi.framework.config.properties; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.regex.Pattern; -import org.apache.commons.lang3.RegExUtils; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.annotation.AnnotationUtils; -import org.springframework.web.method.HandlerMethod; -import org.springframework.web.servlet.mvc.method.RequestMappingInfo; -import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; -import com.ruoyi.common.annotation.Anonymous; - -/** - * 设置Anonymous注解允许匿名访问的url - * - * @author ruoyi - */ -@Configuration -public class PermitAllUrlProperties implements InitializingBean, ApplicationContextAware -{ - private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}"); - - private ApplicationContext applicationContext; - - private List urls = new ArrayList<>(); - - public String ASTERISK = "*"; - - @Override - public void afterPropertiesSet() - { - RequestMappingHandlerMapping mapping = applicationContext.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class); - Map map = mapping.getHandlerMethods(); - - map.keySet().forEach(info -> { - HandlerMethod handlerMethod = map.get(info); - - // 获取方法上边的注解 替代path variable 为 * - Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class); - Optional.ofNullable(method).ifPresent(anonymous -> Objects.requireNonNull(info.getPathPatternsCondition().getPatternValues()) // - .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK)))); - - // 获取类上边的注解, 替代path variable 为 * - Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class); - Optional.ofNullable(controller).ifPresent(anonymous -> Objects.requireNonNull(info.getPathPatternsCondition().getPatternValues()) - .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK)))); - }); - } - - @Override - public void setApplicationContext(ApplicationContext context) throws BeansException - { - this.applicationContext = context; - } - - public List getUrls() - { - return urls; - } - - public void setUrls(List urls) - { - this.urls = urls; - } -} +package com.ruoyi.framework.config.properties; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.regex.Pattern; +import org.apache.commons.lang3.RegExUtils; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.mvc.method.RequestMappingInfo; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; +import com.ruoyi.common.annotation.Anonymous; + +/** + * 设置Anonymous注解允许匿名访问的url + * + * @author ruoyi + */ +@Configuration +public class PermitAllUrlProperties implements InitializingBean, ApplicationContextAware +{ + private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}"); + + private ApplicationContext applicationContext; + + private List urls = new ArrayList<>(); + + public String ASTERISK = "*"; + + @Override + public void afterPropertiesSet() + { + RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class); + Map map = mapping.getHandlerMethods(); + + map.keySet().forEach(info -> { + HandlerMethod handlerMethod = map.get(info); + + // 获取方法上边的注解 替代path variable 为 * + Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class); + Optional.ofNullable(method).ifPresent(anonymous -> Objects.requireNonNull(info.getPatternsCondition().getPatterns()) + .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK)))); + + // 获取类上边的注解, 替代path variable 为 * + Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class); + Optional.ofNullable(controller).ifPresent(anonymous -> Objects.requireNonNull(info.getPatternsCondition().getPatterns()) + .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK)))); + }); + } + + @Override + public void setApplicationContext(ApplicationContext context) throws BeansException + { + this.applicationContext = context; + } + + public List getUrls() + { + return urls; + } + + public void setUrls(List urls) + { + this.urls = urls; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java index e70b8cf..014ae38 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java @@ -1,26 +1,26 @@ -package com.ruoyi.framework.datasource; - -import java.util.Map; -import javax.sql.DataSource; -import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; - -/** - * 动态数据源 - * - * @author ruoyi - */ -public class DynamicDataSource extends AbstractRoutingDataSource -{ - public DynamicDataSource(DataSource defaultTargetDataSource, Map targetDataSources) - { - super.setDefaultTargetDataSource(defaultTargetDataSource); - super.setTargetDataSources(targetDataSources); - super.afterPropertiesSet(); - } - - @Override - protected Object determineCurrentLookupKey() - { - return DynamicDataSourceContextHolder.getDataSourceType(); - } +package com.ruoyi.framework.datasource; + +import java.util.Map; +import javax.sql.DataSource; +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; + +/** + * 动态数据源 + * + * @author ruoyi + */ +public class DynamicDataSource extends AbstractRoutingDataSource +{ + public DynamicDataSource(DataSource defaultTargetDataSource, Map targetDataSources) + { + super.setDefaultTargetDataSource(defaultTargetDataSource); + super.setTargetDataSources(targetDataSources); + super.afterPropertiesSet(); + } + + @Override + protected Object determineCurrentLookupKey() + { + return DynamicDataSourceContextHolder.getDataSourceType(); + } } \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java index 9770af6..859a782 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java @@ -1,45 +1,45 @@ -package com.ruoyi.framework.datasource; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * 数据源切换处理 - * - * @author ruoyi - */ -public class DynamicDataSourceContextHolder -{ - public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class); - - /** - * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本, - * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。 - */ - private static final ThreadLocal CONTEXT_HOLDER = new ThreadLocal<>(); - - /** - * 设置数据源的变量 - */ - public static void setDataSourceType(String dsType) - { - log.info("切换到{}数据源", dsType); - CONTEXT_HOLDER.set(dsType); - } - - /** - * 获得数据源的变量 - */ - public static String getDataSourceType() - { - return CONTEXT_HOLDER.get(); - } - - /** - * 清空数据源变量 - */ - public static void clearDataSourceType() - { - CONTEXT_HOLDER.remove(); - } -} +package com.ruoyi.framework.datasource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 数据源切换处理 + * + * @author ruoyi + */ +public class DynamicDataSourceContextHolder +{ + public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class); + + /** + * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本, + * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。 + */ + private static final ThreadLocal CONTEXT_HOLDER = new ThreadLocal<>(); + + /** + * 设置数据源的变量 + */ + public static void setDataSourceType(String dsType) + { + log.info("切换到{}数据源", dsType); + CONTEXT_HOLDER.set(dsType); + } + + /** + * 获得数据源的变量 + */ + public static String getDataSourceType() + { + return CONTEXT_HOLDER.get(); + } + + /** + * 清空数据源变量 + */ + public static void clearDataSourceType() + { + CONTEXT_HOLDER.remove(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java index 4e8d20f..e41968a 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java @@ -1,56 +1,56 @@ -package com.ruoyi.framework.interceptor; - -import java.lang.reflect.Method; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.stereotype.Component; -import org.springframework.web.method.HandlerMethod; -import org.springframework.web.servlet.HandlerInterceptor; -import com.alibaba.fastjson2.JSON; -import com.ruoyi.common.annotation.RepeatSubmit; -import com.ruoyi.common.core.domain.AjaxResult; -import com.ruoyi.common.utils.ServletUtils; - -/** - * 防止重复提交拦截器 - * - * @author ruoyi - */ -@Component -public abstract class RepeatSubmitInterceptor implements HandlerInterceptor -{ - @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception - { - if (handler instanceof HandlerMethod) - { - HandlerMethod handlerMethod = (HandlerMethod) handler; - Method method = handlerMethod.getMethod(); - RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class); - if (annotation != null) - { - if (this.isRepeatSubmit(request, annotation)) - { - AjaxResult ajaxResult = AjaxResult.error(annotation.message()); - ServletUtils.renderString(response, JSON.toJSONString(ajaxResult)); - return false; - } - } - return true; - } - else - { - return true; - } - } - - /** - * 验证是否重复提交由子类实现具体的防重复提交的规则 - * - * @param request 请求信息 - * @param annotation 防重复注解参数 - * @return 结果 - * @throws Exception - */ - public abstract boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation); -} +package com.ruoyi.framework.interceptor; + +import java.lang.reflect.Method; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.stereotype.Component; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerInterceptor; +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.annotation.RepeatSubmit; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.ServletUtils; + +/** + * 防止重复提交拦截器 + * + * @author ruoyi + */ +@Component +public abstract class RepeatSubmitInterceptor implements HandlerInterceptor +{ + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception + { + if (handler instanceof HandlerMethod) + { + HandlerMethod handlerMethod = (HandlerMethod) handler; + Method method = handlerMethod.getMethod(); + RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class); + if (annotation != null) + { + if (this.isRepeatSubmit(request, annotation)) + { + AjaxResult ajaxResult = AjaxResult.error(annotation.message()); + ServletUtils.renderString(response, JSON.toJSONString(ajaxResult)); + return false; + } + } + return true; + } + else + { + return true; + } + } + + /** + * 验证是否重复提交由子类实现具体的防重复提交的规则 + * + * @param request 请求信息 + * @param annotation 防重复注解参数 + * @return 结果 + * @throws Exception + */ + public abstract boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation); +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java index c93df50..316a370 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java @@ -1,110 +1,110 @@ -package com.ruoyi.framework.interceptor.impl; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.TimeUnit; -import jakarta.servlet.http.HttpServletRequest; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; -import com.alibaba.fastjson2.JSON; -import com.ruoyi.common.annotation.RepeatSubmit; -import com.ruoyi.common.constant.CacheConstants; -import com.ruoyi.common.core.redis.RedisCache; -import com.ruoyi.common.filter.RepeatedlyRequestWrapper; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.common.utils.http.HttpHelper; -import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor; - -/** - * 判断请求url和数据是否和上一次相同, - * 如果和上次相同,则是重复提交表单。 有效时间为10秒内。 - * - * @author ruoyi - */ -@Component -public class SameUrlDataInterceptor extends RepeatSubmitInterceptor -{ - public final String REPEAT_PARAMS = "repeatParams"; - - public final String REPEAT_TIME = "repeatTime"; - - // 令牌自定义标识 - @Value("${token.header}") - private String header; - - @Autowired - private RedisCache redisCache; - - @SuppressWarnings("unchecked") - @Override - public boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation) - { - String nowParams = ""; - if (request instanceof RepeatedlyRequestWrapper) - { - RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request; - nowParams = HttpHelper.getBodyString(repeatedlyRequest); - } - - // body参数为空,获取Parameter的数据 - if (StringUtils.isEmpty(nowParams)) - { - nowParams = JSON.toJSONString(request.getParameterMap()); - } - Map nowDataMap = new HashMap(); - nowDataMap.put(REPEAT_PARAMS, nowParams); - nowDataMap.put(REPEAT_TIME, System.currentTimeMillis()); - - // 请求地址(作为存放cache的key值) - String url = request.getRequestURI(); - - // 唯一值(没有消息头则使用请求地址) - String submitKey = StringUtils.trimToEmpty(request.getHeader(header)); - - // 唯一标识(指定key + url + 消息头) - String cacheRepeatKey = CacheConstants.REPEAT_SUBMIT_KEY + url + submitKey; - - Object sessionObj = redisCache.getCacheObject(cacheRepeatKey); - if (sessionObj != null) - { - Map sessionMap = (Map) sessionObj; - if (sessionMap.containsKey(url)) - { - Map preDataMap = (Map) sessionMap.get(url); - if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, annotation.interval())) - { - return true; - } - } - } - Map cacheMap = new HashMap(); - cacheMap.put(url, nowDataMap); - redisCache.setCacheObject(cacheRepeatKey, cacheMap, annotation.interval(), TimeUnit.MILLISECONDS); - return false; - } - - /** - * 判断参数是否相同 - */ - private boolean compareParams(Map nowMap, Map preMap) - { - String nowParams = (String) nowMap.get(REPEAT_PARAMS); - String preParams = (String) preMap.get(REPEAT_PARAMS); - return nowParams.equals(preParams); - } - - /** - * 判断两次间隔时间 - */ - private boolean compareTime(Map nowMap, Map preMap, int interval) - { - long time1 = (Long) nowMap.get(REPEAT_TIME); - long time2 = (Long) preMap.get(REPEAT_TIME); - if ((time1 - time2) < interval) - { - return true; - } - return false; - } -} +package com.ruoyi.framework.interceptor.impl; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import javax.servlet.http.HttpServletRequest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.annotation.RepeatSubmit; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.filter.RepeatedlyRequestWrapper; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.http.HttpHelper; +import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor; + +/** + * 判断请求url和数据是否和上一次相同, + * 如果和上次相同,则是重复提交表单。 有效时间为10秒内。 + * + * @author ruoyi + */ +@Component +public class SameUrlDataInterceptor extends RepeatSubmitInterceptor +{ + public final String REPEAT_PARAMS = "repeatParams"; + + public final String REPEAT_TIME = "repeatTime"; + + // 令牌自定义标识 + @Value("${token.header}") + private String header; + + @Autowired + private RedisCache redisCache; + + @SuppressWarnings("unchecked") + @Override + public boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation) + { + String nowParams = ""; + if (request instanceof RepeatedlyRequestWrapper) + { + RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request; + nowParams = HttpHelper.getBodyString(repeatedlyRequest); + } + + // body参数为空,获取Parameter的数据 + if (StringUtils.isEmpty(nowParams)) + { + nowParams = JSON.toJSONString(request.getParameterMap()); + } + Map nowDataMap = new HashMap(); + nowDataMap.put(REPEAT_PARAMS, nowParams); + nowDataMap.put(REPEAT_TIME, System.currentTimeMillis()); + + // 请求地址(作为存放cache的key值) + String url = request.getRequestURI(); + + // 唯一值(没有消息头则使用请求地址) + String submitKey = StringUtils.trimToEmpty(request.getHeader(header)); + + // 唯一标识(指定key + url + 消息头) + String cacheRepeatKey = CacheConstants.REPEAT_SUBMIT_KEY + url + submitKey; + + Object sessionObj = redisCache.getCacheObject(cacheRepeatKey); + if (sessionObj != null) + { + Map sessionMap = (Map) sessionObj; + if (sessionMap.containsKey(url)) + { + Map preDataMap = (Map) sessionMap.get(url); + if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, annotation.interval())) + { + return true; + } + } + } + Map cacheMap = new HashMap(); + cacheMap.put(url, nowDataMap); + redisCache.setCacheObject(cacheRepeatKey, cacheMap, annotation.interval(), TimeUnit.MILLISECONDS); + return false; + } + + /** + * 判断参数是否相同 + */ + private boolean compareParams(Map nowMap, Map preMap) + { + String nowParams = (String) nowMap.get(REPEAT_PARAMS); + String preParams = (String) preMap.get(REPEAT_PARAMS); + return nowParams.equals(preParams); + } + + /** + * 判断两次间隔时间 + */ + private boolean compareTime(Map nowMap, Map preMap, int interval) + { + long time1 = (Long) nowMap.get(REPEAT_TIME); + long time2 = (Long) preMap.get(REPEAT_TIME); + if ((time1 - time2) < interval) + { + return true; + } + return false; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java index 7387a02..6ed6d5b 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java @@ -1,55 +1,55 @@ -package com.ruoyi.framework.manager; - -import java.util.TimerTask; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import com.ruoyi.common.utils.Threads; -import com.ruoyi.common.utils.spring.SpringUtils; - -/** - * 异步任务管理器 - * - * @author ruoyi - */ -public class AsyncManager -{ - /** - * 操作延迟10毫秒 - */ - private final int OPERATE_DELAY_TIME = 10; - - /** - * 异步操作任务调度线程池 - */ - private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService"); - - /** - * 单例模式 - */ - private AsyncManager(){} - - private static AsyncManager me = new AsyncManager(); - - public static AsyncManager me() - { - return me; - } - - /** - * 执行任务 - * - * @param task 任务 - */ - public void execute(TimerTask task) - { - executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS); - } - - /** - * 停止任务线程池 - */ - public void shutdown() - { - Threads.shutdownAndAwaitTermination(executor); - } -} +package com.ruoyi.framework.manager; + +import java.util.TimerTask; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import com.ruoyi.common.utils.Threads; +import com.ruoyi.common.utils.spring.SpringUtils; + +/** + * 异步任务管理器 + * + * @author ruoyi + */ +public class AsyncManager +{ + /** + * 操作延迟10毫秒 + */ + private final int OPERATE_DELAY_TIME = 10; + + /** + * 异步操作任务调度线程池 + */ + private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService"); + + /** + * 单例模式 + */ + private AsyncManager(){} + + private static AsyncManager me = new AsyncManager(); + + public static AsyncManager me() + { + return me; + } + + /** + * 执行任务 + * + * @param task 任务 + */ + public void execute(TimerTask task) + { + executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS); + } + + /** + * 停止任务线程池 + */ + public void shutdown() + { + Threads.shutdownAndAwaitTermination(executor); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java index 095b865..8d8fb5a 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java @@ -1,39 +1,39 @@ -package com.ruoyi.framework.manager; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; -import jakarta.annotation.PreDestroy; - -/** - * 确保应用退出时能关闭后台线程 - * - * @author ruoyi - */ -@Component -public class ShutdownManager -{ - private static final Logger logger = LoggerFactory.getLogger("sys-user"); - - @PreDestroy - public void destroy() - { - shutdownAsyncManager(); - } - - /** - * 停止异步执行任务 - */ - private void shutdownAsyncManager() - { - try - { - logger.info("====关闭后台任务任务线程池===="); - AsyncManager.me().shutdown(); - } - catch (Exception e) - { - logger.error(e.getMessage(), e); - } - } -} +package com.ruoyi.framework.manager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import javax.annotation.PreDestroy; + +/** + * 确保应用退出时能关闭后台线程 + * + * @author ruoyi + */ +@Component +public class ShutdownManager +{ + private static final Logger logger = LoggerFactory.getLogger("sys-user"); + + @PreDestroy + public void destroy() + { + shutdownAsyncManager(); + } + + /** + * 停止异步执行任务 + */ + private void shutdownAsyncManager() + { + try + { + logger.info("====关闭后台任务任务线程池===="); + AsyncManager.me().shutdown(); + } + catch (Exception e) + { + logger.error(e.getMessage(), e); + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java index b20ad92..ef88bcf 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java @@ -1,102 +1,102 @@ -package com.ruoyi.framework.manager.factory; - -import java.util.TimerTask; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.utils.LogUtils; -import com.ruoyi.common.utils.ServletUtils; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.common.utils.http.UserAgentUtils; -import com.ruoyi.common.utils.ip.AddressUtils; -import com.ruoyi.common.utils.ip.IpUtils; -import com.ruoyi.common.utils.spring.SpringUtils; -import com.ruoyi.system.domain.SysLogininfor; -import com.ruoyi.system.domain.SysOperLog; -import com.ruoyi.system.service.ISysLogininforService; -import com.ruoyi.system.service.ISysOperLogService; - -/** - * 异步工厂(产生任务用) - * - * @author ruoyi - */ -public class AsyncFactory -{ - private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user"); - - /** - * 记录登录信息 - * - * @param username 用户名 - * @param status 状态 - * @param message 消息 - * @param args 列表 - * @return 任务task - */ - public static TimerTask recordLogininfor(final String username, final String status, final String message, - final Object... args) - { - final String userAgent = ServletUtils.getRequest().getHeader("User-Agent"); - final String ip = IpUtils.getIpAddr(); - return new TimerTask() - { - @Override - public void run() - { - String address = AddressUtils.getRealAddressByIP(ip); - StringBuilder s = new StringBuilder(); - s.append(LogUtils.getBlock(ip)); - s.append(address); - s.append(LogUtils.getBlock(username)); - s.append(LogUtils.getBlock(status)); - s.append(LogUtils.getBlock(message)); - // 打印信息到日志 - sys_user_logger.info(s.toString(), args); - // 获取客户端操作系统 - String os = UserAgentUtils.getOperatingSystem(userAgent); - // 获取客户端浏览器 - String browser = UserAgentUtils.getBrowser(userAgent); - // 封装对象 - SysLogininfor logininfor = new SysLogininfor(); - logininfor.setUserName(username); - logininfor.setIpaddr(ip); - logininfor.setLoginLocation(address); - logininfor.setBrowser(browser); - logininfor.setOs(os); - logininfor.setMsg(message); - // 日志状态 - if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) - { - logininfor.setStatus(Constants.SUCCESS); - } - else if (Constants.LOGIN_FAIL.equals(status)) - { - logininfor.setStatus(Constants.FAIL); - } - // 插入数据 - SpringUtils.getBean(ISysLogininforService.class).insertLogininfor(logininfor); - } - }; - } - - /** - * 操作日志记录 - * - * @param operLog 操作日志信息 - * @return 任务task - */ - public static TimerTask recordOper(final SysOperLog operLog) - { - return new TimerTask() - { - @Override - public void run() - { - // 远程查询操作地点 - operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp())); - SpringUtils.getBean(ISysOperLogService.class).insertOperlog(operLog); - } - }; - } -} +package com.ruoyi.framework.manager.factory; + +import java.util.TimerTask; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.LogUtils; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.http.UserAgentUtils; +import com.ruoyi.common.utils.ip.AddressUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.system.domain.SysLogininfor; +import com.ruoyi.system.domain.SysOperLog; +import com.ruoyi.system.service.ISysLogininforService; +import com.ruoyi.system.service.ISysOperLogService; + +/** + * 异步工厂(产生任务用) + * + * @author ruoyi + */ +public class AsyncFactory +{ + private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user"); + + /** + * 记录登录信息 + * + * @param username 用户名 + * @param status 状态 + * @param message 消息 + * @param args 列表 + * @return 任务task + */ + public static TimerTask recordLogininfor(final String username, final String status, final String message, + final Object... args) + { + final String userAgent = ServletUtils.getRequest().getHeader("User-Agent"); + final String ip = IpUtils.getIpAddr(); + return new TimerTask() + { + @Override + public void run() + { + String address = AddressUtils.getRealAddressByIP(ip); + StringBuilder s = new StringBuilder(); + s.append(LogUtils.getBlock(ip)); + s.append(address); + s.append(LogUtils.getBlock(username)); + s.append(LogUtils.getBlock(status)); + s.append(LogUtils.getBlock(message)); + // 打印信息到日志 + sys_user_logger.info(s.toString(), args); + // 获取客户端操作系统 + String os = UserAgentUtils.getOperatingSystem(userAgent); + // 获取客户端浏览器 + String browser = UserAgentUtils.getBrowser(userAgent); + // 封装对象 + SysLogininfor logininfor = new SysLogininfor(); + logininfor.setUserName(username); + logininfor.setIpaddr(ip); + logininfor.setLoginLocation(address); + logininfor.setBrowser(browser); + logininfor.setOs(os); + logininfor.setMsg(message); + // 日志状态 + if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) + { + logininfor.setStatus(Constants.SUCCESS); + } + else if (Constants.LOGIN_FAIL.equals(status)) + { + logininfor.setStatus(Constants.FAIL); + } + // 插入数据 + SpringUtils.getBean(ISysLogininforService.class).insertLogininfor(logininfor); + } + }; + } + + /** + * 操作日志记录 + * + * @param operLog 操作日志信息 + * @return 任务task + */ + public static TimerTask recordOper(final SysOperLog operLog) + { + return new TimerTask() + { + @Override + public void run() + { + // 远程查询操作地点 + operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp())); + SpringUtils.getBean(ISysOperLogService.class).insertOperlog(operLog); + } + }; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java index 6c776ce..5ee5bbd 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java @@ -1,28 +1,28 @@ -package com.ruoyi.framework.security.context; - -import org.springframework.security.core.Authentication; - -/** - * 身份验证信息 - * - * @author ruoyi - */ -public class AuthenticationContextHolder -{ - private static final ThreadLocal contextHolder = new ThreadLocal<>(); - - public static Authentication getContext() - { - return contextHolder.get(); - } - - public static void setContext(Authentication context) - { - contextHolder.set(context); - } - - public static void clearContext() - { - contextHolder.remove(); - } -} +package com.ruoyi.framework.security.context; + +import org.springframework.security.core.Authentication; + +/** + * 身份验证信息 + * + * @author ruoyi + */ +public class AuthenticationContextHolder +{ + private static final ThreadLocal contextHolder = new ThreadLocal<>(); + + public static Authentication getContext() + { + return contextHolder.get(); + } + + public static void setContext(Authentication context) + { + contextHolder.set(context); + } + + public static void clearContext() + { + contextHolder.remove(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java index 5472f3d..2fcd65b 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java @@ -1,27 +1,27 @@ -package com.ruoyi.framework.security.context; - -import org.springframework.web.context.request.RequestAttributes; -import org.springframework.web.context.request.RequestContextHolder; -import com.ruoyi.common.core.text.Convert; - -/** - * 权限信息 - * - * @author ruoyi - */ -public class PermissionContextHolder -{ - private static final String PERMISSION_CONTEXT_ATTRIBUTES = "PERMISSION_CONTEXT"; - - public static void setContext(String permission) - { - RequestContextHolder.currentRequestAttributes().setAttribute(PERMISSION_CONTEXT_ATTRIBUTES, permission, - RequestAttributes.SCOPE_REQUEST); - } - - public static String getContext() - { - return Convert.toStr(RequestContextHolder.currentRequestAttributes().getAttribute(PERMISSION_CONTEXT_ATTRIBUTES, - RequestAttributes.SCOPE_REQUEST)); - } -} +package com.ruoyi.framework.security.context; + +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import com.ruoyi.common.core.text.Convert; + +/** + * 权限信息 + * + * @author ruoyi + */ +public class PermissionContextHolder +{ + private static final String PERMISSION_CONTEXT_ATTRIBUTES = "PERMISSION_CONTEXT"; + + public static void setContext(String permission) + { + RequestContextHolder.currentRequestAttributes().setAttribute(PERMISSION_CONTEXT_ATTRIBUTES, permission, + RequestAttributes.SCOPE_REQUEST); + } + + public static String getContext() + { + return Convert.toStr(RequestContextHolder.currentRequestAttributes().getAttribute(PERMISSION_CONTEXT_ATTRIBUTES, + RequestAttributes.SCOPE_REQUEST)); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java index 9d2f494..75fd00d 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java @@ -1,44 +1,44 @@ -package com.ruoyi.framework.security.filter; - -import java.io.IOException; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; -import org.springframework.stereotype.Component; -import org.springframework.web.filter.OncePerRequestFilter; -import com.ruoyi.common.core.domain.model.LoginUser; -import com.ruoyi.common.utils.SecurityUtils; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.framework.web.service.TokenService; - -/** - * token过滤器 验证token有效性 - * - * @author ruoyi - */ -@Component -public class JwtAuthenticationTokenFilter extends OncePerRequestFilter -{ - @Autowired - private TokenService tokenService; - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) - throws ServletException, IOException - { - LoginUser loginUser = tokenService.getLoginUser(request); - if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) - { - tokenService.verifyToken(loginUser); - UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); - authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); - SecurityContextHolder.getContext().setAuthentication(authenticationToken); - } - chain.doFilter(request, response); - } -} +package com.ruoyi.framework.security.filter; + +import java.io.IOException; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.service.TokenService; + +/** + * token过滤器 验证token有效性 + * + * @author ruoyi + */ +@Component +public class JwtAuthenticationTokenFilter extends OncePerRequestFilter +{ + @Autowired + private TokenService tokenService; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) + throws ServletException, IOException + { + LoginUser loginUser = tokenService.getLoginUser(request); + if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) + { + tokenService.verifyToken(loginUser); + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); + authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authenticationToken); + } + chain.doFilter(request, response); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java index e1789f8..9d86fea 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java @@ -1,34 +1,34 @@ -package com.ruoyi.framework.security.handle; - -import java.io.IOException; -import java.io.Serializable; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.stereotype.Component; -import com.alibaba.fastjson2.JSON; -import com.ruoyi.common.constant.HttpStatus; -import com.ruoyi.common.core.domain.AjaxResult; -import com.ruoyi.common.utils.ServletUtils; -import com.ruoyi.common.utils.StringUtils; - -/** - * 认证失败处理类 返回未授权 - * - * @author ruoyi - */ -@Component -public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable -{ - private static final long serialVersionUID = -8970718410437077606L; - - @Override - public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) - throws IOException - { - int code = HttpStatus.UNAUTHORIZED; - String msg = StringUtils.format("请求访问:{},认证失败,无法访问系统资源", request.getRequestURI()); - ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg))); - } -} +package com.ruoyi.framework.security.handle; + +import java.io.IOException; +import java.io.Serializable; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; + +/** + * 认证失败处理类 返回未授权 + * + * @author ruoyi + */ +@Component +public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable +{ + private static final long serialVersionUID = -8970718410437077606L; + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) + throws IOException + { + int code = HttpStatus.UNAUTHORIZED; + String msg = StringUtils.format("请求访问:{},认证失败,无法访问系统资源", request.getRequestURI()); + ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg))); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java index 595bd3f..d9b7d52 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java @@ -1,53 +1,53 @@ -package com.ruoyi.framework.security.handle; - -import java.io.IOException; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.core.Authentication; -import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; -import com.alibaba.fastjson2.JSON; -import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.core.domain.AjaxResult; -import com.ruoyi.common.core.domain.model.LoginUser; -import com.ruoyi.common.utils.MessageUtils; -import com.ruoyi.common.utils.ServletUtils; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.framework.manager.AsyncManager; -import com.ruoyi.framework.manager.factory.AsyncFactory; -import com.ruoyi.framework.web.service.TokenService; - -/** - * 自定义退出处理类 返回成功 - * - * @author ruoyi - */ -@Configuration -public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler -{ - @Autowired - private TokenService tokenService; - - /** - * 退出处理 - * - * @return - */ - @Override - public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) - throws IOException, ServletException - { - LoginUser loginUser = tokenService.getLoginUser(request); - if (StringUtils.isNotNull(loginUser)) - { - String userName = loginUser.getUsername(); - // 删除用户缓存记录 - tokenService.delLoginUser(loginUser.getToken()); - // 记录用户退出日志 - AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, MessageUtils.message("user.logout.success"))); - } - ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.success(MessageUtils.message("user.logout.success")))); - } -} +package com.ruoyi.framework.security.handle; + +import java.io.IOException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.framework.web.service.TokenService; + +/** + * 自定义退出处理类 返回成功 + * + * @author ruoyi + */ +@Configuration +public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler +{ + @Autowired + private TokenService tokenService; + + /** + * 退出处理 + * + * @return + */ + @Override + public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) + throws IOException, ServletException + { + LoginUser loginUser = tokenService.getLoginUser(request); + if (StringUtils.isNotNull(loginUser)) + { + String userName = loginUser.getUsername(); + // 删除用户缓存记录 + tokenService.delLoginUser(loginUser.getToken()); + // 记录用户退出日志 + AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, MessageUtils.message("user.logout.success"))); + } + ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.success(MessageUtils.message("user.logout.success")))); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/Server.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/Server.java index 63b03da..30a1957 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/Server.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/Server.java @@ -1,240 +1,240 @@ -package com.ruoyi.framework.web.domain; - -import java.net.UnknownHostException; -import java.util.LinkedList; -import java.util.List; -import java.util.Properties; -import com.ruoyi.common.utils.Arith; -import com.ruoyi.common.utils.ip.IpUtils; -import com.ruoyi.framework.web.domain.server.Cpu; -import com.ruoyi.framework.web.domain.server.Jvm; -import com.ruoyi.framework.web.domain.server.Mem; -import com.ruoyi.framework.web.domain.server.Sys; -import com.ruoyi.framework.web.domain.server.SysFile; -import oshi.SystemInfo; -import oshi.hardware.CentralProcessor; -import oshi.hardware.CentralProcessor.TickType; -import oshi.hardware.GlobalMemory; -import oshi.hardware.HardwareAbstractionLayer; -import oshi.software.os.FileSystem; -import oshi.software.os.OSFileStore; -import oshi.software.os.OperatingSystem; -import oshi.util.Util; - -/** - * 服务器相关信息 - * - * @author ruoyi - */ -public class Server -{ - private static final int OSHI_WAIT_SECOND = 1000; - - /** - * CPU相关信息 - */ - private Cpu cpu = new Cpu(); - - /** - * 內存相关信息 - */ - private Mem mem = new Mem(); - - /** - * JVM相关信息 - */ - private Jvm jvm = new Jvm(); - - /** - * 服务器相关信息 - */ - private Sys sys = new Sys(); - - /** - * 磁盘相关信息 - */ - private List sysFiles = new LinkedList(); - - public Cpu getCpu() - { - return cpu; - } - - public void setCpu(Cpu cpu) - { - this.cpu = cpu; - } - - public Mem getMem() - { - return mem; - } - - public void setMem(Mem mem) - { - this.mem = mem; - } - - public Jvm getJvm() - { - return jvm; - } - - public void setJvm(Jvm jvm) - { - this.jvm = jvm; - } - - public Sys getSys() - { - return sys; - } - - public void setSys(Sys sys) - { - this.sys = sys; - } - - public List getSysFiles() - { - return sysFiles; - } - - public void setSysFiles(List sysFiles) - { - this.sysFiles = sysFiles; - } - - public void copyTo() throws Exception - { - SystemInfo si = new SystemInfo(); - HardwareAbstractionLayer hal = si.getHardware(); - - setCpuInfo(hal.getProcessor()); - - setMemInfo(hal.getMemory()); - - setSysInfo(); - - setJvmInfo(); - - setSysFiles(si.getOperatingSystem()); - } - - /** - * 设置CPU信息 - */ - private void setCpuInfo(CentralProcessor processor) - { - // CPU信息 - long[] prevTicks = processor.getSystemCpuLoadTicks(); - Util.sleep(OSHI_WAIT_SECOND); - long[] ticks = processor.getSystemCpuLoadTicks(); - long nice = ticks[TickType.NICE.getIndex()] - prevTicks[TickType.NICE.getIndex()]; - long irq = ticks[TickType.IRQ.getIndex()] - prevTicks[TickType.IRQ.getIndex()]; - long softirq = ticks[TickType.SOFTIRQ.getIndex()] - prevTicks[TickType.SOFTIRQ.getIndex()]; - long steal = ticks[TickType.STEAL.getIndex()] - prevTicks[TickType.STEAL.getIndex()]; - long cSys = ticks[TickType.SYSTEM.getIndex()] - prevTicks[TickType.SYSTEM.getIndex()]; - long user = ticks[TickType.USER.getIndex()] - prevTicks[TickType.USER.getIndex()]; - long iowait = ticks[TickType.IOWAIT.getIndex()] - prevTicks[TickType.IOWAIT.getIndex()]; - long idle = ticks[TickType.IDLE.getIndex()] - prevTicks[TickType.IDLE.getIndex()]; - long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal; - cpu.setCpuNum(processor.getLogicalProcessorCount()); - cpu.setTotal(totalCpu); - cpu.setSys(cSys); - cpu.setUsed(user); - cpu.setWait(iowait); - cpu.setFree(idle); - } - - /** - * 设置内存信息 - */ - private void setMemInfo(GlobalMemory memory) - { - mem.setTotal(memory.getTotal()); - mem.setUsed(memory.getTotal() - memory.getAvailable()); - mem.setFree(memory.getAvailable()); - } - - /** - * 设置服务器信息 - */ - private void setSysInfo() - { - Properties props = System.getProperties(); - sys.setComputerName(IpUtils.getHostName()); - sys.setComputerIp(IpUtils.getHostIp()); - sys.setOsName(props.getProperty("os.name")); - sys.setOsArch(props.getProperty("os.arch")); - sys.setUserDir(props.getProperty("user.dir")); - } - - /** - * 设置Java虚拟机 - */ - private void setJvmInfo() throws UnknownHostException - { - Properties props = System.getProperties(); - jvm.setTotal(Runtime.getRuntime().totalMemory()); - jvm.setMax(Runtime.getRuntime().maxMemory()); - jvm.setFree(Runtime.getRuntime().freeMemory()); - jvm.setVersion(props.getProperty("java.version")); - jvm.setHome(props.getProperty("java.home")); - } - - /** - * 设置磁盘信息 - */ - private void setSysFiles(OperatingSystem os) - { - FileSystem fileSystem = os.getFileSystem(); - List fsArray = fileSystem.getFileStores(); - for (OSFileStore fs : fsArray) - { - long free = fs.getUsableSpace(); - long total = fs.getTotalSpace(); - long used = total - free; - SysFile sysFile = new SysFile(); - sysFile.setDirName(fs.getMount()); - sysFile.setSysTypeName(fs.getType()); - sysFile.setTypeName(fs.getName()); - sysFile.setTotal(convertFileSize(total)); - sysFile.setFree(convertFileSize(free)); - sysFile.setUsed(convertFileSize(used)); - sysFile.setUsage(Arith.mul(Arith.div(used, total, 4), 100)); - sysFiles.add(sysFile); - } - } - - /** - * 字节转换 - * - * @param size 字节大小 - * @return 转换后值 - */ - public String convertFileSize(long size) - { - long kb = 1024; - long mb = kb * 1024; - long gb = mb * 1024; - if (size >= gb) - { - return String.format("%.1f GB", (float) size / gb); - } - else if (size >= mb) - { - float f = (float) size / mb; - return String.format(f > 100 ? "%.0f MB" : "%.1f MB", f); - } - else if (size >= kb) - { - float f = (float) size / kb; - return String.format(f > 100 ? "%.0f KB" : "%.1f KB", f); - } - else - { - return String.format("%d B", size); - } - } -} +package com.ruoyi.framework.web.domain; + +import java.net.UnknownHostException; +import java.util.LinkedList; +import java.util.List; +import java.util.Properties; +import com.ruoyi.common.utils.Arith; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.framework.web.domain.server.Cpu; +import com.ruoyi.framework.web.domain.server.Jvm; +import com.ruoyi.framework.web.domain.server.Mem; +import com.ruoyi.framework.web.domain.server.Sys; +import com.ruoyi.framework.web.domain.server.SysFile; +import oshi.SystemInfo; +import oshi.hardware.CentralProcessor; +import oshi.hardware.CentralProcessor.TickType; +import oshi.hardware.GlobalMemory; +import oshi.hardware.HardwareAbstractionLayer; +import oshi.software.os.FileSystem; +import oshi.software.os.OSFileStore; +import oshi.software.os.OperatingSystem; +import oshi.util.Util; + +/** + * 服务器相关信息 + * + * @author ruoyi + */ +public class Server +{ + private static final int OSHI_WAIT_SECOND = 1000; + + /** + * CPU相关信息 + */ + private Cpu cpu = new Cpu(); + + /** + * 內存相关信息 + */ + private Mem mem = new Mem(); + + /** + * JVM相关信息 + */ + private Jvm jvm = new Jvm(); + + /** + * 服务器相关信息 + */ + private Sys sys = new Sys(); + + /** + * 磁盘相关信息 + */ + private List sysFiles = new LinkedList(); + + public Cpu getCpu() + { + return cpu; + } + + public void setCpu(Cpu cpu) + { + this.cpu = cpu; + } + + public Mem getMem() + { + return mem; + } + + public void setMem(Mem mem) + { + this.mem = mem; + } + + public Jvm getJvm() + { + return jvm; + } + + public void setJvm(Jvm jvm) + { + this.jvm = jvm; + } + + public Sys getSys() + { + return sys; + } + + public void setSys(Sys sys) + { + this.sys = sys; + } + + public List getSysFiles() + { + return sysFiles; + } + + public void setSysFiles(List sysFiles) + { + this.sysFiles = sysFiles; + } + + public void copyTo() throws Exception + { + SystemInfo si = new SystemInfo(); + HardwareAbstractionLayer hal = si.getHardware(); + + setCpuInfo(hal.getProcessor()); + + setMemInfo(hal.getMemory()); + + setSysInfo(); + + setJvmInfo(); + + setSysFiles(si.getOperatingSystem()); + } + + /** + * 设置CPU信息 + */ + private void setCpuInfo(CentralProcessor processor) + { + // CPU信息 + long[] prevTicks = processor.getSystemCpuLoadTicks(); + Util.sleep(OSHI_WAIT_SECOND); + long[] ticks = processor.getSystemCpuLoadTicks(); + long nice = ticks[TickType.NICE.getIndex()] - prevTicks[TickType.NICE.getIndex()]; + long irq = ticks[TickType.IRQ.getIndex()] - prevTicks[TickType.IRQ.getIndex()]; + long softirq = ticks[TickType.SOFTIRQ.getIndex()] - prevTicks[TickType.SOFTIRQ.getIndex()]; + long steal = ticks[TickType.STEAL.getIndex()] - prevTicks[TickType.STEAL.getIndex()]; + long cSys = ticks[TickType.SYSTEM.getIndex()] - prevTicks[TickType.SYSTEM.getIndex()]; + long user = ticks[TickType.USER.getIndex()] - prevTicks[TickType.USER.getIndex()]; + long iowait = ticks[TickType.IOWAIT.getIndex()] - prevTicks[TickType.IOWAIT.getIndex()]; + long idle = ticks[TickType.IDLE.getIndex()] - prevTicks[TickType.IDLE.getIndex()]; + long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal; + cpu.setCpuNum(processor.getLogicalProcessorCount()); + cpu.setTotal(totalCpu); + cpu.setSys(cSys); + cpu.setUsed(user); + cpu.setWait(iowait); + cpu.setFree(idle); + } + + /** + * 设置内存信息 + */ + private void setMemInfo(GlobalMemory memory) + { + mem.setTotal(memory.getTotal()); + mem.setUsed(memory.getTotal() - memory.getAvailable()); + mem.setFree(memory.getAvailable()); + } + + /** + * 设置服务器信息 + */ + private void setSysInfo() + { + Properties props = System.getProperties(); + sys.setComputerName(IpUtils.getHostName()); + sys.setComputerIp(IpUtils.getHostIp()); + sys.setOsName(props.getProperty("os.name")); + sys.setOsArch(props.getProperty("os.arch")); + sys.setUserDir(props.getProperty("user.dir")); + } + + /** + * 设置Java虚拟机 + */ + private void setJvmInfo() throws UnknownHostException + { + Properties props = System.getProperties(); + jvm.setTotal(Runtime.getRuntime().totalMemory()); + jvm.setMax(Runtime.getRuntime().maxMemory()); + jvm.setFree(Runtime.getRuntime().freeMemory()); + jvm.setVersion(props.getProperty("java.version")); + jvm.setHome(props.getProperty("java.home")); + } + + /** + * 设置磁盘信息 + */ + private void setSysFiles(OperatingSystem os) + { + FileSystem fileSystem = os.getFileSystem(); + List fsArray = fileSystem.getFileStores(); + for (OSFileStore fs : fsArray) + { + long free = fs.getUsableSpace(); + long total = fs.getTotalSpace(); + long used = total - free; + SysFile sysFile = new SysFile(); + sysFile.setDirName(fs.getMount()); + sysFile.setSysTypeName(fs.getType()); + sysFile.setTypeName(fs.getName()); + sysFile.setTotal(convertFileSize(total)); + sysFile.setFree(convertFileSize(free)); + sysFile.setUsed(convertFileSize(used)); + sysFile.setUsage(Arith.mul(Arith.div(used, total, 4), 100)); + sysFiles.add(sysFile); + } + } + + /** + * 字节转换 + * + * @param size 字节大小 + * @return 转换后值 + */ + public String convertFileSize(long size) + { + long kb = 1024; + long mb = kb * 1024; + long gb = mb * 1024; + if (size >= gb) + { + return String.format("%.1f GB", (float) size / gb); + } + else if (size >= mb) + { + float f = (float) size / mb; + return String.format(f > 100 ? "%.0f MB" : "%.1f MB", f); + } + else if (size >= kb) + { + float f = (float) size / kb; + return String.format(f > 100 ? "%.0f KB" : "%.1f KB", f); + } + else + { + return String.format("%d B", size); + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java index a13a66c..cf7b46e 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java @@ -1,101 +1,101 @@ -package com.ruoyi.framework.web.domain.server; - -import com.ruoyi.common.utils.Arith; - -/** - * CPU相关信息 - * - * @author ruoyi - */ -public class Cpu -{ - /** - * 核心数 - */ - private int cpuNum; - - /** - * CPU总的使用率 - */ - private double total; - - /** - * CPU系统使用率 - */ - private double sys; - - /** - * CPU用户使用率 - */ - private double used; - - /** - * CPU当前等待率 - */ - private double wait; - - /** - * CPU当前空闲率 - */ - private double free; - - public int getCpuNum() - { - return cpuNum; - } - - public void setCpuNum(int cpuNum) - { - this.cpuNum = cpuNum; - } - - public double getTotal() - { - return Arith.round(Arith.mul(total, 100), 2); - } - - public void setTotal(double total) - { - this.total = total; - } - - public double getSys() - { - return Arith.round(Arith.mul(sys / total, 100), 2); - } - - public void setSys(double sys) - { - this.sys = sys; - } - - public double getUsed() - { - return Arith.round(Arith.mul(used / total, 100), 2); - } - - public void setUsed(double used) - { - this.used = used; - } - - public double getWait() - { - return Arith.round(Arith.mul(wait / total, 100), 2); - } - - public void setWait(double wait) - { - this.wait = wait; - } - - public double getFree() - { - return Arith.round(Arith.mul(free / total, 100), 2); - } - - public void setFree(double free) - { - this.free = free; - } -} +package com.ruoyi.framework.web.domain.server; + +import com.ruoyi.common.utils.Arith; + +/** + * CPU相关信息 + * + * @author ruoyi + */ +public class Cpu +{ + /** + * 核心数 + */ + private int cpuNum; + + /** + * CPU总的使用率 + */ + private double total; + + /** + * CPU系统使用率 + */ + private double sys; + + /** + * CPU用户使用率 + */ + private double used; + + /** + * CPU当前等待率 + */ + private double wait; + + /** + * CPU当前空闲率 + */ + private double free; + + public int getCpuNum() + { + return cpuNum; + } + + public void setCpuNum(int cpuNum) + { + this.cpuNum = cpuNum; + } + + public double getTotal() + { + return Arith.round(Arith.mul(total, 100), 2); + } + + public void setTotal(double total) + { + this.total = total; + } + + public double getSys() + { + return Arith.round(Arith.mul(sys / total, 100), 2); + } + + public void setSys(double sys) + { + this.sys = sys; + } + + public double getUsed() + { + return Arith.round(Arith.mul(used / total, 100), 2); + } + + public void setUsed(double used) + { + this.used = used; + } + + public double getWait() + { + return Arith.round(Arith.mul(wait / total, 100), 2); + } + + public void setWait(double wait) + { + this.wait = wait; + } + + public double getFree() + { + return Arith.round(Arith.mul(free / total, 100), 2); + } + + public void setFree(double free) + { + this.free = free; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java index 1fdc6ac..8e142ac 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java @@ -1,130 +1,130 @@ -package com.ruoyi.framework.web.domain.server; - -import java.lang.management.ManagementFactory; -import com.ruoyi.common.utils.Arith; -import com.ruoyi.common.utils.DateUtils; - -/** - * JVM相关信息 - * - * @author ruoyi - */ -public class Jvm -{ - /** - * 当前JVM占用的内存总数(M) - */ - private double total; - - /** - * JVM最大可用内存总数(M) - */ - private double max; - - /** - * JVM空闲内存(M) - */ - private double free; - - /** - * JDK版本 - */ - private String version; - - /** - * JDK路径 - */ - private String home; - - public double getTotal() - { - return Arith.div(total, (1024 * 1024), 2); - } - - public void setTotal(double total) - { - this.total = total; - } - - public double getMax() - { - return Arith.div(max, (1024 * 1024), 2); - } - - public void setMax(double max) - { - this.max = max; - } - - public double getFree() - { - return Arith.div(free, (1024 * 1024), 2); - } - - public void setFree(double free) - { - this.free = free; - } - - public double getUsed() - { - return Arith.div(total - free, (1024 * 1024), 2); - } - - public double getUsage() - { - return Arith.mul(Arith.div(total - free, total, 4), 100); - } - - /** - * 获取JDK名称 - */ - public String getName() - { - return ManagementFactory.getRuntimeMXBean().getVmName(); - } - - public String getVersion() - { - return version; - } - - public void setVersion(String version) - { - this.version = version; - } - - public String getHome() - { - return home; - } - - public void setHome(String home) - { - this.home = home; - } - - /** - * JDK启动时间 - */ - public String getStartTime() - { - return DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, DateUtils.getServerStartDate()); - } - - /** - * JDK运行时间 - */ - public String getRunTime() - { - return DateUtils.timeDistance(DateUtils.getNowDate(), DateUtils.getServerStartDate()); - } - - /** - * 运行参数 - */ - public String getInputArgs() - { - return ManagementFactory.getRuntimeMXBean().getInputArguments().toString(); - } -} +package com.ruoyi.framework.web.domain.server; + +import java.lang.management.ManagementFactory; +import com.ruoyi.common.utils.Arith; +import com.ruoyi.common.utils.DateUtils; + +/** + * JVM相关信息 + * + * @author ruoyi + */ +public class Jvm +{ + /** + * 当前JVM占用的内存总数(M) + */ + private double total; + + /** + * JVM最大可用内存总数(M) + */ + private double max; + + /** + * JVM空闲内存(M) + */ + private double free; + + /** + * JDK版本 + */ + private String version; + + /** + * JDK路径 + */ + private String home; + + public double getTotal() + { + return Arith.div(total, (1024 * 1024), 2); + } + + public void setTotal(double total) + { + this.total = total; + } + + public double getMax() + { + return Arith.div(max, (1024 * 1024), 2); + } + + public void setMax(double max) + { + this.max = max; + } + + public double getFree() + { + return Arith.div(free, (1024 * 1024), 2); + } + + public void setFree(double free) + { + this.free = free; + } + + public double getUsed() + { + return Arith.div(total - free, (1024 * 1024), 2); + } + + public double getUsage() + { + return Arith.mul(Arith.div(total - free, total, 4), 100); + } + + /** + * 获取JDK名称 + */ + public String getName() + { + return ManagementFactory.getRuntimeMXBean().getVmName(); + } + + public String getVersion() + { + return version; + } + + public void setVersion(String version) + { + this.version = version; + } + + public String getHome() + { + return home; + } + + public void setHome(String home) + { + this.home = home; + } + + /** + * JDK启动时间 + */ + public String getStartTime() + { + return DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, DateUtils.getServerStartDate()); + } + + /** + * JDK运行时间 + */ + public String getRunTime() + { + return DateUtils.timeDistance(DateUtils.getNowDate(), DateUtils.getServerStartDate()); + } + + /** + * 运行参数 + */ + public String getInputArgs() + { + return ManagementFactory.getRuntimeMXBean().getInputArguments().toString(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java index 13eec52..ac71504 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java @@ -1,61 +1,61 @@ -package com.ruoyi.framework.web.domain.server; - -import com.ruoyi.common.utils.Arith; - -/** - * 內存相关信息 - * - * @author ruoyi - */ -public class Mem -{ - /** - * 内存总量 - */ - private double total; - - /** - * 已用内存 - */ - private double used; - - /** - * 剩余内存 - */ - private double free; - - public double getTotal() - { - return Arith.div(total, (1024 * 1024 * 1024), 2); - } - - public void setTotal(long total) - { - this.total = total; - } - - public double getUsed() - { - return Arith.div(used, (1024 * 1024 * 1024), 2); - } - - public void setUsed(long used) - { - this.used = used; - } - - public double getFree() - { - return Arith.div(free, (1024 * 1024 * 1024), 2); - } - - public void setFree(long free) - { - this.free = free; - } - - public double getUsage() - { - return Arith.mul(Arith.div(used, total, 4), 100); - } -} +package com.ruoyi.framework.web.domain.server; + +import com.ruoyi.common.utils.Arith; + +/** + * 內存相关信息 + * + * @author ruoyi + */ +public class Mem +{ + /** + * 内存总量 + */ + private double total; + + /** + * 已用内存 + */ + private double used; + + /** + * 剩余内存 + */ + private double free; + + public double getTotal() + { + return Arith.div(total, (1024 * 1024 * 1024), 2); + } + + public void setTotal(long total) + { + this.total = total; + } + + public double getUsed() + { + return Arith.div(used, (1024 * 1024 * 1024), 2); + } + + public void setUsed(long used) + { + this.used = used; + } + + public double getFree() + { + return Arith.div(free, (1024 * 1024 * 1024), 2); + } + + public void setFree(long free) + { + this.free = free; + } + + public double getUsage() + { + return Arith.mul(Arith.div(used, total, 4), 100); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java index 45d64d9..a5f65e4 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java @@ -1,84 +1,84 @@ -package com.ruoyi.framework.web.domain.server; - -/** - * 系统相关信息 - * - * @author ruoyi - */ -public class Sys -{ - /** - * 服务器名称 - */ - private String computerName; - - /** - * 服务器Ip - */ - private String computerIp; - - /** - * 项目路径 - */ - private String userDir; - - /** - * 操作系统 - */ - private String osName; - - /** - * 系统架构 - */ - private String osArch; - - public String getComputerName() - { - return computerName; - } - - public void setComputerName(String computerName) - { - this.computerName = computerName; - } - - public String getComputerIp() - { - return computerIp; - } - - public void setComputerIp(String computerIp) - { - this.computerIp = computerIp; - } - - public String getUserDir() - { - return userDir; - } - - public void setUserDir(String userDir) - { - this.userDir = userDir; - } - - public String getOsName() - { - return osName; - } - - public void setOsName(String osName) - { - this.osName = osName; - } - - public String getOsArch() - { - return osArch; - } - - public void setOsArch(String osArch) - { - this.osArch = osArch; - } -} +package com.ruoyi.framework.web.domain.server; + +/** + * 系统相关信息 + * + * @author ruoyi + */ +public class Sys +{ + /** + * 服务器名称 + */ + private String computerName; + + /** + * 服务器Ip + */ + private String computerIp; + + /** + * 项目路径 + */ + private String userDir; + + /** + * 操作系统 + */ + private String osName; + + /** + * 系统架构 + */ + private String osArch; + + public String getComputerName() + { + return computerName; + } + + public void setComputerName(String computerName) + { + this.computerName = computerName; + } + + public String getComputerIp() + { + return computerIp; + } + + public void setComputerIp(String computerIp) + { + this.computerIp = computerIp; + } + + public String getUserDir() + { + return userDir; + } + + public void setUserDir(String userDir) + { + this.userDir = userDir; + } + + public String getOsName() + { + return osName; + } + + public void setOsName(String osName) + { + this.osName = osName; + } + + public String getOsArch() + { + return osArch; + } + + public void setOsArch(String osArch) + { + this.osArch = osArch; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java index 1320cde..47ecf3f 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java @@ -1,114 +1,114 @@ -package com.ruoyi.framework.web.domain.server; - -/** - * 系统文件相关信息 - * - * @author ruoyi - */ -public class SysFile -{ - /** - * 盘符路径 - */ - private String dirName; - - /** - * 盘符类型 - */ - private String sysTypeName; - - /** - * 文件类型 - */ - private String typeName; - - /** - * 总大小 - */ - private String total; - - /** - * 剩余大小 - */ - private String free; - - /** - * 已经使用量 - */ - private String used; - - /** - * 资源的使用率 - */ - private double usage; - - public String getDirName() - { - return dirName; - } - - public void setDirName(String dirName) - { - this.dirName = dirName; - } - - public String getSysTypeName() - { - return sysTypeName; - } - - public void setSysTypeName(String sysTypeName) - { - this.sysTypeName = sysTypeName; - } - - public String getTypeName() - { - return typeName; - } - - public void setTypeName(String typeName) - { - this.typeName = typeName; - } - - public String getTotal() - { - return total; - } - - public void setTotal(String total) - { - this.total = total; - } - - public String getFree() - { - return free; - } - - public void setFree(String free) - { - this.free = free; - } - - public String getUsed() - { - return used; - } - - public void setUsed(String used) - { - this.used = used; - } - - public double getUsage() - { - return usage; - } - - public void setUsage(double usage) - { - this.usage = usage; - } -} +package com.ruoyi.framework.web.domain.server; + +/** + * 系统文件相关信息 + * + * @author ruoyi + */ +public class SysFile +{ + /** + * 盘符路径 + */ + private String dirName; + + /** + * 盘符类型 + */ + private String sysTypeName; + + /** + * 文件类型 + */ + private String typeName; + + /** + * 总大小 + */ + private String total; + + /** + * 剩余大小 + */ + private String free; + + /** + * 已经使用量 + */ + private String used; + + /** + * 资源的使用率 + */ + private double usage; + + public String getDirName() + { + return dirName; + } + + public void setDirName(String dirName) + { + this.dirName = dirName; + } + + public String getSysTypeName() + { + return sysTypeName; + } + + public void setSysTypeName(String sysTypeName) + { + this.sysTypeName = sysTypeName; + } + + public String getTypeName() + { + return typeName; + } + + public void setTypeName(String typeName) + { + this.typeName = typeName; + } + + public String getTotal() + { + return total; + } + + public void setTotal(String total) + { + this.total = total; + } + + public String getFree() + { + return free; + } + + public void setFree(String free) + { + this.free = free; + } + + public String getUsed() + { + return used; + } + + public void setUsed(String used) + { + this.used = used; + } + + public double getUsage() + { + return usage; + } + + public void setUsage(double usage) + { + this.usage = usage; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java index 1ca0283..e118fb5 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java @@ -1,145 +1,145 @@ -package com.ruoyi.framework.web.exception; - -import jakarta.servlet.http.HttpServletRequest; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.validation.BindException; -import org.springframework.web.HttpRequestMethodNotSupportedException; -import org.springframework.web.bind.MethodArgumentNotValidException; -import org.springframework.web.bind.MissingPathVariableException; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.RestControllerAdvice; -import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; -import com.ruoyi.common.constant.HttpStatus; -import com.ruoyi.common.core.domain.AjaxResult; -import com.ruoyi.common.core.text.Convert; -import com.ruoyi.common.exception.DemoModeException; -import com.ruoyi.common.exception.ServiceException; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.common.utils.html.EscapeUtil; - -/** - * 全局异常处理器 - * - * @author ruoyi - */ -@RestControllerAdvice -public class GlobalExceptionHandler -{ - private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); - - /** - * 权限校验异常 - */ - @ExceptionHandler(AccessDeniedException.class) - public AjaxResult handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request) - { - String requestURI = request.getRequestURI(); - log.error("请求地址'{}',权限校验失败'{}'", requestURI, e.getMessage()); - return AjaxResult.error(HttpStatus.FORBIDDEN, "没有权限,请联系管理员授权"); - } - - /** - * 请求方式不支持 - */ - @ExceptionHandler(HttpRequestMethodNotSupportedException.class) - public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, - HttpServletRequest request) - { - String requestURI = request.getRequestURI(); - log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod()); - return AjaxResult.error(e.getMessage()); - } - - /** - * 业务异常 - */ - @ExceptionHandler(ServiceException.class) - public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request) - { - log.error(e.getMessage(), e); - Integer code = e.getCode(); - return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage()); - } - - /** - * 请求路径中缺少必需的路径变量 - */ - @ExceptionHandler(MissingPathVariableException.class) - public AjaxResult handleMissingPathVariableException(MissingPathVariableException e, HttpServletRequest request) - { - String requestURI = request.getRequestURI(); - log.error("请求路径中缺少必需的路径变量'{}',发生系统异常.", requestURI, e); - return AjaxResult.error(String.format("请求路径中缺少必需的路径变量[%s]", e.getVariableName())); - } - - /** - * 请求参数类型不匹配 - */ - @ExceptionHandler(MethodArgumentTypeMismatchException.class) - public AjaxResult handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request) - { - String requestURI = request.getRequestURI(); - String value = Convert.toStr(e.getValue()); - if (StringUtils.isNotEmpty(value)) - { - value = EscapeUtil.clean(value); - } - log.error("请求参数类型不匹配'{}',发生系统异常.", requestURI, e); - return AjaxResult.error(String.format("请求参数类型不匹配,参数[%s]要求类型为:'%s',但输入值为:'%s'", e.getName(), e.getRequiredType().getName(), value)); - } - - /** - * 拦截未知的运行时异常 - */ - @ExceptionHandler(RuntimeException.class) - public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request) - { - String requestURI = request.getRequestURI(); - log.error("请求地址'{}',发生未知异常.", requestURI, e); - return AjaxResult.error(e.getMessage()); - } - - /** - * 系统异常 - */ - @ExceptionHandler(Exception.class) - public AjaxResult handleException(Exception e, HttpServletRequest request) - { - String requestURI = request.getRequestURI(); - log.error("请求地址'{}',发生系统异常.", requestURI, e); - return AjaxResult.error(e.getMessage()); - } - - /** - * 自定义验证异常 - */ - @ExceptionHandler(BindException.class) - public AjaxResult handleBindException(BindException e) - { - log.error(e.getMessage(), e); - String message = e.getAllErrors().get(0).getDefaultMessage(); - return AjaxResult.error(message); - } - - /** - * 自定义验证异常 - */ - @ExceptionHandler(MethodArgumentNotValidException.class) - public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) - { - log.error(e.getMessage(), e); - String message = e.getBindingResult().getFieldError().getDefaultMessage(); - return AjaxResult.error(message); - } - - /** - * 演示模式异常 - */ - @ExceptionHandler(DemoModeException.class) - public AjaxResult handleDemoModeException(DemoModeException e) - { - return AjaxResult.error("演示模式,不允许操作"); - } -} +package com.ruoyi.framework.web.exception; + +import javax.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.validation.BindException; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingPathVariableException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.exception.DemoModeException; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.html.EscapeUtil; + +/** + * 全局异常处理器 + * + * @author ruoyi + */ +@RestControllerAdvice +public class GlobalExceptionHandler +{ + private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); + + /** + * 权限校验异常 + */ + @ExceptionHandler(AccessDeniedException.class) + public AjaxResult handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',权限校验失败'{}'", requestURI, e.getMessage()); + return AjaxResult.error(HttpStatus.FORBIDDEN, "没有权限,请联系管理员授权"); + } + + /** + * 请求方式不支持 + */ + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, + HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod()); + return AjaxResult.error(e.getMessage()); + } + + /** + * 业务异常 + */ + @ExceptionHandler(ServiceException.class) + public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request) + { + log.error(e.getMessage(), e); + Integer code = e.getCode(); + return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage()); + } + + /** + * 请求路径中缺少必需的路径变量 + */ + @ExceptionHandler(MissingPathVariableException.class) + public AjaxResult handleMissingPathVariableException(MissingPathVariableException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求路径中缺少必需的路径变量'{}',发生系统异常.", requestURI, e); + return AjaxResult.error(String.format("请求路径中缺少必需的路径变量[%s]", e.getVariableName())); + } + + /** + * 请求参数类型不匹配 + */ + @ExceptionHandler(MethodArgumentTypeMismatchException.class) + public AjaxResult handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + String value = Convert.toStr(e.getValue()); + if (StringUtils.isNotEmpty(value)) + { + value = EscapeUtil.clean(value); + } + log.error("请求参数类型不匹配'{}',发生系统异常.", requestURI, e); + return AjaxResult.error(String.format("请求参数类型不匹配,参数[%s]要求类型为:'%s',但输入值为:'%s'", e.getName(), e.getRequiredType().getName(), value)); + } + + /** + * 拦截未知的运行时异常 + */ + @ExceptionHandler(RuntimeException.class) + public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生未知异常.", requestURI, e); + return AjaxResult.error(e.getMessage()); + } + + /** + * 系统异常 + */ + @ExceptionHandler(Exception.class) + public AjaxResult handleException(Exception e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生系统异常.", requestURI, e); + return AjaxResult.error(e.getMessage()); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(BindException.class) + public AjaxResult handleBindException(BindException e) + { + log.error(e.getMessage(), e); + String message = e.getAllErrors().get(0).getDefaultMessage(); + return AjaxResult.error(message); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) + { + log.error(e.getMessage(), e); + String message = e.getBindingResult().getFieldError().getDefaultMessage(); + return AjaxResult.error(message); + } + + /** + * 演示模式异常 + */ + @ExceptionHandler(DemoModeException.class) + public AjaxResult handleDemoModeException(DemoModeException e) + { + return AjaxResult.error("演示模式,不允许操作"); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PasswordTransferCryptoService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PasswordTransferCryptoService.java deleted file mode 100644 index a6cd10f..0000000 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PasswordTransferCryptoService.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.ruoyi.framework.web.service; - -import java.nio.charset.StandardCharsets; -import java.util.Base64; -import javax.crypto.Cipher; -import javax.crypto.spec.SecretKeySpec; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; -import com.ruoyi.common.exception.ServiceException; - -@Service -public class PasswordTransferCryptoService -{ - @Value("${security.password-transfer.key}") - private String key; - - public String decrypt(String cipherText) - { - try - { - Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); - cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES")); - return new String(cipher.doFinal(Base64.getDecoder().decode(cipherText)), StandardCharsets.UTF_8); - } - catch (Exception ex) - { - throw new ServiceException("密码解密失败"); - } - } -} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java index fa0dfef..0011526 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java @@ -1,159 +1,159 @@ -package com.ruoyi.framework.web.service; - -import java.util.Set; -import org.springframework.stereotype.Service; -import org.springframework.util.CollectionUtils; -import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.core.domain.entity.SysRole; -import com.ruoyi.common.core.domain.model.LoginUser; -import com.ruoyi.common.utils.SecurityUtils; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.framework.security.context.PermissionContextHolder; - -/** - * RuoYi首创 自定义权限实现,ss取自SpringSecurity首字母 - * - * @author ruoyi - */ -@Service("ss") -public class PermissionService -{ - /** - * 验证用户是否具备某权限 - * - * @param permission 权限字符串 - * @return 用户是否具备某权限 - */ - public boolean hasPermi(String permission) - { - if (StringUtils.isEmpty(permission)) - { - return false; - } - LoginUser loginUser = SecurityUtils.getLoginUser(); - if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) - { - return false; - } - PermissionContextHolder.setContext(permission); - return hasPermissions(loginUser.getPermissions(), permission); - } - - /** - * 验证用户是否不具备某权限,与 hasPermi逻辑相反 - * - * @param permission 权限字符串 - * @return 用户是否不具备某权限 - */ - public boolean lacksPermi(String permission) - { - return hasPermi(permission) != true; - } - - /** - * 验证用户是否具有以下任意一个权限 - * - * @param permissions 以 PERMISSION_DELIMITER 为分隔符的权限列表 - * @return 用户是否具有以下任意一个权限 - */ - public boolean hasAnyPermi(String permissions) - { - if (StringUtils.isEmpty(permissions)) - { - return false; - } - LoginUser loginUser = SecurityUtils.getLoginUser(); - if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) - { - return false; - } - PermissionContextHolder.setContext(permissions); - Set authorities = loginUser.getPermissions(); - for (String permission : permissions.split(Constants.PERMISSION_DELIMITER)) - { - if (permission != null && hasPermissions(authorities, permission)) - { - return true; - } - } - return false; - } - - /** - * 判断用户是否拥有某个角色 - * - * @param role 角色字符串 - * @return 用户是否具备某角色 - */ - public boolean hasRole(String role) - { - if (StringUtils.isEmpty(role)) - { - return false; - } - LoginUser loginUser = SecurityUtils.getLoginUser(); - if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) - { - return false; - } - for (SysRole sysRole : loginUser.getUser().getRoles()) - { - String roleKey = sysRole.getRoleKey(); - if (Constants.SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role))) - { - return true; - } - } - return false; - } - - /** - * 验证用户是否不具备某角色,与 isRole逻辑相反。 - * - * @param role 角色名称 - * @return 用户是否不具备某角色 - */ - public boolean lacksRole(String role) - { - return hasRole(role) != true; - } - - /** - * 验证用户是否具有以下任意一个角色 - * - * @param roles 以 ROLE_DELIMITER 为分隔符的角色列表 - * @return 用户是否具有以下任意一个角色 - */ - public boolean hasAnyRoles(String roles) - { - if (StringUtils.isEmpty(roles)) - { - return false; - } - LoginUser loginUser = SecurityUtils.getLoginUser(); - if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) - { - return false; - } - for (String role : roles.split(Constants.ROLE_DELIMITER)) - { - if (hasRole(role)) - { - return true; - } - } - return false; - } - - /** - * 判断是否包含权限 - * - * @param permissions 权限列表 - * @param permission 权限字符串 - * @return 用户是否具备某权限 - */ - private boolean hasPermissions(Set permissions, String permission) - { - return permissions.contains(Constants.ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission)); - } -} +package com.ruoyi.framework.web.service; + +import java.util.Set; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.security.context.PermissionContextHolder; + +/** + * RuoYi首创 自定义权限实现,ss取自SpringSecurity首字母 + * + * @author ruoyi + */ +@Service("ss") +public class PermissionService +{ + /** + * 验证用户是否具备某权限 + * + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + public boolean hasPermi(String permission) + { + if (StringUtils.isEmpty(permission)) + { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) + { + return false; + } + PermissionContextHolder.setContext(permission); + return hasPermissions(loginUser.getPermissions(), permission); + } + + /** + * 验证用户是否不具备某权限,与 hasPermi逻辑相反 + * + * @param permission 权限字符串 + * @return 用户是否不具备某权限 + */ + public boolean lacksPermi(String permission) + { + return hasPermi(permission) != true; + } + + /** + * 验证用户是否具有以下任意一个权限 + * + * @param permissions 以 PERMISSION_DELIMITER 为分隔符的权限列表 + * @return 用户是否具有以下任意一个权限 + */ + public boolean hasAnyPermi(String permissions) + { + if (StringUtils.isEmpty(permissions)) + { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) + { + return false; + } + PermissionContextHolder.setContext(permissions); + Set authorities = loginUser.getPermissions(); + for (String permission : permissions.split(Constants.PERMISSION_DELIMITER)) + { + if (permission != null && hasPermissions(authorities, permission)) + { + return true; + } + } + return false; + } + + /** + * 判断用户是否拥有某个角色 + * + * @param role 角色字符串 + * @return 用户是否具备某角色 + */ + public boolean hasRole(String role) + { + if (StringUtils.isEmpty(role)) + { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) + { + return false; + } + for (SysRole sysRole : loginUser.getUser().getRoles()) + { + String roleKey = sysRole.getRoleKey(); + if (Constants.SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role))) + { + return true; + } + } + return false; + } + + /** + * 验证用户是否不具备某角色,与 isRole逻辑相反。 + * + * @param role 角色名称 + * @return 用户是否不具备某角色 + */ + public boolean lacksRole(String role) + { + return hasRole(role) != true; + } + + /** + * 验证用户是否具有以下任意一个角色 + * + * @param roles 以 ROLE_DELIMITER 为分隔符的角色列表 + * @return 用户是否具有以下任意一个角色 + */ + public boolean hasAnyRoles(String roles) + { + if (StringUtils.isEmpty(roles)) + { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) + { + return false; + } + for (String role : roles.split(Constants.ROLE_DELIMITER)) + { + if (hasRole(role)) + { + return true; + } + } + return false; + } + + /** + * 判断是否包含权限 + * + * @param permissions 权限列表 + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + private boolean hasPermissions(Set permissions, String permission) + { + return permissions.contains(Constants.ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission)); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java index b8dba65..a7795f3 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java @@ -1,220 +1,176 @@ -package com.ruoyi.framework.web.service; - -import jakarta.annotation.Resource; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.stereotype.Component; -import com.ruoyi.common.constant.CacheConstants; -import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.constant.UserConstants; -import com.ruoyi.common.core.domain.model.LoginUser; -import com.ruoyi.common.core.redis.RedisCache; -import com.ruoyi.common.exception.ServiceException; -import com.ruoyi.common.exception.user.BlackListException; -import com.ruoyi.common.exception.user.CaptchaException; -import com.ruoyi.common.exception.user.CaptchaExpireException; -import com.ruoyi.common.exception.user.UserNotExistsException; -import com.ruoyi.common.exception.user.UserPasswordNotMatchException; -import com.ruoyi.common.utils.DateUtils; -import com.ruoyi.common.utils.MessageUtils; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.common.utils.ip.IpUtils; -import com.ruoyi.framework.manager.AsyncManager; -import com.ruoyi.framework.manager.factory.AsyncFactory; -import com.ruoyi.framework.security.context.AuthenticationContextHolder; -import com.ruoyi.system.service.ISysConfigService; -import com.ruoyi.system.service.ISysUserService; - -/** - * 登录校验方法 - * - * @author ruoyi - */ -@Component -public class SysLoginService -{ - @Autowired - private TokenService tokenService; - - @Resource - private AuthenticationManager authenticationManager; - - @Autowired - private RedisCache redisCache; - - @Autowired - private ISysUserService userService; - - @Autowired - private ISysConfigService configService; - - /** - * 登录验证 - * - * @param username 用户名 - * @param password 密码 - * @param code 验证码 - * @param uuid 唯一标识 - * @return 结果 - */ - public String login(String username, String password, String code, String uuid) - { - // 验证码校验 - validateCaptcha(username, code, uuid); - // 登录前置校验 - loginPreCheck(username, password); - // 用户验证 - Authentication authentication = null; - try - { - UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password); - AuthenticationContextHolder.setContext(authenticationToken); - // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername - authentication = authenticationManager.authenticate(authenticationToken); - } - catch (Exception e) - { - if (e instanceof BadCredentialsException) - { - AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); - throw new UserPasswordNotMatchException(); - } - else - { - AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage())); - throw new ServiceException(e.getMessage()); - } - } - finally - { - AuthenticationContextHolder.clearContext(); - } - AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"))); - LoginUser loginUser = (LoginUser) authentication.getPrincipal(); - recordLoginInfo(loginUser.getUserId()); - // 生成token - return tokenService.createToken(loginUser); - } - - /** - * 登录验证 - * - * @param username 用户名 - * @param password 密码 - * @return 结果 - */ - public String loginWithoutCaptcha(String username, String password) - { - // 登录前置校验 - loginPreCheck(username, password); - // 用户验证 - Authentication authentication = null; - try - { - UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password); - AuthenticationContextHolder.setContext(authenticationToken); - // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername - authentication = authenticationManager.authenticate(authenticationToken); - } - catch (Exception e) - { - if (e instanceof BadCredentialsException) - { - AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); - throw new UserPasswordNotMatchException(); - } - else - { - AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage())); - throw new ServiceException(e.getMessage()); - } - } - finally - { - AuthenticationContextHolder.clearContext(); - } - AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"))); - LoginUser loginUser = (LoginUser) authentication.getPrincipal(); - recordLoginInfo(loginUser.getUserId()); - // 生成token - return tokenService.createToken(loginUser); - } - - /** - * 校验验证码 - * - * @param username 用户名 - * @param code 验证码 - * @param uuid 唯一标识 - * @return 结果 - */ - public void validateCaptcha(String username, String code, String uuid) - { - boolean captchaEnabled = configService.selectCaptchaEnabled(); - if (captchaEnabled) - { - String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, ""); - String captcha = redisCache.getCacheObject(verifyKey); - if (captcha == null) - { - AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"))); - throw new CaptchaExpireException(); - } - redisCache.deleteObject(verifyKey); - if (!code.equalsIgnoreCase(captcha)) - { - AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"))); - throw new CaptchaException(); - } - } - } - - /** - * 登录前置校验 - * @param username 用户名 - * @param password 用户密码 - */ - public void loginPreCheck(String username, String password) - { - // 用户名或密码为空 错误 - if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) - { - AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null"))); - throw new UserNotExistsException(); - } - // 密码如果不在指定范围内 错误 - if (password.length() < UserConstants.PASSWORD_MIN_LENGTH - || password.length() > UserConstants.PASSWORD_MAX_LENGTH) - { - AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); - throw new UserPasswordNotMatchException(); - } - // 用户名不在指定范围内 错误 - if (username.length() < UserConstants.USERNAME_MIN_LENGTH - || username.length() > UserConstants.USERNAME_MAX_LENGTH) - { - AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); - throw new UserPasswordNotMatchException(); - } - // IP黑名单校验 - String blackStr = configService.selectConfigByKey("sys.login.blackIPList"); - if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) - { - AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("login.blocked"))); - throw new BlackListException(); - } - } - - /** - * 记录登录信息 - * - * @param userId 用户ID - */ - public void recordLoginInfo(Long userId) - { - userService.updateLoginInfo(userId, IpUtils.getIpAddr(), DateUtils.getNowDate()); - } -} +package com.ruoyi.framework.web.service; + +import javax.annotation.Resource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.exception.user.BlackListException; +import com.ruoyi.common.exception.user.CaptchaException; +import com.ruoyi.common.exception.user.CaptchaExpireException; +import com.ruoyi.common.exception.user.UserNotExistsException; +import com.ruoyi.common.exception.user.UserPasswordNotMatchException; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.framework.security.context.AuthenticationContextHolder; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.system.service.ISysUserService; + +/** + * 登录校验方法 + * + * @author ruoyi + */ +@Component +public class SysLoginService +{ + @Autowired + private TokenService tokenService; + + @Resource + private AuthenticationManager authenticationManager; + + @Autowired + private RedisCache redisCache; + + @Autowired + private ISysUserService userService; + + @Autowired + private ISysConfigService configService; + + /** + * 登录验证 + * + * @param username 用户名 + * @param password 密码 + * @param code 验证码 + * @param uuid 唯一标识 + * @return 结果 + */ + public String login(String username, String password, String code, String uuid) + { + // 验证码校验 + validateCaptcha(username, code, uuid); + // 登录前置校验 + loginPreCheck(username, password); + // 用户验证 + Authentication authentication = null; + try + { + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password); + AuthenticationContextHolder.setContext(authenticationToken); + // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername + authentication = authenticationManager.authenticate(authenticationToken); + } + catch (Exception e) + { + if (e instanceof BadCredentialsException) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); + throw new UserPasswordNotMatchException(); + } + else + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage())); + throw new ServiceException(e.getMessage()); + } + } + finally + { + AuthenticationContextHolder.clearContext(); + } + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"))); + LoginUser loginUser = (LoginUser) authentication.getPrincipal(); + recordLoginInfo(loginUser.getUserId()); + // 生成token + return tokenService.createToken(loginUser); + } + + /** + * 校验验证码 + * + * @param username 用户名 + * @param code 验证码 + * @param uuid 唯一标识 + * @return 结果 + */ + public void validateCaptcha(String username, String code, String uuid) + { + boolean captchaEnabled = configService.selectCaptchaEnabled(); + if (captchaEnabled) + { + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, ""); + String captcha = redisCache.getCacheObject(verifyKey); + if (captcha == null) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"))); + throw new CaptchaExpireException(); + } + redisCache.deleteObject(verifyKey); + if (!code.equalsIgnoreCase(captcha)) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"))); + throw new CaptchaException(); + } + } + } + + /** + * 登录前置校验 + * @param username 用户名 + * @param password 用户密码 + */ + public void loginPreCheck(String username, String password) + { + // 用户名或密码为空 错误 + if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null"))); + throw new UserNotExistsException(); + } + // 密码如果不在指定范围内 错误 + if (password.length() < UserConstants.PASSWORD_MIN_LENGTH + || password.length() > UserConstants.PASSWORD_MAX_LENGTH) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); + throw new UserPasswordNotMatchException(); + } + // 用户名不在指定范围内 错误 + if (username.length() < UserConstants.USERNAME_MIN_LENGTH + || username.length() > UserConstants.USERNAME_MAX_LENGTH) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); + throw new UserPasswordNotMatchException(); + } + // IP黑名单校验 + String blackStr = configService.selectConfigByKey("sys.login.blackIPList"); + if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("login.blocked"))); + throw new BlackListException(); + } + } + + /** + * 记录登录信息 + * + * @param userId 用户ID + */ + public void recordLoginInfo(Long userId) + { + userService.updateLoginInfo(userId, IpUtils.getIpAddr(), DateUtils.getNowDate()); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPasswordService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPasswordService.java index 6728c7b..7a1f93d 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPasswordService.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPasswordService.java @@ -1,86 +1,86 @@ -package com.ruoyi.framework.web.service; - -import java.util.concurrent.TimeUnit; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.security.core.Authentication; -import org.springframework.stereotype.Component; -import com.ruoyi.common.constant.CacheConstants; -import com.ruoyi.common.core.domain.entity.SysUser; -import com.ruoyi.common.core.redis.RedisCache; -import com.ruoyi.common.exception.user.UserPasswordNotMatchException; -import com.ruoyi.common.exception.user.UserPasswordRetryLimitExceedException; -import com.ruoyi.common.utils.SecurityUtils; -import com.ruoyi.framework.security.context.AuthenticationContextHolder; - -/** - * 登录密码方法 - * - * @author ruoyi - */ -@Component -public class SysPasswordService -{ - @Autowired - private RedisCache redisCache; - - @Value(value = "${user.password.maxRetryCount}") - private int maxRetryCount; - - @Value(value = "${user.password.lockTime}") - private int lockTime; - - /** - * 登录账户密码错误次数缓存键名 - * - * @param username 用户名 - * @return 缓存键key - */ - private String getCacheKey(String username) - { - return CacheConstants.PWD_ERR_CNT_KEY + username; - } - - public void validate(SysUser user) - { - Authentication usernamePasswordAuthenticationToken = AuthenticationContextHolder.getContext(); - String username = usernamePasswordAuthenticationToken.getName(); - String password = usernamePasswordAuthenticationToken.getCredentials().toString(); - - Integer retryCount = redisCache.getCacheObject(getCacheKey(username)); - - if (retryCount == null) - { - retryCount = 0; - } - - if (retryCount >= Integer.valueOf(maxRetryCount).intValue()) - { - throw new UserPasswordRetryLimitExceedException(maxRetryCount, lockTime); - } - - if (!matches(user, password)) - { - retryCount = retryCount + 1; - redisCache.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES); - throw new UserPasswordNotMatchException(); - } - else - { - clearLoginRecordCache(username); - } - } - - public boolean matches(SysUser user, String rawPassword) - { - return SecurityUtils.matchesPassword(rawPassword, user.getPassword()); - } - - public void clearLoginRecordCache(String loginName) - { - if (redisCache.hasKey(getCacheKey(loginName))) - { - redisCache.deleteObject(getCacheKey(loginName)); - } - } -} +package com.ruoyi.framework.web.service; + +import java.util.concurrent.TimeUnit; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.exception.user.UserPasswordNotMatchException; +import com.ruoyi.common.exception.user.UserPasswordRetryLimitExceedException; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.framework.security.context.AuthenticationContextHolder; + +/** + * 登录密码方法 + * + * @author ruoyi + */ +@Component +public class SysPasswordService +{ + @Autowired + private RedisCache redisCache; + + @Value(value = "${user.password.maxRetryCount}") + private int maxRetryCount; + + @Value(value = "${user.password.lockTime}") + private int lockTime; + + /** + * 登录账户密码错误次数缓存键名 + * + * @param username 用户名 + * @return 缓存键key + */ + private String getCacheKey(String username) + { + return CacheConstants.PWD_ERR_CNT_KEY + username; + } + + public void validate(SysUser user) + { + Authentication usernamePasswordAuthenticationToken = AuthenticationContextHolder.getContext(); + String username = usernamePasswordAuthenticationToken.getName(); + String password = usernamePasswordAuthenticationToken.getCredentials().toString(); + + Integer retryCount = redisCache.getCacheObject(getCacheKey(username)); + + if (retryCount == null) + { + retryCount = 0; + } + + if (retryCount >= Integer.valueOf(maxRetryCount).intValue()) + { + throw new UserPasswordRetryLimitExceedException(maxRetryCount, lockTime); + } + + if (!matches(user, password)) + { + retryCount = retryCount + 1; + redisCache.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES); + throw new UserPasswordNotMatchException(); + } + else + { + clearLoginRecordCache(username); + } + } + + public boolean matches(SysUser user, String rawPassword) + { + return SecurityUtils.matchesPassword(rawPassword, user.getPassword()); + } + + public void clearLoginRecordCache(String loginName) + { + if (redisCache.hasKey(getCacheKey(loginName))) + { + redisCache.deleteObject(getCacheKey(loginName)); + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPermissionService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPermissionService.java index 17960f6..ebe0932 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPermissionService.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPermissionService.java @@ -1,89 +1,89 @@ -package com.ruoyi.framework.web.service; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import org.springframework.util.CollectionUtils; -import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.constant.UserConstants; -import com.ruoyi.common.core.domain.entity.SysRole; -import com.ruoyi.common.core.domain.entity.SysUser; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.system.service.ISysMenuService; -import com.ruoyi.system.service.ISysRoleService; - -/** - * 用户权限处理 - * - * @author ruoyi - */ -@Component -public class SysPermissionService -{ - @Autowired - private ISysRoleService roleService; - - @Autowired - private ISysMenuService menuService; - - /** - * 获取角色数据权限 - * - * @param user 用户信息 - * @return 角色权限信息 - */ - public Set getRolePermission(SysUser user) - { - Set roles = new HashSet(); - // 管理员拥有所有权限 - if (user.isAdmin()) - { - roles.add(Constants.SUPER_ADMIN); - } - else - { - roles.addAll(roleService.selectRolePermissionByUserId(user.getUserId())); - } - return roles; - } - - /** - * 获取菜单数据权限 - * - * @param user 用户信息 - * @return 菜单权限信息 - */ - public Set getMenuPermission(SysUser user) - { - Set perms = new HashSet(); - // 管理员拥有所有权限 - if (user.isAdmin()) - { - perms.add(Constants.ALL_PERMISSION); - } - else - { - List roles = user.getRoles(); - if (!CollectionUtils.isEmpty(roles)) - { - // 多角色设置permissions属性,以便数据权限匹配权限 - for (SysRole role : roles) - { - if (StringUtils.equals(role.getStatus(), UserConstants.ROLE_NORMAL) && !role.isAdmin()) - { - Set rolePerms = menuService.selectMenuPermsByRoleId(role.getRoleId()); - role.setPermissions(rolePerms); - perms.addAll(rolePerms); - } - } - } - else - { - perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId())); - } - } - return perms; - } -} +package com.ruoyi.framework.web.service; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.service.ISysMenuService; +import com.ruoyi.system.service.ISysRoleService; + +/** + * 用户权限处理 + * + * @author ruoyi + */ +@Component +public class SysPermissionService +{ + @Autowired + private ISysRoleService roleService; + + @Autowired + private ISysMenuService menuService; + + /** + * 获取角色数据权限 + * + * @param user 用户信息 + * @return 角色权限信息 + */ + public Set getRolePermission(SysUser user) + { + Set roles = new HashSet(); + // 管理员拥有所有权限 + if (user.isAdmin()) + { + roles.add(Constants.SUPER_ADMIN); + } + else + { + roles.addAll(roleService.selectRolePermissionByUserId(user.getUserId())); + } + return roles; + } + + /** + * 获取菜单数据权限 + * + * @param user 用户信息 + * @return 菜单权限信息 + */ + public Set getMenuPermission(SysUser user) + { + Set perms = new HashSet(); + // 管理员拥有所有权限 + if (user.isAdmin()) + { + perms.add(Constants.ALL_PERMISSION); + } + else + { + List roles = user.getRoles(); + if (!CollectionUtils.isEmpty(roles)) + { + // 多角色设置permissions属性,以便数据权限匹配权限 + for (SysRole role : roles) + { + if (StringUtils.equals(role.getStatus(), UserConstants.ROLE_NORMAL) && !role.isAdmin()) + { + Set rolePerms = menuService.selectMenuPermsByRoleId(role.getRoleId()); + role.setPermissions(rolePerms); + perms.addAll(rolePerms); + } + } + } + else + { + perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId())); + } + } + return perms; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java index 8305bcb..a6c22bb 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java @@ -1,117 +1,117 @@ -package com.ruoyi.framework.web.service; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import com.ruoyi.common.constant.CacheConstants; -import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.constant.UserConstants; -import com.ruoyi.common.core.domain.entity.SysUser; -import com.ruoyi.common.core.domain.model.RegisterBody; -import com.ruoyi.common.core.redis.RedisCache; -import com.ruoyi.common.exception.user.CaptchaException; -import com.ruoyi.common.exception.user.CaptchaExpireException; -import com.ruoyi.common.utils.DateUtils; -import com.ruoyi.common.utils.MessageUtils; -import com.ruoyi.common.utils.SecurityUtils; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.framework.manager.AsyncManager; -import com.ruoyi.framework.manager.factory.AsyncFactory; -import com.ruoyi.system.service.ISysConfigService; -import com.ruoyi.system.service.ISysUserService; - -/** - * 注册校验方法 - * - * @author ruoyi - */ -@Component -public class SysRegisterService -{ - @Autowired - private ISysUserService userService; - - @Autowired - private ISysConfigService configService; - - @Autowired - private RedisCache redisCache; - - /** - * 注册 - */ - public String register(RegisterBody registerBody) - { - String msg = "", username = registerBody.getUsername(), password = registerBody.getPassword(); - SysUser sysUser = new SysUser(); - sysUser.setUserName(username); - - // 验证码开关 - boolean captchaEnabled = configService.selectCaptchaEnabled(); - if (captchaEnabled) - { - validateCaptcha(username, registerBody.getCode(), registerBody.getUuid()); - } - - if (StringUtils.isEmpty(username)) - { - msg = "用户名不能为空"; - } - else if (StringUtils.isEmpty(password)) - { - msg = "用户密码不能为空"; - } - else if (username.length() < UserConstants.USERNAME_MIN_LENGTH - || username.length() > UserConstants.USERNAME_MAX_LENGTH) - { - msg = "账户长度必须在2到20个字符之间"; - } - else if (password.length() < UserConstants.PASSWORD_MIN_LENGTH - || password.length() > UserConstants.PASSWORD_MAX_LENGTH) - { - msg = "密码长度必须在5到20个字符之间"; - } - else if (!userService.checkUserNameUnique(sysUser)) - { - msg = "保存用户'" + username + "'失败,注册账号已存在"; - } - else - { - sysUser.setNickName(username); - sysUser.setPwdUpdateDate(DateUtils.getNowDate()); - sysUser.setPassword(SecurityUtils.encryptPassword(password)); - boolean regFlag = userService.registerUser(sysUser); - if (!regFlag) - { - msg = "注册失败,请联系系统管理人员"; - } - else - { - AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.register.success"))); - } - } - return msg; - } - - /** - * 校验验证码 - * - * @param username 用户名 - * @param code 验证码 - * @param uuid 唯一标识 - * @return 结果 - */ - public void validateCaptcha(String username, String code, String uuid) - { - String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, ""); - String captcha = redisCache.getCacheObject(verifyKey); - redisCache.deleteObject(verifyKey); - if (captcha == null) - { - throw new CaptchaExpireException(); - } - if (!code.equalsIgnoreCase(captcha)) - { - throw new CaptchaException(); - } - } -} +package com.ruoyi.framework.web.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.RegisterBody; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.exception.user.CaptchaException; +import com.ruoyi.common.exception.user.CaptchaExpireException; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.system.service.ISysUserService; + +/** + * 注册校验方法 + * + * @author ruoyi + */ +@Component +public class SysRegisterService +{ + @Autowired + private ISysUserService userService; + + @Autowired + private ISysConfigService configService; + + @Autowired + private RedisCache redisCache; + + /** + * 注册 + */ + public String register(RegisterBody registerBody) + { + String msg = "", username = registerBody.getUsername(), password = registerBody.getPassword(); + SysUser sysUser = new SysUser(); + sysUser.setUserName(username); + + // 验证码开关 + boolean captchaEnabled = configService.selectCaptchaEnabled(); + if (captchaEnabled) + { + validateCaptcha(username, registerBody.getCode(), registerBody.getUuid()); + } + + if (StringUtils.isEmpty(username)) + { + msg = "用户名不能为空"; + } + else if (StringUtils.isEmpty(password)) + { + msg = "用户密码不能为空"; + } + else if (username.length() < UserConstants.USERNAME_MIN_LENGTH + || username.length() > UserConstants.USERNAME_MAX_LENGTH) + { + msg = "账户长度必须在2到20个字符之间"; + } + else if (password.length() < UserConstants.PASSWORD_MIN_LENGTH + || password.length() > UserConstants.PASSWORD_MAX_LENGTH) + { + msg = "密码长度必须在5到20个字符之间"; + } + else if (!userService.checkUserNameUnique(sysUser)) + { + msg = "保存用户'" + username + "'失败,注册账号已存在"; + } + else + { + sysUser.setNickName(username); + sysUser.setPwdUpdateDate(DateUtils.getNowDate()); + sysUser.setPassword(SecurityUtils.encryptPassword(password)); + boolean regFlag = userService.registerUser(sysUser); + if (!regFlag) + { + msg = "注册失败,请联系系统管理人员"; + } + else + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.register.success"))); + } + } + return msg; + } + + /** + * 校验验证码 + * + * @param username 用户名 + * @param code 验证码 + * @param uuid 唯一标识 + * @return 结果 + */ + public void validateCaptcha(String username, String code, String uuid) + { + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, ""); + String captcha = redisCache.getCacheObject(verifyKey); + redisCache.deleteObject(verifyKey); + if (captcha == null) + { + throw new CaptchaExpireException(); + } + if (!code.equalsIgnoreCase(captcha)) + { + throw new CaptchaException(); + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java index f096bc6..056ec24 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java @@ -1,232 +1,232 @@ -package com.ruoyi.framework.web.service; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.TimeUnit; -import jakarta.servlet.http.HttpServletRequest; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; -import com.ruoyi.common.constant.CacheConstants; -import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.core.domain.model.LoginUser; -import com.ruoyi.common.core.redis.RedisCache; -import com.ruoyi.common.utils.ServletUtils; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.common.utils.http.UserAgentUtils; -import com.ruoyi.common.utils.ip.AddressUtils; -import com.ruoyi.common.utils.ip.IpUtils; -import com.ruoyi.common.utils.uuid.IdUtils; -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.SignatureAlgorithm; - -/** - * token验证处理 - * - * @author ruoyi - */ -@Component -public class TokenService -{ - private static final Logger log = LoggerFactory.getLogger(TokenService.class); - - // 令牌自定义标识 - @Value("${token.header}") - private String header; - - // 令牌秘钥 - @Value("${token.secret}") - private String secret; - - // 令牌有效期(默认30分钟) - @Value("${token.expireTime}") - private int expireTime; - - protected static final long MILLIS_SECOND = 1000; - - protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND; - - private static final Long MILLIS_MINUTE_TWENTY = 20 * 60 * 1000L; - - @Autowired - private RedisCache redisCache; - - /** - * 获取用户身份信息 - * - * @return 用户信息 - */ - public LoginUser getLoginUser(HttpServletRequest request) - { - // 获取请求携带的令牌 - String token = getToken(request); - if (StringUtils.isNotEmpty(token)) - { - try - { - Claims claims = parseToken(token); - // 解析对应的权限以及用户信息 - String uuid = (String) claims.get(Constants.LOGIN_USER_KEY); - String userKey = getTokenKey(uuid); - LoginUser user = redisCache.getCacheObject(userKey); - return user; - } - catch (Exception e) - { - log.error("获取用户信息异常'{}'", e.getMessage()); - } - } - return null; - } - - /** - * 设置用户身份信息 - */ - public void setLoginUser(LoginUser loginUser) - { - if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken())) - { - refreshToken(loginUser); - } - } - - /** - * 删除用户身份信息 - */ - public void delLoginUser(String token) - { - if (StringUtils.isNotEmpty(token)) - { - String userKey = getTokenKey(token); - redisCache.deleteObject(userKey); - } - } - - /** - * 创建令牌 - * - * @param loginUser 用户信息 - * @return 令牌 - */ - public String createToken(LoginUser loginUser) - { - String token = IdUtils.fastUUID(); - loginUser.setToken(token); - setUserAgent(loginUser); - refreshToken(loginUser); - - Map claims = new HashMap<>(); - claims.put(Constants.LOGIN_USER_KEY, token); - claims.put(Constants.JWT_USERNAME, loginUser.getUsername()); - return createToken(claims); - } - - /** - * 验证令牌有效期,相差不足20分钟,自动刷新缓存 - * - * @param loginUser 登录信息 - * @return 令牌 - */ - public void verifyToken(LoginUser loginUser) - { - long expireTime = loginUser.getExpireTime(); - long currentTime = System.currentTimeMillis(); - if (expireTime - currentTime <= MILLIS_MINUTE_TWENTY) - { - refreshToken(loginUser); - } - } - - /** - * 刷新令牌有效期 - * - * @param loginUser 登录信息 - */ - public void refreshToken(LoginUser loginUser) - { - loginUser.setLoginTime(System.currentTimeMillis()); - loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE); - // 根据uuid将loginUser缓存 - String userKey = getTokenKey(loginUser.getToken()); - redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES); - } - - /** - * 设置用户代理信息 - * - * @param loginUser 登录信息 - */ - public void setUserAgent(LoginUser loginUser) - { - String userAgent = ServletUtils.getRequest().getHeader("User-Agent"); - String ip = IpUtils.getIpAddr(); - loginUser.setIpaddr(ip); - loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); - loginUser.setBrowser(UserAgentUtils.getBrowser(userAgent)); - loginUser.setOs(UserAgentUtils.getOperatingSystem(userAgent)); - } - - /** - * 从数据声明生成令牌 - * - * @param claims 数据声明 - * @return 令牌 - */ - private String createToken(Map claims) - { - String token = Jwts.builder() - .setClaims(claims) - .signWith(SignatureAlgorithm.HS512, secret).compact(); - return token; - } - - /** - * 从令牌中获取数据声明 - * - * @param token 令牌 - * @return 数据声明 - */ - private Claims parseToken(String token) - { - return Jwts.parser() - .setSigningKey(secret) - .parseClaimsJws(token) - .getBody(); - } - - /** - * 从令牌中获取用户名 - * - * @param token 令牌 - * @return 用户名 - */ - public String getUsernameFromToken(String token) - { - Claims claims = parseToken(token); - return claims.getSubject(); - } - - /** - * 获取请求token - * - * @param request - * @return token - */ - private String getToken(HttpServletRequest request) - { - String token = request.getHeader(header); - if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) - { - token = token.replace(Constants.TOKEN_PREFIX, ""); - } - return token; - } - - private String getTokenKey(String uuid) - { - return CacheConstants.LOGIN_TOKEN_KEY + uuid; - } -} +package com.ruoyi.framework.web.service; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import javax.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.http.UserAgentUtils; +import com.ruoyi.common.utils.ip.AddressUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.common.utils.uuid.IdUtils; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; + +/** + * token验证处理 + * + * @author ruoyi + */ +@Component +public class TokenService +{ + private static final Logger log = LoggerFactory.getLogger(TokenService.class); + + // 令牌自定义标识 + @Value("${token.header}") + private String header; + + // 令牌秘钥 + @Value("${token.secret}") + private String secret; + + // 令牌有效期(默认30分钟) + @Value("${token.expireTime}") + private int expireTime; + + protected static final long MILLIS_SECOND = 1000; + + protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND; + + private static final Long MILLIS_MINUTE_TWENTY = 20 * 60 * 1000L; + + @Autowired + private RedisCache redisCache; + + /** + * 获取用户身份信息 + * + * @return 用户信息 + */ + public LoginUser getLoginUser(HttpServletRequest request) + { + // 获取请求携带的令牌 + String token = getToken(request); + if (StringUtils.isNotEmpty(token)) + { + try + { + Claims claims = parseToken(token); + // 解析对应的权限以及用户信息 + String uuid = (String) claims.get(Constants.LOGIN_USER_KEY); + String userKey = getTokenKey(uuid); + LoginUser user = redisCache.getCacheObject(userKey); + return user; + } + catch (Exception e) + { + log.error("获取用户信息异常'{}'", e.getMessage()); + } + } + return null; + } + + /** + * 设置用户身份信息 + */ + public void setLoginUser(LoginUser loginUser) + { + if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken())) + { + refreshToken(loginUser); + } + } + + /** + * 删除用户身份信息 + */ + public void delLoginUser(String token) + { + if (StringUtils.isNotEmpty(token)) + { + String userKey = getTokenKey(token); + redisCache.deleteObject(userKey); + } + } + + /** + * 创建令牌 + * + * @param loginUser 用户信息 + * @return 令牌 + */ + public String createToken(LoginUser loginUser) + { + String token = IdUtils.fastUUID(); + loginUser.setToken(token); + setUserAgent(loginUser); + refreshToken(loginUser); + + Map claims = new HashMap<>(); + claims.put(Constants.LOGIN_USER_KEY, token); + claims.put(Constants.JWT_USERNAME, loginUser.getUsername()); + return createToken(claims); + } + + /** + * 验证令牌有效期,相差不足20分钟,自动刷新缓存 + * + * @param loginUser 登录信息 + * @return 令牌 + */ + public void verifyToken(LoginUser loginUser) + { + long expireTime = loginUser.getExpireTime(); + long currentTime = System.currentTimeMillis(); + if (expireTime - currentTime <= MILLIS_MINUTE_TWENTY) + { + refreshToken(loginUser); + } + } + + /** + * 刷新令牌有效期 + * + * @param loginUser 登录信息 + */ + public void refreshToken(LoginUser loginUser) + { + loginUser.setLoginTime(System.currentTimeMillis()); + loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE); + // 根据uuid将loginUser缓存 + String userKey = getTokenKey(loginUser.getToken()); + redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES); + } + + /** + * 设置用户代理信息 + * + * @param loginUser 登录信息 + */ + public void setUserAgent(LoginUser loginUser) + { + String userAgent = ServletUtils.getRequest().getHeader("User-Agent"); + String ip = IpUtils.getIpAddr(); + loginUser.setIpaddr(ip); + loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); + loginUser.setBrowser(UserAgentUtils.getBrowser(userAgent)); + loginUser.setOs(UserAgentUtils.getOperatingSystem(userAgent)); + } + + /** + * 从数据声明生成令牌 + * + * @param claims 数据声明 + * @return 令牌 + */ + private String createToken(Map claims) + { + String token = Jwts.builder() + .setClaims(claims) + .signWith(SignatureAlgorithm.HS512, secret).compact(); + return token; + } + + /** + * 从令牌中获取数据声明 + * + * @param token 令牌 + * @return 数据声明 + */ + private Claims parseToken(String token) + { + return Jwts.parser() + .setSigningKey(secret) + .parseClaimsJws(token) + .getBody(); + } + + /** + * 从令牌中获取用户名 + * + * @param token 令牌 + * @return 用户名 + */ + public String getUsernameFromToken(String token) + { + Claims claims = parseToken(token); + return claims.getSubject(); + } + + /** + * 获取请求token + * + * @param request + * @return token + */ + private String getToken(HttpServletRequest request) + { + String token = request.getHeader(header); + if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) + { + token = token.replace(Constants.TOKEN_PREFIX, ""); + } + return token; + } + + private String getTokenKey(String uuid) + { + return CacheConstants.LOGIN_TOKEN_KEY + uuid; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java index 5dcdf90..e294fbd 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java @@ -1,66 +1,66 @@ -package com.ruoyi.framework.web.service; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.stereotype.Service; -import com.ruoyi.common.core.domain.entity.SysUser; -import com.ruoyi.common.core.domain.model.LoginUser; -import com.ruoyi.common.enums.UserStatus; -import com.ruoyi.common.exception.ServiceException; -import com.ruoyi.common.utils.MessageUtils; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.system.service.ISysUserService; - -/** - * 用户验证处理 - * - * @author ruoyi - */ -@Service -public class UserDetailsServiceImpl implements UserDetailsService -{ - private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class); - - @Autowired - private ISysUserService userService; - - @Autowired - private SysPasswordService passwordService; - - @Autowired - private SysPermissionService permissionService; - - @Override - public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException - { - SysUser user = userService.selectUserByUserName(username); - if (StringUtils.isNull(user)) - { - log.info("登录用户:{} 不存在.", username); - throw new ServiceException(MessageUtils.message("user.not.exists")); - } - else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) - { - log.info("登录用户:{} 已被删除.", username); - throw new ServiceException(MessageUtils.message("user.password.delete")); - } - else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) - { - log.info("登录用户:{} 已被停用.", username); - throw new ServiceException(MessageUtils.message("user.blocked")); - } - - passwordService.validate(user); - - return createLoginUser(user); - } - - public UserDetails createLoginUser(SysUser user) - { - return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user)); - } -} +package com.ruoyi.framework.web.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.enums.UserStatus; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.service.ISysUserService; + +/** + * 用户验证处理 + * + * @author ruoyi + */ +@Service +public class UserDetailsServiceImpl implements UserDetailsService +{ + private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class); + + @Autowired + private ISysUserService userService; + + @Autowired + private SysPasswordService passwordService; + + @Autowired + private SysPermissionService permissionService; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException + { + SysUser user = userService.selectUserByUserName(username); + if (StringUtils.isNull(user)) + { + log.info("登录用户:{} 不存在.", username); + throw new ServiceException(MessageUtils.message("user.not.exists")); + } + else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) + { + log.info("登录用户:{} 已被删除.", username); + throw new ServiceException(MessageUtils.message("user.password.delete")); + } + else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) + { + log.info("登录用户:{} 已被停用.", username); + throw new ServiceException(MessageUtils.message("user.blocked")); + } + + passwordService.validate(user); + + return createLoginUser(user); + } + + public UserDetails createLoginUser(SysUser user) + { + return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user)); + } +} diff --git a/ruoyi-framework/src/test/java/com/ruoyi/framework/aspectj/RateLimiterAspectTest.java b/ruoyi-framework/src/test/java/com/ruoyi/framework/aspectj/RateLimiterAspectTest.java deleted file mode 100644 index ea5a2d1..0000000 --- a/ruoyi-framework/src/test/java/com/ruoyi/framework/aspectj/RateLimiterAspectTest.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.ruoyi.framework.aspectj; - -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.lang.reflect.Method; -import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.Signature; -import org.aspectj.lang.reflect.MethodSignature; -import org.junit.jupiter.api.Test; -import com.ruoyi.common.annotation.RateLimiter; -import com.ruoyi.common.core.cache.InMemoryCacheStore; -import com.ruoyi.common.core.redis.RedisCache; -import com.ruoyi.common.exception.ServiceException; - -class RateLimiterAspectTest -{ - @Test - void shouldRejectThirdRequestWithinWindow() throws Throwable - { - RateLimiterAspect aspect = new RateLimiterAspect(new RedisCache(new InMemoryCacheStore())); - JoinPoint joinPoint = mockJoinPoint(TestRateLimitTarget.class.getDeclaredMethod("limited")); - RateLimiter rateLimiter = TestRateLimitTarget.class.getDeclaredMethod("limited").getAnnotation(RateLimiter.class); - - assertDoesNotThrow(() -> aspect.doBefore(joinPoint, rateLimiter)); - assertDoesNotThrow(() -> aspect.doBefore(joinPoint, rateLimiter)); - assertThrows(ServiceException.class, () -> aspect.doBefore(joinPoint, rateLimiter)); - } - - private JoinPoint mockJoinPoint(Method method) - { - MethodSignature signature = mock(MethodSignature.class); - when(signature.getMethod()).thenReturn(method); - - JoinPoint joinPoint = mock(JoinPoint.class); - when(joinPoint.getSignature()).thenReturn((Signature) signature); - return joinPoint; - } - - static class TestRateLimitTarget - { - @RateLimiter(count = 2, time = 60) - public void limited() - { - } - } -} diff --git a/ruoyi-framework/src/test/java/com/ruoyi/framework/web/service/PasswordTransferCryptoServiceTest.java b/ruoyi-framework/src/test/java/com/ruoyi/framework/web/service/PasswordTransferCryptoServiceTest.java deleted file mode 100644 index 4113734..0000000 --- a/ruoyi-framework/src/test/java/com/ruoyi/framework/web/service/PasswordTransferCryptoServiceTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.ruoyi.framework.web.service; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import java.nio.charset.StandardCharsets; -import java.util.Base64; -import javax.crypto.Cipher; -import javax.crypto.spec.SecretKeySpec; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.test.util.ReflectionTestUtils; -import com.ruoyi.common.exception.ServiceException; - -class PasswordTransferCryptoServiceTest -{ - private static final String KEY = "1234567890abcdef"; - - private PasswordTransferCryptoService service; - - @BeforeEach - void setUp() - { - service = new PasswordTransferCryptoService(); - ReflectionTestUtils.setField(service, "key", KEY); - } - - @Test - void shouldDecryptValidCipherText() throws Exception - { - String plain = service.decrypt(encrypt("admin123")); - assertEquals("admin123", plain); - } - - @Test - void shouldRejectInvalidCipherText() - { - assertThrows(ServiceException.class, () -> service.decrypt("not-base64")); - } - - private String encrypt(String plainText) throws Exception - { - Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); - cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(KEY.getBytes(StandardCharsets.UTF_8), "AES")); - return Base64.getEncoder().encodeToString(cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8))); - } -} diff --git a/ruoyi-framework/src/test/java/com/ruoyi/framework/web/service/TokenServiceLocalCacheTest.java b/ruoyi-framework/src/test/java/com/ruoyi/framework/web/service/TokenServiceLocalCacheTest.java deleted file mode 100644 index 91d7ed6..0000000 --- a/ruoyi-framework/src/test/java/com/ruoyi/framework/web/service/TokenServiceLocalCacheTest.java +++ /dev/null @@ -1,117 +0,0 @@ -package com.ruoyi.framework.web.service; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.lang.reflect.Method; -import java.util.concurrent.TimeUnit; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; -import com.ruoyi.common.annotation.RepeatSubmit; -import com.ruoyi.common.constant.CacheConstants; -import com.ruoyi.common.core.cache.InMemoryCacheStore; -import com.ruoyi.common.core.domain.entity.SysUser; -import com.ruoyi.common.core.domain.model.LoginUser; -import com.ruoyi.common.core.redis.RedisCache; -import com.ruoyi.framework.interceptor.impl.SameUrlDataInterceptor; -import com.ruoyi.system.service.ISysConfigService; - -class TokenServiceLocalCacheTest -{ - @AfterEach - void clearRequestContext() - { - RequestContextHolder.resetRequestAttributes(); - } - - @Test - void shouldStoreLoginUserWithTokenTtl() - { - RedisCache redisCache = new RedisCache(new InMemoryCacheStore()); - TokenService tokenService = new TokenService(); - ReflectionTestUtils.setField(tokenService, "redisCache", redisCache); - ReflectionTestUtils.setField(tokenService, "header", "Authorization"); - ReflectionTestUtils.setField(tokenService, "secret", "unit-test-secret"); - ReflectionTestUtils.setField(tokenService, "expireTime", 30); - - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setRemoteAddr("127.0.0.1"); - request.addHeader("User-Agent", "Mozilla/5.0"); - RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request)); - - SysUser user = new SysUser(); - user.setUserName("admin"); - user.setPassword("password"); - LoginUser loginUser = new LoginUser(); - loginUser.setUser(user); - - String jwt = tokenService.createToken(loginUser); - - assertNotNull(jwt); - assertNotNull(redisCache.getCacheObject(CacheConstants.LOGIN_TOKEN_KEY + loginUser.getToken())); - assertTrue(redisCache.getExpire(CacheConstants.LOGIN_TOKEN_KEY + loginUser.getToken()) > 0); - } - - @Test - void shouldDeleteCaptchaAfterValidation() - { - RedisCache redisCache = new RedisCache(new InMemoryCacheStore()); - SysLoginService loginService = new SysLoginService(); - ISysConfigService configService = mock(ISysConfigService.class); - when(configService.selectCaptchaEnabled()).thenReturn(true); - ReflectionTestUtils.setField(loginService, "redisCache", redisCache); - ReflectionTestUtils.setField(loginService, "configService", configService); - - redisCache.setCacheObject(CacheConstants.CAPTCHA_CODE_KEY + "uuid", "ABCD", 1, TimeUnit.MINUTES); - - loginService.validateCaptcha("admin", "ABCD", "uuid"); - - assertFalse(redisCache.hasKey(CacheConstants.CAPTCHA_CODE_KEY + "uuid")); - } - - @Test - void shouldSupportMillisecondRepeatSubmitWindow() throws Exception - { - RedisCache redisCache = new RedisCache(new InMemoryCacheStore()); - SameUrlDataInterceptor interceptor = new SameUrlDataInterceptor(); - ReflectionTestUtils.setField(interceptor, "redisCache", redisCache); - ReflectionTestUtils.setField(interceptor, "header", "Authorization"); - - RepeatSubmit repeatSubmit = RepeatSubmitTarget.class.getDeclaredMethod("submit") - .getAnnotation(RepeatSubmit.class); - - MockHttpServletRequest firstRequest = createRepeatRequest(); - assertFalse(interceptor.isRepeatSubmit(firstRequest, repeatSubmit)); - - MockHttpServletRequest secondRequest = createRepeatRequest(); - assertTrue(interceptor.isRepeatSubmit(secondRequest, repeatSubmit)); - - Thread.sleep(70); - MockHttpServletRequest thirdRequest = createRepeatRequest(); - assertFalse(interceptor.isRepeatSubmit(thirdRequest, repeatSubmit)); - } - - private MockHttpServletRequest createRepeatRequest() - { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setRequestURI("/loan/pricing"); - request.addHeader("Authorization", "token-1"); - request.addParameter("loanId", "1001"); - return request; - } - - static class RepeatSubmitTarget - { - @RepeatSubmit(interval = 50) - public void submit() - { - } - } -} diff --git a/ruoyi-generator/pom.xml b/ruoyi-generator/pom.xml index 876ca3b..ab95fcb 100644 --- a/ruoyi-generator/pom.xml +++ b/ruoyi-generator/pom.xml @@ -1,40 +1,40 @@ - - - - ruoyi - com.ruoyi - 3.9.1 - - 4.0.0 - - ruoyi-generator - - - generator代码生成 - - - - - - - org.apache.velocity - velocity-engine-core - - - - - com.ruoyi - ruoyi-common - - - - - com.alibaba - druid-spring-boot-3-starter - - - - + + + + ruoyi + com.ruoyi + 3.9.2 + + 4.0.0 + + ruoyi-generator + + + generator代码生成 + + + + + + + org.apache.velocity + velocity-engine-core + + + + + com.ruoyi + ruoyi-common + + + + + com.alibaba + druid-spring-boot-starter + + + + \ No newline at end of file diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/config/GenConfig.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/config/GenConfig.java index c01857c..43320f5 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/config/GenConfig.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/config/GenConfig.java @@ -1,87 +1,87 @@ -package com.ruoyi.generator.config; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.PropertySource; -import org.springframework.stereotype.Component; - -/** - * 读取代码生成相关配置 - * - * @author ruoyi - */ -@Component -@ConfigurationProperties(prefix = "gen") -@PropertySource(value = { "classpath:generator.yml" }) -public class GenConfig -{ - /** 作者 */ - public static String author; - - /** 生成包路径 */ - public static String packageName; - - /** 自动去除表前缀 */ - public static boolean autoRemovePre; - - /** 表前缀 */ - public static String tablePrefix; - - /** 是否允许生成文件覆盖到本地(自定义路径) */ - public static boolean allowOverwrite; - - public static String getAuthor() - { - return author; - } - - @Value("${author}") - public void setAuthor(String author) - { - GenConfig.author = author; - } - - public static String getPackageName() - { - return packageName; - } - - @Value("${packageName}") - public void setPackageName(String packageName) - { - GenConfig.packageName = packageName; - } - - public static boolean getAutoRemovePre() - { - return autoRemovePre; - } - - @Value("${autoRemovePre}") - public void setAutoRemovePre(boolean autoRemovePre) - { - GenConfig.autoRemovePre = autoRemovePre; - } - - public static String getTablePrefix() - { - return tablePrefix; - } - - @Value("${tablePrefix}") - public void setTablePrefix(String tablePrefix) - { - GenConfig.tablePrefix = tablePrefix; - } - - public static boolean isAllowOverwrite() - { - return allowOverwrite; - } - - @Value("${allowOverwrite}") - public void setAllowOverwrite(boolean allowOverwrite) - { - GenConfig.allowOverwrite = allowOverwrite; - } -} +package com.ruoyi.generator.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.PropertySource; +import org.springframework.stereotype.Component; + +/** + * 读取代码生成相关配置 + * + * @author ruoyi + */ +@Component +@ConfigurationProperties(prefix = "gen") +@PropertySource(value = { "classpath:generator.yml" }) +public class GenConfig +{ + /** 作者 */ + public static String author; + + /** 生成包路径 */ + public static String packageName; + + /** 自动去除表前缀 */ + public static boolean autoRemovePre; + + /** 表前缀 */ + public static String tablePrefix; + + /** 是否允许生成文件覆盖到本地(自定义路径) */ + public static boolean allowOverwrite; + + public static String getAuthor() + { + return author; + } + + @Value("${author}") + public void setAuthor(String author) + { + GenConfig.author = author; + } + + public static String getPackageName() + { + return packageName; + } + + @Value("${packageName}") + public void setPackageName(String packageName) + { + GenConfig.packageName = packageName; + } + + public static boolean getAutoRemovePre() + { + return autoRemovePre; + } + + @Value("${autoRemovePre}") + public void setAutoRemovePre(boolean autoRemovePre) + { + GenConfig.autoRemovePre = autoRemovePre; + } + + public static String getTablePrefix() + { + return tablePrefix; + } + + @Value("${tablePrefix}") + public void setTablePrefix(String tablePrefix) + { + GenConfig.tablePrefix = tablePrefix; + } + + public static boolean isAllowOverwrite() + { + return allowOverwrite; + } + + @Value("${allowOverwrite}") + public void setAllowOverwrite(boolean allowOverwrite) + { + GenConfig.allowOverwrite = allowOverwrite; + } +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java index 4d750e4..44eba0a 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java @@ -1,263 +1,264 @@ -package com.ruoyi.generator.controller; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import jakarta.servlet.http.HttpServletResponse; -import org.apache.commons.io.IOUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -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.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.RestController; -import com.alibaba.druid.DbType; -import com.alibaba.druid.sql.SQLUtils; -import com.alibaba.druid.sql.ast.SQLStatement; -import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement; -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.common.utils.SecurityUtils; -import com.ruoyi.common.utils.sql.SqlUtil; -import com.ruoyi.generator.config.GenConfig; -import com.ruoyi.generator.domain.GenTable; -import com.ruoyi.generator.domain.GenTableColumn; -import com.ruoyi.generator.service.IGenTableColumnService; -import com.ruoyi.generator.service.IGenTableService; - -/** - * 代码生成 操作处理 - * - * @author ruoyi - */ -@RestController -@RequestMapping("/tool/gen") -public class GenController extends BaseController -{ - @Autowired - private IGenTableService genTableService; - - @Autowired - private IGenTableColumnService genTableColumnService; - - /** - * 查询代码生成列表 - */ - @PreAuthorize("@ss.hasPermi('tool:gen:list')") - @GetMapping("/list") - public TableDataInfo genList(GenTable genTable) - { - startPage(); - List list = genTableService.selectGenTableList(genTable); - return getDataTable(list); - } - - /** - * 获取代码生成信息 - */ - @PreAuthorize("@ss.hasPermi('tool:gen:query')") - @GetMapping(value = "/{tableId}") - public AjaxResult getInfo(@PathVariable Long tableId) - { - GenTable table = genTableService.selectGenTableById(tableId); - List tables = genTableService.selectGenTableAll(); - List list = genTableColumnService.selectGenTableColumnListByTableId(tableId); - Map map = new HashMap(); - map.put("info", table); - map.put("rows", list); - map.put("tables", tables); - return success(map); - } - - /** - * 查询数据库列表 - */ - @PreAuthorize("@ss.hasPermi('tool:gen:list')") - @GetMapping("/db/list") - public TableDataInfo dataList(GenTable genTable) - { - startPage(); - List list = genTableService.selectDbTableList(genTable); - return getDataTable(list); - } - - /** - * 查询数据表字段列表 - */ - @PreAuthorize("@ss.hasPermi('tool:gen:list')") - @GetMapping(value = "/column/{tableId}") - public TableDataInfo columnList(Long tableId) - { - TableDataInfo dataInfo = new TableDataInfo(); - List list = genTableColumnService.selectGenTableColumnListByTableId(tableId); - dataInfo.setRows(list); - dataInfo.setTotal(list.size()); - return dataInfo; - } - - /** - * 导入表结构(保存) - */ - @PreAuthorize("@ss.hasPermi('tool:gen:import')") - @Log(title = "代码生成", businessType = BusinessType.IMPORT) - @PostMapping("/importTable") - public AjaxResult importTableSave(String tables) - { - String[] tableNames = Convert.toStrArray(tables); - // 查询表信息 - List tableList = genTableService.selectDbTableListByNames(tableNames); - genTableService.importGenTable(tableList, SecurityUtils.getUsername()); - return success(); - } - - /** - * 创建表结构(保存) - */ - @PreAuthorize("@ss.hasRole('admin')") - @Log(title = "创建表", businessType = BusinessType.OTHER) - @PostMapping("/createTable") - public AjaxResult createTableSave(String sql) - { - try - { - SqlUtil.filterKeyword(sql); - List sqlStatements = SQLUtils.parseStatements(sql, DbType.mysql); - List tableNames = new ArrayList<>(); - for (SQLStatement sqlStatement : sqlStatements) - { - if (sqlStatement instanceof MySqlCreateTableStatement) - { - MySqlCreateTableStatement createTableStatement = (MySqlCreateTableStatement) sqlStatement; - if (genTableService.createTable(createTableStatement.toString())) - { - String tableName = createTableStatement.getTableName().replaceAll("`", ""); - tableNames.add(tableName); - } - } - } - List tableList = genTableService.selectDbTableListByNames(tableNames.toArray(new String[tableNames.size()])); - String operName = SecurityUtils.getUsername(); - genTableService.importGenTable(tableList, operName); - return AjaxResult.success(); - } - catch (Exception e) - { - logger.error(e.getMessage(), e); - return AjaxResult.error("创建表结构异常"); - } - } - - /** - * 修改保存代码生成业务 - */ - @PreAuthorize("@ss.hasPermi('tool:gen:edit')") - @Log(title = "代码生成", businessType = BusinessType.UPDATE) - @PutMapping - public AjaxResult editSave(@Validated @RequestBody GenTable genTable) - { - genTableService.validateEdit(genTable); - genTableService.updateGenTable(genTable); - return success(); - } - - /** - * 删除代码生成 - */ - @PreAuthorize("@ss.hasPermi('tool:gen:remove')") - @Log(title = "代码生成", businessType = BusinessType.DELETE) - @DeleteMapping("/{tableIds}") - public AjaxResult remove(@PathVariable Long[] tableIds) - { - genTableService.deleteGenTableByIds(tableIds); - return success(); - } - - /** - * 预览代码 - */ - @PreAuthorize("@ss.hasPermi('tool:gen:preview')") - @GetMapping("/preview/{tableId}") - public AjaxResult preview(@PathVariable("tableId") Long tableId) throws IOException - { - Map dataMap = genTableService.previewCode(tableId); - return success(dataMap); - } - - /** - * 生成代码(下载方式) - */ - @PreAuthorize("@ss.hasPermi('tool:gen:code')") - @Log(title = "代码生成", businessType = BusinessType.GENCODE) - @GetMapping("/download/{tableName}") - public void download(HttpServletResponse response, @PathVariable("tableName") String tableName) throws IOException - { - byte[] data = genTableService.downloadCode(tableName); - genCode(response, data); - } - - /** - * 生成代码(自定义路径) - */ - @PreAuthorize("@ss.hasPermi('tool:gen:code')") - @Log(title = "代码生成", businessType = BusinessType.GENCODE) - @GetMapping("/genCode/{tableName}") - public AjaxResult genCode(@PathVariable("tableName") String tableName) - { - if (!GenConfig.isAllowOverwrite()) - { - return AjaxResult.error("【系统预设】不允许生成文件覆盖到本地"); - } - genTableService.generatorCode(tableName); - return success(); - } - - /** - * 同步数据库 - */ - @PreAuthorize("@ss.hasPermi('tool:gen:edit')") - @Log(title = "代码生成", businessType = BusinessType.UPDATE) - @GetMapping("/synchDb/{tableName}") - public AjaxResult synchDb(@PathVariable("tableName") String tableName) - { - genTableService.synchDb(tableName); - return success(); - } - - /** - * 批量生成代码 - */ - @PreAuthorize("@ss.hasPermi('tool:gen:code')") - @Log(title = "代码生成", businessType = BusinessType.GENCODE) - @GetMapping("/batchGenCode") - public void batchGenCode(HttpServletResponse response, String tables) throws IOException - { - String[] tableNames = Convert.toStrArray(tables); - byte[] data = genTableService.downloadCode(tableNames); - genCode(response, data); - } - - /** - * 生成zip文件 - */ - private void genCode(HttpServletResponse response, byte[] data) throws IOException - { - response.reset(); - response.addHeader("Access-Control-Allow-Origin", "*"); - response.addHeader("Access-Control-Expose-Headers", "Content-Disposition"); - response.setHeader("Content-Disposition", "attachment; filename=\"ruoyi.zip\""); - response.addHeader("Content-Length", "" + data.length); - response.setContentType("application/octet-stream; charset=UTF-8"); - IOUtils.write(data, response.getOutputStream()); - } +package com.ruoyi.generator.controller; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.servlet.http.HttpServletResponse; +import org.apache.commons.io.IOUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +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.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.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import com.alibaba.druid.DbType; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement; +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.common.utils.SecurityUtils; +import com.ruoyi.common.utils.sql.SqlUtil; +import com.ruoyi.generator.config.GenConfig; +import com.ruoyi.generator.domain.GenTable; +import com.ruoyi.generator.domain.GenTableColumn; +import com.ruoyi.generator.service.IGenTableColumnService; +import com.ruoyi.generator.service.IGenTableService; + +/** + * 代码生成 操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/tool/gen") +public class GenController extends BaseController +{ + @Autowired + private IGenTableService genTableService; + + @Autowired + private IGenTableColumnService genTableColumnService; + + /** + * 查询代码生成列表 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:list')") + @GetMapping("/list") + public TableDataInfo genList(GenTable genTable) + { + startPage(); + List list = genTableService.selectGenTableList(genTable); + return getDataTable(list); + } + + /** + * 获取代码生成信息 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:query')") + @GetMapping(value = "/{tableId}") + public AjaxResult getInfo(@PathVariable Long tableId) + { + GenTable table = genTableService.selectGenTableById(tableId); + List tables = genTableService.selectGenTableAll(); + List list = genTableColumnService.selectGenTableColumnListByTableId(tableId); + Map map = new HashMap(); + map.put("info", table); + map.put("rows", list); + map.put("tables", tables); + return success(map); + } + + /** + * 查询数据库列表 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:list')") + @GetMapping("/db/list") + public TableDataInfo dataList(GenTable genTable) + { + startPage(); + List list = genTableService.selectDbTableList(genTable); + return getDataTable(list); + } + + /** + * 查询数据表字段列表 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:list')") + @GetMapping(value = "/column/{tableId}") + public TableDataInfo columnList(Long tableId) + { + TableDataInfo dataInfo = new TableDataInfo(); + List list = genTableColumnService.selectGenTableColumnListByTableId(tableId); + dataInfo.setRows(list); + dataInfo.setTotal(list.size()); + return dataInfo; + } + + /** + * 导入表结构(保存) + */ + @PreAuthorize("@ss.hasPermi('tool:gen:import')") + @Log(title = "代码生成", businessType = BusinessType.IMPORT) + @PostMapping("/importTable") + public AjaxResult importTableSave(@RequestParam("tables") String tables, @RequestParam("tplWebType") String tplWebType) + { + String[] tableNames = Convert.toStrArray(tables); + // 查询表信息 + List tableList = genTableService.selectDbTableListByNames(tableNames); + genTableService.importGenTable(tableList, tplWebType, SecurityUtils.getUsername()); + return success(); + } + + /** + * 创建表结构(保存) + */ + @PreAuthorize("@ss.hasRole('admin')") + @Log(title = "创建表", businessType = BusinessType.OTHER) + @PostMapping("/createTable") + public AjaxResult createTableSave(@RequestParam("sql") String sql, @RequestParam("tplWebType") String tplWebType) + { + try + { + SqlUtil.filterKeyword(sql); + List sqlStatements = SQLUtils.parseStatements(sql, DbType.mysql); + List tableNames = new ArrayList<>(); + for (SQLStatement sqlStatement : sqlStatements) + { + if (sqlStatement instanceof MySqlCreateTableStatement) + { + MySqlCreateTableStatement createTableStatement = (MySqlCreateTableStatement) sqlStatement; + if (genTableService.createTable(createTableStatement.toString())) + { + String tableName = createTableStatement.getTableName().replaceAll("`", ""); + tableNames.add(tableName); + } + } + } + List tableList = genTableService.selectDbTableListByNames(tableNames.toArray(new String[tableNames.size()])); + String operName = SecurityUtils.getUsername(); + genTableService.importGenTable(tableList, tplWebType, operName); + return AjaxResult.success(); + } + catch (Exception e) + { + logger.error(e.getMessage(), e); + return AjaxResult.error("创建表结构异常"); + } + } + + /** + * 修改保存代码生成业务 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:edit')") + @Log(title = "代码生成", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult editSave(@Validated @RequestBody GenTable genTable) + { + genTableService.validateEdit(genTable); + genTableService.updateGenTable(genTable); + return success(); + } + + /** + * 删除代码生成 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:remove')") + @Log(title = "代码生成", businessType = BusinessType.DELETE) + @DeleteMapping("/{tableIds}") + public AjaxResult remove(@PathVariable Long[] tableIds) + { + genTableService.deleteGenTableByIds(tableIds); + return success(); + } + + /** + * 预览代码 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:preview')") + @GetMapping("/preview/{tableId}") + public AjaxResult preview(@PathVariable("tableId") Long tableId) throws IOException + { + Map dataMap = genTableService.previewCode(tableId); + return success(dataMap); + } + + /** + * 生成代码(下载方式) + */ + @PreAuthorize("@ss.hasPermi('tool:gen:code')") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/download/{tableName}") + public void download(HttpServletResponse response, @PathVariable("tableName") String tableName) throws IOException + { + byte[] data = genTableService.downloadCode(tableName); + genCode(response, data); + } + + /** + * 生成代码(自定义路径) + */ + @PreAuthorize("@ss.hasPermi('tool:gen:code')") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/genCode/{tableName}") + public AjaxResult genCode(@PathVariable("tableName") String tableName) + { + if (!GenConfig.isAllowOverwrite()) + { + return AjaxResult.error("【系统预设】不允许生成文件覆盖到本地"); + } + genTableService.generatorCode(tableName); + return success(); + } + + /** + * 同步数据库 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:edit')") + @Log(title = "代码生成", businessType = BusinessType.UPDATE) + @GetMapping("/synchDb/{tableName}") + public AjaxResult synchDb(@PathVariable("tableName") String tableName) + { + genTableService.synchDb(tableName); + return success(); + } + + /** + * 批量生成代码 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:code')") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/batchGenCode") + public void batchGenCode(HttpServletResponse response, String tables) throws IOException + { + String[] tableNames = Convert.toStrArray(tables); + byte[] data = genTableService.downloadCode(tableNames); + genCode(response, data); + } + + /** + * 生成zip文件 + */ + private void genCode(HttpServletResponse response, byte[] data) throws IOException + { + response.reset(); + response.addHeader("Access-Control-Allow-Origin", "*"); + response.addHeader("Access-Control-Expose-Headers", "Content-Disposition"); + response.setHeader("Content-Disposition", "attachment; filename=\"ruoyi.zip\""); + response.addHeader("Content-Length", "" + data.length); + response.setContentType("application/octet-stream; charset=UTF-8"); + IOUtils.write(data, response.getOutputStream()); + } } \ No newline at end of file diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java index 9ef6cfd..644abd5 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java @@ -1,385 +1,411 @@ -package com.ruoyi.generator.domain; - -import java.util.List; -import jakarta.validation.Valid; -import jakarta.validation.constraints.NotBlank; -import org.apache.commons.lang3.ArrayUtils; -import com.ruoyi.common.constant.GenConstants; -import com.ruoyi.common.core.domain.BaseEntity; -import com.ruoyi.common.utils.StringUtils; - -/** - * 业务表 gen_table - * - * @author ruoyi - */ -public class GenTable extends BaseEntity -{ - private static final long serialVersionUID = 1L; - - /** 编号 */ - private Long tableId; - - /** 表名称 */ - @NotBlank(message = "表名称不能为空") - private String tableName; - - /** 表描述 */ - @NotBlank(message = "表描述不能为空") - private String tableComment; - - /** 关联父表的表名 */ - private String subTableName; - - /** 本表关联父表的外键名 */ - private String subTableFkName; - - /** 实体类名称(首字母大写) */ - @NotBlank(message = "实体类名称不能为空") - private String className; - - /** 使用的模板(crud单表操作 tree树表操作 sub主子表操作) */ - private String tplCategory; - - /** 前端类型(element-ui模版 element-plus模版) */ - private String tplWebType; - - /** 生成包路径 */ - @NotBlank(message = "生成包路径不能为空") - private String packageName; - - /** 生成模块名 */ - @NotBlank(message = "生成模块名不能为空") - private String moduleName; - - /** 生成业务名 */ - @NotBlank(message = "生成业务名不能为空") - private String businessName; - - /** 生成功能名 */ - @NotBlank(message = "生成功能名不能为空") - private String functionName; - - /** 生成作者 */ - @NotBlank(message = "作者不能为空") - private String functionAuthor; - - /** 生成代码方式(0zip压缩包 1自定义路径) */ - private String genType; - - /** 生成路径(不填默认项目路径) */ - private String genPath; - - /** 主键信息 */ - private GenTableColumn pkColumn; - - /** 子表信息 */ - private GenTable subTable; - - /** 表列信息 */ - @Valid - private List columns; - - /** 其它生成选项 */ - private String options; - - /** 树编码字段 */ - private String treeCode; - - /** 树父编码字段 */ - private String treeParentCode; - - /** 树名称字段 */ - private String treeName; - - /** 上级菜单ID字段 */ - private Long parentMenuId; - - /** 上级菜单名称字段 */ - private String parentMenuName; - - public Long getTableId() - { - return tableId; - } - - public void setTableId(Long tableId) - { - this.tableId = tableId; - } - - public String getTableName() - { - return tableName; - } - - public void setTableName(String tableName) - { - this.tableName = tableName; - } - - public String getTableComment() - { - return tableComment; - } - - public void setTableComment(String tableComment) - { - this.tableComment = tableComment; - } - - public String getSubTableName() - { - return subTableName; - } - - public void setSubTableName(String subTableName) - { - this.subTableName = subTableName; - } - - public String getSubTableFkName() - { - return subTableFkName; - } - - public void setSubTableFkName(String subTableFkName) - { - this.subTableFkName = subTableFkName; - } - - public String getClassName() - { - return className; - } - - public void setClassName(String className) - { - this.className = className; - } - - public String getTplCategory() - { - return tplCategory; - } - - public void setTplCategory(String tplCategory) - { - this.tplCategory = tplCategory; - } - - public String getTplWebType() - { - return tplWebType; - } - - public void setTplWebType(String tplWebType) - { - this.tplWebType = tplWebType; - } - - public String getPackageName() - { - return packageName; - } - - public void setPackageName(String packageName) - { - this.packageName = packageName; - } - - public String getModuleName() - { - return moduleName; - } - - public void setModuleName(String moduleName) - { - this.moduleName = moduleName; - } - - public String getBusinessName() - { - return businessName; - } - - public void setBusinessName(String businessName) - { - this.businessName = businessName; - } - - public String getFunctionName() - { - return functionName; - } - - public void setFunctionName(String functionName) - { - this.functionName = functionName; - } - - public String getFunctionAuthor() - { - return functionAuthor; - } - - public void setFunctionAuthor(String functionAuthor) - { - this.functionAuthor = functionAuthor; - } - - public String getGenType() - { - return genType; - } - - public void setGenType(String genType) - { - this.genType = genType; - } - - public String getGenPath() - { - return genPath; - } - - public void setGenPath(String genPath) - { - this.genPath = genPath; - } - - public GenTableColumn getPkColumn() - { - return pkColumn; - } - - public void setPkColumn(GenTableColumn pkColumn) - { - this.pkColumn = pkColumn; - } - - public GenTable getSubTable() - { - return subTable; - } - - public void setSubTable(GenTable subTable) - { - this.subTable = subTable; - } - - public List getColumns() - { - return columns; - } - - public void setColumns(List columns) - { - this.columns = columns; - } - - public String getOptions() - { - return options; - } - - public void setOptions(String options) - { - this.options = options; - } - - public String getTreeCode() - { - return treeCode; - } - - public void setTreeCode(String treeCode) - { - this.treeCode = treeCode; - } - - public String getTreeParentCode() - { - return treeParentCode; - } - - public void setTreeParentCode(String treeParentCode) - { - this.treeParentCode = treeParentCode; - } - - public String getTreeName() - { - return treeName; - } - - public void setTreeName(String treeName) - { - this.treeName = treeName; - } - - public Long getParentMenuId() - { - return parentMenuId; - } - - public void setParentMenuId(Long parentMenuId) - { - this.parentMenuId = parentMenuId; - } - - public String getParentMenuName() - { - return parentMenuName; - } - - public void setParentMenuName(String parentMenuName) - { - this.parentMenuName = parentMenuName; - } - - public boolean isSub() - { - return isSub(this.tplCategory); - } - - public static boolean isSub(String tplCategory) - { - return tplCategory != null && StringUtils.equals(GenConstants.TPL_SUB, tplCategory); - } - - public boolean isTree() - { - return isTree(this.tplCategory); - } - - public static boolean isTree(String tplCategory) - { - return tplCategory != null && StringUtils.equals(GenConstants.TPL_TREE, tplCategory); - } - - public boolean isCrud() - { - return isCrud(this.tplCategory); - } - - public static boolean isCrud(String tplCategory) - { - return tplCategory != null && StringUtils.equals(GenConstants.TPL_CRUD, tplCategory); - } - - public boolean isSuperColumn(String javaField) - { - return isSuperColumn(this.tplCategory, javaField); - } - - public static boolean isSuperColumn(String tplCategory, String javaField) - { - if (isTree(tplCategory)) - { - return StringUtils.equalsAnyIgnoreCase(javaField, - ArrayUtils.addAll(GenConstants.TREE_ENTITY, GenConstants.BASE_ENTITY)); - } - return StringUtils.equalsAnyIgnoreCase(javaField, GenConstants.BASE_ENTITY); - } +package com.ruoyi.generator.domain; + +import java.util.List; +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import org.apache.commons.lang3.ArrayUtils; +import com.ruoyi.common.constant.GenConstants; +import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.utils.StringUtils; + +/** + * 业务表 gen_table + * + * @author ruoyi + */ +public class GenTable extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 编号 */ + private Long tableId; + + /** 表名称 */ + @NotBlank(message = "表名称不能为空") + private String tableName; + + /** 表描述 */ + @NotBlank(message = "表描述不能为空") + private String tableComment; + + /** 关联父表的表名 */ + private String subTableName; + + /** 本表关联父表的外键名 */ + private String subTableFkName; + + /** 实体类名称(首字母大写) */ + @NotBlank(message = "实体类名称不能为空") + private String className; + + /** 使用的模板(crud单表操作 tree树表操作 sub主子表操作) */ + private String tplCategory; + + /** 前端类型(element-ui模版 element-plus模版 element-plus-typescript模版) */ + private String tplWebType; + + /** 生成包路径 */ + @NotBlank(message = "生成包路径不能为空") + private String packageName; + + /** 生成模块名 */ + @NotBlank(message = "生成模块名不能为空") + private String moduleName; + + /** 生成业务名 */ + @NotBlank(message = "生成业务名不能为空") + private String businessName; + + /** 生成功能名 */ + @NotBlank(message = "生成功能名不能为空") + private String functionName; + + /** 生成作者 */ + @NotBlank(message = "作者不能为空") + private String functionAuthor; + + /** 表单布局(单列 双列 三列) */ + private Integer formColNum; + + /** 生成代码方式(0zip压缩包 1自定义路径) */ + private String genType; + + /** 生成路径(不填默认项目路径) */ + private String genPath; + + /** 主键信息 */ + private GenTableColumn pkColumn; + + /** 子表信息 */ + private GenTable subTable; + + /** 表列信息 */ + @Valid + private List columns; + + /** 其它生成选项 */ + private String options; + + /** 树编码字段 */ + private String treeCode; + + /** 树父编码字段 */ + private String treeParentCode; + + /** 树名称字段 */ + private String treeName; + + /** 上级菜单ID字段 */ + private Long parentMenuId; + + /** 上级菜单名称字段 */ + private String parentMenuName; + + /** 是否生成详情页 */ + private boolean isView; + + public Long getTableId() + { + return tableId; + } + + public void setTableId(Long tableId) + { + this.tableId = tableId; + } + + public String getTableName() + { + return tableName; + } + + public void setTableName(String tableName) + { + this.tableName = tableName; + } + + public String getTableComment() + { + return tableComment; + } + + public void setTableComment(String tableComment) + { + this.tableComment = tableComment; + } + + public String getSubTableName() + { + return subTableName; + } + + public void setSubTableName(String subTableName) + { + this.subTableName = subTableName; + } + + public String getSubTableFkName() + { + return subTableFkName; + } + + public void setSubTableFkName(String subTableFkName) + { + this.subTableFkName = subTableFkName; + } + + public String getClassName() + { + return className; + } + + public void setClassName(String className) + { + this.className = className; + } + + public String getTplCategory() + { + return tplCategory; + } + + public void setTplCategory(String tplCategory) + { + this.tplCategory = tplCategory; + } + + public String getTplWebType() + { + return tplWebType; + } + + public void setTplWebType(String tplWebType) + { + this.tplWebType = tplWebType; + } + + public String getPackageName() + { + return packageName; + } + + public void setPackageName(String packageName) + { + this.packageName = packageName; + } + + public String getModuleName() + { + return moduleName; + } + + public void setModuleName(String moduleName) + { + this.moduleName = moduleName; + } + + public String getBusinessName() + { + return businessName; + } + + public void setBusinessName(String businessName) + { + this.businessName = businessName; + } + + public String getFunctionName() + { + return functionName; + } + + public void setFunctionName(String functionName) + { + this.functionName = functionName; + } + + public String getFunctionAuthor() + { + return functionAuthor; + } + + public void setFunctionAuthor(String functionAuthor) + { + this.functionAuthor = functionAuthor; + } + + public Integer getFormColNum() + { + return formColNum; + } + + public void setFormColNum(Integer formColNum) + { + this.formColNum = formColNum; + } + + public String getGenType() + { + return genType; + } + + public void setGenType(String genType) + { + this.genType = genType; + } + + public String getGenPath() + { + return genPath; + } + + public void setGenPath(String genPath) + { + this.genPath = genPath; + } + + public GenTableColumn getPkColumn() + { + return pkColumn; + } + + public void setPkColumn(GenTableColumn pkColumn) + { + this.pkColumn = pkColumn; + } + + public GenTable getSubTable() + { + return subTable; + } + + public void setSubTable(GenTable subTable) + { + this.subTable = subTable; + } + + public List getColumns() + { + return columns; + } + + public void setColumns(List columns) + { + this.columns = columns; + } + + public String getOptions() + { + return options; + } + + public void setOptions(String options) + { + this.options = options; + } + + public String getTreeCode() + { + return treeCode; + } + + public void setTreeCode(String treeCode) + { + this.treeCode = treeCode; + } + + public String getTreeParentCode() + { + return treeParentCode; + } + + public void setTreeParentCode(String treeParentCode) + { + this.treeParentCode = treeParentCode; + } + + public String getTreeName() + { + return treeName; + } + + public void setTreeName(String treeName) + { + this.treeName = treeName; + } + + public Long getParentMenuId() + { + return parentMenuId; + } + + public void setParentMenuId(Long parentMenuId) + { + this.parentMenuId = parentMenuId; + } + + public String getParentMenuName() + { + return parentMenuName; + } + + public void setParentMenuName(String parentMenuName) + { + this.parentMenuName = parentMenuName; + } + + public boolean isView() + { + return isView; + } + + public void setView(boolean isView) + { + this.isView = isView; + } + + public boolean isSub() + { + return isSub(this.tplCategory); + } + + public static boolean isSub(String tplCategory) + { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_SUB, tplCategory); + } + + public boolean isTree() + { + return isTree(this.tplCategory); + } + + public static boolean isTree(String tplCategory) + { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_TREE, tplCategory); + } + + public boolean isCrud() + { + return isCrud(this.tplCategory); + } + + public static boolean isCrud(String tplCategory) + { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_CRUD, tplCategory); + } + + public boolean isSuperColumn(String javaField) + { + return isSuperColumn(this.tplCategory, javaField); + } + + public static boolean isSuperColumn(String tplCategory, String javaField) + { + if (isTree(tplCategory)) + { + return StringUtils.equalsAnyIgnoreCase(javaField, + ArrayUtils.addAll(GenConstants.TREE_ENTITY, GenConstants.BASE_ENTITY)); + } + return StringUtils.equalsAnyIgnoreCase(javaField, GenConstants.BASE_ENTITY); + } } \ No newline at end of file diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java index 3dc6d62..9140d4d 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java @@ -1,373 +1,373 @@ -package com.ruoyi.generator.domain; - -import jakarta.validation.constraints.NotBlank; -import com.ruoyi.common.core.domain.BaseEntity; -import com.ruoyi.common.utils.StringUtils; - -/** - * 代码生成业务字段表 gen_table_column - * - * @author ruoyi - */ -public class GenTableColumn extends BaseEntity -{ - private static final long serialVersionUID = 1L; - - /** 编号 */ - private Long columnId; - - /** 归属表编号 */ - private Long tableId; - - /** 列名称 */ - private String columnName; - - /** 列描述 */ - private String columnComment; - - /** 列类型 */ - private String columnType; - - /** JAVA类型 */ - private String javaType; - - /** JAVA字段名 */ - @NotBlank(message = "Java属性不能为空") - private String javaField; - - /** 是否主键(1是) */ - private String isPk; - - /** 是否自增(1是) */ - private String isIncrement; - - /** 是否必填(1是) */ - private String isRequired; - - /** 是否为插入字段(1是) */ - private String isInsert; - - /** 是否编辑字段(1是) */ - private String isEdit; - - /** 是否列表字段(1是) */ - private String isList; - - /** 是否查询字段(1是) */ - private String isQuery; - - /** 查询方式(EQ等于、NE不等于、GT大于、LT小于、LIKE模糊、BETWEEN范围) */ - private String queryType; - - /** 显示类型(input文本框、textarea文本域、select下拉框、checkbox复选框、radio单选框、datetime日期控件、image图片上传控件、upload文件上传控件、editor富文本控件) */ - private String htmlType; - - /** 字典类型 */ - private String dictType; - - /** 排序 */ - private Integer sort; - - public void setColumnId(Long columnId) - { - this.columnId = columnId; - } - - public Long getColumnId() - { - return columnId; - } - - public void setTableId(Long tableId) - { - this.tableId = tableId; - } - - public Long getTableId() - { - return tableId; - } - - public void setColumnName(String columnName) - { - this.columnName = columnName; - } - - public String getColumnName() - { - return columnName; - } - - public void setColumnComment(String columnComment) - { - this.columnComment = columnComment; - } - - public String getColumnComment() - { - return columnComment; - } - - public void setColumnType(String columnType) - { - this.columnType = columnType; - } - - public String getColumnType() - { - return columnType; - } - - public void setJavaType(String javaType) - { - this.javaType = javaType; - } - - public String getJavaType() - { - return javaType; - } - - public void setJavaField(String javaField) - { - this.javaField = javaField; - } - - public String getJavaField() - { - return javaField; - } - - public String getCapJavaField() - { - return StringUtils.capitalize(javaField); - } - - public void setIsPk(String isPk) - { - this.isPk = isPk; - } - - public String getIsPk() - { - return isPk; - } - - public boolean isPk() - { - return isPk(this.isPk); - } - - public boolean isPk(String isPk) - { - return isPk != null && StringUtils.equals("1", isPk); - } - - public String getIsIncrement() - { - return isIncrement; - } - - public void setIsIncrement(String isIncrement) - { - this.isIncrement = isIncrement; - } - - public boolean isIncrement() - { - return isIncrement(this.isIncrement); - } - - public boolean isIncrement(String isIncrement) - { - return isIncrement != null && StringUtils.equals("1", isIncrement); - } - - public void setIsRequired(String isRequired) - { - this.isRequired = isRequired; - } - - public String getIsRequired() - { - return isRequired; - } - - public boolean isRequired() - { - return isRequired(this.isRequired); - } - - public boolean isRequired(String isRequired) - { - return isRequired != null && StringUtils.equals("1", isRequired); - } - - public void setIsInsert(String isInsert) - { - this.isInsert = isInsert; - } - - public String getIsInsert() - { - return isInsert; - } - - public boolean isInsert() - { - return isInsert(this.isInsert); - } - - public boolean isInsert(String isInsert) - { - return isInsert != null && StringUtils.equals("1", isInsert); - } - - public void setIsEdit(String isEdit) - { - this.isEdit = isEdit; - } - - public String getIsEdit() - { - return isEdit; - } - - public boolean isEdit() - { - return isInsert(this.isEdit); - } - - public boolean isEdit(String isEdit) - { - return isEdit != null && StringUtils.equals("1", isEdit); - } - - public void setIsList(String isList) - { - this.isList = isList; - } - - public String getIsList() - { - return isList; - } - - public boolean isList() - { - return isList(this.isList); - } - - public boolean isList(String isList) - { - return isList != null && StringUtils.equals("1", isList); - } - - public void setIsQuery(String isQuery) - { - this.isQuery = isQuery; - } - - public String getIsQuery() - { - return isQuery; - } - - public boolean isQuery() - { - return isQuery(this.isQuery); - } - - public boolean isQuery(String isQuery) - { - return isQuery != null && StringUtils.equals("1", isQuery); - } - - public void setQueryType(String queryType) - { - this.queryType = queryType; - } - - public String getQueryType() - { - return queryType; - } - - public String getHtmlType() - { - return htmlType; - } - - public void setHtmlType(String htmlType) - { - this.htmlType = htmlType; - } - - public void setDictType(String dictType) - { - this.dictType = dictType; - } - - public String getDictType() - { - return dictType; - } - - public void setSort(Integer sort) - { - this.sort = sort; - } - - public Integer getSort() - { - return sort; - } - - public boolean isSuperColumn() - { - return isSuperColumn(this.javaField); - } - - public static boolean isSuperColumn(String javaField) - { - return StringUtils.equalsAnyIgnoreCase(javaField, - // BaseEntity - "createBy", "createTime", "updateBy", "updateTime", "remark", - // TreeEntity - "parentName", "parentId", "orderNum", "ancestors"); - } - - public boolean isUsableColumn() - { - return isUsableColumn(javaField); - } - - public static boolean isUsableColumn(String javaField) - { - // isSuperColumn()中的名单用于避免生成多余Domain属性,若某些属性在生成页面时需要用到不能忽略,则放在此处白名单 - return StringUtils.equalsAnyIgnoreCase(javaField, "parentId", "orderNum", "remark"); - } - - public String readConverterExp() - { - String remarks = StringUtils.substringBetween(this.columnComment, "(", ")"); - StringBuffer sb = new StringBuffer(); - if (StringUtils.isNotEmpty(remarks)) - { - for (String value : remarks.split(" ")) - { - if (StringUtils.isNotEmpty(value)) - { - Object startStr = value.subSequence(0, 1); - String endStr = value.substring(1); - sb.append("").append(startStr).append("=").append(endStr).append(","); - } - } - return sb.deleteCharAt(sb.length() - 1).toString(); - } - else - { - return this.columnComment; - } - } -} +package com.ruoyi.generator.domain; + +import javax.validation.constraints.NotBlank; +import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.utils.StringUtils; + +/** + * 代码生成业务字段表 gen_table_column + * + * @author ruoyi + */ +public class GenTableColumn extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 编号 */ + private Long columnId; + + /** 归属表编号 */ + private Long tableId; + + /** 列名称 */ + private String columnName; + + /** 列描述 */ + private String columnComment; + + /** 列类型 */ + private String columnType; + + /** JAVA类型 */ + private String javaType; + + /** JAVA字段名 */ + @NotBlank(message = "Java属性不能为空") + private String javaField; + + /** 是否主键(1是) */ + private String isPk; + + /** 是否自增(1是) */ + private String isIncrement; + + /** 是否必填(1是) */ + private String isRequired; + + /** 是否为插入字段(1是) */ + private String isInsert; + + /** 是否编辑字段(1是) */ + private String isEdit; + + /** 是否列表字段(1是) */ + private String isList; + + /** 是否查询字段(1是) */ + private String isQuery; + + /** 查询方式(EQ等于、NE不等于、GT大于、LT小于、LIKE模糊、BETWEEN范围) */ + private String queryType; + + /** 显示类型(input文本框、textarea文本域、select下拉框、checkbox复选框、radio单选框、datetime日期控件、image图片上传控件、upload文件上传控件、editor富文本控件) */ + private String htmlType; + + /** 字典类型 */ + private String dictType; + + /** 排序 */ + private Integer sort; + + public void setColumnId(Long columnId) + { + this.columnId = columnId; + } + + public Long getColumnId() + { + return columnId; + } + + public void setTableId(Long tableId) + { + this.tableId = tableId; + } + + public Long getTableId() + { + return tableId; + } + + public void setColumnName(String columnName) + { + this.columnName = columnName; + } + + public String getColumnName() + { + return columnName; + } + + public void setColumnComment(String columnComment) + { + this.columnComment = columnComment; + } + + public String getColumnComment() + { + return columnComment; + } + + public void setColumnType(String columnType) + { + this.columnType = columnType; + } + + public String getColumnType() + { + return columnType; + } + + public void setJavaType(String javaType) + { + this.javaType = javaType; + } + + public String getJavaType() + { + return javaType; + } + + public void setJavaField(String javaField) + { + this.javaField = javaField; + } + + public String getJavaField() + { + return javaField; + } + + public String getCapJavaField() + { + return StringUtils.capitalize(javaField); + } + + public void setIsPk(String isPk) + { + this.isPk = isPk; + } + + public String getIsPk() + { + return isPk; + } + + public boolean isPk() + { + return isPk(this.isPk); + } + + public boolean isPk(String isPk) + { + return isPk != null && StringUtils.equals("1", isPk); + } + + public String getIsIncrement() + { + return isIncrement; + } + + public void setIsIncrement(String isIncrement) + { + this.isIncrement = isIncrement; + } + + public boolean isIncrement() + { + return isIncrement(this.isIncrement); + } + + public boolean isIncrement(String isIncrement) + { + return isIncrement != null && StringUtils.equals("1", isIncrement); + } + + public void setIsRequired(String isRequired) + { + this.isRequired = isRequired; + } + + public String getIsRequired() + { + return isRequired; + } + + public boolean isRequired() + { + return isRequired(this.isRequired); + } + + public boolean isRequired(String isRequired) + { + return isRequired != null && StringUtils.equals("1", isRequired); + } + + public void setIsInsert(String isInsert) + { + this.isInsert = isInsert; + } + + public String getIsInsert() + { + return isInsert; + } + + public boolean isInsert() + { + return isInsert(this.isInsert); + } + + public boolean isInsert(String isInsert) + { + return isInsert != null && StringUtils.equals("1", isInsert); + } + + public void setIsEdit(String isEdit) + { + this.isEdit = isEdit; + } + + public String getIsEdit() + { + return isEdit; + } + + public boolean isEdit() + { + return isInsert(this.isEdit); + } + + public boolean isEdit(String isEdit) + { + return isEdit != null && StringUtils.equals("1", isEdit); + } + + public void setIsList(String isList) + { + this.isList = isList; + } + + public String getIsList() + { + return isList; + } + + public boolean isList() + { + return isList(this.isList); + } + + public boolean isList(String isList) + { + return isList != null && StringUtils.equals("1", isList); + } + + public void setIsQuery(String isQuery) + { + this.isQuery = isQuery; + } + + public String getIsQuery() + { + return isQuery; + } + + public boolean isQuery() + { + return isQuery(this.isQuery); + } + + public boolean isQuery(String isQuery) + { + return isQuery != null && StringUtils.equals("1", isQuery); + } + + public void setQueryType(String queryType) + { + this.queryType = queryType; + } + + public String getQueryType() + { + return queryType; + } + + public String getHtmlType() + { + return htmlType; + } + + public void setHtmlType(String htmlType) + { + this.htmlType = htmlType; + } + + public void setDictType(String dictType) + { + this.dictType = dictType; + } + + public String getDictType() + { + return dictType; + } + + public void setSort(Integer sort) + { + this.sort = sort; + } + + public Integer getSort() + { + return sort; + } + + public boolean isSuperColumn() + { + return isSuperColumn(this.javaField); + } + + public static boolean isSuperColumn(String javaField) + { + return StringUtils.equalsAnyIgnoreCase(javaField, + // BaseEntity + "createBy", "createTime", "updateBy", "updateTime", "remark", + // TreeEntity + "parentName", "parentId", "orderNum", "ancestors"); + } + + public boolean isUsableColumn() + { + return isUsableColumn(javaField); + } + + public static boolean isUsableColumn(String javaField) + { + // isSuperColumn()中的名单用于避免生成多余Domain属性,若某些属性在生成页面时需要用到不能忽略,则放在此处白名单 + return StringUtils.equalsAnyIgnoreCase(javaField, "parentId", "orderNum", "remark"); + } + + public String readConverterExp() + { + String remarks = StringUtils.substringBetween(this.columnComment, "(", ")"); + StringBuffer sb = new StringBuffer(); + if (StringUtils.isNotEmpty(remarks)) + { + for (String value : remarks.split(" ")) + { + if (StringUtils.isNotEmpty(value)) + { + Object startStr = value.subSequence(0, 1); + String endStr = value.substring(1); + sb.append("").append(startStr).append("=").append(endStr).append(","); + } + } + return sb.deleteCharAt(sb.length() - 1).toString(); + } + else + { + return this.columnComment; + } + } +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableColumnMapper.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableColumnMapper.java index 951e166..0dc3428 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableColumnMapper.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableColumnMapper.java @@ -1,60 +1,60 @@ -package com.ruoyi.generator.mapper; - -import java.util.List; -import com.ruoyi.generator.domain.GenTableColumn; - -/** - * 业务字段 数据层 - * - * @author ruoyi - */ -public interface GenTableColumnMapper -{ - /** - * 根据表名称查询列信息 - * - * @param tableName 表名称 - * @return 列信息 - */ - public List selectDbTableColumnsByName(String tableName); - - /** - * 查询业务字段列表 - * - * @param tableId 业务字段编号 - * @return 业务字段集合 - */ - public List selectGenTableColumnListByTableId(Long tableId); - - /** - * 新增业务字段 - * - * @param genTableColumn 业务字段信息 - * @return 结果 - */ - public int insertGenTableColumn(GenTableColumn genTableColumn); - - /** - * 修改业务字段 - * - * @param genTableColumn 业务字段信息 - * @return 结果 - */ - public int updateGenTableColumn(GenTableColumn genTableColumn); - - /** - * 删除业务字段 - * - * @param genTableColumns 列数据 - * @return 结果 - */ - public int deleteGenTableColumns(List genTableColumns); - - /** - * 批量删除业务字段 - * - * @param ids 需要删除的数据ID - * @return 结果 - */ - public int deleteGenTableColumnByIds(Long[] ids); -} +package com.ruoyi.generator.mapper; + +import java.util.List; +import com.ruoyi.generator.domain.GenTableColumn; + +/** + * 业务字段 数据层 + * + * @author ruoyi + */ +public interface GenTableColumnMapper +{ + /** + * 根据表名称查询列信息 + * + * @param tableName 表名称 + * @return 列信息 + */ + public List selectDbTableColumnsByName(String tableName); + + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * @return 业务字段集合 + */ + public List selectGenTableColumnListByTableId(Long tableId); + + /** + * 新增业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + public int insertGenTableColumn(GenTableColumn genTableColumn); + + /** + * 修改业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + public int updateGenTableColumn(GenTableColumn genTableColumn); + + /** + * 删除业务字段 + * + * @param genTableColumns 列数据 + * @return 结果 + */ + public int deleteGenTableColumns(List genTableColumns); + + /** + * 批量删除业务字段 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteGenTableColumnByIds(Long[] ids); +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java index 937656d..a5ce3c6 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java @@ -1,91 +1,91 @@ -package com.ruoyi.generator.mapper; - -import java.util.List; -import com.ruoyi.generator.domain.GenTable; - -/** - * 业务 数据层 - * - * @author ruoyi - */ -public interface GenTableMapper -{ - /** - * 查询业务列表 - * - * @param genTable 业务信息 - * @return 业务集合 - */ - public List selectGenTableList(GenTable genTable); - - /** - * 查询据库列表 - * - * @param genTable 业务信息 - * @return 数据库表集合 - */ - public List selectDbTableList(GenTable genTable); - - /** - * 查询据库列表 - * - * @param tableNames 表名称组 - * @return 数据库表集合 - */ - public List selectDbTableListByNames(String[] tableNames); - - /** - * 查询所有表信息 - * - * @return 表信息集合 - */ - public List selectGenTableAll(); - - /** - * 查询表ID业务信息 - * - * @param id 业务ID - * @return 业务信息 - */ - public GenTable selectGenTableById(Long id); - - /** - * 查询表名称业务信息 - * - * @param tableName 表名称 - * @return 业务信息 - */ - public GenTable selectGenTableByName(String tableName); - - /** - * 新增业务 - * - * @param genTable 业务信息 - * @return 结果 - */ - public int insertGenTable(GenTable genTable); - - /** - * 修改业务 - * - * @param genTable 业务信息 - * @return 结果 - */ - public int updateGenTable(GenTable genTable); - - /** - * 批量删除业务 - * - * @param ids 需要删除的数据ID - * @return 结果 - */ - public int deleteGenTableByIds(Long[] ids); - - /** - * 创建表 - * - * @param sql 表结构 - * @return 结果 - */ - public int createTable(String sql); -} +package com.ruoyi.generator.mapper; + +import java.util.List; +import com.ruoyi.generator.domain.GenTable; + +/** + * 业务 数据层 + * + * @author ruoyi + */ +public interface GenTableMapper +{ + /** + * 查询业务列表 + * + * @param genTable 业务信息 + * @return 业务集合 + */ + public List selectGenTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param genTable 业务信息 + * @return 数据库表集合 + */ + public List selectDbTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * @return 数据库表集合 + */ + public List selectDbTableListByNames(String[] tableNames); + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + public List selectGenTableAll(); + + /** + * 查询表ID业务信息 + * + * @param id 业务ID + * @return 业务信息 + */ + public GenTable selectGenTableById(Long id); + + /** + * 查询表名称业务信息 + * + * @param tableName 表名称 + * @return 业务信息 + */ + public GenTable selectGenTableByName(String tableName); + + /** + * 新增业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + public int insertGenTable(GenTable genTable); + + /** + * 修改业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + public int updateGenTable(GenTable genTable); + + /** + * 批量删除业务 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteGenTableByIds(Long[] ids); + + /** + * 创建表 + * + * @param sql 表结构 + * @return 结果 + */ + public int createTable(String sql); +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableColumnServiceImpl.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableColumnServiceImpl.java index 0679689..3a2d66a 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableColumnServiceImpl.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableColumnServiceImpl.java @@ -1,68 +1,68 @@ -package com.ruoyi.generator.service; - -import java.util.List; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import com.ruoyi.common.core.text.Convert; -import com.ruoyi.generator.domain.GenTableColumn; -import com.ruoyi.generator.mapper.GenTableColumnMapper; - -/** - * 业务字段 服务层实现 - * - * @author ruoyi - */ -@Service -public class GenTableColumnServiceImpl implements IGenTableColumnService -{ - @Autowired - private GenTableColumnMapper genTableColumnMapper; - - /** - * 查询业务字段列表 - * - * @param tableId 业务字段编号 - * @return 业务字段集合 - */ - @Override - public List selectGenTableColumnListByTableId(Long tableId) - { - return genTableColumnMapper.selectGenTableColumnListByTableId(tableId); - } - - /** - * 新增业务字段 - * - * @param genTableColumn 业务字段信息 - * @return 结果 - */ - @Override - public int insertGenTableColumn(GenTableColumn genTableColumn) - { - return genTableColumnMapper.insertGenTableColumn(genTableColumn); - } - - /** - * 修改业务字段 - * - * @param genTableColumn 业务字段信息 - * @return 结果 - */ - @Override - public int updateGenTableColumn(GenTableColumn genTableColumn) - { - return genTableColumnMapper.updateGenTableColumn(genTableColumn); - } - - /** - * 删除业务字段对象 - * - * @param ids 需要删除的数据ID - * @return 结果 - */ - @Override - public int deleteGenTableColumnByIds(String ids) - { - return genTableColumnMapper.deleteGenTableColumnByIds(Convert.toLongArray(ids)); - } -} +package com.ruoyi.generator.service; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.generator.domain.GenTableColumn; +import com.ruoyi.generator.mapper.GenTableColumnMapper; + +/** + * 业务字段 服务层实现 + * + * @author ruoyi + */ +@Service +public class GenTableColumnServiceImpl implements IGenTableColumnService +{ + @Autowired + private GenTableColumnMapper genTableColumnMapper; + + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * @return 业务字段集合 + */ + @Override + public List selectGenTableColumnListByTableId(Long tableId) + { + return genTableColumnMapper.selectGenTableColumnListByTableId(tableId); + } + + /** + * 新增业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + @Override + public int insertGenTableColumn(GenTableColumn genTableColumn) + { + return genTableColumnMapper.insertGenTableColumn(genTableColumn); + } + + /** + * 修改业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + @Override + public int updateGenTableColumn(GenTableColumn genTableColumn) + { + return genTableColumnMapper.updateGenTableColumn(genTableColumn); + } + + /** + * 删除业务字段对象 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + @Override + public int deleteGenTableColumnByIds(String ids) + { + return genTableColumnMapper.deleteGenTableColumnByIds(Convert.toLongArray(ids)); + } +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java index fe4312f..f3f099f 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java @@ -1,531 +1,564 @@ -package com.ruoyi.generator.service; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.StringWriter; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.zip.ZipEntry; -import java.util.zip.ZipOutputStream; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; -import org.apache.velocity.Template; -import org.apache.velocity.VelocityContext; -import org.apache.velocity.app.Velocity; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import com.alibaba.fastjson2.JSON; -import com.alibaba.fastjson2.JSONObject; -import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.constant.GenConstants; -import com.ruoyi.common.core.text.CharsetKit; -import com.ruoyi.common.exception.ServiceException; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.generator.domain.GenTable; -import com.ruoyi.generator.domain.GenTableColumn; -import com.ruoyi.generator.mapper.GenTableColumnMapper; -import com.ruoyi.generator.mapper.GenTableMapper; -import com.ruoyi.generator.util.GenUtils; -import com.ruoyi.generator.util.VelocityInitializer; -import com.ruoyi.generator.util.VelocityUtils; - -/** - * 业务 服务层实现 - * - * @author ruoyi - */ -@Service -public class GenTableServiceImpl implements IGenTableService -{ - private static final Logger log = LoggerFactory.getLogger(GenTableServiceImpl.class); - - @Autowired - private GenTableMapper genTableMapper; - - @Autowired - private GenTableColumnMapper genTableColumnMapper; - - /** - * 查询业务信息 - * - * @param id 业务ID - * @return 业务信息 - */ - @Override - public GenTable selectGenTableById(Long id) - { - GenTable genTable = genTableMapper.selectGenTableById(id); - setTableFromOptions(genTable); - return genTable; - } - - /** - * 查询业务列表 - * - * @param genTable 业务信息 - * @return 业务集合 - */ - @Override - public List selectGenTableList(GenTable genTable) - { - return genTableMapper.selectGenTableList(genTable); - } - - /** - * 查询据库列表 - * - * @param genTable 业务信息 - * @return 数据库表集合 - */ - @Override - public List selectDbTableList(GenTable genTable) - { - return genTableMapper.selectDbTableList(genTable); - } - - /** - * 查询据库列表 - * - * @param tableNames 表名称组 - * @return 数据库表集合 - */ - @Override - public List selectDbTableListByNames(String[] tableNames) - { - return genTableMapper.selectDbTableListByNames(tableNames); - } - - /** - * 查询所有表信息 - * - * @return 表信息集合 - */ - @Override - public List selectGenTableAll() - { - return genTableMapper.selectGenTableAll(); - } - - /** - * 修改业务 - * - * @param genTable 业务信息 - * @return 结果 - */ - @Override - @Transactional - public void updateGenTable(GenTable genTable) - { - String options = JSON.toJSONString(genTable.getParams()); - genTable.setOptions(options); - int row = genTableMapper.updateGenTable(genTable); - if (row > 0) - { - for (GenTableColumn genTableColumn : genTable.getColumns()) - { - genTableColumnMapper.updateGenTableColumn(genTableColumn); - } - } - } - - /** - * 删除业务对象 - * - * @param tableIds 需要删除的数据ID - * @return 结果 - */ - @Override - @Transactional - public void deleteGenTableByIds(Long[] tableIds) - { - genTableMapper.deleteGenTableByIds(tableIds); - genTableColumnMapper.deleteGenTableColumnByIds(tableIds); - } - - /** - * 创建表 - * - * @param sql 创建表语句 - * @return 结果 - */ - @Override - public boolean createTable(String sql) - { - return genTableMapper.createTable(sql) == 0; - } - - /** - * 导入表结构 - * - * @param tableList 导入表列表 - */ - @Override - @Transactional - public void importGenTable(List tableList, String operName) - { - try - { - for (GenTable table : tableList) - { - String tableName = table.getTableName(); - GenUtils.initTable(table, operName); - int row = genTableMapper.insertGenTable(table); - if (row > 0) - { - // 保存列信息 - List genTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName); - for (GenTableColumn column : genTableColumns) - { - GenUtils.initColumnField(column, table); - genTableColumnMapper.insertGenTableColumn(column); - } - } - } - } - catch (Exception e) - { - throw new ServiceException("导入失败:" + e.getMessage()); - } - } - - /** - * 预览代码 - * - * @param tableId 表编号 - * @return 预览数据列表 - */ - @Override - public Map previewCode(Long tableId) - { - Map dataMap = new LinkedHashMap<>(); - // 查询表信息 - GenTable table = genTableMapper.selectGenTableById(tableId); - // 设置主子表信息 - setSubTable(table); - // 设置主键列信息 - setPkColumn(table); - VelocityInitializer.initVelocity(); - - VelocityContext context = VelocityUtils.prepareContext(table); - - // 获取模板列表 - List templates = VelocityUtils.getTemplateList(table.getTplCategory(), table.getTplWebType()); - for (String template : templates) - { - // 渲染模板 - StringWriter sw = new StringWriter(); - Template tpl = Velocity.getTemplate(template, Constants.UTF8); - tpl.merge(context, sw); - dataMap.put(template, sw.toString()); - } - return dataMap; - } - - /** - * 生成代码(下载方式) - * - * @param tableName 表名称 - * @return 数据 - */ - @Override - public byte[] downloadCode(String tableName) - { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - ZipOutputStream zip = new ZipOutputStream(outputStream); - generatorCode(tableName, zip); - IOUtils.closeQuietly(zip); - return outputStream.toByteArray(); - } - - /** - * 生成代码(自定义路径) - * - * @param tableName 表名称 - */ - @Override - public void generatorCode(String tableName) - { - // 查询表信息 - GenTable table = genTableMapper.selectGenTableByName(tableName); - // 设置主子表信息 - setSubTable(table); - // 设置主键列信息 - setPkColumn(table); - - VelocityInitializer.initVelocity(); - - VelocityContext context = VelocityUtils.prepareContext(table); - - // 获取模板列表 - List templates = VelocityUtils.getTemplateList(table.getTplCategory(), table.getTplWebType()); - for (String template : templates) - { - if (!StringUtils.containsAny(template, "sql.vm", "api.js.vm", "index.vue.vm", "index-tree.vue.vm")) - { - // 渲染模板 - StringWriter sw = new StringWriter(); - Template tpl = Velocity.getTemplate(template, Constants.UTF8); - tpl.merge(context, sw); - try - { - String path = getGenPath(table, template); - FileUtils.writeStringToFile(new File(path), sw.toString(), CharsetKit.UTF_8); - } - catch (IOException e) - { - throw new ServiceException("渲染模板失败,表名:" + table.getTableName()); - } - } - } - } - - /** - * 同步数据库 - * - * @param tableName 表名称 - */ - @Override - @Transactional - public void synchDb(String tableName) - { - GenTable table = genTableMapper.selectGenTableByName(tableName); - List tableColumns = table.getColumns(); - Map tableColumnMap = tableColumns.stream().collect(Collectors.toMap(GenTableColumn::getColumnName, Function.identity())); - - List dbTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName); - if (StringUtils.isEmpty(dbTableColumns)) - { - throw new ServiceException("同步数据失败,原表结构不存在"); - } - List dbTableColumnNames = dbTableColumns.stream().map(GenTableColumn::getColumnName).collect(Collectors.toList()); - - dbTableColumns.forEach(column -> { - GenUtils.initColumnField(column, table); - if (tableColumnMap.containsKey(column.getColumnName())) - { - GenTableColumn prevColumn = tableColumnMap.get(column.getColumnName()); - column.setColumnId(prevColumn.getColumnId()); - if (column.isList()) - { - // 如果是列表,继续保留查询方式/字典类型选项 - column.setDictType(prevColumn.getDictType()); - column.setQueryType(prevColumn.getQueryType()); - } - if (StringUtils.isNotEmpty(prevColumn.getIsRequired()) && !column.isPk() - && (column.isInsert() || column.isEdit()) - && ((column.isUsableColumn()) || (!column.isSuperColumn()))) - { - // 如果是(新增/修改&非主键/非忽略及父属性),继续保留必填/显示类型选项 - column.setIsRequired(prevColumn.getIsRequired()); - column.setHtmlType(prevColumn.getHtmlType()); - } - genTableColumnMapper.updateGenTableColumn(column); - } - else - { - genTableColumnMapper.insertGenTableColumn(column); - } - }); - - List delColumns = tableColumns.stream().filter(column -> !dbTableColumnNames.contains(column.getColumnName())).collect(Collectors.toList()); - if (StringUtils.isNotEmpty(delColumns)) - { - genTableColumnMapper.deleteGenTableColumns(delColumns); - } - } - - /** - * 批量生成代码(下载方式) - * - * @param tableNames 表数组 - * @return 数据 - */ - @Override - public byte[] downloadCode(String[] tableNames) - { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - ZipOutputStream zip = new ZipOutputStream(outputStream); - for (String tableName : tableNames) - { - generatorCode(tableName, zip); - } - IOUtils.closeQuietly(zip); - return outputStream.toByteArray(); - } - - /** - * 查询表信息并生成代码 - */ - private void generatorCode(String tableName, ZipOutputStream zip) - { - // 查询表信息 - GenTable table = genTableMapper.selectGenTableByName(tableName); - // 设置主子表信息 - setSubTable(table); - // 设置主键列信息 - setPkColumn(table); - - VelocityInitializer.initVelocity(); - - VelocityContext context = VelocityUtils.prepareContext(table); - - // 获取模板列表 - List templates = VelocityUtils.getTemplateList(table.getTplCategory(), table.getTplWebType()); - for (String template : templates) - { - // 渲染模板 - StringWriter sw = new StringWriter(); - Template tpl = Velocity.getTemplate(template, Constants.UTF8); - tpl.merge(context, sw); - try - { - // 添加到zip - zip.putNextEntry(new ZipEntry(VelocityUtils.getFileName(template, table))); - IOUtils.write(sw.toString(), zip, Constants.UTF8); - IOUtils.closeQuietly(sw); - zip.flush(); - zip.closeEntry(); - } - catch (IOException e) - { - log.error("渲染模板失败,表名:" + table.getTableName(), e); - } - } - } - - /** - * 修改保存参数校验 - * - * @param genTable 业务信息 - */ - @Override - public void validateEdit(GenTable genTable) - { - if (GenConstants.TPL_TREE.equals(genTable.getTplCategory())) - { - String options = JSON.toJSONString(genTable.getParams()); - JSONObject paramsObj = JSON.parseObject(options); - if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_CODE))) - { - throw new ServiceException("树编码字段不能为空"); - } - else if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_PARENT_CODE))) - { - throw new ServiceException("树父编码字段不能为空"); - } - else if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_NAME))) - { - throw new ServiceException("树名称字段不能为空"); - } - } - else if (GenConstants.TPL_SUB.equals(genTable.getTplCategory())) - { - if (StringUtils.isEmpty(genTable.getSubTableName())) - { - throw new ServiceException("关联子表的表名不能为空"); - } - else if (StringUtils.isEmpty(genTable.getSubTableFkName())) - { - throw new ServiceException("子表关联的外键名不能为空"); - } - } - } - - /** - * 设置主键列信息 - * - * @param table 业务表信息 - */ - public void setPkColumn(GenTable table) - { - for (GenTableColumn column : table.getColumns()) - { - if (column.isPk()) - { - table.setPkColumn(column); - break; - } - } - if (StringUtils.isNull(table.getPkColumn())) - { - table.setPkColumn(table.getColumns().get(0)); - } - if (GenConstants.TPL_SUB.equals(table.getTplCategory())) - { - for (GenTableColumn column : table.getSubTable().getColumns()) - { - if (column.isPk()) - { - table.getSubTable().setPkColumn(column); - break; - } - } - if (StringUtils.isNull(table.getSubTable().getPkColumn())) - { - table.getSubTable().setPkColumn(table.getSubTable().getColumns().get(0)); - } - } - } - - /** - * 设置主子表信息 - * - * @param table 业务表信息 - */ - public void setSubTable(GenTable table) - { - String subTableName = table.getSubTableName(); - if (StringUtils.isNotEmpty(subTableName)) - { - table.setSubTable(genTableMapper.selectGenTableByName(subTableName)); - } - } - - /** - * 设置代码生成其他选项值 - * - * @param genTable 设置后的生成对象 - */ - public void setTableFromOptions(GenTable genTable) - { - JSONObject paramsObj = JSON.parseObject(genTable.getOptions()); - if (StringUtils.isNotNull(paramsObj)) - { - String treeCode = paramsObj.getString(GenConstants.TREE_CODE); - String treeParentCode = paramsObj.getString(GenConstants.TREE_PARENT_CODE); - String treeName = paramsObj.getString(GenConstants.TREE_NAME); - Long parentMenuId = paramsObj.getLongValue(GenConstants.PARENT_MENU_ID); - String parentMenuName = paramsObj.getString(GenConstants.PARENT_MENU_NAME); - - genTable.setTreeCode(treeCode); - genTable.setTreeParentCode(treeParentCode); - genTable.setTreeName(treeName); - genTable.setParentMenuId(parentMenuId); - genTable.setParentMenuName(parentMenuName); - } - } - - /** - * 获取代码生成地址 - * - * @param table 业务表信息 - * @param template 模板文件路径 - * @return 生成地址 - */ - public static String getGenPath(GenTable table, String template) - { - String genPath = table.getGenPath(); - if (StringUtils.equals(genPath, "/")) - { - return System.getProperty("user.dir") + File.separator + "src" + File.separator + VelocityUtils.getFileName(template, table); - } - return genPath + File.separator + VelocityUtils.getFileName(template, table); - } +package com.ruoyi.generator.service; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.velocity.Template; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.Velocity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.GenConstants; +import com.ruoyi.common.core.text.CharsetKit; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.generator.domain.GenTable; +import com.ruoyi.generator.domain.GenTableColumn; +import com.ruoyi.generator.mapper.GenTableColumnMapper; +import com.ruoyi.generator.mapper.GenTableMapper; +import com.ruoyi.generator.util.GenUtils; +import com.ruoyi.generator.util.VelocityInitializer; +import com.ruoyi.generator.util.VelocityUtils; + +/** + * 业务 服务层实现 + * + * @author ruoyi + */ +@Service +public class GenTableServiceImpl implements IGenTableService +{ + private static final Logger log = LoggerFactory.getLogger(GenTableServiceImpl.class); + + @Autowired + private GenTableMapper genTableMapper; + + @Autowired + private GenTableColumnMapper genTableColumnMapper; + + /** + * 查询业务信息 + * + * @param id 业务ID + * @return 业务信息 + */ + @Override + public GenTable selectGenTableById(Long id) + { + GenTable genTable = genTableMapper.selectGenTableById(id); + setTableFromOptions(genTable); + return genTable; + } + + /** + * 查询业务列表 + * + * @param genTable 业务信息 + * @return 业务集合 + */ + @Override + public List selectGenTableList(GenTable genTable) + { + return genTableMapper.selectGenTableList(genTable); + } + + /** + * 查询据库列表 + * + * @param genTable 业务信息 + * @return 数据库表集合 + */ + @Override + public List selectDbTableList(GenTable genTable) + { + return genTableMapper.selectDbTableList(genTable); + } + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * @return 数据库表集合 + */ + @Override + public List selectDbTableListByNames(String[] tableNames) + { + return genTableMapper.selectDbTableListByNames(tableNames); + } + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + @Override + public List selectGenTableAll() + { + return genTableMapper.selectGenTableAll(); + } + + /** + * 修改业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + @Override + @Transactional + public void updateGenTable(GenTable genTable) + { + String options = JSON.toJSONString(genTable.getParams()); + genTable.setOptions(options); + int row = genTableMapper.updateGenTable(genTable); + if (row > 0) + { + for (GenTableColumn genTableColumn : genTable.getColumns()) + { + genTableColumnMapper.updateGenTableColumn(genTableColumn); + } + } + } + + /** + * 删除业务对象 + * + * @param tableIds 需要删除的数据ID + * @return 结果 + */ + @Override + @Transactional + public void deleteGenTableByIds(Long[] tableIds) + { + genTableMapper.deleteGenTableByIds(tableIds); + genTableColumnMapper.deleteGenTableColumnByIds(tableIds); + } + + /** + * 创建表 + * + * @param sql 创建表语句 + * @return 结果 + */ + @Override + public boolean createTable(String sql) + { + return genTableMapper.createTable(sql) == 0; + } + + /** + * 导入表结构 + * + * @param tableList 导入表列表 + */ + @Override + @Transactional + public void importGenTable(List tableList, String tplWebType, String operName) + { + try + { + for (GenTable table : tableList) + { + String tableName = table.getTableName(); + table.setTplWebType(tplWebType); + GenUtils.initTable(table, operName); + int row = genTableMapper.insertGenTable(table); + if (row > 0) + { + // 保存列信息 + List genTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName); + for (GenTableColumn column : genTableColumns) + { + GenUtils.initColumnField(column, table); + genTableColumnMapper.insertGenTableColumn(column); + } + } + } + } + catch (Exception e) + { + throw new ServiceException("导入失败:" + e.getMessage()); + } + } + + /** + * 预览代码 + * + * @param tableId 表编号 + * @return 预览数据列表 + */ + @Override + public Map previewCode(Long tableId) + { + Map dataMap = new LinkedHashMap<>(); + // 查询表信息 + GenTable table = genTableMapper.selectGenTableById(tableId); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List templates = VelocityUtils.getTemplateList(table); + for (String template : templates) + { + // 渲染模板 + StringWriter sw = new StringWriter(); + Template tpl = Velocity.getTemplate(template, Constants.UTF8); + tpl.merge(context, sw); + dataMap.put(template, sw.toString()); + } + return dataMap; + } + + /** + * 生成代码(下载方式) + * + * @param tableName 表名称 + * @return 数据 + */ + @Override + public byte[] downloadCode(String tableName) + { + return downloadCode(new String[] { tableName }); + } + + /** + * 生成代码(自定义路径) + * + * @param tableName 表名称 + */ + @Override + public void generatorCode(String tableName) + { + // 查询表信息 + GenTable table = genTableMapper.selectGenTableByName(tableName); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); + + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List templates = VelocityUtils.getTemplateList(table); + for (String template : templates) + { + if (!StringUtils.containsAny(template, "sql.vm", "api.js.vm", "api.ts.vm", "type.ts.vm", "index.ts.vm", "index.vue.vm", "index-tree.vue.vm", "view.vue.vm")) + { + // 渲染模板 + StringWriter sw = new StringWriter(); + Template tpl = Velocity.getTemplate(template, Constants.UTF8); + tpl.merge(context, sw); + try + { + String path = getGenPath(table, template); + FileUtils.writeStringToFile(new File(path), sw.toString(), CharsetKit.UTF_8); + } + catch (IOException e) + { + throw new ServiceException("渲染模板失败,表名:" + table.getTableName()); + } + } + } + } + + /** + * 同步数据库 + * + * @param tableName 表名称 + */ + @Override + @Transactional + public void synchDb(String tableName) + { + GenTable table = genTableMapper.selectGenTableByName(tableName); + List tableColumns = table.getColumns(); + Map tableColumnMap = tableColumns.stream().collect(Collectors.toMap(GenTableColumn::getColumnName, Function.identity())); + + List dbTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName); + if (StringUtils.isEmpty(dbTableColumns)) + { + throw new ServiceException("同步数据失败,原表结构不存在"); + } + List dbTableColumnNames = dbTableColumns.stream().map(GenTableColumn::getColumnName).collect(Collectors.toList()); + + dbTableColumns.forEach(column -> { + GenUtils.initColumnField(column, table); + if (tableColumnMap.containsKey(column.getColumnName())) + { + GenTableColumn prevColumn = tableColumnMap.get(column.getColumnName()); + column.setColumnId(prevColumn.getColumnId()); + if (column.isList()) + { + // 如果是列表,继续保留查询方式/字典类型选项 + column.setDictType(prevColumn.getDictType()); + column.setQueryType(prevColumn.getQueryType()); + } + if (StringUtils.isNotEmpty(prevColumn.getIsRequired()) && !column.isPk() + && (column.isInsert() || column.isEdit()) + && ((column.isUsableColumn()) || (!column.isSuperColumn()))) + { + // 如果是(新增/修改&非主键/非忽略及父属性),继续保留必填/显示类型选项 + column.setIsRequired(prevColumn.getIsRequired()); + column.setHtmlType(prevColumn.getHtmlType()); + } + genTableColumnMapper.updateGenTableColumn(column); + } + else + { + genTableColumnMapper.insertGenTableColumn(column); + } + }); + + List delColumns = tableColumns.stream().filter(column -> !dbTableColumnNames.contains(column.getColumnName())).collect(Collectors.toList()); + if (StringUtils.isNotEmpty(delColumns)) + { + genTableColumnMapper.deleteGenTableColumns(delColumns); + } + } + + /** + * 批量生成代码(下载方式) + * + * @param tableNames 表数组 + * @return 数据 + */ + @Override + public byte[] downloadCode(String[] tableNames) + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ZipOutputStream zip = new ZipOutputStream(outputStream); + Map typeFiles = new HashMap<>(); + for (String tableName : tableNames) + { + generatorCode(tableName, zip, typeFiles); + } + for (Map.Entry entry : typeFiles.entrySet()) + { + writeToZip(zip, entry.getKey(), entry.getValue().toString()); + } + IOUtils.closeQuietly(zip); + return outputStream.toByteArray(); + } + + /** + * 查询表信息并生成代码 + */ + private void generatorCode(String tableName, ZipOutputStream zip, Map typeFiles) + { + // 查询表信息 + GenTable table = genTableMapper.selectGenTableByName(tableName); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); + + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List templates = VelocityUtils.getTemplateList(table); + for (String template : templates) + { + // 渲染模板 + StringWriter sw = new StringWriter(); + Template tpl = Velocity.getTemplate(template, Constants.UTF8); + tpl.merge(context, sw); + String fileName = VelocityUtils.getFileName(template, table); + // index-bak.ts 模版,追加内容 + if (fileName.contains("index-bak.ts")) + { + if (!typeFiles.containsKey(fileName)) + { + typeFiles.put(fileName, new StringBuffer(sw.toString())); + } + else + { + Arrays.stream(sw.toString().split("\n")).filter(line -> line.startsWith("export * from")).forEach(line -> typeFiles.get(fileName).append("\n").append(line)); + } + } + else + { + // 其他文件正常添加 + writeToZip(zip, fileName, sw.toString()); + } + } + } + + /** + * 将字符串内容写入ZIP输出流 + * + * @param zip ZIP输出流 + * @param fileName ZIP条目名称(即文件名) + * @param content 要写入的内容 + */ + private void writeToZip(ZipOutputStream zip, String fileName, String content) + { + try + { + zip.putNextEntry(new ZipEntry(fileName)); + IOUtils.write(content, zip, Constants.UTF8); + zip.flush(); + zip.closeEntry(); + } + catch (IOException e) + { + log.error("写入ZIP文件失败,文件名: " + fileName, e); + } + } + + /** + * 修改保存参数校验 + * + * @param genTable 业务信息 + */ + @Override + public void validateEdit(GenTable genTable) + { + if (GenConstants.TPL_TREE.equals(genTable.getTplCategory())) + { + String options = JSON.toJSONString(genTable.getParams()); + JSONObject paramsObj = JSON.parseObject(options); + if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_CODE))) + { + throw new ServiceException("树编码字段不能为空"); + } + else if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_PARENT_CODE))) + { + throw new ServiceException("树父编码字段不能为空"); + } + else if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_NAME))) + { + throw new ServiceException("树名称字段不能为空"); + } + } + else if (GenConstants.TPL_SUB.equals(genTable.getTplCategory())) + { + if (StringUtils.isEmpty(genTable.getSubTableName())) + { + throw new ServiceException("关联子表的表名不能为空"); + } + else if (StringUtils.isEmpty(genTable.getSubTableFkName())) + { + throw new ServiceException("子表关联的外键名不能为空"); + } + } + } + + /** + * 设置主键列信息 + * + * @param table 业务表信息 + */ + public void setPkColumn(GenTable table) + { + for (GenTableColumn column : table.getColumns()) + { + if (column.isPk()) + { + table.setPkColumn(column); + break; + } + } + if (StringUtils.isNull(table.getPkColumn())) + { + table.setPkColumn(table.getColumns().get(0)); + } + if (GenConstants.TPL_SUB.equals(table.getTplCategory())) + { + for (GenTableColumn column : table.getSubTable().getColumns()) + { + if (column.isPk()) + { + table.getSubTable().setPkColumn(column); + break; + } + } + if (StringUtils.isNull(table.getSubTable().getPkColumn())) + { + table.getSubTable().setPkColumn(table.getSubTable().getColumns().get(0)); + } + } + } + + /** + * 设置主子表信息 + * + * @param table 业务表信息 + */ + public void setSubTable(GenTable table) + { + String subTableName = table.getSubTableName(); + if (StringUtils.isNotEmpty(subTableName)) + { + table.setSubTable(genTableMapper.selectGenTableByName(subTableName)); + } + } + + /** + * 设置代码生成其他选项值 + * + * @param genTable 设置后的生成对象 + */ + public void setTableFromOptions(GenTable genTable) + { + JSONObject paramsObj = JSON.parseObject(genTable.getOptions()); + if (StringUtils.isNotNull(paramsObj)) + { + String treeCode = paramsObj.getString(GenConstants.TREE_CODE); + String treeParentCode = paramsObj.getString(GenConstants.TREE_PARENT_CODE); + String treeName = paramsObj.getString(GenConstants.TREE_NAME); + Long parentMenuId = paramsObj.getLongValue(GenConstants.PARENT_MENU_ID); + String parentMenuName = paramsObj.getString(GenConstants.PARENT_MENU_NAME); + boolean isView = paramsObj.getBooleanValue(GenConstants.GEN_VIEW); + + genTable.setTreeCode(treeCode); + genTable.setTreeParentCode(treeParentCode); + genTable.setTreeName(treeName); + genTable.setParentMenuId(parentMenuId); + genTable.setParentMenuName(parentMenuName); + genTable.setView(isView); + } + } + + /** + * 获取代码生成地址 + * + * @param table 业务表信息 + * @param template 模板文件路径 + * @return 生成地址 + */ + public static String getGenPath(GenTable table, String template) + { + String genPath = table.getGenPath(); + if (StringUtils.equals(genPath, "/")) + { + return System.getProperty("user.dir") + File.separator + "src" + File.separator + VelocityUtils.getFileName(template, table); + } + return genPath + File.separator + VelocityUtils.getFileName(template, table); + } } \ No newline at end of file diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableColumnService.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableColumnService.java index 3037f70..2130d04 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableColumnService.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableColumnService.java @@ -1,44 +1,44 @@ -package com.ruoyi.generator.service; - -import java.util.List; -import com.ruoyi.generator.domain.GenTableColumn; - -/** - * 业务字段 服务层 - * - * @author ruoyi - */ -public interface IGenTableColumnService -{ - /** - * 查询业务字段列表 - * - * @param tableId 业务字段编号 - * @return 业务字段集合 - */ - public List selectGenTableColumnListByTableId(Long tableId); - - /** - * 新增业务字段 - * - * @param genTableColumn 业务字段信息 - * @return 结果 - */ - public int insertGenTableColumn(GenTableColumn genTableColumn); - - /** - * 修改业务字段 - * - * @param genTableColumn 业务字段信息 - * @return 结果 - */ - public int updateGenTableColumn(GenTableColumn genTableColumn); - - /** - * 删除业务字段信息 - * - * @param ids 需要删除的数据ID - * @return 结果 - */ - public int deleteGenTableColumnByIds(String ids); -} +package com.ruoyi.generator.service; + +import java.util.List; +import com.ruoyi.generator.domain.GenTableColumn; + +/** + * 业务字段 服务层 + * + * @author ruoyi + */ +public interface IGenTableColumnService +{ + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * @return 业务字段集合 + */ + public List selectGenTableColumnListByTableId(Long tableId); + + /** + * 新增业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + public int insertGenTableColumn(GenTableColumn genTableColumn); + + /** + * 修改业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + public int updateGenTableColumn(GenTableColumn genTableColumn); + + /** + * 删除业务字段信息 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteGenTableColumnByIds(String ids); +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java index 695426e..155c497 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java @@ -1,130 +1,131 @@ -package com.ruoyi.generator.service; - -import java.util.List; -import java.util.Map; -import com.ruoyi.generator.domain.GenTable; - -/** - * 业务 服务层 - * - * @author ruoyi - */ -public interface IGenTableService -{ - /** - * 查询业务列表 - * - * @param genTable 业务信息 - * @return 业务集合 - */ - public List selectGenTableList(GenTable genTable); - - /** - * 查询据库列表 - * - * @param genTable 业务信息 - * @return 数据库表集合 - */ - public List selectDbTableList(GenTable genTable); - - /** - * 查询据库列表 - * - * @param tableNames 表名称组 - * @return 数据库表集合 - */ - public List selectDbTableListByNames(String[] tableNames); - - /** - * 查询所有表信息 - * - * @return 表信息集合 - */ - public List selectGenTableAll(); - - /** - * 查询业务信息 - * - * @param id 业务ID - * @return 业务信息 - */ - public GenTable selectGenTableById(Long id); - - /** - * 修改业务 - * - * @param genTable 业务信息 - * @return 结果 - */ - public void updateGenTable(GenTable genTable); - - /** - * 删除业务信息 - * - * @param tableIds 需要删除的表数据ID - * @return 结果 - */ - public void deleteGenTableByIds(Long[] tableIds); - - /** - * 创建表 - * - * @param sql 创建表语句 - * @return 结果 - */ - public boolean createTable(String sql); - - /** - * 导入表结构 - * - * @param tableList 导入表列表 - * @param operName 操作人员 - */ - public void importGenTable(List tableList, String operName); - - /** - * 预览代码 - * - * @param tableId 表编号 - * @return 预览数据列表 - */ - public Map previewCode(Long tableId); - - /** - * 生成代码(下载方式) - * - * @param tableName 表名称 - * @return 数据 - */ - public byte[] downloadCode(String tableName); - - /** - * 生成代码(自定义路径) - * - * @param tableName 表名称 - * @return 数据 - */ - public void generatorCode(String tableName); - - /** - * 同步数据库 - * - * @param tableName 表名称 - */ - public void synchDb(String tableName); - - /** - * 批量生成代码(下载方式) - * - * @param tableNames 表数组 - * @return 数据 - */ - public byte[] downloadCode(String[] tableNames); - - /** - * 修改保存参数校验 - * - * @param genTable 业务信息 - */ - public void validateEdit(GenTable genTable); -} +package com.ruoyi.generator.service; + +import java.util.List; +import java.util.Map; +import com.ruoyi.generator.domain.GenTable; + +/** + * 业务 服务层 + * + * @author ruoyi + */ +public interface IGenTableService +{ + /** + * 查询业务列表 + * + * @param genTable 业务信息 + * @return 业务集合 + */ + public List selectGenTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param genTable 业务信息 + * @return 数据库表集合 + */ + public List selectDbTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * @return 数据库表集合 + */ + public List selectDbTableListByNames(String[] tableNames); + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + public List selectGenTableAll(); + + /** + * 查询业务信息 + * + * @param id 业务ID + * @return 业务信息 + */ + public GenTable selectGenTableById(Long id); + + /** + * 修改业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + public void updateGenTable(GenTable genTable); + + /** + * 删除业务信息 + * + * @param tableIds 需要删除的表数据ID + * @return 结果 + */ + public void deleteGenTableByIds(Long[] tableIds); + + /** + * 创建表 + * + * @param sql 创建表语句 + * @return 结果 + */ + public boolean createTable(String sql); + + /** + * 导入表结构 + * + * @param tableList 导入表列表 + * @param tplWebType 前端类型 + * @param operName 操作人员 + */ + public void importGenTable(List tableList, String tplWebType, String operName); + + /** + * 预览代码 + * + * @param tableId 表编号 + * @return 预览数据列表 + */ + public Map previewCode(Long tableId); + + /** + * 生成代码(下载方式) + * + * @param tableName 表名称 + * @return 数据 + */ + public byte[] downloadCode(String tableName); + + /** + * 生成代码(自定义路径) + * + * @param tableName 表名称 + * @return 数据 + */ + public void generatorCode(String tableName); + + /** + * 同步数据库 + * + * @param tableName 表名称 + */ + public void synchDb(String tableName); + + /** + * 批量生成代码(下载方式) + * + * @param tableNames 表数组 + * @return 数据 + */ + public byte[] downloadCode(String[] tableNames); + + /** + * 修改保存参数校验 + * + * @param genTable 业务信息 + */ + public void validateEdit(GenTable genTable); +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java index e7ebc20..e28c0bb 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java @@ -1,257 +1,257 @@ -package com.ruoyi.generator.util; - -import java.util.Arrays; -import org.apache.commons.lang3.RegExUtils; -import com.ruoyi.common.constant.GenConstants; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.generator.config.GenConfig; -import com.ruoyi.generator.domain.GenTable; -import com.ruoyi.generator.domain.GenTableColumn; - -/** - * 代码生成器 工具类 - * - * @author ruoyi - */ -public class GenUtils -{ - /** - * 初始化表信息 - */ - public static void initTable(GenTable genTable, String operName) - { - genTable.setClassName(convertClassName(genTable.getTableName())); - genTable.setPackageName(GenConfig.getPackageName()); - genTable.setModuleName(getModuleName(GenConfig.getPackageName())); - genTable.setBusinessName(getBusinessName(genTable.getTableName())); - genTable.setFunctionName(replaceText(genTable.getTableComment())); - genTable.setFunctionAuthor(GenConfig.getAuthor()); - genTable.setCreateBy(operName); - } - - /** - * 初始化列属性字段 - */ - public static void initColumnField(GenTableColumn column, GenTable table) - { - String dataType = getDbType(column.getColumnType()); - String columnName = column.getColumnName(); - column.setTableId(table.getTableId()); - column.setCreateBy(table.getCreateBy()); - // 设置java字段名 - column.setJavaField(StringUtils.toCamelCase(columnName)); - // 设置默认类型 - column.setJavaType(GenConstants.TYPE_STRING); - column.setQueryType(GenConstants.QUERY_EQ); - - if (arraysContains(GenConstants.COLUMNTYPE_STR, dataType) || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType)) - { - // 字符串长度超过500设置为文本域 - Integer columnLength = getColumnLength(column.getColumnType()); - String htmlType = columnLength >= 500 || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType) ? GenConstants.HTML_TEXTAREA : GenConstants.HTML_INPUT; - column.setHtmlType(htmlType); - } - else if (arraysContains(GenConstants.COLUMNTYPE_TIME, dataType)) - { - column.setJavaType(GenConstants.TYPE_DATE); - column.setHtmlType(GenConstants.HTML_DATETIME); - } - else if (arraysContains(GenConstants.COLUMNTYPE_NUMBER, dataType)) - { - column.setHtmlType(GenConstants.HTML_INPUT); - - // 如果是浮点型 统一用BigDecimal - String[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), "(", ")"), ","); - if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0) - { - column.setJavaType(GenConstants.TYPE_BIGDECIMAL); - } - // 如果是整形 - else if (str != null && str.length == 1 && Integer.parseInt(str[0]) <= 10) - { - column.setJavaType(GenConstants.TYPE_INTEGER); - } - // 长整形 - else - { - column.setJavaType(GenConstants.TYPE_LONG); - } - } - - // 插入字段(默认所有字段都需要插入) - column.setIsInsert(GenConstants.REQUIRE); - - // 编辑字段 - if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName) && !column.isPk()) - { - column.setIsEdit(GenConstants.REQUIRE); - } - // 列表字段 - if (!arraysContains(GenConstants.COLUMNNAME_NOT_LIST, columnName) && !column.isPk()) - { - column.setIsList(GenConstants.REQUIRE); - } - // 查询字段 - if (!arraysContains(GenConstants.COLUMNNAME_NOT_QUERY, columnName) && !column.isPk()) - { - column.setIsQuery(GenConstants.REQUIRE); - } - - // 查询字段类型 - if (StringUtils.endsWithIgnoreCase(columnName, "name")) - { - column.setQueryType(GenConstants.QUERY_LIKE); - } - // 状态字段设置单选框 - if (StringUtils.endsWithIgnoreCase(columnName, "status")) - { - column.setHtmlType(GenConstants.HTML_RADIO); - } - // 类型&性别字段设置下拉框 - else if (StringUtils.endsWithIgnoreCase(columnName, "type") - || StringUtils.endsWithIgnoreCase(columnName, "sex")) - { - column.setHtmlType(GenConstants.HTML_SELECT); - } - // 图片字段设置图片上传控件 - else if (StringUtils.endsWithIgnoreCase(columnName, "image")) - { - column.setHtmlType(GenConstants.HTML_IMAGE_UPLOAD); - } - // 文件字段设置文件上传控件 - else if (StringUtils.endsWithIgnoreCase(columnName, "file")) - { - column.setHtmlType(GenConstants.HTML_FILE_UPLOAD); - } - // 内容字段设置富文本控件 - else if (StringUtils.endsWithIgnoreCase(columnName, "content")) - { - column.setHtmlType(GenConstants.HTML_EDITOR); - } - } - - /** - * 校验数组是否包含指定值 - * - * @param arr 数组 - * @param targetValue 值 - * @return 是否包含 - */ - public static boolean arraysContains(String[] arr, String targetValue) - { - return Arrays.asList(arr).contains(targetValue); - } - - /** - * 获取模块名 - * - * @param packageName 包名 - * @return 模块名 - */ - public static String getModuleName(String packageName) - { - int lastIndex = packageName.lastIndexOf("."); - int nameLength = packageName.length(); - return StringUtils.substring(packageName, lastIndex + 1, nameLength); - } - - /** - * 获取业务名 - * - * @param tableName 表名 - * @return 业务名 - */ - public static String getBusinessName(String tableName) - { - int lastIndex = tableName.lastIndexOf("_"); - int nameLength = tableName.length(); - return StringUtils.substring(tableName, lastIndex + 1, nameLength); - } - - /** - * 表名转换成Java类名 - * - * @param tableName 表名称 - * @return 类名 - */ - public static String convertClassName(String tableName) - { - boolean autoRemovePre = GenConfig.getAutoRemovePre(); - String tablePrefix = GenConfig.getTablePrefix(); - if (autoRemovePre && StringUtils.isNotEmpty(tablePrefix)) - { - String[] searchList = StringUtils.split(tablePrefix, ","); - tableName = replaceFirst(tableName, searchList); - } - return StringUtils.convertToCamelCase(tableName); - } - - /** - * 批量替换前缀 - * - * @param replacementm 替换值 - * @param searchList 替换列表 - * @return - */ - public static String replaceFirst(String replacementm, String[] searchList) - { - String text = replacementm; - for (String searchString : searchList) - { - if (replacementm.startsWith(searchString)) - { - text = replacementm.replaceFirst(searchString, ""); - break; - } - } - return text; - } - - /** - * 关键字替换 - * - * @param text 需要被替换的名字 - * @return 替换后的名字 - */ - public static String replaceText(String text) - { - return RegExUtils.replaceAll(text, "(?:表|若依)", ""); - } - - /** - * 获取数据库类型字段 - * - * @param columnType 列类型 - * @return 截取后的列类型 - */ - public static String getDbType(String columnType) - { - if (StringUtils.indexOf(columnType, "(") > 0) - { - return StringUtils.substringBefore(columnType, "("); - } - else - { - return columnType; - } - } - - /** - * 获取字段长度 - * - * @param columnType 列类型 - * @return 截取后的列类型 - */ - public static Integer getColumnLength(String columnType) - { - if (StringUtils.indexOf(columnType, "(") > 0) - { - String length = StringUtils.substringBetween(columnType, "(", ")"); - return Integer.valueOf(length); - } - else - { - return 0; - } - } -} +package com.ruoyi.generator.util; + +import java.util.Arrays; +import org.apache.commons.lang3.RegExUtils; +import com.ruoyi.common.constant.GenConstants; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.generator.config.GenConfig; +import com.ruoyi.generator.domain.GenTable; +import com.ruoyi.generator.domain.GenTableColumn; + +/** + * 代码生成器 工具类 + * + * @author ruoyi + */ +public class GenUtils +{ + /** + * 初始化表信息 + */ + public static void initTable(GenTable genTable, String operName) + { + genTable.setClassName(convertClassName(genTable.getTableName())); + genTable.setPackageName(GenConfig.getPackageName()); + genTable.setModuleName(getModuleName(GenConfig.getPackageName())); + genTable.setBusinessName(getBusinessName(genTable.getTableName())); + genTable.setFunctionName(replaceText(genTable.getTableComment())); + genTable.setFunctionAuthor(GenConfig.getAuthor()); + genTable.setCreateBy(operName); + } + + /** + * 初始化列属性字段 + */ + public static void initColumnField(GenTableColumn column, GenTable table) + { + String dataType = getDbType(column.getColumnType()); + String columnName = column.getColumnName(); + column.setTableId(table.getTableId()); + column.setCreateBy(table.getCreateBy()); + // 设置java字段名 + column.setJavaField(StringUtils.toCamelCase(columnName)); + // 设置默认类型 + column.setJavaType(GenConstants.TYPE_STRING); + column.setQueryType(GenConstants.QUERY_EQ); + + if (arraysContains(GenConstants.COLUMNTYPE_STR, dataType) || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType)) + { + // 字符串长度超过500设置为文本域 + Integer columnLength = getColumnLength(column.getColumnType()); + String htmlType = columnLength >= 500 || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType) ? GenConstants.HTML_TEXTAREA : GenConstants.HTML_INPUT; + column.setHtmlType(htmlType); + } + else if (arraysContains(GenConstants.COLUMNTYPE_TIME, dataType)) + { + column.setJavaType(GenConstants.TYPE_DATE); + column.setHtmlType(GenConstants.HTML_DATETIME); + } + else if (arraysContains(GenConstants.COLUMNTYPE_NUMBER, dataType)) + { + column.setHtmlType(GenConstants.HTML_INPUT); + + // 如果是浮点型 统一用BigDecimal + String[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), "(", ")"), ","); + if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0) + { + column.setJavaType(GenConstants.TYPE_BIGDECIMAL); + } + // 如果是整形 + else if (str != null && str.length == 1 && Integer.parseInt(str[0]) <= 10) + { + column.setJavaType(GenConstants.TYPE_INTEGER); + } + // 长整形 + else + { + column.setJavaType(GenConstants.TYPE_LONG); + } + } + + // 插入字段(默认所有字段都需要插入) + column.setIsInsert(GenConstants.REQUIRE); + + // 编辑字段 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName) && !column.isPk()) + { + column.setIsEdit(GenConstants.REQUIRE); + } + // 列表字段 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_LIST, columnName) && !column.isPk()) + { + column.setIsList(GenConstants.REQUIRE); + } + // 查询字段 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_QUERY, columnName) && !column.isPk()) + { + column.setIsQuery(GenConstants.REQUIRE); + } + + // 查询字段类型 + if (StringUtils.endsWithIgnoreCase(columnName, "name")) + { + column.setQueryType(GenConstants.QUERY_LIKE); + } + // 状态字段设置单选框 + if (StringUtils.endsWithIgnoreCase(columnName, "status")) + { + column.setHtmlType(GenConstants.HTML_RADIO); + } + // 类型&性别字段设置下拉框 + else if (StringUtils.endsWithIgnoreCase(columnName, "type") + || StringUtils.endsWithIgnoreCase(columnName, "sex")) + { + column.setHtmlType(GenConstants.HTML_SELECT); + } + // 图片字段设置图片上传控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "image")) + { + column.setHtmlType(GenConstants.HTML_IMAGE_UPLOAD); + } + // 文件字段设置文件上传控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "file")) + { + column.setHtmlType(GenConstants.HTML_FILE_UPLOAD); + } + // 内容字段设置富文本控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "content")) + { + column.setHtmlType(GenConstants.HTML_EDITOR); + } + } + + /** + * 校验数组是否包含指定值 + * + * @param arr 数组 + * @param targetValue 值 + * @return 是否包含 + */ + public static boolean arraysContains(String[] arr, String targetValue) + { + return Arrays.asList(arr).contains(targetValue); + } + + /** + * 获取模块名 + * + * @param packageName 包名 + * @return 模块名 + */ + public static String getModuleName(String packageName) + { + int lastIndex = packageName.lastIndexOf("."); + int nameLength = packageName.length(); + return StringUtils.substring(packageName, lastIndex + 1, nameLength); + } + + /** + * 获取业务名 + * + * @param tableName 表名 + * @return 业务名 + */ + public static String getBusinessName(String tableName) + { + int lastIndex = tableName.lastIndexOf("_"); + int nameLength = tableName.length(); + return StringUtils.substring(tableName, lastIndex + 1, nameLength); + } + + /** + * 表名转换成Java类名 + * + * @param tableName 表名称 + * @return 类名 + */ + public static String convertClassName(String tableName) + { + boolean autoRemovePre = GenConfig.getAutoRemovePre(); + String tablePrefix = GenConfig.getTablePrefix(); + if (autoRemovePre && StringUtils.isNotEmpty(tablePrefix)) + { + String[] searchList = StringUtils.split(tablePrefix, ","); + tableName = replaceFirst(tableName, searchList); + } + return StringUtils.convertToCamelCase(tableName); + } + + /** + * 批量替换前缀 + * + * @param replacementm 替换值 + * @param searchList 替换列表 + * @return + */ + public static String replaceFirst(String replacementm, String[] searchList) + { + String text = replacementm; + for (String searchString : searchList) + { + if (replacementm.startsWith(searchString)) + { + text = replacementm.replaceFirst(searchString, ""); + break; + } + } + return text; + } + + /** + * 关键字替换 + * + * @param text 需要被替换的名字 + * @return 替换后的名字 + */ + public static String replaceText(String text) + { + return RegExUtils.replaceAll(text, "(?:表|若依)", ""); + } + + /** + * 获取数据库类型字段 + * + * @param columnType 列类型 + * @return 截取后的列类型 + */ + public static String getDbType(String columnType) + { + if (StringUtils.indexOf(columnType, "(") > 0) + { + return StringUtils.substringBefore(columnType, "("); + } + else + { + return columnType; + } + } + + /** + * 获取字段长度 + * + * @param columnType 列类型 + * @return 截取后的列类型 + */ + public static Integer getColumnLength(String columnType) + { + if (StringUtils.indexOf(columnType, "(") > 0) + { + String length = StringUtils.substringBetween(columnType, "(", ")"); + return Integer.valueOf(length); + } + else + { + return 0; + } + } +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityInitializer.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityInitializer.java index 9f69403..2645633 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityInitializer.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityInitializer.java @@ -1,34 +1,34 @@ -package com.ruoyi.generator.util; - -import java.util.Properties; -import org.apache.velocity.app.Velocity; -import com.ruoyi.common.constant.Constants; - -/** - * VelocityEngine工厂 - * - * @author ruoyi - */ -public class VelocityInitializer -{ - /** - * 初始化vm方法 - */ - public static void initVelocity() - { - Properties p = new Properties(); - try - { - // 加载classpath目录下的vm文件 - p.setProperty("resource.loader.file.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); - // 定义字符集 - p.setProperty(Velocity.INPUT_ENCODING, Constants.UTF8); - // 初始化Velocity引擎,指定配置Properties - Velocity.init(p); - } - catch (Exception e) - { - throw new RuntimeException(e); - } - } -} +package com.ruoyi.generator.util; + +import java.util.Properties; +import org.apache.velocity.app.Velocity; +import com.ruoyi.common.constant.Constants; + +/** + * VelocityEngine工厂 + * + * @author ruoyi + */ +public class VelocityInitializer +{ + /** + * 初始化vm方法 + */ + public static void initVelocity() + { + Properties p = new Properties(); + try + { + // 加载classpath目录下的vm文件 + p.setProperty("resource.loader.file.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); + // 定义字符集 + p.setProperty(Velocity.INPUT_ENCODING, Constants.UTF8); + // 初始化Velocity引擎,指定配置Properties + Velocity.init(p); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java index 1a14681..c8585b0 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java @@ -1,408 +1,492 @@ -package com.ruoyi.generator.util; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import org.apache.velocity.VelocityContext; -import com.alibaba.fastjson2.JSON; -import com.alibaba.fastjson2.JSONObject; -import com.ruoyi.common.constant.GenConstants; -import com.ruoyi.common.utils.DateUtils; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.generator.domain.GenTable; -import com.ruoyi.generator.domain.GenTableColumn; - -/** - * 模板处理工具类 - * - * @author ruoyi - */ -public class VelocityUtils -{ - /** 项目空间路径 */ - private static final String PROJECT_PATH = "main/java"; - - /** mybatis空间路径 */ - private static final String MYBATIS_PATH = "main/resources/mapper"; - - /** 默认上级菜单,系统工具 */ - private static final String DEFAULT_PARENT_MENU_ID = "3"; - - /** - * 设置模板变量信息 - * - * @return 模板列表 - */ - public static VelocityContext prepareContext(GenTable genTable) - { - String moduleName = genTable.getModuleName(); - String businessName = genTable.getBusinessName(); - String packageName = genTable.getPackageName(); - String tplCategory = genTable.getTplCategory(); - String functionName = genTable.getFunctionName(); - - VelocityContext velocityContext = new VelocityContext(); - velocityContext.put("tplCategory", genTable.getTplCategory()); - velocityContext.put("tableName", genTable.getTableName()); - velocityContext.put("functionName", StringUtils.isNotEmpty(functionName) ? functionName : "【请填写功能名称】"); - velocityContext.put("ClassName", genTable.getClassName()); - velocityContext.put("className", StringUtils.uncapitalize(genTable.getClassName())); - velocityContext.put("moduleName", genTable.getModuleName()); - velocityContext.put("BusinessName", StringUtils.capitalize(genTable.getBusinessName())); - velocityContext.put("businessName", genTable.getBusinessName()); - velocityContext.put("basePackage", getPackagePrefix(packageName)); - velocityContext.put("packageName", packageName); - velocityContext.put("author", genTable.getFunctionAuthor()); - velocityContext.put("datetime", DateUtils.getDate()); - velocityContext.put("pkColumn", genTable.getPkColumn()); - velocityContext.put("importList", getImportList(genTable)); - velocityContext.put("permissionPrefix", getPermissionPrefix(moduleName, businessName)); - velocityContext.put("columns", genTable.getColumns()); - velocityContext.put("table", genTable); - velocityContext.put("dicts", getDicts(genTable)); - setMenuVelocityContext(velocityContext, genTable); - if (GenConstants.TPL_TREE.equals(tplCategory)) - { - setTreeVelocityContext(velocityContext, genTable); - } - if (GenConstants.TPL_SUB.equals(tplCategory)) - { - setSubVelocityContext(velocityContext, genTable); - } - return velocityContext; - } - - public static void setMenuVelocityContext(VelocityContext context, GenTable genTable) - { - String options = genTable.getOptions(); - JSONObject paramsObj = JSON.parseObject(options); - String parentMenuId = getParentMenuId(paramsObj); - context.put("parentMenuId", parentMenuId); - } - - public static void setTreeVelocityContext(VelocityContext context, GenTable genTable) - { - String options = genTable.getOptions(); - JSONObject paramsObj = JSON.parseObject(options); - String treeCode = getTreecode(paramsObj); - String treeParentCode = getTreeParentCode(paramsObj); - String treeName = getTreeName(paramsObj); - - context.put("treeCode", treeCode); - context.put("treeParentCode", treeParentCode); - context.put("treeName", treeName); - context.put("expandColumn", getExpandColumn(genTable)); - if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) - { - context.put("tree_parent_code", paramsObj.getString(GenConstants.TREE_PARENT_CODE)); - } - if (paramsObj.containsKey(GenConstants.TREE_NAME)) - { - context.put("tree_name", paramsObj.getString(GenConstants.TREE_NAME)); - } - } - - public static void setSubVelocityContext(VelocityContext context, GenTable genTable) - { - GenTable subTable = genTable.getSubTable(); - String subTableName = genTable.getSubTableName(); - String subTableFkName = genTable.getSubTableFkName(); - String subClassName = genTable.getSubTable().getClassName(); - String subTableFkClassName = StringUtils.convertToCamelCase(subTableFkName); - - context.put("subTable", subTable); - context.put("subTableName", subTableName); - context.put("subTableFkName", subTableFkName); - context.put("subTableFkClassName", subTableFkClassName); - context.put("subTableFkclassName", StringUtils.uncapitalize(subTableFkClassName)); - context.put("subClassName", subClassName); - context.put("subclassName", StringUtils.uncapitalize(subClassName)); - context.put("subImportList", getImportList(genTable.getSubTable())); - } - - /** - * 获取模板信息 - * @param tplCategory 生成的模板 - * @param tplWebType 前端类型 - * @return 模板列表 - */ - public static List getTemplateList(String tplCategory, String tplWebType) - { - String useWebType = "vm/vue"; - if ("element-plus".equals(tplWebType)) - { - useWebType = "vm/vue/v3"; - } - List templates = new ArrayList(); - templates.add("vm/java/domain.java.vm"); - templates.add("vm/java/mapper.java.vm"); - templates.add("vm/java/service.java.vm"); - templates.add("vm/java/serviceImpl.java.vm"); - templates.add("vm/java/controller.java.vm"); - templates.add("vm/xml/mapper.xml.vm"); - templates.add("vm/sql/sql.vm"); - templates.add("vm/js/api.js.vm"); - if (GenConstants.TPL_CRUD.equals(tplCategory)) - { - templates.add(useWebType + "/index.vue.vm"); - } - else if (GenConstants.TPL_TREE.equals(tplCategory)) - { - templates.add(useWebType + "/index-tree.vue.vm"); - } - else if (GenConstants.TPL_SUB.equals(tplCategory)) - { - templates.add(useWebType + "/index.vue.vm"); - templates.add("vm/java/sub-domain.java.vm"); - } - return templates; - } - - /** - * 获取文件名 - */ - public static String getFileName(String template, GenTable genTable) - { - // 文件名称 - String fileName = ""; - // 包路径 - String packageName = genTable.getPackageName(); - // 模块名 - String moduleName = genTable.getModuleName(); - // 大写类名 - String className = genTable.getClassName(); - // 业务名称 - String businessName = genTable.getBusinessName(); - - String javaPath = PROJECT_PATH + "/" + StringUtils.replace(packageName, ".", "/"); - String mybatisPath = MYBATIS_PATH + "/" + moduleName; - String vuePath = "vue"; - - if (template.contains("domain.java.vm")) - { - fileName = StringUtils.format("{}/domain/{}.java", javaPath, className); - } - if (template.contains("sub-domain.java.vm") && StringUtils.equals(GenConstants.TPL_SUB, genTable.getTplCategory())) - { - fileName = StringUtils.format("{}/domain/{}.java", javaPath, genTable.getSubTable().getClassName()); - } - else if (template.contains("mapper.java.vm")) - { - fileName = StringUtils.format("{}/mapper/{}Mapper.java", javaPath, className); - } - else if (template.contains("service.java.vm")) - { - fileName = StringUtils.format("{}/service/I{}Service.java", javaPath, className); - } - else if (template.contains("serviceImpl.java.vm")) - { - fileName = StringUtils.format("{}/service/impl/{}ServiceImpl.java", javaPath, className); - } - else if (template.contains("controller.java.vm")) - { - fileName = StringUtils.format("{}/controller/{}Controller.java", javaPath, className); - } - else if (template.contains("mapper.xml.vm")) - { - fileName = StringUtils.format("{}/{}Mapper.xml", mybatisPath, className); - } - else if (template.contains("sql.vm")) - { - fileName = businessName + "Menu.sql"; - } - else if (template.contains("api.js.vm")) - { - fileName = StringUtils.format("{}/api/{}/{}.js", vuePath, moduleName, businessName); - } - else if (template.contains("index.vue.vm")) - { - fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName); - } - else if (template.contains("index-tree.vue.vm")) - { - fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName); - } - return fileName; - } - - /** - * 获取包前缀 - * - * @param packageName 包名称 - * @return 包前缀名称 - */ - public static String getPackagePrefix(String packageName) - { - int lastIndex = packageName.lastIndexOf("."); - return StringUtils.substring(packageName, 0, lastIndex); - } - - /** - * 根据列类型获取导入包 - * - * @param genTable 业务表对象 - * @return 返回需要导入的包列表 - */ - public static HashSet getImportList(GenTable genTable) - { - List columns = genTable.getColumns(); - GenTable subGenTable = genTable.getSubTable(); - HashSet importList = new HashSet(); - if (StringUtils.isNotNull(subGenTable)) - { - importList.add("java.util.List"); - } - for (GenTableColumn column : columns) - { - if (!column.isSuperColumn() && GenConstants.TYPE_DATE.equals(column.getJavaType())) - { - importList.add("java.util.Date"); - importList.add("com.fasterxml.jackson.annotation.JsonFormat"); - } - else if (!column.isSuperColumn() && GenConstants.TYPE_BIGDECIMAL.equals(column.getJavaType())) - { - importList.add("java.math.BigDecimal"); - } - } - return importList; - } - - /** - * 根据列类型获取字典组 - * - * @param genTable 业务表对象 - * @return 返回字典组 - */ - public static String getDicts(GenTable genTable) - { - List columns = genTable.getColumns(); - Set dicts = new HashSet(); - addDicts(dicts, columns); - if (StringUtils.isNotNull(genTable.getSubTable())) - { - List subColumns = genTable.getSubTable().getColumns(); - addDicts(dicts, subColumns); - } - return StringUtils.join(dicts, ", "); - } - - /** - * 添加字典列表 - * - * @param dicts 字典列表 - * @param columns 列集合 - */ - public static void addDicts(Set dicts, List columns) - { - for (GenTableColumn column : columns) - { - if (!column.isSuperColumn() && StringUtils.isNotEmpty(column.getDictType()) && StringUtils.equalsAny( - column.getHtmlType(), - new String[] { GenConstants.HTML_SELECT, GenConstants.HTML_RADIO, GenConstants.HTML_CHECKBOX })) - { - dicts.add("'" + column.getDictType() + "'"); - } - } - } - - /** - * 获取权限前缀 - * - * @param moduleName 模块名称 - * @param businessName 业务名称 - * @return 返回权限前缀 - */ - public static String getPermissionPrefix(String moduleName, String businessName) - { - return StringUtils.format("{}:{}", moduleName, businessName); - } - - /** - * 获取上级菜单ID字段 - * - * @param paramsObj 生成其他选项 - * @return 上级菜单ID字段 - */ - public static String getParentMenuId(JSONObject paramsObj) - { - if (StringUtils.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.PARENT_MENU_ID) - && StringUtils.isNotEmpty(paramsObj.getString(GenConstants.PARENT_MENU_ID))) - { - return paramsObj.getString(GenConstants.PARENT_MENU_ID); - } - return DEFAULT_PARENT_MENU_ID; - } - - /** - * 获取树编码 - * - * @param paramsObj 生成其他选项 - * @return 树编码 - */ - public static String getTreecode(JSONObject paramsObj) - { - if (paramsObj.containsKey(GenConstants.TREE_CODE)) - { - return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_CODE)); - } - return StringUtils.EMPTY; - } - - /** - * 获取树父编码 - * - * @param paramsObj 生成其他选项 - * @return 树父编码 - */ - public static String getTreeParentCode(JSONObject paramsObj) - { - if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) - { - return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_PARENT_CODE)); - } - return StringUtils.EMPTY; - } - - /** - * 获取树名称 - * - * @param paramsObj 生成其他选项 - * @return 树名称 - */ - public static String getTreeName(JSONObject paramsObj) - { - if (paramsObj.containsKey(GenConstants.TREE_NAME)) - { - return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_NAME)); - } - return StringUtils.EMPTY; - } - - /** - * 获取需要在哪一列上面显示展开按钮 - * - * @param genTable 业务表对象 - * @return 展开按钮列序号 - */ - public static int getExpandColumn(GenTable genTable) - { - String options = genTable.getOptions(); - JSONObject paramsObj = JSON.parseObject(options); - String treeName = paramsObj.getString(GenConstants.TREE_NAME); - int num = 0; - for (GenTableColumn column : genTable.getColumns()) - { - if (column.isList()) - { - num++; - String columnName = column.getColumnName(); - if (columnName.equals(treeName)) - { - break; - } - } - } - return num; - } -} +package com.ruoyi.generator.util; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.apache.velocity.VelocityContext; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.common.constant.GenConstants; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.generator.domain.GenTable; +import com.ruoyi.generator.domain.GenTableColumn; + +/** + * 模板处理工具类 + * + * @author ruoyi + */ +public class VelocityUtils +{ + /** 项目空间路径 */ + private static final String PROJECT_PATH = "main/java"; + + /** mybatis空间路径 */ + private static final String MYBATIS_PATH = "main/resources/mapper"; + + /** 默认上级菜单,系统工具 */ + private static final String DEFAULT_PARENT_MENU_ID = "3"; + + /** Vue3 Element Plus 模版 */ + private static final String ELEMENT_PLUS = "element-plus"; + + /** Vue3 Element Plus TypeScript 模版 */ + private static final String ELEMENT_PLUS_TYPESSRIPT = "element-plus-typescript"; + + /** + * 设置模板变量信息 + * + * @return 模板列表 + */ + public static VelocityContext prepareContext(GenTable genTable) + { + String moduleName = genTable.getModuleName(); + String businessName = genTable.getBusinessName(); + String packageName = genTable.getPackageName(); + String tplCategory = genTable.getTplCategory(); + String functionName = genTable.getFunctionName(); + + VelocityContext velocityContext = new VelocityContext(); + velocityContext.put("tplCategory", genTable.getTplCategory()); + velocityContext.put("tableName", genTable.getTableName()); + velocityContext.put("functionName", StringUtils.isNotEmpty(functionName) ? functionName : "【请填写功能名称】"); + velocityContext.put("ClassName", genTable.getClassName()); + velocityContext.put("className", StringUtils.uncapitalize(genTable.getClassName())); + velocityContext.put("moduleName", genTable.getModuleName()); + velocityContext.put("BusinessName", StringUtils.capitalize(genTable.getBusinessName())); + velocityContext.put("businessName", genTable.getBusinessName()); + velocityContext.put("basePackage", getPackagePrefix(packageName)); + velocityContext.put("packageName", packageName); + velocityContext.put("author", genTable.getFunctionAuthor()); + velocityContext.put("colSpan", getColSpan(genTable.getFormColNum())); + velocityContext.put("datetime", DateUtils.getDate()); + velocityContext.put("pkColumn", genTable.getPkColumn()); + velocityContext.put("importList", getImportList(genTable)); + velocityContext.put("permissionPrefix", getPermissionPrefix(moduleName, businessName)); + velocityContext.put("columns", genTable.getColumns()); + velocityContext.put("table", genTable); + velocityContext.put("dicts", getDicts(genTable)); + setExtensionsContext(velocityContext, genTable.getOptions()); + setMenuVelocityContext(velocityContext, genTable); + if (GenConstants.TPL_TREE.equals(tplCategory)) + { + setTreeVelocityContext(velocityContext, genTable); + } + if (GenConstants.TPL_SUB.equals(tplCategory)) + { + setSubVelocityContext(velocityContext, genTable); + } + return velocityContext; + } + + public static void setExtensionsContext(VelocityContext context, String options) + { + JSONObject paramsObj = JSONObject.parseObject(options); + boolean genView = genView(paramsObj); + context.put("genView", genView); + } + + public static void setMenuVelocityContext(VelocityContext context, GenTable genTable) + { + String options = genTable.getOptions(); + JSONObject paramsObj = JSON.parseObject(options); + String parentMenuId = getParentMenuId(paramsObj); + context.put("parentMenuId", parentMenuId); + } + + public static void setTreeVelocityContext(VelocityContext context, GenTable genTable) + { + String options = genTable.getOptions(); + JSONObject paramsObj = JSON.parseObject(options); + String treeCode = getTreecode(paramsObj); + String treeParentCode = getTreeParentCode(paramsObj); + String treeName = getTreeName(paramsObj); + + context.put("treeCode", treeCode); + context.put("treeParentCode", treeParentCode); + context.put("treeName", treeName); + context.put("expandColumn", getExpandColumn(genTable)); + if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) + { + context.put("tree_parent_code", paramsObj.getString(GenConstants.TREE_PARENT_CODE)); + } + if (paramsObj.containsKey(GenConstants.TREE_NAME)) + { + context.put("tree_name", paramsObj.getString(GenConstants.TREE_NAME)); + } + } + + public static void setSubVelocityContext(VelocityContext context, GenTable genTable) + { + GenTable subTable = genTable.getSubTable(); + String subTableName = genTable.getSubTableName(); + String subTableFkName = genTable.getSubTableFkName(); + String subClassName = genTable.getSubTable().getClassName(); + String subTableFkClassName = StringUtils.convertToCamelCase(subTableFkName); + + context.put("subTable", subTable); + context.put("subTableName", subTableName); + context.put("subTableFkName", subTableFkName); + context.put("subTableFkClassName", subTableFkClassName); + context.put("subTableFkclassName", StringUtils.uncapitalize(subTableFkClassName)); + context.put("subClassName", subClassName); + context.put("subclassName", StringUtils.uncapitalize(subClassName)); + context.put("subImportList", getImportList(genTable.getSubTable())); + } + + /** + * 获取模板信息 + * @param tplCategory 生成的模板 + * @param tplWebType 前端类型 + * @return 模板列表 + */ + public static List getTemplateList(GenTable table) + { + String tplWebType = table.getTplWebType(); + String tplCategory = table.getTplCategory(); + JSONObject paramsObj = JSONObject.parseObject(table.getOptions()); + boolean isView = genView(paramsObj); + String useWebType = "vm/vue"; + String apiTemplate = "vm/js/api.js.vm"; + if (StringUtils.equals(ELEMENT_PLUS, tplWebType)) + { + useWebType = "vm/vue/v3"; + } + else if (StringUtils.equals(ELEMENT_PLUS_TYPESSRIPT, tplWebType)) + { + useWebType = "vm/vue/v3ts"; + apiTemplate = "vm/ts/api.ts.vm"; + } + List templates = new ArrayList(); + templates.add("vm/java/domain.java.vm"); + templates.add("vm/java/mapper.java.vm"); + templates.add("vm/java/service.java.vm"); + templates.add("vm/java/serviceImpl.java.vm"); + templates.add("vm/java/controller.java.vm"); + templates.add("vm/xml/mapper.xml.vm"); + templates.add("vm/sql/sql.vm"); + templates.add(apiTemplate); + if (StringUtils.equals(ELEMENT_PLUS_TYPESSRIPT, tplWebType)) + { + templates.add("vm/ts/type.ts.vm"); + templates.add("vm/ts/index.ts.vm"); + } + if (GenConstants.TPL_CRUD.equals(tplCategory)) + { + templates.add(useWebType + "/index.vue.vm"); + } + else if (GenConstants.TPL_TREE.equals(tplCategory)) + { + templates.add(useWebType + "/index-tree.vue.vm"); + } + else if (GenConstants.TPL_SUB.equals(tplCategory)) + { + templates.add(useWebType + "/index.vue.vm"); + templates.add("vm/java/sub-domain.java.vm"); + } + if (isView) + { + templates.add(useWebType + "/view.vue.vm"); + } + return templates; + } + + /** + * 获取文件名 + */ + public static String getFileName(String template, GenTable genTable) + { + // 文件名称 + String fileName = ""; + // 包路径 + String packageName = genTable.getPackageName(); + // 模块名 + String moduleName = genTable.getModuleName(); + // 大写类名 + String className = genTable.getClassName(); + // 业务名称 + String businessName = genTable.getBusinessName(); + + String javaPath = PROJECT_PATH + "/" + StringUtils.replace(packageName, ".", "/"); + String mybatisPath = MYBATIS_PATH + "/" + moduleName; + String vuePath = "vue"; + + if (template.contains("domain.java.vm")) + { + fileName = StringUtils.format("{}/domain/{}.java", javaPath, className); + } + if (template.contains("sub-domain.java.vm") && StringUtils.equals(GenConstants.TPL_SUB, genTable.getTplCategory())) + { + fileName = StringUtils.format("{}/domain/{}.java", javaPath, genTable.getSubTable().getClassName()); + } + else if (template.contains("mapper.java.vm")) + { + fileName = StringUtils.format("{}/mapper/{}Mapper.java", javaPath, className); + } + else if (template.contains("service.java.vm")) + { + fileName = StringUtils.format("{}/service/I{}Service.java", javaPath, className); + } + else if (template.contains("serviceImpl.java.vm")) + { + fileName = StringUtils.format("{}/service/impl/{}ServiceImpl.java", javaPath, className); + } + else if (template.contains("controller.java.vm")) + { + fileName = StringUtils.format("{}/controller/{}Controller.java", javaPath, className); + } + else if (template.contains("mapper.xml.vm")) + { + fileName = StringUtils.format("{}/{}Mapper.xml", mybatisPath, className); + } + else if (template.contains("sql.vm")) + { + fileName = businessName + "Menu.sql"; + } + else if (template.contains("api.js.vm")) + { + fileName = StringUtils.format("{}/api/{}/{}.js", vuePath, moduleName, businessName); + } + else if (template.contains("api.ts.vm")) + { + fileName = StringUtils.format("{}/api/{}/{}.ts", vuePath, moduleName, businessName); + } + else if (template.contains("type.ts.vm")) + { + fileName = StringUtils.format("{}/types/api/{}/{}.ts", vuePath, moduleName, businessName); + } + else if (template.contains("index.ts.vm")) + { + fileName = StringUtils.format("{}/types/api/index-bak.ts", vuePath); + } + else if (template.contains("index.vue.vm")) + { + fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName); + } + else if (template.contains("index-tree.vue.vm")) + { + fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName); + } + else if (template.contains("view.vue.vm")) + { + fileName = StringUtils.format("{}/views/{}/{}/view.vue", vuePath, moduleName, businessName); + } + return fileName; + } + + /** + * 获取包前缀 + * + * @param packageName 包名称 + * @return 包前缀名称 + */ + public static String getPackagePrefix(String packageName) + { + int lastIndex = packageName.lastIndexOf("."); + return StringUtils.substring(packageName, 0, lastIndex); + } + + /** + * 根据列类型获取导入包 + * + * @param genTable 业务表对象 + * @return 返回需要导入的包列表 + */ + public static HashSet getImportList(GenTable genTable) + { + List columns = genTable.getColumns(); + GenTable subGenTable = genTable.getSubTable(); + HashSet importList = new HashSet(); + if (StringUtils.isNotNull(subGenTable)) + { + importList.add("java.util.List"); + } + for (GenTableColumn column : columns) + { + if (!column.isSuperColumn() && GenConstants.TYPE_DATE.equals(column.getJavaType())) + { + importList.add("java.util.Date"); + importList.add("com.fasterxml.jackson.annotation.JsonFormat"); + } + else if (!column.isSuperColumn() && GenConstants.TYPE_BIGDECIMAL.equals(column.getJavaType())) + { + importList.add("java.math.BigDecimal"); + } + } + return importList; + } + + /** + * 根据列类型获取字典组 + * + * @param genTable 业务表对象 + * @return 返回字典组 + */ + public static String getDicts(GenTable genTable) + { + List columns = genTable.getColumns(); + Set dicts = new HashSet(); + addDicts(dicts, columns); + if (StringUtils.isNotNull(genTable.getSubTable())) + { + List subColumns = genTable.getSubTable().getColumns(); + addDicts(dicts, subColumns); + } + return StringUtils.join(dicts, ", "); + } + + /** + * 添加字典列表 + * + * @param dicts 字典列表 + * @param columns 列集合 + */ + public static void addDicts(Set dicts, List columns) + { + for (GenTableColumn column : columns) + { + if (!column.isSuperColumn() && StringUtils.isNotEmpty(column.getDictType()) && StringUtils.equalsAny( + column.getHtmlType(), + new String[] { GenConstants.HTML_SELECT, GenConstants.HTML_RADIO, GenConstants.HTML_CHECKBOX })) + { + dicts.add("'" + column.getDictType() + "'"); + } + } + } + + /** + * 获取权限前缀 + * + * @param moduleName 模块名称 + * @param businessName 业务名称 + * @return 返回权限前缀 + */ + public static String getPermissionPrefix(String moduleName, String businessName) + { + return StringUtils.format("{}:{}", moduleName, businessName); + } + + /** + * 获取上级菜单ID字段 + * + * @param paramsObj 生成其他选项 + * @return 上级菜单ID字段 + */ + public static String getParentMenuId(JSONObject paramsObj) + { + if (StringUtils.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.PARENT_MENU_ID) + && StringUtils.isNotEmpty(paramsObj.getString(GenConstants.PARENT_MENU_ID))) + { + return paramsObj.getString(GenConstants.PARENT_MENU_ID); + } + return DEFAULT_PARENT_MENU_ID; + } + + /** + * 获取树编码 + * + * @param paramsObj 生成其他选项 + * @return 树编码 + */ + public static String getTreecode(JSONObject paramsObj) + { + if (paramsObj.containsKey(GenConstants.TREE_CODE)) + { + return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_CODE)); + } + return StringUtils.EMPTY; + } + + /** + * 获取树父编码 + * + * @param paramsObj 生成其他选项 + * @return 树父编码 + */ + public static String getTreeParentCode(JSONObject paramsObj) + { + if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) + { + return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_PARENT_CODE)); + } + return StringUtils.EMPTY; + } + + /** + * 扩展功能/生成详情页 + * + * @param paramsObj 生成其他选项 + * @return 是否生成详细页 + */ + public static boolean genView(JSONObject paramsObj) + { + if (StringUtils.isNotNull(paramsObj) && paramsObj.containsKey(GenConstants.GEN_VIEW)) + { + return paramsObj.getBoolean(GenConstants.GEN_VIEW); + } + return false; + } + + /** + * 获取树名称 + * + * @param paramsObj 生成其他选项 + * @return 树名称 + */ + public static String getTreeName(JSONObject paramsObj) + { + if (paramsObj.containsKey(GenConstants.TREE_NAME)) + { + return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_NAME)); + } + return StringUtils.EMPTY; + } + + /** + * 获取需要在哪一列上面显示展开按钮 + * + * @param genTable 业务表对象 + * @return 展开按钮列序号 + */ + public static int getExpandColumn(GenTable genTable) + { + String options = genTable.getOptions(); + JSONObject paramsObj = JSON.parseObject(options); + String treeName = paramsObj.getString(GenConstants.TREE_NAME); + int num = 0; + for (GenTableColumn column : genTable.getColumns()) + { + if (column.isList()) + { + num++; + String columnName = column.getColumnName(); + if (columnName.equals(treeName)) + { + break; + } + } + } + return num; + } + + /** + * 获取表单 el-col span + * + * @param formColNum 表单布局方式(1单列 2双列 3三列) + * @return span 数值字符串 + */ + public static String getColSpan(int formColNum) + { + if (formColNum == 2) + { + return "12"; + } + else if (formColNum == 3) + { + return "8"; + } + return "24"; + } +} diff --git a/ruoyi-generator/src/main/resources/generator.yml b/ruoyi-generator/src/main/resources/generator.yml index d1cbf48..db66353 100644 --- a/ruoyi-generator/src/main/resources/generator.yml +++ b/ruoyi-generator/src/main/resources/generator.yml @@ -1,12 +1,12 @@ -# 代码生成 -gen: - # 作者 - author: ruoyi - # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool - packageName: com.ruoyi.system - # 自动去除表前缀,默认是false - autoRemovePre: false - # 表前缀(生成类名不会包含表前缀,多个用逗号分隔) - tablePrefix: sys_ - # 是否允许生成文件覆盖到本地(自定义路径),默认不允许 +# 代码生成 +gen: + # 作者 + author: ruoyi + # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool + packageName: com.ruoyi.system + # 自动去除表前缀,默认是false + autoRemovePre: false + # 表前缀(生成类名不会包含表前缀,多个用逗号分隔) + tablePrefix: sys_ + # 是否允许生成文件覆盖到本地(自定义路径),默认不允许 allowOverwrite: false \ No newline at end of file diff --git a/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml b/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml index 52857e8..efd0e70 100644 --- a/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml +++ b/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml @@ -1,127 +1,127 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - select column_id, table_id, column_name, column_comment, column_type, java_type, java_field, is_pk, is_increment, is_required, is_insert, is_edit, is_list, is_query, query_type, html_type, dict_type, sort, create_by, create_time, update_by, update_time from gen_table_column - - - - - - - - insert into gen_table_column ( - table_id, - column_name, - column_comment, - column_type, - java_type, - java_field, - is_pk, - is_increment, - is_required, - is_insert, - is_edit, - is_list, - is_query, - query_type, - html_type, - dict_type, - sort, - create_by, - create_time - )values( - #{tableId}, - #{columnName}, - #{columnComment}, - #{columnType}, - #{javaType}, - #{javaField}, - #{isPk}, - #{isIncrement}, - #{isRequired}, - #{isInsert}, - #{isEdit}, - #{isList}, - #{isQuery}, - #{queryType}, - #{htmlType}, - #{dictType}, - #{sort}, - #{createBy}, - sysdate() - ) - - - - update gen_table_column - - column_comment = #{columnComment}, - java_type = #{javaType}, - java_field = #{javaField}, - is_insert = #{isInsert}, - is_edit = #{isEdit}, - is_list = #{isList}, - is_query = #{isQuery}, - is_required = #{isRequired}, - query_type = #{queryType}, - html_type = #{htmlType}, - dict_type = #{dictType}, - sort = #{sort}, - update_by = #{updateBy}, - update_time = sysdate() - - where column_id = #{columnId} - - - - delete from gen_table_column where table_id in - - #{tableId} - - - - - delete from gen_table_column where column_id in - - #{item.columnId} - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select column_id, table_id, column_name, column_comment, column_type, java_type, java_field, is_pk, is_increment, is_required, is_insert, is_edit, is_list, is_query, query_type, html_type, dict_type, sort, create_by, create_time, update_by, update_time from gen_table_column + + + + + + + + insert into gen_table_column ( + table_id, + column_name, + column_comment, + column_type, + java_type, + java_field, + is_pk, + is_increment, + is_required, + is_insert, + is_edit, + is_list, + is_query, + query_type, + html_type, + dict_type, + sort, + create_by, + create_time + )values( + #{tableId}, + #{columnName}, + #{columnComment}, + #{columnType}, + #{javaType}, + #{javaField}, + #{isPk}, + #{isIncrement}, + #{isRequired}, + #{isInsert}, + #{isEdit}, + #{isList}, + #{isQuery}, + #{queryType}, + #{htmlType}, + #{dictType}, + #{sort}, + #{createBy}, + sysdate() + ) + + + + update gen_table_column + + column_comment = #{columnComment}, + java_type = #{javaType}, + java_field = #{javaField}, + is_insert = #{isInsert}, + is_edit = #{isEdit}, + is_list = #{isList}, + is_query = #{isQuery}, + is_required = #{isRequired}, + query_type = #{queryType}, + html_type = #{htmlType}, + dict_type = #{dictType}, + sort = #{sort}, + update_by = #{updateBy}, + update_time = sysdate() + + where column_id = #{columnId} + + + + delete from gen_table_column where table_id in + + #{tableId} + + + + + delete from gen_table_column where column_id in + + #{item.columnId} + + + \ No newline at end of file diff --git a/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml b/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml index d1110f7..9d5a967 100644 --- a/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml +++ b/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml @@ -1,210 +1,214 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - select table_id, table_name, table_comment, sub_table_name, sub_table_fk_name, class_name, tpl_category, tpl_web_type, package_name, module_name, business_name, function_name, function_author, gen_type, gen_path, options, create_by, create_time, update_by, update_time, remark from gen_table - - - - - - - - - - - - - - - - - - insert into gen_table ( - table_name, - table_comment, - class_name, - tpl_category, - tpl_web_type, - package_name, - module_name, - business_name, - function_name, - function_author, - gen_type, - gen_path, - remark, - create_by, - create_time - )values( - #{tableName}, - #{tableComment}, - #{className}, - #{tplCategory}, - #{tplWebType}, - #{packageName}, - #{moduleName}, - #{businessName}, - #{functionName}, - #{functionAuthor}, - #{genType}, - #{genPath}, - #{remark}, - #{createBy}, - sysdate() - ) - - - - ${sql} - - - - update gen_table - - table_name = #{tableName}, - table_comment = #{tableComment}, - sub_table_name = #{subTableName}, - sub_table_fk_name = #{subTableFkName}, - class_name = #{className}, - function_author = #{functionAuthor}, - gen_type = #{genType}, - gen_path = #{genPath}, - tpl_category = #{tplCategory}, - tpl_web_type = #{tplWebType}, - package_name = #{packageName}, - module_name = #{moduleName}, - business_name = #{businessName}, - function_name = #{functionName}, - options = #{options}, - update_by = #{updateBy}, - remark = #{remark}, - update_time = sysdate() - - where table_id = #{tableId} - - - - delete from gen_table where table_id in - - #{tableId} - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select table_id, table_name, table_comment, sub_table_name, sub_table_fk_name, class_name, tpl_category, tpl_web_type, package_name, module_name, business_name, function_name, function_author, form_col_num, gen_type, gen_path, options, create_by, create_time, update_by, update_time, remark from gen_table + + + + + + + + + + + + + + + + + + insert into gen_table ( + table_name, + table_comment, + class_name, + tpl_category, + tpl_web_type, + package_name, + module_name, + business_name, + function_name, + function_author, + form_col_num, + gen_type, + gen_path, + remark, + create_by, + create_time + )values( + #{tableName}, + #{tableComment}, + #{className}, + #{tplCategory}, + #{tplWebType}, + #{packageName}, + #{moduleName}, + #{businessName}, + #{functionName}, + #{functionAuthor}, + #{formColNum}, + #{genType}, + #{genPath}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + ${sql} + + + + update gen_table + + table_name = #{tableName}, + table_comment = #{tableComment}, + sub_table_name = #{subTableName}, + sub_table_fk_name = #{subTableFkName}, + class_name = #{className}, + function_author = #{functionAuthor}, + form_col_num = #{formColNum}, + gen_type = #{genType}, + gen_path = #{genPath}, + tpl_category = #{tplCategory}, + tpl_web_type = #{tplWebType}, + package_name = #{packageName}, + module_name = #{moduleName}, + business_name = #{businessName}, + function_name = #{functionName}, + options = #{options}, + update_by = #{updateBy}, + remark = #{remark}, + update_time = sysdate() + + where table_id = #{tableId} + + + + delete from gen_table where table_id in + + #{tableId} + + + \ No newline at end of file diff --git a/ruoyi-generator/src/main/resources/vm/java/controller.java.vm b/ruoyi-generator/src/main/resources/vm/java/controller.java.vm index b8457a3..bf88988 100644 --- a/ruoyi-generator/src/main/resources/vm/java/controller.java.vm +++ b/ruoyi-generator/src/main/resources/vm/java/controller.java.vm @@ -1,7 +1,7 @@ package ${packageName}.controller; import java.util.List; -import jakarta.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpServletResponse; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; diff --git a/ruoyi-generator/src/main/resources/vm/java/domain.java.vm b/ruoyi-generator/src/main/resources/vm/java/domain.java.vm index b236ef0..4535e84 100644 --- a/ruoyi-generator/src/main/resources/vm/java/domain.java.vm +++ b/ruoyi-generator/src/main/resources/vm/java/domain.java.vm @@ -1,105 +1,105 @@ -package ${packageName}.domain; - -#foreach ($import in $importList) -import ${import}; -#end -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; -import com.ruoyi.common.annotation.Excel; -#if($table.crud || $table.sub) -import com.ruoyi.common.core.domain.BaseEntity; -#elseif($table.tree) -import com.ruoyi.common.core.domain.TreeEntity; -#end - -/** - * ${functionName}对象 ${tableName} - * - * @author ${author} - * @date ${datetime} - */ -#if($table.crud || $table.sub) -#set($Entity="BaseEntity") -#elseif($table.tree) -#set($Entity="TreeEntity") -#end -public class ${ClassName} extends ${Entity} -{ - private static final long serialVersionUID = 1L; - -#foreach ($column in $columns) -#if(!$table.isSuperColumn($column.javaField)) - /** $column.columnComment */ -#if($column.list) -#set($parentheseIndex=$column.columnComment.indexOf("(")) -#if($parentheseIndex != -1) -#set($comment=$column.columnComment.substring(0, $parentheseIndex)) -#else -#set($comment=$column.columnComment) -#end -#if($parentheseIndex != -1) - @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") -#elseif($column.javaType == 'Date') - @JsonFormat(pattern = "yyyy-MM-dd") - @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd") -#else - @Excel(name = "${comment}") -#end -#end - private $column.javaType $column.javaField; - -#end -#end -#if($table.sub) - /** $table.subTable.functionName信息 */ - private List<${subClassName}> ${subclassName}List; - -#end -#foreach ($column in $columns) -#if(!$table.isSuperColumn($column.javaField)) -#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) -#set($AttrName=$column.javaField) -#else -#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) -#end - public void set${AttrName}($column.javaType $column.javaField) - { - this.$column.javaField = $column.javaField; - } - - public $column.javaType get${AttrName}() - { - return $column.javaField; - } - -#end -#end -#if($table.sub) - public List<${subClassName}> get${subClassName}List() - { - return ${subclassName}List; - } - - public void set${subClassName}List(List<${subClassName}> ${subclassName}List) - { - this.${subclassName}List = ${subclassName}List; - } - -#end - @Override - public String toString() { - return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) -#foreach ($column in $columns) -#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) -#set($AttrName=$column.javaField) -#else -#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) -#end - .append("${column.javaField}", get${AttrName}()) -#end -#if($table.sub) - .append("${subclassName}List", get${subClassName}List()) -#end - .toString(); - } -} +package ${packageName}.domain; + +#foreach ($import in $importList) +import ${import}; +#end +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +#if($table.crud || $table.sub) +import com.ruoyi.common.core.domain.BaseEntity; +#elseif($table.tree) +import com.ruoyi.common.core.domain.TreeEntity; +#end + +/** + * ${functionName}对象 ${tableName} + * + * @author ${author} + * @date ${datetime} + */ +#if($table.crud || $table.sub) +#set($Entity="BaseEntity") +#elseif($table.tree) +#set($Entity="TreeEntity") +#end +public class ${ClassName} extends ${Entity} +{ + private static final long serialVersionUID = 1L; + +#foreach ($column in $columns) +#if(!$table.isSuperColumn($column.javaField)) + /** $column.columnComment */ +#if($column.list) +#set($parentheseIndex=$column.columnComment.indexOf("(")) +#if($parentheseIndex != -1) +#set($comment=$column.columnComment.substring(0, $parentheseIndex)) +#else +#set($comment=$column.columnComment) +#end +#if($parentheseIndex != -1) + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") +#elseif($column.javaType == 'Date') + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd") +#else + @Excel(name = "${comment}") +#end +#end + private $column.javaType $column.javaField; + +#end +#end +#if($table.sub) + /** $table.subTable.functionName信息 */ + private List<${subClassName}> ${subclassName}List; + +#end +#foreach ($column in $columns) +#if(!$table.isSuperColumn($column.javaField)) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + public void set${AttrName}($column.javaType $column.javaField) + { + this.$column.javaField = $column.javaField; + } + + public $column.javaType get${AttrName}() + { + return $column.javaField; + } + +#end +#end +#if($table.sub) + public List<${subClassName}> get${subClassName}List() + { + return ${subclassName}List; + } + + public void set${subClassName}List(List<${subClassName}> ${subclassName}List) + { + this.${subclassName}List = ${subclassName}List; + } + +#end + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) +#foreach ($column in $columns) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + .append("${column.javaField}", get${AttrName}()) +#end +#if($table.sub) + .append("${subclassName}List", get${subClassName}List()) +#end + .toString(); + } +} diff --git a/ruoyi-generator/src/main/resources/vm/java/service.java.vm b/ruoyi-generator/src/main/resources/vm/java/service.java.vm index 264882b..250f68c 100644 --- a/ruoyi-generator/src/main/resources/vm/java/service.java.vm +++ b/ruoyi-generator/src/main/resources/vm/java/service.java.vm @@ -1,61 +1,61 @@ -package ${packageName}.service; - -import java.util.List; -import ${packageName}.domain.${ClassName}; - -/** - * ${functionName}Service接口 - * - * @author ${author} - * @date ${datetime} - */ -public interface I${ClassName}Service -{ - /** - * 查询${functionName} - * - * @param ${pkColumn.javaField} ${functionName}主键 - * @return ${functionName} - */ - public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); - - /** - * 查询${functionName}列表 - * - * @param ${className} ${functionName} - * @return ${functionName}集合 - */ - public List<${ClassName}> select${ClassName}List(${ClassName} ${className}); - - /** - * 新增${functionName} - * - * @param ${className} ${functionName} - * @return 结果 - */ - public int insert${ClassName}(${ClassName} ${className}); - - /** - * 修改${functionName} - * - * @param ${className} ${functionName} - * @return 结果 - */ - public int update${ClassName}(${ClassName} ${className}); - - /** - * 批量删除${functionName} - * - * @param ${pkColumn.javaField}s 需要删除的${functionName}主键集合 - * @return 结果 - */ - public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s); - - /** - * 删除${functionName}信息 - * - * @param ${pkColumn.javaField} ${functionName}主键 - * @return 结果 - */ - public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); -} +package ${packageName}.service; + +import java.util.List; +import ${packageName}.domain.${ClassName}; + +/** + * ${functionName}Service接口 + * + * @author ${author} + * @date ${datetime} + */ +public interface I${ClassName}Service +{ + /** + * 查询${functionName} + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return ${functionName} + */ + public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); + + /** + * 查询${functionName}列表 + * + * @param ${className} ${functionName} + * @return ${functionName}集合 + */ + public List<${ClassName}> select${ClassName}List(${ClassName} ${className}); + + /** + * 新增${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ + public int insert${ClassName}(${ClassName} ${className}); + + /** + * 修改${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ + public int update${ClassName}(${ClassName} ${className}); + + /** + * 批量删除${functionName} + * + * @param ${pkColumn.javaField}s 需要删除的${functionName}主键集合 + * @return 结果 + */ + public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s); + + /** + * 删除${functionName}信息 + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return 结果 + */ + public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); +} diff --git a/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm b/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm index 14746e1..116f6c5 100644 --- a/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm +++ b/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm @@ -1,169 +1,169 @@ -package ${packageName}.service.impl; - -import java.util.List; -#foreach ($column in $columns) -#if($column.javaField == 'createTime' || $column.javaField == 'updateTime') -import com.ruoyi.common.utils.DateUtils; -#break -#end -#end -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -#if($table.sub) -import java.util.ArrayList; -import com.ruoyi.common.utils.StringUtils; -import org.springframework.transaction.annotation.Transactional; -import ${packageName}.domain.${subClassName}; -#end -import ${packageName}.mapper.${ClassName}Mapper; -import ${packageName}.domain.${ClassName}; -import ${packageName}.service.I${ClassName}Service; - -/** - * ${functionName}Service业务层处理 - * - * @author ${author} - * @date ${datetime} - */ -@Service -public class ${ClassName}ServiceImpl implements I${ClassName}Service -{ - @Autowired - private ${ClassName}Mapper ${className}Mapper; - - /** - * 查询${functionName} - * - * @param ${pkColumn.javaField} ${functionName}主键 - * @return ${functionName} - */ - @Override - public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}) - { - return ${className}Mapper.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField}); - } - - /** - * 查询${functionName}列表 - * - * @param ${className} ${functionName} - * @return ${functionName} - */ - @Override - public List<${ClassName}> select${ClassName}List(${ClassName} ${className}) - { - return ${className}Mapper.select${ClassName}List(${className}); - } - - /** - * 新增${functionName} - * - * @param ${className} ${functionName} - * @return 结果 - */ -#if($table.sub) - @Transactional -#end - @Override - public int insert${ClassName}(${ClassName} ${className}) - { -#foreach ($column in $columns) -#if($column.javaField == 'createTime') - ${className}.setCreateTime(DateUtils.getNowDate()); -#end -#end -#if($table.sub) - int rows = ${className}Mapper.insert${ClassName}(${className}); - insert${subClassName}(${className}); - return rows; -#else - return ${className}Mapper.insert${ClassName}(${className}); -#end - } - - /** - * 修改${functionName} - * - * @param ${className} ${functionName} - * @return 结果 - */ -#if($table.sub) - @Transactional -#end - @Override - public int update${ClassName}(${ClassName} ${className}) - { -#foreach ($column in $columns) -#if($column.javaField == 'updateTime') - ${className}.setUpdateTime(DateUtils.getNowDate()); -#end -#end -#if($table.sub) - ${className}Mapper.delete${subClassName}By${subTableFkClassName}(${className}.get${pkColumn.capJavaField}()); - insert${subClassName}(${className}); -#end - return ${className}Mapper.update${ClassName}(${className}); - } - - /** - * 批量删除${functionName} - * - * @param ${pkColumn.javaField}s 需要删除的${functionName}主键 - * @return 结果 - */ -#if($table.sub) - @Transactional -#end - @Override - public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s) - { -#if($table.sub) - ${className}Mapper.delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaField}s); -#end - return ${className}Mapper.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s); - } - - /** - * 删除${functionName}信息 - * - * @param ${pkColumn.javaField} ${functionName}主键 - * @return 结果 - */ -#if($table.sub) - @Transactional -#end - @Override - public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}) - { -#if($table.sub) - ${className}Mapper.delete${subClassName}By${subTableFkClassName}(${pkColumn.javaField}); -#end - return ${className}Mapper.delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField}); - } -#if($table.sub) - - /** - * 新增${subTable.functionName}信息 - * - * @param ${className} ${functionName}对象 - */ - public void insert${subClassName}(${ClassName} ${className}) - { - List<${subClassName}> ${subclassName}List = ${className}.get${subClassName}List(); - ${pkColumn.javaType} ${pkColumn.javaField} = ${className}.get${pkColumn.capJavaField}(); - if (StringUtils.isNotNull(${subclassName}List)) - { - List<${subClassName}> list = new ArrayList<${subClassName}>(); - for (${subClassName} ${subclassName} : ${subclassName}List) - { - ${subclassName}.set${subTableFkClassName}(${pkColumn.javaField}); - list.add(${subclassName}); - } - if (list.size() > 0) - { - ${className}Mapper.batch${subClassName}(list); - } - } - } -#end -} +package ${packageName}.service.impl; + +import java.util.List; +#foreach ($column in $columns) +#if($column.javaField == 'createTime' || $column.javaField == 'updateTime') +import com.ruoyi.common.utils.DateUtils; +#break +#end +#end +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +#if($table.sub) +import java.util.ArrayList; +import com.ruoyi.common.utils.StringUtils; +import org.springframework.transaction.annotation.Transactional; +import ${packageName}.domain.${subClassName}; +#end +import ${packageName}.mapper.${ClassName}Mapper; +import ${packageName}.domain.${ClassName}; +import ${packageName}.service.I${ClassName}Service; + +/** + * ${functionName}Service业务层处理 + * + * @author ${author} + * @date ${datetime} + */ +@Service +public class ${ClassName}ServiceImpl implements I${ClassName}Service +{ + @Autowired + private ${ClassName}Mapper ${className}Mapper; + + /** + * 查询${functionName} + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return ${functionName} + */ + @Override + public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}) + { + return ${className}Mapper.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField}); + } + + /** + * 查询${functionName}列表 + * + * @param ${className} ${functionName} + * @return ${functionName} + */ + @Override + public List<${ClassName}> select${ClassName}List(${ClassName} ${className}) + { + return ${className}Mapper.select${ClassName}List(${className}); + } + + /** + * 新增${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int insert${ClassName}(${ClassName} ${className}) + { +#foreach ($column in $columns) +#if($column.javaField == 'createTime') + ${className}.setCreateTime(DateUtils.getNowDate()); +#end +#end +#if($table.sub) + int rows = ${className}Mapper.insert${ClassName}(${className}); + insert${subClassName}(${className}); + return rows; +#else + return ${className}Mapper.insert${ClassName}(${className}); +#end + } + + /** + * 修改${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int update${ClassName}(${ClassName} ${className}) + { +#foreach ($column in $columns) +#if($column.javaField == 'updateTime') + ${className}.setUpdateTime(DateUtils.getNowDate()); +#end +#end +#if($table.sub) + ${className}Mapper.delete${subClassName}By${subTableFkClassName}(${className}.get${pkColumn.capJavaField}()); + insert${subClassName}(${className}); +#end + return ${className}Mapper.update${ClassName}(${className}); + } + + /** + * 批量删除${functionName} + * + * @param ${pkColumn.javaField}s 需要删除的${functionName}主键 + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s) + { +#if($table.sub) + ${className}Mapper.delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaField}s); +#end + return ${className}Mapper.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s); + } + + /** + * 删除${functionName}信息 + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}) + { +#if($table.sub) + ${className}Mapper.delete${subClassName}By${subTableFkClassName}(${pkColumn.javaField}); +#end + return ${className}Mapper.delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField}); + } +#if($table.sub) + + /** + * 新增${subTable.functionName}信息 + * + * @param ${className} ${functionName}对象 + */ + public void insert${subClassName}(${ClassName} ${className}) + { + List<${subClassName}> ${subclassName}List = ${className}.get${subClassName}List(); + ${pkColumn.javaType} ${pkColumn.javaField} = ${className}.get${pkColumn.capJavaField}(); + if (StringUtils.isNotNull(${subclassName}List)) + { + List<${subClassName}> list = new ArrayList<${subClassName}>(); + for (${subClassName} ${subclassName} : ${subclassName}List) + { + ${subclassName}.set${subTableFkClassName}(${pkColumn.javaField}); + list.add(${subclassName}); + } + if (list.size() > 0) + { + ${className}Mapper.batch${subClassName}(list); + } + } + } +#end +} diff --git a/ruoyi-generator/src/main/resources/vm/java/sub-domain.java.vm b/ruoyi-generator/src/main/resources/vm/java/sub-domain.java.vm index a3f53eb..b5e308c 100644 --- a/ruoyi-generator/src/main/resources/vm/java/sub-domain.java.vm +++ b/ruoyi-generator/src/main/resources/vm/java/sub-domain.java.vm @@ -1,76 +1,76 @@ -package ${packageName}.domain; - -#foreach ($import in $subImportList) -import ${import}; -#end -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; -import com.ruoyi.common.annotation.Excel; -import com.ruoyi.common.core.domain.BaseEntity; - -/** - * ${subTable.functionName}对象 ${subTableName} - * - * @author ${author} - * @date ${datetime} - */ -public class ${subClassName} extends BaseEntity -{ - private static final long serialVersionUID = 1L; - -#foreach ($column in $subTable.columns) -#if(!$table.isSuperColumn($column.javaField)) - /** $column.columnComment */ -#if($column.list) -#set($parentheseIndex=$column.columnComment.indexOf("(")) -#if($parentheseIndex != -1) -#set($comment=$column.columnComment.substring(0, $parentheseIndex)) -#else -#set($comment=$column.columnComment) -#end -#if($parentheseIndex != -1) - @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") -#elseif($column.javaType == 'Date') - @JsonFormat(pattern = "yyyy-MM-dd") - @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd") -#else - @Excel(name = "${comment}") -#end -#end - private $column.javaType $column.javaField; - -#end -#end -#foreach ($column in $subTable.columns) -#if(!$table.isSuperColumn($column.javaField)) -#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) -#set($AttrName=$column.javaField) -#else -#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) -#end - public void set${AttrName}($column.javaType $column.javaField) - { - this.$column.javaField = $column.javaField; - } - - public $column.javaType get${AttrName}() - { - return $column.javaField; - } -#end -#end - - @Override - public String toString() { - return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) -#foreach ($column in $subTable.columns) -#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) -#set($AttrName=$column.javaField) -#else -#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) -#end - .append("${column.javaField}", get${AttrName}()) -#end - .toString(); - } -} +package ${packageName}.domain; + +#foreach ($import in $subImportList) +import ${import}; +#end +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * ${subTable.functionName}对象 ${subTableName} + * + * @author ${author} + * @date ${datetime} + */ +public class ${subClassName} extends BaseEntity +{ + private static final long serialVersionUID = 1L; + +#foreach ($column in $subTable.columns) +#if(!$table.isSuperColumn($column.javaField)) + /** $column.columnComment */ +#if($column.list) +#set($parentheseIndex=$column.columnComment.indexOf("(")) +#if($parentheseIndex != -1) +#set($comment=$column.columnComment.substring(0, $parentheseIndex)) +#else +#set($comment=$column.columnComment) +#end +#if($parentheseIndex != -1) + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") +#elseif($column.javaType == 'Date') + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd") +#else + @Excel(name = "${comment}") +#end +#end + private $column.javaType $column.javaField; + +#end +#end +#foreach ($column in $subTable.columns) +#if(!$table.isSuperColumn($column.javaField)) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + public void set${AttrName}($column.javaType $column.javaField) + { + this.$column.javaField = $column.javaField; + } + + public $column.javaType get${AttrName}() + { + return $column.javaField; + } +#end +#end + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) +#foreach ($column in $subTable.columns) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + .append("${column.javaField}", get${AttrName}()) +#end + .toString(); + } +} diff --git a/ruoyi-generator/src/main/resources/vm/js/api.js.vm b/ruoyi-generator/src/main/resources/vm/js/api.js.vm index 9295524..d78cd2f 100644 --- a/ruoyi-generator/src/main/resources/vm/js/api.js.vm +++ b/ruoyi-generator/src/main/resources/vm/js/api.js.vm @@ -1,44 +1,44 @@ -import request from '@/utils/request' - -// 查询${functionName}列表 -export function list${BusinessName}(query) { - return request({ - url: '/${moduleName}/${businessName}/list', - method: 'get', - params: query - }) -} - -// 查询${functionName}详细 -export function get${BusinessName}(${pkColumn.javaField}) { - return request({ - url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, - method: 'get' - }) -} - -// 新增${functionName} -export function add${BusinessName}(data) { - return request({ - url: '/${moduleName}/${businessName}', - method: 'post', - data: data - }) -} - -// 修改${functionName} -export function update${BusinessName}(data) { - return request({ - url: '/${moduleName}/${businessName}', - method: 'put', - data: data - }) -} - -// 删除${functionName} -export function del${BusinessName}(${pkColumn.javaField}) { - return request({ - url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, - method: 'delete' - }) -} +import request from '@/utils/request' + +// 查询${functionName}列表 +export function list${BusinessName}(query) { + return request({ + url: '/${moduleName}/${businessName}/list', + method: 'get', + params: query + }) +} + +// 查询${functionName}详细 +export function get${BusinessName}(${pkColumn.javaField}) { + return request({ + url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, + method: 'get' + }) +} + +// 新增${functionName} +export function add${BusinessName}(data) { + return request({ + url: '/${moduleName}/${businessName}', + method: 'post', + data: data + }) +} + +// 修改${functionName} +export function update${BusinessName}(data) { + return request({ + url: '/${moduleName}/${businessName}', + method: 'put', + data: data + }) +} + +// 删除${functionName} +export function del${BusinessName}(${pkColumn.javaField}) { + return request({ + url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, + method: 'delete' + }) +} diff --git a/ruoyi-generator/src/main/resources/vm/sql/sql.vm b/ruoyi-generator/src/main/resources/vm/sql/sql.vm index 0575583..b50ef21 100644 --- a/ruoyi-generator/src/main/resources/vm/sql/sql.vm +++ b/ruoyi-generator/src/main/resources/vm/sql/sql.vm @@ -1,22 +1,22 @@ --- 菜单 SQL -insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -values('${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', sysdate(), '', null, '${functionName}菜单'); - --- 按钮父菜单ID -SELECT @parentId := LAST_INSERT_ID(); - --- 按钮 SQL -insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -values('${functionName}查询', @parentId, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 'admin', sysdate(), '', null, ''); - -insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -values('${functionName}新增', @parentId, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 'admin', sysdate(), '', null, ''); - -insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -values('${functionName}修改', @parentId, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 'admin', sysdate(), '', null, ''); - -insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -values('${functionName}删除', @parentId, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 'admin', sysdate(), '', null, ''); - -insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +-- 菜单 SQL +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', sysdate(), '', null, '${functionName}菜单'); + +-- 按钮父菜单ID +SELECT @parentId := LAST_INSERT_ID(); + +-- 按钮 SQL +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}查询', @parentId, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}新增', @parentId, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}修改', @parentId, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}删除', @parentId, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) values('${functionName}导出', @parentId, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 'admin', sysdate(), '', null, ''); \ No newline at end of file diff --git a/ruoyi-generator/src/main/resources/vm/ts/api.ts.vm b/ruoyi-generator/src/main/resources/vm/ts/api.ts.vm new file mode 100644 index 0000000..8ed5122 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/ts/api.ts.vm @@ -0,0 +1,51 @@ +import request from '@/utils/request' +import type { AjaxResult, TableDataInfo, ${BusinessName}QueryParams, ${ClassName} } from '@/types' + +// 查询${functionName}列表 +#if($table.tree) +export function list${BusinessName}(query?: ${BusinessName}QueryParams): Promise> { +#else +export function list${BusinessName}(query: ${BusinessName}QueryParams): Promise> { +#end + return request({ + url: '/${moduleName}/${businessName}/list', + method: 'get', + params: query + }) +} + +// 查询${functionName}详细 +export function get${BusinessName}(${pkColumn.javaField}: number): Promise> { + return request({ + url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, + method: 'get' + }) +} + +// 新增${functionName} +export function add${BusinessName}(data: ${ClassName}): Promise { + return request({ + url: '/${moduleName}/${businessName}', + method: 'post', + data: data + }) +} + +// 修改${functionName} +export function update${BusinessName}(data: ${ClassName}): Promise { + return request({ + url: '/${moduleName}/${businessName}', + method: 'put', + data: data + }) +} + +// 删除${functionName} +export function del${BusinessName}(${pkColumn.javaField}: number | number[]): Promise { + return request({ + url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, + method: 'delete' + }) +} + + diff --git a/ruoyi-generator/src/main/resources/vm/ts/index.ts.vm b/ruoyi-generator/src/main/resources/vm/ts/index.ts.vm new file mode 100644 index 0000000..e51ef15 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/ts/index.ts.vm @@ -0,0 +1,9 @@ +/** + * API 类型统一导出 + */ +.... + +// 防止覆盖,需手动追加下面代码到index.ts文件中,追加好后此文件可删除 + +// ${moduleName} 模块 +export * from "./${moduleName}/${businessName}"; \ No newline at end of file diff --git a/ruoyi-generator/src/main/resources/vm/ts/type.ts.vm b/ruoyi-generator/src/main/resources/vm/ts/type.ts.vm new file mode 100644 index 0000000..fff1d4c --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/ts/type.ts.vm @@ -0,0 +1,51 @@ +import type { PageDomain, BaseEntity } from "../common"; + +/** ${functionName}配置分页查询参数 */ +export interface ${BusinessName}QueryParams extends PageDomain { +#foreach($column in $columns) +#if($column.query) +#set($type = "string") +#if($column.javaType == "Long" || $column.javaType == "Integer") + #set($type = "number") +#elseif($column.javaType == "Boolean") + #set($type = "boolean") +#end + /** ${column.columnComment} */ + ${column.javaField}?: ${type}; +#end +#end +} + +/** ${functionName}配置信息 */ +export interface ${ClassName} extends BaseEntity { +#foreach($column in $columns) +#set($type = "string") +#if($column.javaType == "Long" || $column.javaType == "Integer") + #set($type = "number") +#elseif($column.javaType == "Boolean") + #set($type = "boolean") +#end + /** ${column.columnComment} */ + ${column.javaField}?: ${type}; +#end +#if($table.sub) + /** $table.subTable.functionName信息 */ + ${subclassName}List?: ${subClassName}[]; +#end +} +#if($table.sub) + +/** ${subTable.functionName}配置信息 */ +export interface ${subClassName} extends BaseEntity { +#foreach ($column in $subTable.columns) +#set($type = "string") +#if($column.javaType == "Long" || $column.javaType == "Integer") + #set($type = "number") +#elseif($column.javaType == "Boolean") + #set($type = "boolean") +#end + /** ${column.columnComment} */ + ${column.javaField}?: ${type}; +#end +} +#end diff --git a/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm index 4e35fc9..b26d70d 100644 --- a/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm +++ b/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm @@ -1,505 +1,565 @@ - - - + + + diff --git a/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm index 04ebe16..007c545 100644 --- a/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm +++ b/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm @@ -1,602 +1,660 @@ - - - + + + diff --git a/ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm index fabd921..51e2673 100644 --- a/ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm +++ b/ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm @@ -136,6 +136,9 @@ #end - - - - + + + + + + + \ No newline at end of file diff --git a/ruoyi-ui/src/components/TopNav/index.vue b/ruoyi-ui/src/layout/components/TopNav/index.vue similarity index 100% rename from ruoyi-ui/src/components/TopNav/index.vue rename to ruoyi-ui/src/layout/components/TopNav/index.vue diff --git a/ruoyi-ui/src/layout/components/index.js b/ruoyi-ui/src/layout/components/index.js index 104bd3a..7f83004 100644 --- a/ruoyi-ui/src/layout/components/index.js +++ b/ruoyi-ui/src/layout/components/index.js @@ -1,5 +1,5 @@ -export { default as AppMain } from './AppMain' -export { default as Navbar } from './Navbar' -export { default as Settings } from './Settings' -export { default as Sidebar } from './Sidebar/index.vue' -export { default as TagsView } from './TagsView/index.vue' +export { default as AppMain } from './AppMain' +export { default as Navbar } from './Navbar' +export { default as Settings } from './Settings' +export { default as Sidebar } from './Sidebar/index.vue' +export { default as TagsView } from './TagsView/index.vue' diff --git a/ruoyi-ui/src/layout/index.vue b/ruoyi-ui/src/layout/index.vue index e8b7c38..532edfe 100644 --- a/ruoyi-ui/src/layout/index.vue +++ b/ruoyi-ui/src/layout/index.vue @@ -1,5 +1,5 @@ @@ -159,58 +159,17 @@ @pagination="getList" /> - - - - - - {{ form.title }} / {{ typeFormat(form) }} - {{ form.operName }} / {{ form.operIp }} / {{ form.operLocation }} - - - {{ form.operUrl }} - {{ form.requestMethod }} - - - {{ form.method }} - - - {{ form.operParam }} - - - {{ form.jsonResult }} - - - -
正常
-
失败
-
-
- - {{ form.costTime }}毫秒 - - - {{ parseTime(form.operTime) }} - - - {{ form.errorMsg }} - -
-
- -
+ - - + + + + + diff --git a/ruoyi-ui/src/views/system/config/index.vue b/ruoyi-ui/src/views/system/config/index.vue index a0794e1..beb6c3a 100644 --- a/ruoyi-ui/src/views/system/config/index.vue +++ b/ruoyi-ui/src/views/system/config/index.vue @@ -283,7 +283,7 @@ export default { // 多选框选中数据 handleSelectionChange(selection) { this.ids = selection.map(item => item.configId) - this.single = selection.length!=1 + this.single = selection.length != 1 this.multiple = !selection.length }, /** 修改按钮操作 */ @@ -297,17 +297,17 @@ export default { }) }, /** 提交按钮 */ - submitForm: function() { + submitForm() { this.$refs["form"].validate(valid => { if (valid) { if (this.form.configId != undefined) { - updateConfig(this.form).then(response => { + updateConfig(this.form).then(() => { this.$modal.msgSuccess("修改成功") this.open = false this.getList() }) } else { - addConfig(this.form).then(response => { + addConfig(this.form).then(() => { this.$modal.msgSuccess("新增成功") this.open = false this.getList() diff --git a/ruoyi-ui/src/views/system/dept/index.vue b/ruoyi-ui/src/views/system/dept/index.vue index 088f09d..3d41ee3 100644 --- a/ruoyi-ui/src/views/system/dept/index.vue +++ b/ruoyi-ui/src/views/system/dept/index.vue @@ -36,6 +36,16 @@ v-hasPermi="['system:dept:add']" >新增 + + 保存排序 + - + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/system/dict/detail.vue b/ruoyi-ui/src/views/system/dict/detail.vue new file mode 100644 index 0000000..b1e3dc9 --- /dev/null +++ b/ruoyi-ui/src/views/system/dict/detail.vue @@ -0,0 +1,203 @@ + + + + + diff --git a/ruoyi-ui/src/views/system/dict/index.vue b/ruoyi-ui/src/views/system/dict/index.vue index ada7e07..c63ceb0 100644 --- a/ruoyi-ui/src/views/system/dict/index.vue +++ b/ruoyi-ui/src/views/system/dict/index.vue @@ -1,347 +1,375 @@ - - - \ No newline at end of file diff --git a/ruoyi-ui/src/views/system/menu/index.vue b/ruoyi-ui/src/views/system/menu/index.vue index b61cf6f..0255d0e 100644 --- a/ruoyi-ui/src/views/system/menu/index.vue +++ b/ruoyi-ui/src/views/system/menu/index.vue @@ -36,6 +36,16 @@ v-hasPermi="['system:menu:add']" >新增 + + 保存排序 + - - + - - - + + + + + + + + - - - @@ -299,7 +317,7 @@ \ No newline at end of file diff --git a/ruoyi-ui/src/views/system/role/index.vue b/ruoyi-ui/src/views/system/role/index.vue index ab01a4a..1b50e66 100644 --- a/ruoyi-ui/src/views/system/role/index.vue +++ b/ruoyi-ui/src/views/system/role/index.vue @@ -1,605 +1,605 @@ - - - \ No newline at end of file diff --git a/ruoyi-ui/src/views/system/role/selectUser.vue b/ruoyi-ui/src/views/system/role/selectUser.vue index d2b21bf..b44880d 100644 --- a/ruoyi-ui/src/views/system/role/selectUser.vue +++ b/ruoyi-ui/src/views/system/role/selectUser.vue @@ -1,136 +1,136 @@ - - - + + + diff --git a/ruoyi-ui/src/views/system/user/authRole.vue b/ruoyi-ui/src/views/system/user/authRole.vue index 1f862d7..de0807d 100644 --- a/ruoyi-ui/src/views/system/user/authRole.vue +++ b/ruoyi-ui/src/views/system/user/authRole.vue @@ -1,123 +1,123 @@ - - - \ No newline at end of file diff --git a/ruoyi-ui/src/views/system/user/index.vue b/ruoyi-ui/src/views/system/user/index.vue index ba5ac3c..7d3f940 100644 --- a/ruoyi-ui/src/views/system/user/index.vue +++ b/ruoyi-ui/src/views/system/user/index.vue @@ -1,565 +1,476 @@ - - - \ No newline at end of file + + + diff --git a/ruoyi-ui/src/views/system/user/profile/index.vue b/ruoyi-ui/src/views/system/user/profile/index.vue index c070cd1..97c0b64 100644 --- a/ruoyi-ui/src/views/system/user/profile/index.vue +++ b/ruoyi-ui/src/views/system/user/profile/index.vue @@ -1,95 +1,95 @@ - - - + + + diff --git a/ruoyi-ui/src/views/system/user/profile/resetPwd.vue b/ruoyi-ui/src/views/system/user/profile/resetPwd.vue index 954d341..acfdff1 100644 --- a/ruoyi-ui/src/views/system/user/profile/resetPwd.vue +++ b/ruoyi-ui/src/views/system/user/profile/resetPwd.vue @@ -1,69 +1,69 @@ - - - + + + diff --git a/ruoyi-ui/src/views/system/user/profile/userInfo.vue b/ruoyi-ui/src/views/system/user/profile/userInfo.vue index 486adfc..5e66632 100644 --- a/ruoyi-ui/src/views/system/user/profile/userInfo.vue +++ b/ruoyi-ui/src/views/system/user/profile/userInfo.vue @@ -1,88 +1,88 @@ - - - + + + diff --git a/ruoyi-ui/src/views/system/user/view.vue b/ruoyi-ui/src/views/system/user/view.vue new file mode 100644 index 0000000..04726c0 --- /dev/null +++ b/ruoyi-ui/src/views/system/user/view.vue @@ -0,0 +1,177 @@ + + + diff --git a/ruoyi-ui/src/views/tool/build/DraggableItem.vue b/ruoyi-ui/src/views/tool/build/DraggableItem.vue index e881778..f669ac0 100644 --- a/ruoyi-ui/src/views/tool/build/DraggableItem.vue +++ b/ruoyi-ui/src/views/tool/build/DraggableItem.vue @@ -1,100 +1,100 @@ - + diff --git a/ruoyi-ui/src/views/tool/build/IconsDialog.vue b/ruoyi-ui/src/views/tool/build/IconsDialog.vue index 958be50..0d12778 100644 --- a/ruoyi-ui/src/views/tool/build/IconsDialog.vue +++ b/ruoyi-ui/src/views/tool/build/IconsDialog.vue @@ -1,123 +1,123 @@ - - - + + + diff --git a/ruoyi-ui/src/views/tool/build/RightPanel.vue b/ruoyi-ui/src/views/tool/build/RightPanel.vue index c2760eb..863d74c 100644 --- a/ruoyi-ui/src/views/tool/build/RightPanel.vue +++ b/ruoyi-ui/src/views/tool/build/RightPanel.vue @@ -1,946 +1,946 @@ - - - - - + + + + + diff --git a/ruoyi-ui/src/views/tool/gen/basicInfoForm.vue b/ruoyi-ui/src/views/tool/gen/basicInfoForm.vue index 3a6e2ed..d086a65 100644 --- a/ruoyi-ui/src/views/tool/gen/basicInfoForm.vue +++ b/ruoyi-ui/src/views/tool/gen/basicInfoForm.vue @@ -1,60 +1,60 @@ - - - + + + diff --git a/ruoyi-ui/src/views/tool/gen/createTable.vue b/ruoyi-ui/src/views/tool/gen/createTable.vue index e71996c..dc71092 100644 --- a/ruoyi-ui/src/views/tool/gen/createTable.vue +++ b/ruoyi-ui/src/views/tool/gen/createTable.vue @@ -32,7 +32,7 @@ export default { this.$modal.msgError("请输入建表语句") return } - createTable({ sql: this.content }).then(res => { + createTable({ sql: this.content, tplWebType: 'element-ui' }).then(res => { this.$modal.msgSuccess(res.msg) if (res.code === 200) { this.visible = false diff --git a/ruoyi-ui/src/views/tool/gen/editTable.vue b/ruoyi-ui/src/views/tool/gen/editTable.vue index 512f66e..e8371a1 100644 --- a/ruoyi-ui/src/views/tool/gen/editTable.vue +++ b/ruoyi-ui/src/views/tool/gen/editTable.vue @@ -183,6 +183,7 @@ export default { const genTable = Object.assign({}, basicForm.model, genForm.model) genTable.columns = this.columns genTable.params = { + genView: genTable.view ? '1' : '0', treeCode: genTable.treeCode, treeName: genTable.treeName, treeParentCode: genTable.treeParentCode, diff --git a/ruoyi-ui/src/views/tool/gen/genInfoForm.vue b/ruoyi-ui/src/views/tool/gen/genInfoForm.vue index 60d9e87..3c3015f 100644 --- a/ruoyi-ui/src/views/tool/gen/genInfoForm.vue +++ b/ruoyi-ui/src/views/tool/gen/genInfoForm.vue @@ -1,312 +1,336 @@ - - - + + + diff --git a/ruoyi-ui/src/views/tool/gen/importTable.vue b/ruoyi-ui/src/views/tool/gen/importTable.vue index cb080ce..89ff6ee 100644 --- a/ruoyi-ui/src/views/tool/gen/importTable.vue +++ b/ruoyi-ui/src/views/tool/gen/importTable.vue @@ -1,120 +1,120 @@ - - - + + + diff --git a/ruoyi-ui/src/views/tool/gen/index.vue b/ruoyi-ui/src/views/tool/gen/index.vue index 9859722..05bd735 100644 --- a/ruoyi-ui/src/views/tool/gen/index.vue +++ b/ruoyi-ui/src/views/tool/gen/index.vue @@ -98,9 +98,9 @@ {{(queryParams.pageNum - 1) * queryParams.pageSize + scope.$index + 1}} - - - + + + @@ -180,6 +180,7 @@ hljs.registerLanguage("xml", require("highlight.js/lib/languages/xml")) hljs.registerLanguage("html", require("highlight.js/lib/languages/xml")) hljs.registerLanguage("vue", require("highlight.js/lib/languages/xml")) hljs.registerLanguage("javascript", require("highlight.js/lib/languages/javascript")) +hljs.registerLanguage("typescript", require("highlight.js/lib/languages/typescript")) hljs.registerLanguage("sql", require("highlight.js/lib/languages/sql")) export default { @@ -262,7 +263,7 @@ export default { return } if(row.genType === "1") { - genCode(row.tableName).then(response => { + genCode(row.tableName).then(() => { this.$modal.msgSuccess("成功生成到自定义路径:" + row.genPath) }) } else { diff --git a/ruoyi-ui/src/views/tool/swagger/index.vue b/ruoyi-ui/src/views/tool/swagger/index.vue index dd6b4a2..622a40d 100644 --- a/ruoyi-ui/src/views/tool/swagger/index.vue +++ b/ruoyi-ui/src/views/tool/swagger/index.vue @@ -1,15 +1,15 @@ - - + + diff --git a/ruoyi-ui/tests/login-default-credentials.test.js b/ruoyi-ui/tests/login-default-credentials.test.js deleted file mode 100644 index 39bf690..0000000 --- a/ruoyi-ui/tests/login-default-credentials.test.js +++ /dev/null @@ -1,30 +0,0 @@ -const fs = require('fs') -const path = require('path') -const assert = require('assert') - -const loginViewSource = fs.readFileSync( - path.join(__dirname, '../src/views/login.vue'), - 'utf8' -) - -assert( - /loginForm:\s*\{[\s\S]*username:\s*""/.test(loginViewSource), - '登录页默认用户名应为空字符串' -) - -assert( - /loginForm:\s*\{[\s\S]*password:\s*""/.test(loginViewSource), - '登录页默认密码应为空字符串' -) - -assert( - /username:\s*username === undefined \? this\.loginForm\.username : username/.test(loginViewSource), - '登录页应继续支持从 cookie 回填用户名' -) - -assert( - /password:\s*password === undefined \? this\.loginForm\.password : decrypt\(password\)/.test(loginViewSource), - '登录页应继续支持从 cookie 回填密码' -) - -console.log('login default credentials assertions passed') diff --git a/ruoyi-ui/tests/personal-final-calculate-rate-display.test.js b/ruoyi-ui/tests/personal-final-calculate-rate-display.test.js deleted file mode 100644 index e1791e5..0000000 --- a/ruoyi-ui/tests/personal-final-calculate-rate-display.test.js +++ /dev/null @@ -1,21 +0,0 @@ -const fs = require('fs') -const path = require('path') -const assert = require('assert') - -function read(relativePath) { - return fs.readFileSync(path.join(__dirname, '..', relativePath), 'utf8') -} - -const personalDetail = read('src/views/loanPricing/workflow/components/PersonalWorkflowDetail.vue') - -assert( - /label="最终测算利率"/.test(personalDetail), - '个人流程详情左侧缺少“最终测算利率”标签' -) - -assert( - /return this\.retailOutput\?\.finalCalculateRate \|\| '-'/.test(personalDetail), - '个人流程详情没有使用 finalCalculateRate 展示最终测算利率' -) - -console.log('personal final calculate rate display assertions passed') diff --git a/ruoyi-ui/tests/workflow-detail-card-order.test.js b/ruoyi-ui/tests/workflow-detail-card-order.test.js deleted file mode 100644 index 442db7a..0000000 --- a/ruoyi-ui/tests/workflow-detail-card-order.test.js +++ /dev/null @@ -1,27 +0,0 @@ -const fs = require('fs') -const path = require('path') -const assert = require('assert') - -function read(relativePath) { - return fs.readFileSync(path.join(__dirname, '..', relativePath), 'utf8') -} - -function assertModelOutputBeforeDetailCard(source, label) { - const modelOutputIndex = source.indexOf('') - - assert(modelOutputIndex !== -1, `${label} 缺少模型输出卡片`) - assert(detailCardIndex !== -1, `${label} 缺少流程详情卡片`) - assert( - modelOutputIndex < detailCardIndex, - `${label} 的模型输出卡片应位于流程详情卡片上方` - ) -} - -const personalDetail = read('src/views/loanPricing/workflow/components/PersonalWorkflowDetail.vue') -const corporateDetail = read('src/views/loanPricing/workflow/components/CorporateWorkflowDetail.vue') - -assertModelOutputBeforeDetailCard(personalDetail, '个人流程详情') -assertModelOutputBeforeDetailCard(corporateDetail, '企业流程详情') - -console.log('workflow detail card order assertions passed') diff --git a/ruoyi-ui/tests/workflow-index-refresh.test.js b/ruoyi-ui/tests/workflow-index-refresh.test.js deleted file mode 100644 index b049447..0000000 --- a/ruoyi-ui/tests/workflow-index-refresh.test.js +++ /dev/null @@ -1,52 +0,0 @@ -const assert = require('assert') -const fs = require('fs') -const path = require('path') -const vm = require('vm') - -function loadComponentOptions(filePath) { - const source = fs.readFileSync(filePath, 'utf8') - const scriptMatch = source.match(/