AgileBoot V1.0 发布 基于Ruoyi改造 基本都已重构

This commit is contained in:
valarchie 2022-10-08 13:50:29 +08:00
parent e3349d3575
commit 41cf60b3d8
286 changed files with 18581 additions and 0 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
custom: http://doc.ruoyi.vip/ruoyi-vue/other/donate.html

48
.gitignore vendored Normal file
View File

@ -0,0 +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/
######################################################################
# Others
*.log
*.xml.versionsBackup
*.swp
!*/build/*.java
!*/build/*.html
!*/build/*.xml
/agileboot-admin/src/main/resources/application-dev.yml

567
GoogleStyle.xml Normal file
View File

@ -0,0 +1,567 @@
<code_scheme name="GoogleStyle" version="173">
<option name="OTHER_INDENT_OPTIONS">
<value>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="TAB_SIZE" value="2" />
</value>
</option>
<option name="RIGHT_MARGIN" value="100" />
<AndroidXmlCodeStyleSettings>
<option name="USE_CUSTOM_SETTINGS" value="true" />
<option name="LAYOUT_SETTINGS">
<value>
<option name="INSERT_BLANK_LINE_BEFORE_TAG" value="false" />
</value>
</option>
</AndroidXmlCodeStyleSettings>
<JSCodeStyleSettings version="0">
<option name="INDENT_CHAINED_CALLS" value="false" />
</JSCodeStyleSettings>
<JavaCodeStyleSettings>
<option name="INSERT_INNER_CLASS_IMPORTS" value="true" />
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="999" />
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="999" />
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
<value />
</option>
<option name="IMPORT_LAYOUT_TABLE">
<value>
<package name="" withSubpackages="true" static="true" />
<emptyLine />
<package name="" withSubpackages="true" static="false" />
</value>
</option>
<option name="JD_ALIGN_PARAM_COMMENTS" value="false" />
<option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false" />
<option name="JD_P_AT_EMPTY_LINES" value="false" />
<option name="JD_KEEP_EMPTY_PARAMETER" value="false" />
<option name="JD_KEEP_EMPTY_EXCEPTION" value="false" />
<option name="JD_KEEP_EMPTY_RETURN" value="false" />
</JavaCodeStyleSettings>
<Objective-C>
<option name="INDENT_NAMESPACE_MEMBERS" value="0" />
<option name="INDENT_C_STRUCT_MEMBERS" value="2" />
<option name="INDENT_CLASS_MEMBERS" value="2" />
<option name="INDENT_VISIBILITY_KEYWORDS" value="1" />
<option name="INDENT_INSIDE_CODE_BLOCK" value="2" />
<option name="KEEP_STRUCTURES_IN_ONE_LINE" value="true" />
<option name="FUNCTION_PARAMETERS_WRAP" value="5" />
<option name="FUNCTION_CALL_ARGUMENTS_WRAP" value="5" />
<option name="TEMPLATE_CALL_ARGUMENTS_WRAP" value="5" />
<option name="TEMPLATE_CALL_ARGUMENTS_ALIGN_MULTILINE" value="true" />
<option name="ALIGN_INIT_LIST_IN_COLUMNS" value="false" />
<option name="SPACE_BEFORE_SUPERCLASS_COLON" value="false" />
</Objective-C>
<Objective-C-extensions>
<option name="GENERATE_INSTANCE_VARIABLES_FOR_PROPERTIES" value="ASK" />
<option name="RELEASE_STYLE" value="IVAR" />
<option name="TYPE_QUALIFIERS_PLACEMENT" value="BEFORE" />
<file>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
</file>
<class>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
</class>
<extensions>
<pair source="cc" header="h" />
<pair source="c" header="h" />
</extensions>
</Objective-C-extensions>
<Python>
<option name="USE_CONTINUATION_INDENT_FOR_ARGUMENTS" value="true" />
</Python>
<TypeScriptCodeStyleSettings version="0">
<option name="INDENT_CHAINED_CALLS" value="false" />
</TypeScriptCodeStyleSettings>
<XML>
<option name="XML_ALIGN_ATTRIBUTES" value="false" />
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML>
<codeStyleSettings language="CSS">
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="ECMA Script Level 4">
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
<option name="ALIGN_MULTILINE_FOR" value="false" />
<option name="CALL_PARAMETERS_WRAP" value="1" />
<option name="METHOD_PARAMETERS_WRAP" value="1" />
<option name="EXTENDS_LIST_WRAP" value="1" />
<option name="BINARY_OPERATION_WRAP" value="1" />
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
<option name="TERNARY_OPERATION_WRAP" value="1" />
<option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
<option name="FOR_STATEMENT_WRAP" value="1" />
<option name="ARRAY_INITIALIZER_WRAP" value="1" />
<option name="IF_BRACE_FORCE" value="3" />
<option name="DOWHILE_BRACE_FORCE" value="3" />
<option name="WHILE_BRACE_FORCE" value="3" />
<option name="FOR_BRACE_FORCE" value="3" />
</codeStyleSettings>
<codeStyleSettings language="HTML">
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="JAVA">
<option name="RIGHT_MARGIN" value="120" />
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
<option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" />
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
<option name="ALIGN_MULTILINE_RESOURCES" value="false" />
<option name="ALIGN_MULTILINE_FOR" value="false" />
<option name="CALL_PARAMETERS_WRAP" value="1" />
<option name="METHOD_PARAMETERS_WRAP" value="1" />
<option name="EXTENDS_LIST_WRAP" value="1" />
<option name="THROWS_KEYWORD_WRAP" value="1" />
<option name="METHOD_CALL_CHAIN_WRAP" value="1" />
<option name="BINARY_OPERATION_WRAP" value="1" />
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
<option name="TERNARY_OPERATION_WRAP" value="1" />
<option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
<option name="FOR_STATEMENT_WRAP" value="1" />
<option name="ARRAY_INITIALIZER_WRAP" value="1" />
<option name="WRAP_COMMENTS" value="true" />
<option name="IF_BRACE_FORCE" value="3" />
<option name="DOWHILE_BRACE_FORCE" value="3" />
<option name="WHILE_BRACE_FORCE" value="3" />
<option name="FOR_BRACE_FORCE" value="3" />
<option name="WRAP_ON_TYPING" value="0" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="JSON">
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="JavaScript">
<option name="RIGHT_MARGIN" value="80" />
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
<option name="ALIGN_MULTILINE_FOR" value="false" />
<option name="CALL_PARAMETERS_WRAP" value="1" />
<option name="METHOD_PARAMETERS_WRAP" value="1" />
<option name="BINARY_OPERATION_WRAP" value="1" />
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
<option name="TERNARY_OPERATION_WRAP" value="1" />
<option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
<option name="FOR_STATEMENT_WRAP" value="1" />
<option name="ARRAY_INITIALIZER_WRAP" value="1" />
<option name="IF_BRACE_FORCE" value="3" />
<option name="DOWHILE_BRACE_FORCE" value="3" />
<option name="WHILE_BRACE_FORCE" value="3" />
<option name="FOR_BRACE_FORCE" value="3" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="ObjectiveC">
<option name="RIGHT_MARGIN" value="80" />
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1" />
<option name="BLANK_LINES_BEFORE_IMPORTS" value="0" />
<option name="BLANK_LINES_AFTER_IMPORTS" value="0" />
<option name="BLANK_LINES_AROUND_CLASS" value="0" />
<option name="BLANK_LINES_AROUND_METHOD" value="0" />
<option name="BLANK_LINES_AROUND_METHOD_IN_INTERFACE" value="0" />
<option name="ALIGN_MULTILINE_BINARY_OPERATION" value="false" />
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
<option name="FOR_STATEMENT_WRAP" value="1" />
<option name="ASSIGNMENT_WRAP" value="1" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="PROTO">
<option name="RIGHT_MARGIN" value="80" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="Python">
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
<option name="RIGHT_MARGIN" value="80" />
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
<option name="PARENT_SETTINGS_INSTALLED" value="true" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="SASS">
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="SCSS">
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="TypeScript">
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="XML">
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:.*Style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_width</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_height</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_weight</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_margin</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_marginTop</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_marginBottom</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_marginStart</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_marginEnd</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_marginLeft</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_marginRight</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:padding</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:paddingTop</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:paddingBottom</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:paddingStart</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:paddingEnd</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:paddingLeft</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:paddingRight</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res-auto</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>http://schemas.android.com/tools</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="protobuf">
<option name="RIGHT_MARGIN" value="80" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
</code_scheme>

103
README.md Normal file
View File

@ -0,0 +1,103 @@
<p align="center">
<img alt="logo" src="https://oscimg.oschina.net/oscnet/up-d3d0a9303e11d522a06cd263f3079027715.png">
</p>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v3.8.2</h1>
<h4 align="center">基于SpringBoot+Vue前后端分离的Java快速开发框架</h4>
<p align="center">
<a href="https://gitee.com/y_project/RuoYi-Vue/stargazers"><img src="https://gitee.com/y_project/RuoYi-Vue/badge/star.svg?theme=dark"></a>
<a href="https://gitee.com/y_project/RuoYi-Vue"><img src="https://img.shields.io/badge/RuoYi-v3.8.2-brightgreen.svg"></a>
<a href="https://gitee.com/y_project/RuoYi-Vue/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a>
</p>
## 平台简介
AgileBoot是一套全部开源的快速开发平台毫无保留给个人及企业免费使用。
* 前端采用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://github.com/yangzongzhuan/RuoYi-Vue3),保持同步更新。
* 提供了单应用版本[RuoYi-Vue-fast](https://github.com/yangzongzhuan/RuoYi-Vue-fast)Oracle版本[RuoYi-Vue-Oracle](https://github.com/yangzongzhuan/RuoYi-Vue-Oracle),保持同步更新。
* 不分离版本,请移步[RuoYi](https://gitee.com/y_project/RuoYi),微服务版本,请移步[RuoYi-Cloud](https://gitee.com/y_project/RuoYi-Cloud)
* 特别鸣谢:[element](https://github.com/ElemeFE/element)[vue-element-admin](https://github.com/PanJiaChen/vue-element-admin)[eladmin-web](https://github.com/elunez/eladmin-web)。
* 阿里云折扣场:[点我进入](http://aly.ruoyi.vip),腾讯云秒杀场:[点我进入](http://txy.ruoyi.vip)&nbsp;&nbsp;
* 阿里云优惠券:[点我领取](https://www.aliyun.com/minisite/goods?userCode=brki8iof&share_source=copy_link),腾讯云优惠券:[点我领取](https://cloud.tencent.com/redirect.php?redirect=1025&cps_key=198c8df2ed259157187173bc7f4f32fd&from=console)&nbsp;&nbsp;
## 内置功能
1. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
2. 部门管理:配置系统组织机构(公司、部门、小组),树结构展现支持数据权限。
3. 岗位管理:配置系统用户所属担任职务。
4. 菜单管理:配置系统菜单,操作权限,按钮权限标识等。
5. 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。
6. 字典管理:对系统中经常使用的一些较为固定的数据进行维护。
7. 参数管理:对系统动态配置常用参数。
8. 通知公告:系统通知公告信息发布维护。
9. 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
10. 登录日志:系统登录日志记录查询包含登录异常。
11. 在线用户:当前系统中活跃用户状态监控。
12. 定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。
13. 代码生成前后端代码的生成java、html、xml、sql支持CRUD下载 。
14. 系统接口根据业务代码自动生成相关的api接口文档。
15. 服务监控监视当前系统CPU、内存、磁盘、堆栈等相关信息。
16. 缓存监控:对系统的缓存信息查询,命令统计等。
17. 在线构建器拖动表单元素生成相应的HTML代码。
18. 连接池监视监视当前系统数据库连接池状态可进行分析SQL找出系统性能瓶颈。
## 在线体验
- admin/admin123
- 陆陆续续收到一些打赏,为了更好的体验已用于演示服务器升级。谢谢各位小伙伴。
演示地址http://vue.ruoyi.vip
文档地址http://doc.ruoyi.vip
## 演示图
<table>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/cd1f90be5f2684f4560c9519c0f2a232ee8.jpg"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/1cbcf0e6f257c7d3a063c0e3f2ff989e4b3.jpg"/></td>
</tr>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/up-8074972883b5ba0622e13246738ebba237a.png"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/up-9f88719cdfca9af2e58b352a20e23d43b12.png"/></td>
</tr>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/up-39bf2584ec3a529b0d5a3b70d15c9b37646.png"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/up-936ec82d1f4872e1bc980927654b6007307.png"/></td>
</tr>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/up-b2d62ceb95d2dd9b3fbe157bb70d26001e9.png"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/up-d67451d308b7a79ad6819723396f7c3d77a.png"/></td>
</tr>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/5e8c387724954459291aafd5eb52b456f53.jpg"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/644e78da53c2e92a95dfda4f76e6d117c4b.jpg"/></td>
</tr>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/up-8370a0d02977eebf6dbf854c8450293c937.png"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/up-49003ed83f60f633e7153609a53a2b644f7.png"/></td>
</tr>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/up-d4fe726319ece268d4746602c39cffc0621.png"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/up-c195234bbcd30be6927f037a6755e6ab69c.png"/></td>
</tr>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/b6115bc8c31de52951982e509930b20684a.jpg"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/up-5e4daac0bb59612c5038448acbcef235e3a.png"/></td>
</tr>
</table>
## AG前后端分离交流群
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) 点击按钮入群。
TODO
plan to do encrypt request and response. reference:https://github.com/ishuibo/rsa-encrypt-body-spring-boot
### 如果老是出现项目中能找到包 但是编译的时候却找不到 可以运行 mvn clean install

58
agileboot-admin/pom.xml Normal file
View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>agileboot</artifactId>
<groupId>com.agileboot</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>agileboot-admin</artifactId>
<description>
web服务入口
</description>
<dependencies>
<!-- spring-boot-devtools -->
<!--<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
&lt;!&ndash; 表示依赖不会传递 &ndash;&gt;
<optional>true</optional>
</dependency>-->
<!-- Mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 核心模块-->
<dependency>
<groupId>com.agileboot</groupId>
<artifactId>agileboot-infrastructure</artifactId>
</dependency>
<!-- 定时任务-->
<dependency>
<groupId>com.agileboot</groupId>
<artifactId>agileboot-api</artifactId>
</dependency>
<!-- 代码生成-->
<dependency>
<groupId>com.agileboot</groupId>
<artifactId>agileboot-domain</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,123 @@
package com.agileboot.admin.controller.common;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.util.StrUtil;
import com.agileboot.admin.response.UploadDTO;
import com.agileboot.common.config.AgileBootConfig;
import com.agileboot.common.core.dto.ResponseDTO;
import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode;
import com.agileboot.common.utils.ServletHolderUtil;
import com.agileboot.common.utils.file.FileUploadUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
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;
/**
* 通用请求处理
*
* @author valarchie
*/
@RestController
@RequestMapping("/file")
@Slf4j
public class FileController {
/**
* 通用下载请求
*
* @param fileName 文件名称
*/
@GetMapping("/download")
public ResponseEntity<byte[]> fileDownload(String fileName, HttpServletResponse response) {
try {
if (!FileUploadUtils.isAllowDownload(fileName)) {
throw new Exception(StrUtil.format("文件名称({})非法,不允许下载。 ", fileName));
}
String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);
String filePath = AgileBootConfig.getDownloadPath() + fileName;
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
HttpHeaders headers = new HttpHeaders();
headers.set("Content-Disposition", String.format("attachment;filename=%s", realFileName));
return new ResponseEntity<>(FileUtil.readBytes(filePath), headers, HttpStatus.OK);
} catch (Exception e) {
log.error("下载文件失败", e);
return null;
}
}
/**
* 通用上传请求单个
*/
@PostMapping("/upload")
public ResponseDTO<UploadDTO> uploadFile(MultipartFile file) throws IOException {
if (file == null) {
throw new ApiException(ErrorCode.Business.UPLOAD_FILE_IS_EMPTY);
}
// 上传文件路径
String filePath = AgileBootConfig.getUploadPath();
// 上传并返回新文件名称
String fileName = FileUploadUtils.upload(filePath, file);
String url = ServletHolderUtil.getContextUrl() + fileName;
UploadDTO uploadDTO = UploadDTO.builder()
.url(url)
.fileName(fileName)
.newFileName(FileNameUtil.getName(fileName))
.originalFilename(file.getOriginalFilename()).build();
return ResponseDTO.ok(uploadDTO);
}
/**
* 通用上传请求多个
*/
@PostMapping("/uploads")
public ResponseDTO<List<UploadDTO>> uploadFiles(List<MultipartFile> files) throws Exception {
if (CollUtil.isEmpty(files)) {
throw new ApiException(ErrorCode.Business.UPLOAD_FILE_IS_EMPTY);
}
// 上传文件路径
String filePath = AgileBootConfig.getUploadPath();
List<UploadDTO> uploads = new ArrayList<>();
for (MultipartFile file : files) {
if (file != null) {
// 上传并返回新文件名称
String fileName = FileUploadUtils.upload(filePath, file);
String url = ServletHolderUtil.getContextUrl() + fileName;
UploadDTO uploadDTO = UploadDTO.builder()
.url(url)
.fileName(fileName)
.newFileName(FileNameUtil.getName(fileName))
.originalFilename(file.getOriginalFilename()).build();
uploads.add(uploadDTO);
}
}
return ResponseDTO.ok(uploads);
}
}

View File

@ -0,0 +1,120 @@
package com.agileboot.admin.controller.common;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.agileboot.admin.request.LoginDTO;
import com.agileboot.admin.request.RegisterDTO;
import com.agileboot.admin.response.UserPermissionDTO;
import com.agileboot.common.config.AgileBootConfig;
import com.agileboot.common.constant.Constants.Token;
import com.agileboot.common.core.dto.ResponseDTO;
import com.agileboot.common.exception.error.ErrorCode.Business;
import com.agileboot.domain.system.menu.MenuDomainService;
import com.agileboot.domain.system.menu.RouterVo;
import com.agileboot.domain.system.user.UserDTO;
import com.agileboot.infrastructure.cache.map.MapCache;
import com.agileboot.infrastructure.web.domain.login.CaptchaDTO;
import com.agileboot.infrastructure.web.domain.login.LoginUser;
import com.agileboot.infrastructure.web.service.LoginService;
import com.agileboot.infrastructure.web.util.AuthenticationUtils;
import java.util.List;
import java.util.Map;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 首页
*
* @author valarchie
*/
@RestController
public class LoginController {
private final LoginService loginService;
private final MenuDomainService menuDomainService;
/**
* 系统基础配置
*/
private final AgileBootConfig agileBootConfig;
public LoginController(LoginService loginService,
MenuDomainService menuDomainService, AgileBootConfig agileBootConfig) {
this.loginService = loginService;
this.menuDomainService = menuDomainService;
this.agileBootConfig = agileBootConfig;
}
/**
* 访问首页提示语
*/
@RequestMapping("/")
public String index() {
return StrUtil.format("欢迎使用{}后台管理框架当前版本v{},请通过前端地址访问。",
agileBootConfig.getName(), agileBootConfig.getVersion());
}
/**
* 生成验证码
*/
@GetMapping("/captchaImage")
public ResponseDTO<CaptchaDTO> getCaptchaImg() {
CaptchaDTO captchaImg = loginService.getCaptchaImg();
return ResponseDTO.ok(captchaImg);
}
/**
* 登录方法
*
* @param loginDTO 登录信息
* @return 结果
*/
@PostMapping("/login")
public ResponseDTO<Map> login(@RequestBody LoginDTO loginDTO) {
// 生成令牌
String token = loginService.login(loginDTO.getUsername(), loginDTO.getPassword(), loginDTO.getCode(),
loginDTO.getUuid());
return ResponseDTO.ok(MapUtil.of(Token.TOKEN_FIELD, token));
}
/**
* 获取用户信息
*
* @return 用户信息
*/
@GetMapping("/getLoginUserInfo")
public ResponseDTO getLoginUserInfo() {
LoginUser loginUser = AuthenticationUtils.getLoginUser();
UserPermissionDTO permissionDTO = new UserPermissionDTO();
permissionDTO.setUser(new UserDTO(loginUser.getEntity()));
permissionDTO.setRoleKey(loginUser.getRoleKey());
permissionDTO.setPermissions(loginUser.getMenuPermissions());
permissionDTO.setDictTypes(MapCache.dictionaryCache());
return ResponseDTO.ok(permissionDTO);
}
/**
* 获取路由信息
*
* @return 路由信息
*/
@GetMapping("/getRouters")
public ResponseDTO<List<RouterVo>> getRouters() {
Long userId = AuthenticationUtils.getUserId();
List<RouterVo> routerTree = menuDomainService.getRouterTree(userId);
return ResponseDTO.ok(routerTree);
}
@PostMapping("/register")
public ResponseDTO register(@RequestBody RegisterDTO user) {
return ResponseDTO.fail(Business.UNSUPPORTED_OPERATION);
}
}

View File

@ -0,0 +1,77 @@
package com.agileboot.admin.controller.monitor;
import com.agileboot.common.core.base.BaseController;
import com.agileboot.common.core.dto.ResponseDTO;
import com.agileboot.common.core.page.PageDTO;
import com.agileboot.domain.system.monitor.MonitorDomainService;
import com.agileboot.domain.system.monitor.dto.RedisCacheInfoDTO;
import com.agileboot.infrastructure.annotations.AccessLog;
import com.agileboot.infrastructure.cache.redis.RedisCacheService;
import com.agileboot.infrastructure.web.domain.OnlineUser;
import com.agileboot.infrastructure.web.domain.server.ServerInfo;
import com.agileboot.orm.enums.BusinessType;
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;
/**
* 缓存监控
*
* @author ruoyi
*/
@RestController
@RequestMapping("/monitor")
public class MonitorController extends BaseController {
@Autowired
private MonitorDomainService monitorDomainService;
@Autowired
private RedisCacheService redisCacheService;
@PreAuthorize("@ss.hasPerm('monitor:cache:list')")
@GetMapping("/cacheInfo")
public ResponseDTO<RedisCacheInfoDTO> getRedisCacheInfo() {
RedisCacheInfoDTO redisCacheInfo = monitorDomainService.getRedisCacheInfo();
return ResponseDTO.ok(redisCacheInfo);
}
@PreAuthorize("@ss.hasPerm('monitor:server:list')")
@GetMapping("/serverInfo")
public ResponseDTO<ServerInfo> getServerInfo() {
ServerInfo serverInfo = monitorDomainService.getServerInfo();
return ResponseDTO.ok(serverInfo);
}
/**
* 获取在线用户列表
* @param ipaddr
* @param userName
* @return
*/
@PreAuthorize("@ss.hasPerm('monitor:online:list')")
@GetMapping("/onlineUser/list")
public ResponseDTO<PageDTO> list(String ipaddr, String userName) {
List<OnlineUser> onlineUserList = monitorDomainService.getOnlineUserList(userName, ipaddr);
return ResponseDTO.ok(new PageDTO(onlineUserList));
}
/**
* 强退用户
*/
@PreAuthorize("@ss.hasPerm('monitor:online:forceLogout')")
@AccessLog(title = "在线用户", businessType = BusinessType.FORCE)
@DeleteMapping("/onlineUser/{tokenId}")
public ResponseDTO<Object> forceLogout(@PathVariable String tokenId) {
redisCacheService.loginUserCache.delete(tokenId);
return ResponseDTO.ok();
}
}

View File

@ -0,0 +1,98 @@
package com.agileboot.admin.controller.system;
import com.agileboot.common.core.base.BaseController;
import com.agileboot.common.core.dto.ResponseDTO;
import com.agileboot.common.core.page.PageDTO;
import com.agileboot.domain.system.config.ConfigDTO;
import com.agileboot.domain.system.config.ConfigDomainService;
import com.agileboot.domain.system.config.ConfigQuery;
import com.agileboot.domain.system.config.ConfigUpdateCommand;
import com.agileboot.infrastructure.annotations.AccessLog;
import com.agileboot.infrastructure.cache.guava.GuavaCacheService;
import com.agileboot.infrastructure.cache.map.MapCache;
import com.agileboot.infrastructure.web.util.AuthenticationUtils;
import com.agileboot.orm.enums.BusinessType;
import com.agileboot.orm.result.DictionaryData;
import java.util.List;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Positive;
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.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 参数配置 信息操作处理
* @author valarchie
*/
@RestController
@RequestMapping("/system/config")
@Validated
public class SysConfigController extends BaseController {
@Autowired
private ConfigDomainService configDomainService;
@Autowired
private GuavaCacheService guavaCacheService;
/**
* 获取参数配置列表
*/
@PreAuthorize("@ss.hasPerm('system:config:list')")
@GetMapping("/list")
public ResponseDTO<PageDTO> list(ConfigQuery query) {
PageDTO page = configDomainService.getConfigList(query);
return ResponseDTO.ok(page);
}
/**
* 根据字典类型查询字典数据信息
* 换成用Enum
*/
@GetMapping(value = "/dict/{dictType}")
public ResponseDTO<List<DictionaryData>> dictType(@PathVariable String dictType) {
List<DictionaryData> dictionaryData = MapCache.dictionaryCache().get(dictType);
return ResponseDTO.ok(dictionaryData);
}
/**
* 根据参数编号获取详细信息
*/
@PreAuthorize("@ss.hasPerm('system:config:query')")
@GetMapping(value = "/{configId}")
public ResponseDTO<ConfigDTO> getInfo(@NotNull @Positive @PathVariable Long configId) {
ConfigDTO config = configDomainService.getConfigInfo(configId);
return ResponseDTO.ok(config);
}
/**
* 修改参数配置
*/
@PreAuthorize("@ss.hasPerm('system:config:edit')")
@AccessLog(title = "参数管理", businessType = BusinessType.UPDATE)
@PutMapping
public ResponseDTO edit(@RequestBody ConfigUpdateCommand config) {
configDomainService.updateConfig(config, AuthenticationUtils.getLoginUser());
return ResponseDTO.ok();
}
/**
* 刷新参数缓存
*/
@PreAuthorize("@ss.hasPerm('system:config:remove')")
@AccessLog(title = "参数管理", businessType = BusinessType.CLEAN)
@DeleteMapping("/refreshCache")
public ResponseDTO<?> refreshCache() {
guavaCacheService.configCache.invalidateAll();
return ResponseDTO.ok();
}
}

View File

@ -0,0 +1,126 @@
package com.agileboot.admin.controller.system;
import cn.hutool.core.lang.tree.Tree;
import com.agileboot.common.core.base.BaseController;
import com.agileboot.common.core.dto.ResponseDTO;
import com.agileboot.domain.system.TreeSelectedDTO;
import com.agileboot.domain.system.dept.AddDeptCommand;
import com.agileboot.domain.system.dept.DeptDTO;
import com.agileboot.domain.system.dept.DeptDomainService;
import com.agileboot.domain.system.dept.DeptQuery;
import com.agileboot.domain.system.dept.UpdateDeptCommand;
import com.agileboot.infrastructure.annotations.AccessLog;
import com.agileboot.infrastructure.web.util.AuthenticationUtils;
import com.agileboot.orm.enums.BusinessType;
import java.util.List;
import javax.validation.constraints.NotNull;
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;
/**
* 部门信息
*
* @author valarchie
*/
@RestController
@RequestMapping("/system/dept")
@Validated
public class SysDeptController extends BaseController {
@Autowired
private DeptDomainService deptDomainService;
/**
* 获取部门列表
*/
@PreAuthorize("@ss.hasPerm('system:dept:list')")
@GetMapping("/list")
public ResponseDTO list(DeptQuery query) {
List<DeptDTO> deptList = deptDomainService.getDeptList(query);
return ResponseDTO.ok(deptList);
}
/**
* 查询部门列表排除当前部门比如在修改部门的上级部门的时候需要排除自身当前的部门因为上级部门不能选自己
*/
@PreAuthorize("@ss.hasPerm('system:dept:list')")
@GetMapping("/list/exclude/{deptId}")
public ResponseDTO excludeCurrentDeptItself(@PathVariable(value = "deptId", required = false) Long deptId) {
DeptQuery query = new DeptQuery();
query.setDeptId(deptId);
query.setExcludeCurrentDept(true);
List<DeptDTO> deptList = deptDomainService.getDeptList(query);
return ResponseDTO.ok(deptList);
}
/**
* 根据部门编号获取详细信息
*/
@PreAuthorize("@ss.hasPerm('system:dept:query')")
@GetMapping(value = "/{deptId}")
public ResponseDTO<DeptDTO> getInfo(@PathVariable Long deptId) {
DeptDTO dept = deptDomainService.getDeptInfo(deptId);
return ResponseDTO.ok(dept);
}
/**
* 获取部门下拉树列表
*/
@GetMapping("/dropdownList")
public ResponseDTO<List> dropdownList() {
List<Tree<Long>> deptTree = deptDomainService.getDeptTree();
return ResponseDTO.ok(deptTree);
}
/**
* 加载对应角色部门列表树
*/
@GetMapping(value = "/dropdownList/role/{roleId}")
public ResponseDTO dropdownListForRole(@PathVariable("roleId") Long roleId) {
TreeSelectedDTO deptTreeForRole = deptDomainService.getDeptTreeForRole(roleId);
return ResponseDTO.ok(deptTreeForRole);
}
/**
* 新增部门
*/
@PreAuthorize("@ss.hasPerm('system:dept:add')")
@AccessLog(title = "部门管理", businessType = BusinessType.INSERT)
@PostMapping
public ResponseDTO add(@RequestBody AddDeptCommand addCommand) {
deptDomainService.addDept(addCommand, AuthenticationUtils.getLoginUser());
return ResponseDTO.ok();
}
/**
* 修改部门
*/
@PreAuthorize("@ss.hasPerm('system:dept:edit') AND @ss.checkDataScopeWithDeptId(#updateCommand.deptId)")
@AccessLog(title = "部门管理", businessType = BusinessType.UPDATE)
@PutMapping
public ResponseDTO edit(@RequestBody UpdateDeptCommand updateCommand) {
deptDomainService.updateDept(updateCommand, AuthenticationUtils.getLoginUser());
return ResponseDTO.ok();
}
/**
* 删除部门
*/
@PreAuthorize("@ss.hasPerm('system:dept:remove') AND @ss.checkDataScopeWithDeptId(#deptId)")
@AccessLog(title = "部门管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{deptId}")
public ResponseDTO remove(@PathVariable @NotNull Long deptId) {
deptDomainService.removeDept(deptId);
return ResponseDTO.ok();
}
}

View File

@ -0,0 +1,71 @@
package com.agileboot.admin.controller.system;
import com.agileboot.common.core.base.BaseController;
import com.agileboot.common.core.dto.ResponseDTO;
import com.agileboot.common.core.page.PageDTO;
import com.agileboot.common.exception.error.ErrorCode;
import com.agileboot.common.utils.poi.CustomExcelUtil;
import com.agileboot.domain.common.BulkOperationCommand;
import com.agileboot.domain.system.loginInfo.LoginInfoDTO;
import com.agileboot.domain.system.loginInfo.LoginInfoDomainService;
import com.agileboot.domain.system.loginInfo.LoginInfoQuery;
import com.agileboot.infrastructure.annotations.AccessLog;
import com.agileboot.orm.enums.BusinessType;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 系统访问记录
*
* @author valarchie
*/
@RestController
@RequestMapping("/loginInfo")
@Validated
public class SysLoginInfoController extends BaseController {
@Autowired
private LoginInfoDomainService loginInfoDomainService;
@PreAuthorize("@ss.hasPerm('monitor:logininfor:list')")
@GetMapping("/list")
public ResponseDTO<PageDTO> list(LoginInfoQuery query) {
PageDTO pageDTO = loginInfoDomainService.getLoginInfoList(query);
return ResponseDTO.ok(pageDTO);
}
@AccessLog(title = "登录日志", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPerm('monitor:logininfor:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, LoginInfoQuery query) {
PageDTO pageDTO = loginInfoDomainService.getLoginInfoList(query);
CustomExcelUtil.writeToResponse(pageDTO.getRows(), LoginInfoDTO.class, response);
}
@PreAuthorize("@ss.hasPerm('monitor:logininfor:remove')")
@AccessLog(title = "登录日志", businessType = BusinessType.DELETE)
@DeleteMapping("/{infoIds}")
public ResponseDTO remove(@PathVariable @NotNull @NotEmpty List<Long> infoIds) {
loginInfoDomainService.deleteLoginInfo(new BulkOperationCommand<>(infoIds));
return ResponseDTO.ok();
}
@PreAuthorize("@ss.hasPerm('monitor:logininfor:remove')")
@AccessLog(title = "登录日志", businessType = BusinessType.CLEAN)
@DeleteMapping("/clean")
public ResponseDTO clean() {
return ResponseDTO.fail(ErrorCode.Business.UNSUPPORTED_OPERATION);
}
}

View File

@ -0,0 +1,120 @@
package com.agileboot.admin.controller.system;
import cn.hutool.core.lang.tree.Tree;
import com.agileboot.common.core.base.BaseController;
import com.agileboot.common.core.dto.ResponseDTO;
import com.agileboot.domain.system.TreeSelectedDTO;
import com.agileboot.domain.system.menu.AddMenuCommand;
import com.agileboot.domain.system.menu.MenuDTO;
import com.agileboot.domain.system.menu.MenuDomainService;
import com.agileboot.domain.system.menu.MenuQuery;
import com.agileboot.domain.system.menu.UpdateMenuCommand;
import com.agileboot.infrastructure.annotations.AccessLog;
import com.agileboot.infrastructure.web.domain.login.LoginUser;
import com.agileboot.infrastructure.web.util.AuthenticationUtils;
import com.agileboot.orm.enums.BusinessType;
import java.util.List;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.PositiveOrZero;
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;
/**
* 菜单信息
*
* @author ruoyi
*/
@RestController
@RequestMapping("/system/menu")
@Validated
public class SysMenuController extends BaseController {
@Autowired
MenuDomainService menuDomainService;
/**
* 获取菜单列表
*/
@PreAuthorize("@ss.hasPerm('system:menu:list')")
@GetMapping("/list")
public ResponseDTO<List> list(MenuQuery query) {
List<MenuDTO> menuList = menuDomainService.getMenuList(query);
return ResponseDTO.ok(menuList);
}
/**
* 根据菜单编号获取详细信息
*/
@PreAuthorize("@ss.hasPerm('system:menu:query')")
@GetMapping(value = "/{menuId}")
public ResponseDTO<MenuDTO> getInfo(@PathVariable @NotNull @PositiveOrZero Long menuId) {
MenuDTO menu = menuDomainService.getMenuInfo(menuId);
return ResponseDTO.ok(menu);
}
/**
* 获取菜单下拉树列表
*/
@GetMapping("/dropdownList")
public ResponseDTO dropdownList() {
LoginUser loginUser = AuthenticationUtils.getLoginUser();
List<Tree<Long>> dropdownList = menuDomainService.getDropdownList(loginUser);
return ResponseDTO.ok(dropdownList);
}
/**
* 加载对应角色菜单列表树
*/
@GetMapping(value = "/roleMenuTreeSelect/{roleId}")
public ResponseDTO roleMenuTreeSelect(@PathVariable("roleId") Long roleId) {
LoginUser loginUser = AuthenticationUtils.getLoginUser();
TreeSelectedDTO roleDropdownList = menuDomainService.getRoleDropdownList(loginUser, roleId);
return ResponseDTO.ok(roleDropdownList);
}
/**
* 新增菜单
*/
@PreAuthorize("@ss.hasPerm('system:menu:add')")
@AccessLog(title = "菜单管理", businessType = BusinessType.INSERT)
@PostMapping
public ResponseDTO add(@RequestBody AddMenuCommand addCommand) {
LoginUser loginUser = AuthenticationUtils.getLoginUser();
menuDomainService.addMenu(addCommand, loginUser);
return ResponseDTO.ok();
}
/**
* 修改菜单
*/
@PreAuthorize("@ss.hasPerm('system:menu:edit')")
@AccessLog(title = "菜单管理", businessType = BusinessType.UPDATE)
@PutMapping
public ResponseDTO edit(@RequestBody UpdateMenuCommand updateCommand) {
LoginUser loginUser = AuthenticationUtils.getLoginUser();
menuDomainService.updateMenu(updateCommand, loginUser);
return ResponseDTO.ok();
}
/**
* 删除菜单
*/
@PreAuthorize("@ss.hasPerm('system:menu:remove')")
@AccessLog(title = "菜单管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{menuId}")
public ResponseDTO remove(@PathVariable("menuId") Long menuId) {
menuDomainService.remove(menuId);
return ResponseDTO.ok();
}
}

View File

@ -0,0 +1,97 @@
package com.agileboot.admin.controller.system;
import com.agileboot.common.core.base.BaseController;
import com.agileboot.common.core.dto.ResponseDTO;
import com.agileboot.common.core.page.PageDTO;
import com.agileboot.domain.common.BulkOperationCommand;
import com.agileboot.domain.system.notice.NoticeAddCommand;
import com.agileboot.domain.system.notice.NoticeDTO;
import com.agileboot.domain.system.notice.NoticeDomainService;
import com.agileboot.domain.system.notice.NoticeQuery;
import com.agileboot.domain.system.notice.NoticeUpdateCommand;
import com.agileboot.infrastructure.annotations.AccessLog;
import com.agileboot.infrastructure.web.util.AuthenticationUtils;
import com.agileboot.orm.enums.BusinessType;
import java.util.List;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Positive;
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;
/**
* 公告 信息操作处理
*
* @author valarchie
*/
@RestController
@RequestMapping("/system/notice")
@Validated
public class SysNoticeController extends BaseController {
@Autowired
private NoticeDomainService noticeDomainService;
/**
* 获取通知公告列表
*/
@PreAuthorize("@ss.hasPerm('system:notice:list')")
@GetMapping("/list")
public ResponseDTO<PageDTO> list(NoticeQuery query) {
PageDTO pageDTO = noticeDomainService.getNoticeList(query);
return ResponseDTO.ok(pageDTO);
}
/**
* 根据通知公告编号获取详细信息
*/
@PreAuthorize("@ss.hasPerm('system:notice:query')")
@GetMapping(value = "/{noticeId}")
public ResponseDTO<NoticeDTO> getInfo(@PathVariable @NotNull @Positive Long noticeId) {
return ResponseDTO.ok(noticeDomainService.getNoticeInfo(noticeId));
}
/**
* 新增通知公告
*/
@PreAuthorize("@ss.hasPerm('system:notice:add')")
@AccessLog(title = "通知公告", businessType = BusinessType.INSERT)
@PostMapping
public ResponseDTO add(@RequestBody NoticeAddCommand addCommand) {
noticeDomainService.addNotice(addCommand, AuthenticationUtils.getLoginUser());
return ResponseDTO.ok();
}
/**
* 修改通知公告
*/
@PreAuthorize("@ss.hasPerm('system:notice:edit')")
@AccessLog(title = "通知公告", businessType = BusinessType.UPDATE)
@PutMapping
public ResponseDTO edit(@RequestBody NoticeUpdateCommand updateCommand) {
noticeDomainService.updateNotice(updateCommand, AuthenticationUtils.getLoginUser());
return ResponseDTO.ok();
}
/**
* 删除通知公告
*/
@PreAuthorize("@ss.hasPerm('system:notice:remove')")
@AccessLog(title = "通知公告", businessType = BusinessType.DELETE)
@DeleteMapping("/{noticeIds}")
public ResponseDTO remove(@PathVariable List<Long> noticeIds) {
noticeDomainService.deleteNotice(new BulkOperationCommand<>(noticeIds));
return ResponseDTO.ok();
}
}

View File

@ -0,0 +1,67 @@
package com.agileboot.admin.controller.system;
import com.agileboot.common.core.base.BaseController;
import com.agileboot.common.core.dto.ResponseDTO;
import com.agileboot.common.core.page.PageDTO;
import com.agileboot.common.exception.error.ErrorCode;
import com.agileboot.common.utils.poi.CustomExcelUtil;
import com.agileboot.domain.common.BulkOperationCommand;
import com.agileboot.domain.system.operationLog.OperationLogDTO;
import com.agileboot.domain.system.operationLog.OperationLogDomainService;
import com.agileboot.domain.system.operationLog.OperationLogQuery;
import com.agileboot.infrastructure.annotations.AccessLog;
import com.agileboot.orm.enums.BusinessType;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
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;
/**
* 操作日志记录
*
* @author valarchie
*/
@RestController
@RequestMapping("/operationLog")
public class SysOperationLogController extends BaseController {
@Autowired
private OperationLogDomainService operationLogDomainService;
@PreAuthorize("@ss.hasPerm('monitor:operlog:list')")
@GetMapping("/list")
public ResponseDTO<PageDTO> list(OperationLogQuery query, HttpServletRequest request) {
PageDTO pageDTO = operationLogDomainService.getOperationLogList(query);
return ResponseDTO.ok(pageDTO);
}
@AccessLog(title = "操作日志", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPerm('monitor:operlog:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, OperationLogQuery query) {
PageDTO pageDTO = operationLogDomainService.getOperationLogList(query);
CustomExcelUtil.writeToResponse(pageDTO.getRows(), OperationLogDTO.class, response);
}
@AccessLog(title = "操作日志", businessType = BusinessType.DELETE)
@PreAuthorize("@ss.hasPerm('monitor:operlog:remove')")
@DeleteMapping("/{operationIds}")
public ResponseDTO remove(@PathVariable List<Long> operationIds) {
operationLogDomainService.deleteOperationLog(new BulkOperationCommand<>(operationIds));
return ResponseDTO.ok();
}
@AccessLog(title = "操作日志", businessType = BusinessType.CLEAN)
@PreAuthorize("@ss.hasPerm('monitor:operlog:remove')")
@DeleteMapping("/clean")
public ResponseDTO clean() {
return ResponseDTO.fail(ErrorCode.Business.UNSUPPORTED_OPERATION);
}
}

View File

@ -0,0 +1,104 @@
package com.agileboot.admin.controller.system;
import com.agileboot.common.core.base.BaseController;
import com.agileboot.common.core.dto.ResponseDTO;
import com.agileboot.common.core.page.PageDTO;
import com.agileboot.common.utils.poi.CustomExcelUtil;
import com.agileboot.domain.common.BulkOperationCommand;
import com.agileboot.domain.system.post.AddPostCommand;
import com.agileboot.domain.system.post.PostDTO;
import com.agileboot.domain.system.post.PostDomainService;
import com.agileboot.domain.system.post.PostQuery;
import com.agileboot.domain.system.post.UpdatePostCommand;
import com.agileboot.infrastructure.annotations.AccessLog;
import com.agileboot.infrastructure.web.util.AuthenticationUtils;
import com.agileboot.orm.enums.BusinessType;
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;
/**
* 岗位信息操作处理
*
* @author ruoyi
*/
@RestController
@RequestMapping("/system/post")
@Validated
public class SysPostController extends BaseController {
@Autowired
private PostDomainService postDomainService;
/**
* 获取岗位列表
*/
@PreAuthorize("@ss.hasPerm('system:post:list')")
@GetMapping("/list")
public ResponseDTO<PageDTO> list(PostQuery query) {
PageDTO pageDTO = postDomainService.getPostList(query);
return ResponseDTO.ok(pageDTO);
}
@AccessLog(title = "岗位管理", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPerm('system:post:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, PostQuery query) {
PageDTO pageDTO = postDomainService.getPostList(query);
CustomExcelUtil.writeToResponse(pageDTO.getRows(), PostDTO.class, response);
}
/**
* 根据岗位编号获取详细信息
*/
@PreAuthorize("@ss.hasPerm('system:post:query')")
@GetMapping(value = "/{postId}")
public ResponseDTO getInfo(@PathVariable Long postId) {
PostDTO post = postDomainService.getPostInfo(postId);
return ResponseDTO.ok(post);
}
/**
* 新增岗位
*/
@PreAuthorize("@ss.hasPerm('system:post:add')")
@AccessLog(title = "岗位管理", businessType = BusinessType.INSERT)
@PostMapping
public ResponseDTO add(@RequestBody AddPostCommand addCommand) {
postDomainService.addPost(addCommand, AuthenticationUtils.getLoginUser());
return ResponseDTO.ok();
}
/**
* 修改岗位
*/
@PreAuthorize("@ss.hasPerm('system:post:edit')")
@AccessLog(title = "岗位管理", businessType = BusinessType.UPDATE)
@PutMapping
public ResponseDTO edit(@RequestBody UpdatePostCommand updateCommand) {
postDomainService.updatePost(updateCommand, AuthenticationUtils.getLoginUser());
return ResponseDTO.ok();
}
/**
* 删除岗位
*/
@PreAuthorize("@ss.hasPerm('system:post:remove')")
@AccessLog(title = "岗位管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{postIds}")
public ResponseDTO remove(@PathVariable List<Long> postIds) {
postDomainService.deletePost(new BulkOperationCommand<>(postIds));
return ResponseDTO.ok();
}
}

View File

@ -0,0 +1,91 @@
package com.agileboot.admin.controller.system;
import com.agileboot.common.config.AgileBootConfig;
import com.agileboot.common.core.base.BaseController;
import com.agileboot.common.core.dto.ResponseDTO;
import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode;
import com.agileboot.common.utils.file.FileUploadUtils;
import com.agileboot.domain.common.UploadFileDTO;
import com.agileboot.domain.system.user.UserDomainService;
import com.agileboot.domain.system.user.UserProfileDTO;
import com.agileboot.domain.system.user.command.UpdateProfileCommand;
import com.agileboot.domain.system.user.command.UpdateUserAvatarCommand;
import com.agileboot.domain.system.user.command.UpdateUserPasswordCommand;
import com.agileboot.infrastructure.annotations.AccessLog;
import com.agileboot.infrastructure.web.domain.login.LoginUser;
import com.agileboot.infrastructure.web.util.AuthenticationUtils;
import com.agileboot.orm.enums.BusinessType;
import java.io.IOException;
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;
/**
* 个人信息 业务处理
*
* @author ruoyi
*/
@RestController
@RequestMapping("/system/user/profile")
public class SysProfileController extends BaseController {
@Autowired
private UserDomainService userDomainService;
/**
* 个人信息
*/
@GetMapping
public ResponseDTO profile() {
LoginUser user = AuthenticationUtils.getLoginUser();
UserProfileDTO userProfile = userDomainService.getUserProfile(user.getUserId());
return ResponseDTO.ok(userProfile);
}
/**
* 修改用户
*/
@AccessLog(title = "个人信息", businessType = BusinessType.UPDATE)
@PutMapping
public ResponseDTO updateProfile(@RequestBody UpdateProfileCommand command) {
LoginUser loginUser = AuthenticationUtils.getLoginUser();
command.setUserId(loginUser.getUserId());
userDomainService.updateUserProfile(command, loginUser);
return ResponseDTO.ok();
}
/**
* 重置密码
*/
@AccessLog(title = "个人信息", businessType = BusinessType.UPDATE)
@PutMapping("/password")
public ResponseDTO updatePassword(@RequestBody UpdateUserPasswordCommand command) {
LoginUser loginUser = AuthenticationUtils.getLoginUser();
command.setUserId(loginUser.getUserId());
userDomainService.updateUserPassword(loginUser, command);
return ResponseDTO.ok();
}
/**
* 头像上传
*/
@AccessLog(title = "用户头像", businessType = BusinessType.UPDATE)
@PostMapping("/avatar")
public ResponseDTO avatar(@RequestParam("avatarfile") MultipartFile file) throws IOException {
if (file.isEmpty()) {
throw new ApiException(ErrorCode.Business.USER_UPLOAD_FILE_FAILED);
}
LoginUser loginUser = AuthenticationUtils.getLoginUser();
String avatar = FileUploadUtils.upload(AgileBootConfig.getAvatarPath(), file);
userDomainService.updateUserAvatar(loginUser, new UpdateUserAvatarCommand(loginUser.getUserId(), avatar));
return ResponseDTO.ok(new UploadFileDTO(avatar));
}
}

View File

@ -0,0 +1,192 @@
package com.agileboot.admin.controller.system;
import com.agileboot.common.core.base.BaseController;
import com.agileboot.common.core.dto.ResponseDTO;
import com.agileboot.common.core.page.PageDTO;
import com.agileboot.common.utils.poi.CustomExcelUtil;
import com.agileboot.domain.system.role.AddRoleCommand;
import com.agileboot.domain.system.role.AllocatedRoleQuery;
import com.agileboot.domain.system.role.RoleDTO;
import com.agileboot.domain.system.role.RoleDomainService;
import com.agileboot.domain.system.role.RoleQuery;
import com.agileboot.domain.system.role.UnallocatedRoleQuery;
import com.agileboot.domain.system.role.UpdateDataScopeCommand;
import com.agileboot.domain.system.role.UpdateRoleCommand;
import com.agileboot.domain.system.role.UpdateStatusCommand;
import com.agileboot.infrastructure.annotations.AccessLog;
import com.agileboot.infrastructure.web.domain.login.LoginUser;
import com.agileboot.infrastructure.web.util.AuthenticationUtils;
import com.agileboot.orm.enums.BusinessType;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
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;
/**
* 角色信息
*
* @author ruoyi
*/
@RestController
@RequestMapping("/system/role")
@Validated
public class SysRoleController extends BaseController {
@Autowired
private RoleDomainService roleDomainService;
@PreAuthorize("@ss.hasPerm('system:role:list')")
@GetMapping("/list")
public ResponseDTO<PageDTO> list(RoleQuery query) {
PageDTO pageDTO = roleDomainService.getRoleList(query);
return ResponseDTO.ok(pageDTO);
}
@AccessLog(title = "角色管理", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPerm('system:role:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, RoleQuery query) {
PageDTO pageDTO = roleDomainService.getRoleList(query);
CustomExcelUtil.writeToResponse(pageDTO.getRows(), RoleDTO.class, response);
}
/**
* 根据角色编号获取详细信息
*/
@PreAuthorize("@ss.hasPerm('system:role:query')")
@GetMapping(value = "/{roleId}")
public ResponseDTO getInfo(@PathVariable @NotNull Long roleId) {
RoleDTO roleInfo = roleDomainService.getRoleInfo(roleId);
return ResponseDTO.ok(roleInfo);
}
/**
* 新增角色
*/
@PreAuthorize("@ss.hasPerm('system:role:add')")
@AccessLog(title = "角色管理", businessType = BusinessType.INSERT)
@PostMapping
public ResponseDTO add(@RequestBody AddRoleCommand addCommand) {
LoginUser loginUser = AuthenticationUtils.getLoginUser();
roleDomainService.addRole(addCommand, loginUser);
return ResponseDTO.ok();
}
/**
* 新增角色
*/
@PreAuthorize("@ss.hasPerm('system:role:remove')")
@AccessLog(title = "角色管理", businessType = BusinessType.INSERT)
@DeleteMapping(value = "/{roleId}")
public ResponseDTO remove(@PathVariable("roleId")List<Long> roleIds) {
LoginUser loginUser = AuthenticationUtils.getLoginUser();
roleDomainService.deleteRoleByBulk(roleIds, loginUser);
return ResponseDTO.ok();
}
/**
* 修改保存角色
*/
@PreAuthorize("@ss.hasPerm('system:role:edit')")
@AccessLog(title = "角色管理", businessType = BusinessType.UPDATE)
@PutMapping
public ResponseDTO edit(@Validated @RequestBody UpdateRoleCommand updateCommand) {
LoginUser loginUser = AuthenticationUtils.getLoginUser();
roleDomainService.updateRole(updateCommand, loginUser);
return ResponseDTO.ok();
}
/**
* 修改保存数据权限
*/
@PreAuthorize("@ss.hasPerm('system:role:edit')")
@AccessLog(title = "角色管理", businessType = BusinessType.UPDATE)
@PutMapping("/{roleId}/dataScope")
public ResponseDTO dataScope(@PathVariable("roleId")Long roleId, @RequestBody UpdateDataScopeCommand command) {
command.setRoleId(roleId);
roleDomainService.updateDataScope(command);
return ResponseDTO.ok();
}
/**
* 状态修改
*/
@PreAuthorize("@ss.hasPerm('system:role:edit')")
@AccessLog(title = "角色管理", businessType = BusinessType.UPDATE)
@PutMapping("/{roleId}/status")
public ResponseDTO changeStatus(@PathVariable("roleId")Long roleId, @RequestBody UpdateStatusCommand command) {
LoginUser loginUser = AuthenticationUtils.getLoginUser();
command.setRoleId(roleId);
roleDomainService.updateStatus(command, loginUser);
return ResponseDTO.ok();
}
/**
* 查询已分配用户角色列表
*/
@PreAuthorize("@ss.hasPerm('system:role:list')")
@GetMapping("/{roleId}/allocated/list")
public ResponseDTO<PageDTO> allocatedUserList(@PathVariable("roleId")Long roleId, AllocatedRoleQuery query) {
query.setRoleId(roleId);
PageDTO page = roleDomainService.getAllocatedUserList(query);
return ResponseDTO.ok(page);
}
/**
* 查询未分配用户角色列表
*/
@PreAuthorize("@ss.hasPerm('system:role:list')")
@GetMapping("/{roleId}/unallocated/list")
public ResponseDTO<PageDTO> unallocatedUserList(@PathVariable("roleId")Long roleId, UnallocatedRoleQuery query) {
query.setRoleId(roleId);
PageDTO page = roleDomainService.getUnallocatedUserList(query);
return ResponseDTO.ok(page);
}
/**
* 取消授权用户
*/
@PreAuthorize("@ss.hasPerm('system:role:edit')")
@AccessLog(title = "角色管理", businessType = BusinessType.GRANT)
@DeleteMapping("/{roleId}/user/grant")
public ResponseDTO deleteRoleOfUser(@PathVariable("roleId")Long roleId, @RequestBody Long userId) {
roleDomainService.deleteRoleOfUser(userId);
return ResponseDTO.ok();
}
/**
* 批量取消授权用户
*/
@PreAuthorize("@ss.hasPerm('system:role:edit')")
@AccessLog(title = "角色管理", businessType = BusinessType.GRANT)
@DeleteMapping("/users/{userIds}/grant/bulk")
public ResponseDTO deleteRoleOfUserByBulk(@PathVariable("userIds") List<Long> userIds) {
roleDomainService.deleteRoleOfUserByBulk(userIds);
return ResponseDTO.ok();
}
/**
* 批量选择用户授权
*/
@PreAuthorize("@ss.hasPerm('system:role:edit')")
@AccessLog(title = "角色管理", businessType = BusinessType.GRANT)
@PostMapping("/{roleId}/users/{userIds}/grant/bulk")
public ResponseDTO addRoleForUserByBulk(@PathVariable("roleId") Long roleId,
@PathVariable("userIds") List<Long> userIds) {
roleDomainService.addRoleOfUserByBulk(roleId, userIds);
return ResponseDTO.ok();
}
}

View File

@ -0,0 +1,171 @@
package com.agileboot.admin.controller.system;
import cn.hutool.core.collection.ListUtil;
import com.agileboot.common.core.base.BaseController;
import com.agileboot.common.core.dto.ResponseDTO;
import com.agileboot.common.core.page.PageDTO;
import com.agileboot.common.utils.poi.CustomExcelUtil;
import com.agileboot.domain.common.BulkOperationCommand;
import com.agileboot.domain.system.loginInfo.SearchUserQuery;
import com.agileboot.domain.system.user.UserDTO;
import com.agileboot.domain.system.user.UserDetailDTO;
import com.agileboot.domain.system.user.UserDomainService;
import com.agileboot.domain.system.user.UserInfoDTO;
import com.agileboot.domain.system.user.command.AddUserCommand;
import com.agileboot.domain.system.user.command.ChangeStatusCommand;
import com.agileboot.domain.system.user.command.ResetPasswordCommand;
import com.agileboot.domain.system.user.command.UpdateUserCommand;
import com.agileboot.infrastructure.annotations.AccessLog;
import com.agileboot.infrastructure.web.domain.login.LoginUser;
import com.agileboot.infrastructure.web.util.AuthenticationUtils;
import com.agileboot.orm.enums.BusinessType;
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 org.springframework.web.multipart.MultipartFile;
/**
* 用户信息
*
* @author ruoyi
*/
@RestController
@RequestMapping("/system/user")
public class SysUserController extends BaseController {
@Autowired
private UserDomainService userDomainService;
/**
* 获取用户列表
*/
@PreAuthorize("@ss.hasPerm('system:user:list') AND @ss.checkDataScopeWithDeptId(#query.deptId)")
@GetMapping("/list")
public ResponseDTO<PageDTO> list(SearchUserQuery query) {
PageDTO page = userDomainService.getUserList(query);
return ResponseDTO.ok(page);
}
@AccessLog(title = "用户管理", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPerm('system:user:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SearchUserQuery query) {
PageDTO userList = userDomainService.getUserList(query);
CustomExcelUtil.writeToResponse(userList.getRows(), UserDTO.class, response);
}
@AccessLog(title = "用户管理", businessType = BusinessType.IMPORT)
@PreAuthorize("@ss.hasPerm('system:user:import')")
@PostMapping("/importData")
public ResponseDTO importData(MultipartFile file) {
List<?> commands = CustomExcelUtil.readFromResponse(AddUserCommand.class, file);
LoginUser loginUser = AuthenticationUtils.getLoginUser();
for (Object command : commands) {
AddUserCommand addUserCommand = (AddUserCommand) command;
userDomainService.addUser(loginUser, addUserCommand);
}
return ResponseDTO.ok();
}
@PostMapping("/importTemplate")
public void importTemplate(HttpServletResponse response) {
CustomExcelUtil.writeToResponse(ListUtil.toList(new AddUserCommand()), AddUserCommand.class, response);
}
/**
* 根据用户编号获取详细信息
*/
@PreAuthorize("@ss.hasPerm('system:user:query')")
@GetMapping(value = {"/", "/{userId}"})
public ResponseDTO<UserDetailDTO> getUserDetailInfo(@PathVariable(value = "userId", required = false) Long userId) {
UserDetailDTO userDetailInfo = userDomainService.getUserDetailInfo(userId);
return ResponseDTO.ok(userDetailInfo);
}
/**
* 新增用户
*/
@PreAuthorize("@ss.hasPerm('system:user:add') AND @ss.checkDataScopeWithDeptId(#command.deptId)")
@AccessLog(title = "用户管理", businessType = BusinessType.INSERT)
@PostMapping
public ResponseDTO add(@Validated @RequestBody AddUserCommand command) {
LoginUser loginUser = AuthenticationUtils.getLoginUser();
userDomainService.addUser(loginUser, command);
return ResponseDTO.ok();
}
/**
* 修改用户
*/
@PreAuthorize("@ss.hasPerm('system:user:edit') AND @ss.checkDataScopeWithUserId(#command.userId)")
@AccessLog(title = "用户管理", businessType = BusinessType.UPDATE)
@PutMapping
public ResponseDTO edit(@Validated @RequestBody UpdateUserCommand command) {
LoginUser loginUser = AuthenticationUtils.getLoginUser();
userDomainService.updateUser(loginUser, command);
return ResponseDTO.ok();
}
/**
* 删除用户
*/
@PreAuthorize("@ss.hasPerm('system:user:remove') AND @ss.checkDataScopeWithUserIds(#userIds)")
@AccessLog(title = "用户管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{userIds}")
public ResponseDTO remove(@PathVariable List<Long> userIds) {
BulkOperationCommand<Long> bulkDeleteCommand = new BulkOperationCommand(userIds);
LoginUser loginUser = AuthenticationUtils.getLoginUser();
userDomainService.deleteUsers(loginUser, bulkDeleteCommand);
return ResponseDTO.ok();
}
/**
* 重置密码
*/
@PreAuthorize("@ss.hasPerm('system:user:resetPwd') AND @ss.checkDataScopeWithUserId(#userId)")
@AccessLog(title = "用户管理", businessType = BusinessType.UPDATE)
@PutMapping("/{userId}/password/reset")
public ResponseDTO resetPassword(@PathVariable Long userId, @RequestBody ResetPasswordCommand command) {
command.setUserId(userId);
LoginUser loginUser = AuthenticationUtils.getLoginUser();
userDomainService.resetUserPassword(loginUser, command);
return ResponseDTO.ok();
}
/**
* 状态修改
*/
@PreAuthorize("@ss.hasPerm('system:user:edit') AND @ss.checkDataScopeWithUserId(#command.userId)")
@AccessLog(title = "用户管理", businessType = BusinessType.UPDATE)
@PutMapping("/{userId}/status")
public ResponseDTO changeStatus(@PathVariable Long userId, @RequestBody ChangeStatusCommand command) {
command.setUserId(userId);
LoginUser loginUser = AuthenticationUtils.getLoginUser();
userDomainService.changeUserStatus(loginUser, command);
return ResponseDTO.ok();
}
/**
* 根据用户编号获取授权角色
*/
@PreAuthorize("@ss.hasPerm('system:user:query')")
@GetMapping("/{userId}/role")
public ResponseDTO<UserInfoDTO> getRoleOfUser(@PathVariable("userId") Long userId) {
UserInfoDTO userWithRole = userDomainService.getUserWithRole(userId);
return ResponseDTO.ok(userWithRole);
}
}

View File

@ -0,0 +1,23 @@
package com.agileboot.admin.controller.tool;
import com.agileboot.common.core.base.BaseController;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* swagger 接口
*
* @author ruoyi
*/
@Controller
@RequestMapping("/tool/swagger")
public class SwaggerController extends BaseController {
@PreAuthorize("@ss.hasPerm('tool:swagger:view')")
@GetMapping()
public String index() {
return redirect("/swagger-ui.html");
}
}

View File

@ -0,0 +1,126 @@
package com.agileboot.admin.controller.tool;
import com.agileboot.common.core.base.BaseController;
import com.agileboot.common.core.dto.ResponseDTO;
import com.agileboot.common.exception.error.ErrorCode;
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;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
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;
@RestController
@RequestMapping("/test/user")
/**
* swagger 用户测试方法
*
* @author valarchie
*/
@Api("用户信息管理")
public class SwaggerTemplateController extends BaseController {
private final static Map<Integer, UserEntity> USER_ENTITY_MAP = new LinkedHashMap<>();
static {
USER_ENTITY_MAP.put(1, new UserEntity(1, "admin", "admin123", "15888888888"));
USER_ENTITY_MAP.put(2, new UserEntity(2, "agileBoot", "admin123", "15666666666"));
}
@ApiOperation("获取用户列表")
@GetMapping("/list")
public ResponseDTO<List<UserEntity>> userList() {
List<UserEntity> userList = new ArrayList<>(USER_ENTITY_MAP.values());
return ResponseDTO.ok(userList);
}
@ApiOperation("获取用户详细")
@ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path",
dataTypeClass = Integer.class)
@GetMapping("/{userId}")
public ResponseDTO<UserEntity> getUser(@PathVariable Integer userId) {
if (!USER_ENTITY_MAP.isEmpty() && USER_ENTITY_MAP.containsKey(userId)) {
return ResponseDTO.ok(USER_ENTITY_MAP.get(userId));
} else {
return ResponseDTO.fail(ErrorCode.Business.USER_NON_EXIST);
}
}
@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 ResponseDTO<String> save(UserEntity user) {
if (user == null || user.getUserId() == null) {
return ResponseDTO.fail("用户ID不能为空");
}
USER_ENTITY_MAP.put(user.getUserId(), user);
return ResponseDTO.ok();
}
@ApiOperation("更新用户")
@PutMapping("/update")
public ResponseDTO<String> update(@RequestBody UserEntity user) {
if (user == null || user.getUserId() == null) {
return ResponseDTO.fail("用户ID不能为空");
}
if (USER_ENTITY_MAP.isEmpty() || !USER_ENTITY_MAP.containsKey(user.getUserId())) {
return ResponseDTO.fail("用户不存在");
}
USER_ENTITY_MAP.remove(user.getUserId());
USER_ENTITY_MAP.put(user.getUserId(), user);
return ResponseDTO.ok();
}
@ApiOperation("删除用户信息")
@ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path",
dataTypeClass = Integer.class)
@DeleteMapping("/{userId}")
public ResponseDTO<String> delete(@PathVariable Integer userId) {
if (!USER_ENTITY_MAP.isEmpty() && USER_ENTITY_MAP.containsKey(userId)) {
USER_ENTITY_MAP.remove(userId);
return ResponseDTO.ok();
} else {
return ResponseDTO.fail("用户不存在");
}
}
@ApiModel(value = "UserEntity", description = "用户实体")
@Data
@AllArgsConstructor
@NoArgsConstructor
static class UserEntity {
@ApiModelProperty("用户ID")
private Integer userId;
@ApiModelProperty("用户名称")
private String username;
@ApiModelProperty("用户密码")
private String password;
@ApiModelProperty("用户手机")
private String mobile;
}
}

View File

@ -0,0 +1,33 @@
package com.agileboot.admin.request;
import lombok.Data;
/**
* 用户登录对象
*
* @author valarchie
*/
@Data
public class LoginDTO {
/**
* 用户名
*/
private String username;
/**
* 用户密码
*/
private String password;
/**
* 验证码
*/
private String code;
/**
* 唯一标识
*/
private String uuid;
}

View File

@ -0,0 +1,27 @@
package com.agileboot.admin.request;
import com.agileboot.domain.system.user.RegisterUserModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 用户注册对象
*
* @author valarchie
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class RegisterDTO extends LoginDTO {
public RegisterUserModel toModel() {
RegisterUserModel model = new RegisterUserModel();
model.setCode(this.getCode());
model.setUuid(this.getUuid());
model.setUsername(this.getUsername());
model.setPassword(this.getPassword());
return model;
}
}

View File

@ -0,0 +1,18 @@
package com.agileboot.admin.response;
import lombok.Builder;
import lombok.Data;
/**
* @author valarchie
*/
@Data
@Builder
public class UploadDTO {
private String url;
private String fileName;
private String newFileName;
private String originalFilename;
}

View File

@ -0,0 +1,16 @@
package com.agileboot.admin.response;
import com.agileboot.domain.system.user.UserDTO;
import java.util.Map;
import java.util.Set;
import lombok.Data;
@Data
public class UserPermissionDTO {
private UserDTO user;
private String roleKey;
private Set<String> permissions;
private Map dictTypes;
}

40
agileboot-api/pom.xml Normal file
View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>agileboot</artifactId>
<groupId>com.agileboot</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>agileboot-api</artifactId>
<description>
quartz定时任务
</description>
<dependencies>
<!-- 定时任务 -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<exclusions>
<exclusion>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 通用工具-->
<dependency>
<groupId>com.agileboot</groupId>
<artifactId>agileboot-common</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,16 @@
package com.agileboot.api.controller;
import com.agileboot.common.core.base.BaseController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 调度日志操作处理
*
* @author ruoyi
*/
@RestController
@RequestMapping("/api/order")
public class OrderController extends BaseController {
}

166
agileboot-common/pom.xml Normal file
View File

@ -0,0 +1,166 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>agileboot</artifactId>
<groupId>com.agileboot</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>agileboot-common</artifactId>
<description>
common通用工具
</description>
<dependencies>
<!-- Spring框架基本的核心工具 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<!-- SpringWeb模块 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<!-- spring security 安全认证 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- pagehelper 分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
</dependency>
<!-- 自定义验证注解 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!--常用工具类 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- JSON工具类 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- io常用工具类 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<!-- 文件上传工具类 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
</dependency>
<!-- excel工具 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
</dependency>
<!-- yml解析器 -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</dependency>
<!-- Token生成与解析-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
<!-- Jaxb -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency>
<!-- redis 缓存操作 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- pool 对象池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- 解析客户端操作系统、浏览器等 -->
<dependency>
<groupId>eu.bitwalker</groupId>
<artifactId>UserAgentUtils</artifactId>
</dependency>
<!-- servlet包 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-parameter-names</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<!-- swagger3-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>2.6.5</version>
</dependency>
<!-- 防止进入swagger页面报类型转换错误排除3.0.0中的引用手动增加1.6.2版本 -->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>1.6.2</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,19 @@
package com.agileboot.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义导出Excel数据注解
*
* @author valarchie
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelColumn {
String name() default "";
}

View File

@ -0,0 +1,20 @@
package com.agileboot.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author valarchie
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ExcelSheet {
/**
* sheet名称
*/
String name() default "";
}

View File

@ -0,0 +1,146 @@
package com.agileboot.common.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 读取项目相关配置
*
* @author ruoyi
*/
@Component
@ConfigurationProperties(prefix = "agileboot")
public class AgileBootConfig {
/**
* 项目名称
*/
private String name;
/**
* 版本
*/
private String version;
/**
* 版权年份
*/
private String copyrightYear;
/**
* 实例演示开关
*/
private boolean demoEnabled;
/**
* 上传路径
*/
private static String profile;
/**
* 获取地址开关
*/
private static boolean addressEnabled;
/**
* 验证码类型
*/
private static String captchaType;
/**
* rsa private key 静态属性的注入 set方法一定不能是static 方法
*/
private static String rsaPrivateKey;
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 boolean isDemoEnabled() {
return demoEnabled;
}
public void setDemoEnabled(boolean demoEnabled) {
this.demoEnabled = demoEnabled;
}
public static String getProfile() {
return profile;
}
public void setProfile(String profile) {
AgileBootConfig.profile = profile;
}
public static boolean isAddressEnabled() {
return addressEnabled;
}
public void setAddressEnabled(boolean addressEnabled) {
AgileBootConfig.addressEnabled = addressEnabled;
}
public static String getCaptchaType() {
return captchaType;
}
public void setCaptchaType(String captchaType) {
AgileBootConfig.captchaType = captchaType;
}
public static String getRsaPrivateKey() {
return rsaPrivateKey;
}
public void setRsaPrivateKey(String rsaPrivateKey) {
AgileBootConfig.rsaPrivateKey = rsaPrivateKey;
}
/**
* 获取导入上传路径
*/
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";
}
}

View File

@ -0,0 +1,66 @@
package com.agileboot.common.constant;
/**
* 通用常量信息
*
* @author valarchie
*/
public class Constants {
public static final int KB = 1024;
public static final int MB = KB * 1024;
public static final int GB = MB * 1024;
/**
* http请求
*/
public static final String HTTP = "http://";
/**
* https请求
*/
public static final String HTTPS = "https://";
/**
* 资源映射路径 前缀
*/
public static final String RESOURCE_PREFIX = "/profile";
public static class Token {
/**
* 令牌
*/
public static final String TOKEN_FIELD = "token";
/**
* 令牌前缀
*/
public static final String TOKEN_PREFIX = "Bearer ";
/**
* 令牌前缀
*/
public static final String LOGIN_USER_KEY = "login_user_key";
}
public static class Captcha {
/**
* 令牌
*/
public static final String MATH_TYPE = "math";
/**
* 令牌前缀
*/
public static final String CHAR_TYPE = "char";
}
}

View File

@ -0,0 +1,40 @@
package com.agileboot.common.core.base;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import java.beans.PropertyEditorSupport;
import java.util.Date;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
/**
* @author valarchie
*/
@Slf4j
public class BaseController {
/**
*
* 将前台传递过来的日期格式的字符串自动转化为Date类型
*/
@InitBinder
public void initBinder(WebDataBinder binder) {
// Date 类型转换
binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
@Override
public void setAsText(String text) {
setValue(DateUtil.parseDate(text));
}
});
}
/**
* 页面跳转
*/
public String redirect(String url) {
return StrUtil.format("redirect:{}", url);
}
}

View File

@ -0,0 +1,62 @@
package com.agileboot.common.core.base;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import io.swagger.annotations.ApiModelProperty;
import java.util.Date;
import lombok.Data;
/**
* Entity基类
*
* @author valarchie
*/
@Data
public class BaseEntity<T extends Model<?>> extends Model<T> {
@ApiModelProperty("创建者ID")
@TableField("creator_id")
private Long creatorId;
@ApiModelProperty("创建者")
@TableField("creator_name")
private String creatorName;
@ApiModelProperty("创建时间")
@TableField(value = "create_time", fill = FieldFill.INSERT)
private Date createTime;
@ApiModelProperty("更新者ID")
@TableField("updater_id")
private Long updaterId;
@ApiModelProperty("更新者")
@TableField("updater_name")
private String updaterName;
@ApiModelProperty("更新时间")
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
@ApiModelProperty("删除标志0代表存在 1代表删除")
@TableField("deleted")
@TableLogic
private Boolean deleted;
public void logCreator(BaseUser user) {
if (user != null) {
this.creatorId = user.getUserId();
this.creatorName = user.getUsername();
}
}
public void logUpdater(BaseUser user) {
if (user != null) {
this.updaterId = user.getUserId();
this.updaterName = user.getUsername();
}
}
}

View File

@ -0,0 +1,13 @@
package com.agileboot.common.core.base;
import lombok.Data;
@Data
public class BaseUser {
private Long userId;
private String username;
private Long deptId;
private Long roleId;
}

View File

@ -0,0 +1,64 @@
package com.agileboot.common.core.dto;
import cn.hutool.core.util.StrUtil;
import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode;
import com.agileboot.common.exception.error.ErrorCodeInterface;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* 响应信息主体
*
* @author valarchie
*/
@Data
@AllArgsConstructor
public class ResponseDTO<T> {
private Integer code;
private String msg;
private T data;
public static <T> ResponseDTO<T> ok() {
return build(null, ErrorCode.SUCCESS);
}
public static <T> ResponseDTO<T> ok(T data) {
return build(data, ErrorCode.SUCCESS);
}
public static <T> ResponseDTO<T> fail() {
return build(null, ErrorCode.FAIL);
}
public static <T> ResponseDTO<T> fail(ErrorCodeInterface code) {
return build(null, code);
}
public static <T> ResponseDTO<T> fail(ApiException exception) {
return new ResponseDTO<>(exception.getErrorCode().code(), exception.getMessage(), null);
}
public static <T> ResponseDTO<T> fail(ErrorCodeInterface code, Object... args) {
return build( code, args);
}
public static <T> ResponseDTO<T> fail(T data) { return build(ErrorCode.FAIL, data); }
public static <T> ResponseDTO<T> build(T data, ErrorCodeInterface code) {
return new ResponseDTO<>(code.code(), code.message(), data);
}
public static <T> ResponseDTO<T> build(ErrorCodeInterface code, Object... args) {
return new ResponseDTO<>(code.code(), StrUtil.format(code.message(), args), null);
}
public static <T> ResponseDTO<T> build(T data, ErrorCodeInterface code, Object... args) {
return new ResponseDTO<>(code.code(), StrUtil.format(code.message(), args), data);
}
}

View File

@ -0,0 +1,37 @@
package com.agileboot.common.core.page;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.util.List;
import lombok.Data;
/**
* @author valarchie
*/
@Data
public class PageDTO {
/**
* 总记录数
*/
private Long total;
/**
* 列表数据
*/
private List<?> rows;
public PageDTO(List<?> list) {
this.rows = list;
this.total = (long) list.size();
}
public PageDTO(Page page) {
this.rows = page.getRecords();
this.total = page.getTotal();
}
public PageDTO(List<?> list, Long count) {
this.rows = list;
this.total = count;
}
}

View File

@ -0,0 +1,18 @@
package com.agileboot.common.enums;
/**
* 数据源
*
* @author ruoyi
*/
public enum DataSourceType {
/**
* 主库
*/
MASTER,
/**
* 从库
*/
SLAVE
}

View File

@ -0,0 +1,19 @@
package com.agileboot.common.enums;
/**
* 限流类型
*
* @author ruoyi
*/
public enum LimitType {
/**
* 默认策略全局限流
*/
DEFAULT,
/**
* 根据请求者IP进行限流
*/
IP
}

View File

@ -0,0 +1,76 @@
package com.agileboot.common.exception;
import cn.hutool.core.util.StrUtil;
import com.agileboot.common.exception.error.ErrorCodeInterface;
import com.agileboot.common.utils.i18n.MessageUtils;
import lombok.extern.slf4j.Slf4j;
/**
* @author valarchie
*/
@Slf4j
public class ApiException extends RuntimeException{
protected ErrorCodeInterface errorCode;
protected String message;
protected Object[] args;
protected String formattedMessage;
protected String i18nFormattedMessage;
public ApiException(Throwable e, ErrorCodeInterface errorCode, Object... args) {
super(e);
fillErrorCode(errorCode, args);
}
public ApiException(Throwable e, ErrorCodeInterface errorCode) {
super(e);
fillErrorCode(errorCode);
}
public ApiException(ErrorCodeInterface errorCode, Object... args) {
fillErrorCode(errorCode, args);
}
public ApiException(ErrorCodeInterface errorCode) {
fillErrorCode(errorCode);
}
private void fillErrorCode(ErrorCodeInterface errorCode, Object... args) {
this.errorCode = errorCode;
this.message = errorCode.message();
this.args = args;
this.formattedMessage = StrUtil.format(this.message, args);
try {
this.i18nFormattedMessage = MessageUtils.message(errorCode.i18n(), args);
} catch (Exception e) {
log.error("could not found i18n error i18nMessage entry : " + e.getMessage());
}
}
public ErrorCodeInterface getErrorCode() {
return errorCode;
}
public void setErrorCode(ErrorCodeInterface errorCode) {
this.errorCode = errorCode;
}
@Override
public String getMessage() {
return i18nFormattedMessage != null ? i18nFormattedMessage : formattedMessage;
}
@Override
public String getLocalizedMessage() {
return i18nFormattedMessage;
}
}

View File

@ -0,0 +1,322 @@
package com.agileboot.common.exception.error;
/**
* 常用错误码 以及 保留错误码
* @author valarchie
*/
public enum ErrorCode implements ErrorCodeInterface {
/**
* 错误码集合
* 1~9999 为保留错误码 或者 常用错误码
* 10000~19999 为内部错误码
* 20000~29999 客户端错误码 客户端异常调用之类的错误
* 30000~39999 为第三方错误码 代码正常但是第三方异常
* 40000~49999 为业务逻辑 错误码 无异常代码正常流转并返回提示给用户
* 由于系统内的错误码都是独一无二的所以错误码应该放在common包集中管理
*/
// -------------- 普通错误码 及保留错误码 ---------------
SUCCESS(0,"操作成功"),
FAIL(9999, "操作失败"),
UNKNOWN_ERROR(99999,"未知错误");
private final int code;
private final String msg;
ErrorCode(int code, String msg) {
this.code = code;
this.msg = msg;
}
@Override
public int code() {
return this.code;
}
@Override
public String message() {
return this.msg;
}
/**
* 40000~49999 为业务逻辑 错误码 无代码异常代码正常流转并返回提示给用户
*/
public enum Business implements ErrorCodeInterface{
// ----------------------------- Common -----------------------------------------
OBJECT_NOT_FOUND(Module.COMMON, 1, "找不到ID为%s 的%s"),
UNSUPPORTED_OPERATION(Module.COMMON, 2, "不支持的操作"),
BULK_DELETE_IDS_IS_INVALID(Module.COMMON, 3, "批量参数ID列表为空"),
// ----------------------------- Permission -----------------------------------------
FORBIDDEN_TO_MODIFY_ADMIN(Module.PERMISSION, 1, "不允许修改管理员的信息"),
NO_PERMISSION_TO_OPERATE(Module.PERMISSION, 2, "没有权限进行此操作,请联系管理员"),
// ----------------------------- Login -----------------------------------------
LOGIN_WRONG_USER_PASSWORD(Module.LOGIN, 1, "用户密码错误,请重输"),
LOGIN_ERROR(Module.LOGIN, 2, "登录失败:{}"),
CAPTCHA_CODE_WRONG(Module.LOGIN, 3, "验证码错误"),
CAPTCHA_CODE_EXPIRE(Module.LOGIN, 4, "验证码过期"),
CAPTCHA_CODE_NULL(Module.LOGIN, 5, "验证码为空"),
// ----------------------------- Upload -----------------------------------------
UPLOAD_FILE_TYPE_NOT_ALLOWED(Module.UPLOAD, 1, "不允许上传的文件类型,仅允许:%s"),
UPLOAD_FILE_NAME_EXCEED_MAX_LENGTH(Module.UPLOAD, 2, "文件名长度超过:%s "),
UPLOAD_FILE_SIZE_EXCEED_MAX_SIZE(Module.UPLOAD, 3, "文件名大小超过:%s MB"),
UPLOAD_IMPORT_EXCEL_FAILED(Module.UPLOAD, 4, "导入excel失败%s"),
UPLOAD_FILE_IS_EMPTY(Module.UPLOAD, 5, "上传文件为空"),
// ----------------------------- Config -----------------------------------------
CONFIG_VALUE_IS_NOT_ALLOW_TO_EMPTY(Module.CONFIG, 1, "参数键值不允许为空"),
CONFIG_VALUE_IS_NOT_IN_OPTIONS(Module.CONFIG, 2, "参数键值不存在列表中"),
// ----------------------------- Post -----------------------------------------
POST_NAME_IS_NOT_UNIQUE(Module.POST, 1, "岗位名称:%s, 已存在"),
POST_CODE_IS_NOT_UNIQUE(Module.POST, 2, "岗位编号:%s, 已存在"),
POST_ALREADY_ASSIGNED_TO_USER_CAN_NOT_BE_DELETED(Module.POST, 3, "职位已分配给用户,请先取消分配再删除"),
// ------------------------------- Dept ---------------------------------------------
DEPT_NAME_IS_NOT_UNIQUE(Module.DEPT, 1, "部门名称:%s, 已存在"),
DEPT_PARENT_ID_IS_NOT_ALLOWED_SELF(Module.DEPT, 2, "父级部门不能选择自己"),
DEPT_STATUS_ID_IS_NOT_ALLOWED_CHANGE(Module.DEPT, 3, "子部门还有正在启用的部门,暂时不能停用该部门"),
DEPT_EXIST_CHILD_DEPT_NOT_ALLOW_DELETE(Module.DEPT, 4, "该部门存在下级部门不允许删除"),
DEPT_EXIST_LINK_USER_NOT_ALLOW_DELETE(Module.DEPT, 5, "该部门存在关联的用户不允许删除"),
DEPT_PARENT_DEPT_NO_EXIST_OR_DISABLED(Module.DEPT, 6, "该父级部门不存在或已停用"),
// ------------------------------- Menu -------------------------------------------------------
MENU_NAME_IS_NOT_UNIQUE(Module.MENU, 1, "新增菜单:%s 失败,菜单名称已存在"),
MENU_EXTERNAL_LINK_MUST_BE_HTTP(Module.MENU, 2, "菜单外链必须以 http(s)://开头"),
MENU_PARENT_ID_NOT_ALLOW_SELF(Module.MENU, 3, "父级菜单不能选择自身"),
MENU_EXIST_CHILD_MENU_NOT_ALLOW_DELETE(Module.MENU, 4, "存在子菜单不允许删除"),
MENU_ALREADY_ASSIGN_TO_ROLE_NOT_ALLOW_DELETE(Module.MENU, 5, "菜单已分配给角色,不允许"),
// -------------------------------- Role ----------------------------------------------------
ROLE_NAME_IS_NOT_UNIQUE(Module.ROLE, 1, "角色名称:%s, 已存在"),
ROLE_KEY_IS_NOT_UNIQUE(Module.ROLE, 2, "角色标识:%s, 已存在"),
ROLE_DATA_SCOPE_DUPLICATED_DEPT(Module.ROLE, 3, "重复的部门id"),
ROLE_ALREADY_ASSIGN_TO_USER(Module.ROLE, 4, "角色已分配给用户,请先取消分配,再删除角色"),
// ------------------------ User ------------------------------
USER_NON_EXIST(Module.USER, 1, "登录用户:%s 不存在"),
USER_IS_DISABLE(Module.USER, 2, "对不起, 您的账号:{} 已停用"),
USER_CACHE_IS_EXPIRE(Module.USER, 3, "用户缓存信息已经过期"),
USER_FAIL_TO_GET_USER_ID(Module.USER, 3, "获取用户ID失败"),
USER_FAIL_TO_GET_DEPT_ID(Module.USER, 4, "获取用户部门ID失败"),
USER_FAIL_TO_GET_ACCOUNT(Module.USER, 5, "获取用户账户失败"),
USER_FAIL_TO_GET_USER_INFO(Module.USER, 6, "获取用户信息失败"),
USER_IMPORT_DATA_IS_NULL(Module.USER, 7, "导入的用户为空"),
USER_PHONE_NUMBER_IS_NOT_UNIQUE(Module.USER, 8, "该电话号码已被其他用户占用"),
USER_EMAIL_IS_NOT_UNIQUE(Module.USER, 9, "该邮件地址已被其他用户占用"),
USER_PASSWORD_IS_NOT_CORRECT(Module.USER, 10, "用户密码错误"),
USER_NEW_PASSWORD_IS_THE_SAME_AS_OLD(Module.USER, 11, "用户新密码与旧密码相同"),
USER_UPLOAD_FILE_FAILED(Module.USER, 12, "用户上传文件失败"),
USER_NAME_IS_NOT_UNIQUE(Module.USER, 13, "用户名已被其他用户占用"),
USER_CURRENT_USER_CAN_NOT_BE_DELETE(Module.USER, 14, "当前用户不允许被删除"),
;
private final int code;
private final String msg;
private static final int BASE_CODE = 40000;
Business(Module module, int code, String msg) {
this.code = BASE_CODE + module.code() + code;
this.msg = msg;
}
@Override
public int code() {
return this.code;
}
@Override
public String message() {
return this.msg;
}
}
/**
* 30000~39999是外部错误码 比如调用支付失败
*/
public enum External implements ErrorCodeInterface {
/**
* 支付宝调用失败
*/
FAIL_TO_PAY_ON_ALIPAY(Module.COMMON, 1,"支付宝调用失败");
private final int code;
private final String msg;
private static final int BASE_CODE = 30000;
External(Module module, int code, String msg) {
this.code = BASE_CODE + module.code() + code;
this.msg = msg;
}
@Override
public int code() {
return this.code;
}
@Override
public String message() { return this.msg; }
}
/**
* 20000~29999是客户端错误码
*/
public enum Client implements ErrorCodeInterface {
/**
* 客户端错误码
*/
COMMON_FORBIDDEN_TO_CALL(Module.COMMON, 1,"禁止调用"),
COMMON_REQUEST_TO_OFTEN(Module.COMMON, 2, "调用太过频繁"),
COMMON_REQUEST_PARAMETERS_INVALID(Module.COMMON, 3, "请求参数异常,%s"),
COMMON_REQUEST_METHOD_INVALID(Module.COMMON, 4, "请求方式不支持"),
COMMON_NO_AUTHORIZATION(Module.PERMISSION, 1, "请求接口:%s 失败,用户未授权"),
;
private final int code;
private final String msg;
private static final int BASE_CODE = 20000;
Client(Module module, int code, String msg) {
this.code = BASE_CODE + module.code() + code;
this.msg = msg;
}
@Override
public int code() {
return this.code;
}
@Override
public String message() {
return this.msg;
}
}
/**
* 10000~19999是内部错误码 例如 框架有问题之类的
*/
public enum Internal implements ErrorCodeInterface {
/**
* 内部错误码
*/
INVALID_PARAMETER(Module.COMMON, 1,"参数异常"),
UNKNOWN_ERROR(Module.COMMON, 2,"未知异常: %s"),
GET_ENUM_FAILED(Module.COMMON, 3,"获取枚举类型失败, 枚举类:%s"),
LOGIN_CAPTCHA_GENERATE_FAIL(Module.LOGIN, 1,"验证码生成失败"),
INVALID_TOKEN(Module.PERMISSION, 1,"token异常"),
DB_INTERNAL_ERROR(Module.DB, 1, "数据库异常:%s"),
;
private final int code;
private final String msg;
private static final int BASE_CODE = 10000;
Internal(Module module, int code, String msg) {
this.code = BASE_CODE + module.code() + code;
this.msg = msg;
}
@Override
public int code() {
return this.code;
}
@Override
public String message() {
return this.msg;
}
}
}

View File

@ -0,0 +1,15 @@
package com.agileboot.common.exception.error;
public interface ErrorCodeInterface {
String name();
int code();
String message();
default String i18n() {
return code() + "_" + name();
}
}

View File

@ -0,0 +1,46 @@
package com.agileboot.common.exception.error;
/**
* 系统内的模块
*/
public enum Module {
/**
* 普通模块
*/
COMMON(0),
/**
* 权限模块
*/
PERMISSION(1),
LOGIN(2),
DB(3),
UPLOAD(4),
USER(5),
CONFIG(6),
POST(7),
DEPT(8),
MENU(9),
ROLE(10),
;
private final int code;
Module(int code) { this.code = code * 100; }
public int code() {return code; }
}

View File

@ -0,0 +1,71 @@
package com.agileboot.common.utils;
import cn.hutool.core.util.StrUtil;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
/**
* 客户端工具类
*
* @author ruoyi
*/
public class ServletHolderUtil {
/**
* 获取request
*/
public static HttpServletRequest getRequest() {
return getRequestAttributes().getRequest();
}
/**
* 获取response
*/
public static HttpServletResponse getResponse() {
return getRequestAttributes().getResponse();
}
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();
}
}
/**
* 获取仅含有项目根路径的url
* 比如 localhost:8080/agileboot/user/list
* 返回 localhost:8080/agileboot
* @return
*/
public static String getContextUrl() {
HttpServletRequest request = getRequest();
StringBuffer url = request.getRequestURL();
String contextPath = request.getServletContext().getContextPath();
String strip = StrUtil.strip(url, null, request.getRequestURI());
return strip + contextPath;
}
}

View File

@ -0,0 +1,216 @@
package com.agileboot.common.utils.file;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import com.agileboot.common.config.AgileBootConfig;
import com.agileboot.common.constant.Constants;
import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.Objects;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.FilenameUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.util.MimeType;
import org.springframework.util.MimeTypeUtils;
import org.springframework.web.multipart.MultipartFile;
/**
* 文件上传工具类
*
* @author ruoyi 待改进
*/
public class FileUploadUtils {
/**
* 默认大小 50M
*/
public static final long MAX_FILE_SIZE = 50 * Constants.MB;
/**
* 默认的文件名最大长度 100
*/
public static final int MAX_FILE_NAME_LENGTH = 100;
public static final String[] ALLOWED_DOWNLOAD_EXTENSIONS = {
// 图片
"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"};
/**
* 默认上传的地址
*/
private static String defaultBaseDir = AgileBootConfig.getProfile();
public static String getDefaultBaseDir() {
return defaultBaseDir;
}
/**
* 以默认配置进行文件上传
*
* @param file 上传的文件
* @return 文件名称
*/
public static String upload(MultipartFile file) throws IOException {
try {
return upload(getDefaultBaseDir(), file, ALLOWED_DOWNLOAD_EXTENSIONS);
} catch (Exception e) {
throw new IOException(e.getMessage(), e);
}
}
/**
* 根据文件路径上传
*
* @param baseDir 相对应用的基目录
* @param file 上传的文件
* @return 文件名称
*/
public static String upload(String baseDir, MultipartFile file) throws IOException {
try {
return upload(baseDir, file, ALLOWED_DOWNLOAD_EXTENSIONS);
} catch (Exception e) {
throw new IOException(e.getMessage(), e);
}
}
/**
* 文件上传
*
* @param baseDir 相对应用的基目录
* @param file 上传的文件
* @param allowedExtension 上传文件类型
* @return 返回上传成功的文件名
* @throws IOException 比如读写文件出错时
*/
public static String upload(String baseDir, MultipartFile file, String[] allowedExtension)
throws IOException {
int fileNameLength = Objects.requireNonNull(file.getOriginalFilename()).length();
if (fileNameLength > FileUploadUtils.MAX_FILE_NAME_LENGTH) {
throw new ApiException(ErrorCode.Business.UPLOAD_FILE_NAME_EXCEED_MAX_LENGTH, MAX_FILE_NAME_LENGTH);
}
assertAllowed(file, allowedExtension);
String fileName = generateFilename(file);
String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath();
file.transferTo(Paths.get(absPath));
return getPathFileName(baseDir, fileName);
}
/**
* 编码文件名
*/
public static String generateFilename(MultipartFile file) {
return StrUtil.format("{}_{}_{}.{}", DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_PATTERN),
FilenameUtils.getBaseName(file.getOriginalFilename()), IdUtil.simpleUUID(), getExtension(file));
}
public static File getAbsoluteFile(String uploadDir, String fileName) {
File desc = new File(uploadDir + File.separator + fileName);
if (!desc.exists()) {
if (!desc.getParentFile().exists()) {
desc.getParentFile().mkdirs();
}
}
return desc;
}
public static String getPathFileName(String uploadDir, String fileName) {
String currentDir = StrUtil.strip(uploadDir, AgileBootConfig.getProfile() + "/");
return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
}
/**
* 文件大小校验
*
* @param file 上传的文件
*/
public static void assertAllowed(MultipartFile file, String[] allowedExtension) {
long size = file.getSize();
if (size > MAX_FILE_SIZE) {
throw new ApiException(ErrorCode.Business.UPLOAD_FILE_SIZE_EXCEED_MAX_SIZE, MAX_FILE_SIZE / Constants.MB);
}
String extension = getExtension(file);
if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) {
throw new ApiException(ErrorCode.Business.UPLOAD_FILE_TYPE_NOT_ALLOWED,
StrUtil.join(",", (Object[]) allowedExtension));
}
}
/**
* 判断MIME类型是否是允许的MIME类型
*/
public static boolean isAllowedExtension(String extension, String[] allowedExtension) {
return StrUtil.containsAnyIgnoreCase(extension, allowedExtension);
}
/**
* 获取文件名的后缀
*
* @param file 表单文件
* @return 后缀名
*/
public static String getExtension(MultipartFile file) {
String extension = FilenameUtils.getExtension(file.getOriginalFilename());
if (StrUtil.isEmpty(extension)) {
MimeType mimeType = MimeTypeUtils.parseMimeType(Objects.requireNonNull(file.getContentType()));
extension = mimeType.getSubtype();
}
return extension;
}
/**
* 检查文件是否可下载
*
* @param resource 需要下载的文件
* @return true 正常 false 非法
*/
public static boolean isAllowDownload(String resource) {
// 禁止目录上跳级别
return !StrUtil.contains(resource, "..") &&
// 检查允许下载的文件规则
StrUtil.containsAnyIgnoreCase(FileNameUtil.getSuffix(resource), ALLOWED_DOWNLOAD_EXTENSIONS);
}
/**
* 下载文件名重新编码
*
* @param response 响应对象
* @param realFileName 真实文件名
*/
public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) {
String fileNameUrlEncoded = URLUtil.encode(realFileName, CharsetUtil.CHARSET_UTF_8);
String contentDisposition = String.format("attachment; filename=%s;filename*=utf-8''%s", fileNameUrlEncoded,
fileNameUrlEncoded);
response.addHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "Content-Disposition,download-filename");
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, contentDisposition);
response.setHeader("download-filename", fileNameUrlEncoded);
}
}

View File

@ -0,0 +1,25 @@
package com.agileboot.common.utils.i18n;
import cn.hutool.extra.spring.SpringUtil;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
/**
* 获取i18n资源文件
*
* @author ruoyi
*/
public class MessageUtils {
/**
* 根据消息键和参数 获取消息 委托给spring messageSource
*
* @param code 消息键
* @param args 参数
* @return 获取国际化翻译值
*/
public static String message(String code, Object... args) {
MessageSource messageSource = SpringUtil.getBean(MessageSource.class);
return messageSource.getMessage(code, args, LocaleContextHolder.getLocale());
}
}

View File

@ -0,0 +1,32 @@
package com.agileboot.common.utils.ip;
import cn.hutool.core.text.CharSequenceUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class IpRegion {
private static final String UNKNOWN = "未知";
private String country;
private String region;
private String province;
private String city;
private String isp;
public IpRegion(String province, String city) {
this.province = province;
this.city = city;
}
public String briefLocation() {
return String.format("%s %s",
CharSequenceUtil.nullToDefault(province, UNKNOWN),
CharSequenceUtil.nullToDefault(city, UNKNOWN));
}
}

View File

@ -0,0 +1,26 @@
package com.agileboot.common.utils.ip;
/**
* @author valarchie
*/
public class IpRegionUtil {
public static IpRegion getIpRegion(String ip) {
IpRegion ipRegionOffline = OfflineIpRegionUtil.getIpRegion(ip);
if (ipRegionOffline != null) {
return ipRegionOffline;
}
IpRegion ipRegionOnline = OnlineIpRegionUtil.getIpRegion(ip);
if (ipRegionOnline != null) {
return ipRegionOnline;
}
return new IpRegion();
}
public static String getBriefLocationByIp(String ip) {
return getIpRegion(ip).briefLocation();
}
}

View File

@ -0,0 +1,51 @@
package com.agileboot.common.utils.ip;
import cn.hutool.core.util.StrUtil;
import java.io.IOException;
import java.io.InputStream;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.lionsoul.ip2region.xdb.Searcher;
@Slf4j
public class OfflineIpRegionUtil {
private static Searcher searcher;
static {
InputStream resourceAsStream = OfflineIpRegionUtil.class.getResourceAsStream("/ip2region.xdb");
byte[] bytes = null;
try {
bytes = new byte[resourceAsStream.available()];
IOUtils.read(resourceAsStream, bytes);
} catch (IOException e) {
log.error("读取本地Ip文件失败", e);
}
try {
searcher = Searcher.newWithBuffer(bytes);
} catch (Exception e) {
log.error("构建本地Ip缓存失败", e);
}
}
public static IpRegion getIpRegion(String ip) {
try {
String rawRegion = searcher.search(ip);
if (StrUtil.isNotEmpty(rawRegion)) {
String[] split = rawRegion.split("\\|");
return new IpRegion(split[0], split[1], split[2], split[3], split[4]);
}
return null;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

View File

@ -0,0 +1,49 @@
package com.agileboot.common.utils.ip;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.agileboot.common.config.AgileBootConfig;
import com.agileboot.common.utils.jackson.JacksonUtil;
import lombok.extern.slf4j.Slf4j;
/**
* query geography address from ip
*
* @author valarchie
*/
@Slf4j
public class OnlineIpRegionUtil {
/**
* website for query geography address from ip
*/
public static final String ADDRESS_QUERY_SITE = "http://whois.pconline.com.cn/ipJson.jsp";
public static IpRegion getIpRegion(String ip) {
// no need to query address for inner ip
if (NetUtil.isInnerIP(ip)) {
return new IpRegion("internal", "IP");
}
if (AgileBootConfig.isAddressEnabled()) {
try {
String rspStr = HttpUtil.get(ADDRESS_QUERY_SITE + "ip=" + ip + "&json=true",
CharsetUtil.CHARSET_GBK);
if (StrUtil.isEmpty(rspStr)) {
log.error("获取地理位置异常 {}", ip);
return null;
}
String province = JacksonUtil.getAsString(rspStr, "pro");
String city = JacksonUtil.getAsString(rspStr, "city");
return new IpRegion(province, city);
} catch (Exception e) {
log.error("获取地理位置异常 {}", ip);
}
}
return null;
}
}

View File

@ -0,0 +1,23 @@
package com.agileboot.common.utils.jackson;
/**
* @author valarchie
*/
public class JacksonException extends RuntimeException {
public JacksonException() {
super();
}
public JacksonException(String message) {
super(message);
}
public JacksonException(String message, Exception e) {
super(message, e);
}
public JacksonException(Throwable cause) {
super(cause);
}
}

View File

@ -0,0 +1,679 @@
package com.agileboot.common.utils.jackson;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.json.JsonReadFeature;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.fasterxml.jackson.databind.type.MapType;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
/**
* Jackson工具类 优势 数据量高于百万的时候速度和FastJson相差极小 API和注解支持最完善可定制性最强
* 支持的数据源最广泛字符串对象文件URL
* @author valarchie
*/
@Slf4j
public class JacksonUtil {
private static ObjectMapper mapper;
private static final Set<JsonReadFeature> JSON_READ_FEATURES_ENABLED = CollUtil.newHashSet(
//允许在JSON中使用Java注释
JsonReadFeature.ALLOW_JAVA_COMMENTS,
//允许 json 存在没用双引号括起来的 field
JsonReadFeature.ALLOW_UNQUOTED_FIELD_NAMES,
//允许 json 存在使用单引号括起来的 field
JsonReadFeature.ALLOW_SINGLE_QUOTES,
//允许 json 存在没用引号括起来的 ascii 控制字符
JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS,
//允许 json number 类型的数存在前导 0 (: 0001)
JsonReadFeature.ALLOW_LEADING_ZEROS_FOR_NUMBERS,
//允许 json 存在 NaN, INF, -INF 作为 number 类型
JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS,
//允许 只有Key没有Value的情况
JsonReadFeature.ALLOW_MISSING_VALUES,
//允许数组json的结尾多逗号
JsonReadFeature.ALLOW_TRAILING_COMMA
);
static {
try {
//初始化
mapper = initMapper();
} catch (Exception e) {
log.error("jackson config error", e);
}
}
public static ObjectMapper initMapper() {
JsonMapper.Builder builder = JsonMapper.builder()
.enable(JSON_READ_FEATURES_ENABLED.toArray(new JsonReadFeature[0]));
return initMapperConfig(builder.build());
}
public static ObjectMapper initMapperConfig(ObjectMapper objectMapper) {
String dateTimeFormat = "yyyy-MM-dd HH:mm:ss";
objectMapper.setDateFormat(new SimpleDateFormat(dateTimeFormat));
//配置序列化级别
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
//配置JSON缩进支持
objectMapper.configure(SerializationFeature.INDENT_OUTPUT, false);
//允许单个数值当做数组处理
objectMapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
//禁止重复键, 抛出异常
objectMapper.enable(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY);
//禁止使用int代表Enum的order()來反序列化Enum, 抛出异常
objectMapper.enable(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS);
//有属性不能映射的时候不报错
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
//对象为空时不抛异常
objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
//时间格式
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
//允许未知字段
objectMapper.enable(JsonGenerator.Feature.IGNORE_UNKNOWN);
//序列化BigDecimal时之间输出原始数字还是科学计数, 默认false, 即是否以toPlainString()科学计数方式来输出
objectMapper.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
//识别Java8时间
objectMapper.registerModule(new ParameterNamesModule());
objectMapper.registerModule(new Jdk8Module());
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDateTime.class,
new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(dateTimeFormat)))
.addDeserializer(LocalDateTime.class,
new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(dateTimeFormat)));
objectMapper.registerModule(javaTimeModule);
// if we use guava, we can add this line of code: objectMapper.registerModule(new GuavaModule())
return objectMapper;
}
public static ObjectMapper getObjectMapper() {
return mapper;
}
/**
* JSON反序列化
*/
public static <V> V from(URL url, Class<V> type) {
try {
return mapper.readValue(url, type);
} catch (IOException e) {
throw new JacksonException(StrUtil.format("jackson from error, url: {}, type: {}", url.getPath(), type), e);
}
}
/**
* JSON反序列化
*/
public static <V> V from(URL url, TypeReference<V> type) {
try {
return mapper.readValue(url, type);
} catch (IOException e) {
throw new JacksonException(StrUtil.format("jackson from error, url: {}, type: {}", url.getPath(), type), e);
}
}
/**
* JSON反序列化List
*/
public static <V> List<V> fromList(URL url, Class<V> type) {
try {
CollectionType collectionType = mapper.getTypeFactory().constructCollectionType(ArrayList.class, type);
return mapper.readValue(url, collectionType);
} catch (IOException e) {
throw new JacksonException(StrUtil.format("jackson from error, url: {}, type: {}", url.getPath(), type), e);
}
}
/**
* JSON反序列化
*/
public static <V> V from(InputStream inputStream, Class<V> type) {
try {
return mapper.readValue(inputStream, type);
} catch (IOException e) {
throw new JacksonException(StrUtil.format("jackson from error, type: {}", type), e);
}
}
/**
* JSON反序列化
*/
public static <V> V from(InputStream inputStream, TypeReference<V> type) {
try {
return mapper.readValue(inputStream, type);
} catch (IOException e) {
throw new JacksonException(StrUtil.format("jackson from error, type: {}", type), e);
}
}
/**
* JSON反序列化List
*/
public static <V> List<V> fromList(InputStream inputStream, Class<V> type) {
try {
CollectionType collectionType = mapper.getTypeFactory().constructCollectionType(ArrayList.class, type);
return mapper.readValue(inputStream, collectionType);
} catch (IOException e) {
throw new JacksonException(StrUtil.format("jackson from error, type: {}", type), e);
}
}
/**
* JSON反序列化
*/
public static <V> V from(File file, Class<V> type) {
try {
return mapper.readValue(file, type);
} catch (IOException e) {
throw new JacksonException(
StrUtil.format("jackson from error, file path: {}, type: {}", file.getPath(), type), e);
}
}
/**
* JSON反序列化
*/
public static <V> V from(File file, TypeReference<V> type) {
try {
return mapper.readValue(file, type);
} catch (IOException e) {
throw new JacksonException(
StrUtil.format("jackson from error, file path: {}, type: {}", file.getPath(), type), e);
}
}
/**
* JSON反序列化List
*/
public static <V> List<V> fromList(File file, Class<V> type) {
try {
CollectionType collectionType = mapper.getTypeFactory().constructCollectionType(ArrayList.class, type);
return mapper.readValue(file, collectionType);
} catch (IOException e) {
throw new JacksonException(
StrUtil.format("jackson from error, file path: {}, type: {}", file.getPath(), type), e);
}
}
/**
* JSON反序列化
*/
public static <V> V from(String json, Class<V> type) {
return from(json, (Type) type);
}
/**
* JSON反序列化
*/
public static <V> V from(String json, TypeReference<V> type) {
return from(json, type.getType());
}
/**
* JSON反序列化
*/
public static <V> V from(String json, Type type) {
if (StringUtils.isEmpty(json)) {
return null;
}
try {
JavaType javaType = mapper.getTypeFactory().constructType(type);
return mapper.readValue(json, javaType);
} catch (IOException e) {
throw new JacksonException(StrUtil.format("jackson from error, json: {}, type: {}", json, type), e);
}
}
/**
* JSON反序列化List
*/
public static <V> List<V> fromList(String json, Class<V> type) {
if (StringUtils.isEmpty(json)) {
return null;
}
try {
CollectionType collectionType = mapper.getTypeFactory().constructCollectionType(ArrayList.class, type);
return mapper.readValue(json, collectionType);
} catch (IOException e) {
throw new JacksonException(StrUtil.format("jackson from error, json: {}, type: {}", json, type), e);
}
}
/**
* JSON反序列化Map
*/
public static Map<String, Object> fromMap(String json) {
if (StringUtils.isEmpty(json)) {
return null;
}
try {
MapType mapType = mapper.getTypeFactory().constructMapType(HashMap.class, String.class, Object.class);
return mapper.readValue(json, mapType);
} catch (IOException e) {
throw new JacksonException(StrUtil.format("jackson from error, json: {}, type: {}", json), e);
}
}
/**
* 序列化为JSON
*/
public static <V> String to(List<V> list) {
try {
return mapper.writeValueAsString(list);
} catch (JsonProcessingException e) {
throw new JacksonException(StrUtil.format("jackson to error, data: {}", list), e);
}
}
/**
* 序列化为JSON
*/
public static <V> String to(V v) {
try {
return mapper.writeValueAsString(v);
} catch (JsonProcessingException e) {
throw new JacksonException(StrUtil.format("jackson to error, data: {}", v), e);
}
}
/**
* 序列化为JSON
*/
public static <V> void toFile(String path, List<V> list) {
try (Writer writer = new FileWriter(new File(path), true)) {
mapper.writer().writeValues(writer).writeAll(list);
} catch (Exception e) {
throw new JacksonException(StrUtil.format("jackson to file error, path: {}, list: {}", path, list), e);
}
}
/**
* 序列化为JSON
*/
public static <V> void toFile(String path, V v) {
try (Writer writer = new FileWriter(new File(path), true)) {
mapper.writer().writeValues(writer).write(v);
} catch (Exception e) {
throw new JacksonException(StrUtil.format("jackson to file error, path: {}, data: {}", path, v), e);
}
}
/**
* 从json串中获取某个字段
*
* @return String默认为 null
*/
public static String getAsString(String json, String key) {
if (StringUtils.isEmpty(json)) {
return null;
}
try {
JsonNode jsonNode = getAsJsonObject(json, key);
if (null == jsonNode) {
return null;
}
return getAsString(jsonNode);
} catch (Exception e) {
throw new JacksonException(StrUtil.format("jackson get string error, json: {}, key: {}", json, key), e);
}
}
private static String getAsString(JsonNode jsonNode) {
return jsonNode.isTextual() ? jsonNode.textValue() : jsonNode.toString();
}
/**
* 从json串中获取某个字段
*
* @return int默认为 0
*/
public static int getAsInt(String json, String key) {
if (StringUtils.isEmpty(json)) {
return 0;
}
try {
JsonNode jsonNode = getAsJsonObject(json, key);
if (null == jsonNode) {
return 0;
}
return jsonNode.isInt() ? jsonNode.intValue() : Integer.parseInt(getAsString(jsonNode));
} catch (Exception e) {
throw new JacksonException(StrUtil.format("jackson get int error, json: {}, key: {}", json, key), e);
}
}
/**
* 从json串中获取某个字段
*
* @return long默认为 0
*/
public static long getAsLong(String json, String key) {
if (StringUtils.isEmpty(json)) {
return 0L;
}
try {
JsonNode jsonNode = getAsJsonObject(json, key);
if (null == jsonNode) {
return 0L;
}
return jsonNode.isLong() ? jsonNode.longValue() : Long.parseLong(getAsString(jsonNode));
} catch (Exception e) {
throw new JacksonException(StrUtil.format("jackson get long error, json: {}, key: {}", json, key), e);
}
}
/**
* 从json串中获取某个字段
*
* @return double默认为 0.0
*/
public static double getAsDouble(String json, String key) {
if (StringUtils.isEmpty(json)) {
return 0.0;
}
try {
JsonNode jsonNode = getAsJsonObject(json, key);
if (null == jsonNode) {
return 0.0;
}
return jsonNode.isDouble() ? jsonNode.doubleValue() : Double.parseDouble(getAsString(jsonNode));
} catch (Exception e) {
throw new JacksonException(StrUtil.format("jackson get double error, json: {}, key: {}", json, key), e);
}
}
/**
* 从json串中获取某个字段
*
* @return BigInteger默认为 0.0
*/
public static BigInteger getAsBigInteger(String json, String key) {
if (StringUtils.isEmpty(json)) {
return new BigInteger(String.valueOf(0.00));
}
try {
JsonNode jsonNode = getAsJsonObject(json, key);
if (null == jsonNode) {
return new BigInteger(String.valueOf(0.00));
}
return jsonNode.isBigInteger() ? jsonNode.bigIntegerValue() : new BigInteger(getAsString(jsonNode));
} catch (Exception e) {
throw new JacksonException(StrUtil.format("jackson get big integer error, json: {}, key: {}", json, key),
e);
}
}
/**
* 从json串中获取某个字段
*
* @return BigDecimal默认为 0.00
*/
public static BigDecimal getAsBigDecimal(String json, String key) {
if (StringUtils.isEmpty(json)) {
return new BigDecimal("0.00");
}
try {
JsonNode jsonNode = getAsJsonObject(json, key);
if (null == jsonNode) {
return new BigDecimal("0.00");
}
return jsonNode.isBigDecimal() ? jsonNode.decimalValue() : new BigDecimal(getAsString(jsonNode));
} catch (Exception e) {
throw new JacksonException(StrUtil.format("jackson get big decimal error, json: {}, key: {}", json, key),
e);
}
}
/**
* 从json串中获取某个字段
*
* @return boolean, 默认为false
*/
public static boolean getAsBoolean(String json, String key) {
if (StringUtils.isEmpty(json)) {
return false;
}
try {
JsonNode jsonNode = getAsJsonObject(json, key);
if (null == jsonNode) {
return false;
}
if (jsonNode.isBoolean()) {
return jsonNode.booleanValue();
} else {
if (jsonNode.isTextual()) {
String textValue = jsonNode.textValue();
return Convert.toBool(textValue);
} else {//number
return BooleanUtils.toBoolean(jsonNode.intValue());
}
}
} catch (Exception e) {
throw new JacksonException(StrUtil.format("jackson get boolean error, json: {}, key: {}", json, key), e);
}
}
/**
* 从json串中获取某个字段
*
* @return byte[], 默认为 null
*/
public static byte[] getAsBytes(String json, String key) {
if (StringUtils.isEmpty(json)) {
return null;
}
try {
JsonNode jsonNode = getAsJsonObject(json, key);
if (null == jsonNode) {
return null;
}
return jsonNode.isBinary() ? jsonNode.binaryValue() : getAsString(jsonNode).getBytes();
} catch (Exception e) {
throw new JacksonException(StrUtil.format("jackson get byte error, json: {}, key: {}", json, key), e);
}
}
/**
* 从json串中获取某个字段
*
* @return object, 默认为 null
*/
public static <V> V getAsObject(String json, String key, Class<V> type) {
if (StringUtils.isEmpty(json)) {
return null;
}
try {
JsonNode jsonNode = getAsJsonObject(json, key);
if (null == jsonNode) {
return null;
}
JavaType javaType = mapper.getTypeFactory().constructType(type);
return from(getAsString(jsonNode), javaType);
} catch (Exception e) {
throw new JacksonException(
StrUtil.format("jackson get list error, json: {}, key: {}, type: {}", json, key, type), e);
}
}
/**
* 从json串中获取某个字段
*
* @return list, 默认为 null
*/
public static <V> List<V> getAsList(String json, String key, Class<V> type) {
if (StringUtils.isEmpty(json)) {
return null;
}
try {
JsonNode jsonNode = getAsJsonObject(json, key);
if (null == jsonNode) {
return null;
}
CollectionType collectionType = mapper.getTypeFactory().constructCollectionType(ArrayList.class, type);
return from(getAsString(jsonNode), collectionType);
} catch (Exception e) {
throw new JacksonException(
StrUtil.format("jackson get list error, json: {}, key: {}, type: {}", json, key, type), e);
}
}
/**
* 从json串中获取某个字段
*
* @return JsonNode, 默认为 null
*/
public static JsonNode getAsJsonObject(String json, String key) {
try {
JsonNode node = mapper.readTree(json);
if (null == node) {
return null;
}
return node.get(key);
} catch (IOException e) {
throw new JacksonException(
StrUtil.format("jackson get object from json error, json: {}, key: {}", json, key), e);
}
}
/**
* 向json中添加属性
*
* @return json
*/
public static <V> String add(String json, String key, V value) {
try {
JsonNode node = mapper.readTree(json);
add(node, key, value);
return node.toString();
} catch (IOException e) {
throw new JacksonException(
StrUtil.format("jackson add error, json: {}, key: {}, value: {}", json, key, value), e);
}
}
/**
* 向json中添加属性
*/
private static <V> void add(JsonNode jsonNode, String key, V value) {
if (value instanceof String) {
((ObjectNode) jsonNode).put(key, (String) value);
} else if (value instanceof Short) {
((ObjectNode) jsonNode).put(key, (Short) value);
} else if (value instanceof Integer) {
((ObjectNode) jsonNode).put(key, (Integer) value);
} else if (value instanceof Long) {
((ObjectNode) jsonNode).put(key, (Long) value);
} else if (value instanceof Float) {
((ObjectNode) jsonNode).put(key, (Float) value);
} else if (value instanceof Double) {
((ObjectNode) jsonNode).put(key, (Double) value);
} else if (value instanceof BigDecimal) {
((ObjectNode) jsonNode).put(key, (BigDecimal) value);
} else if (value instanceof BigInteger) {
((ObjectNode) jsonNode).put(key, (BigInteger) value);
} else if (value instanceof Boolean) {
((ObjectNode) jsonNode).put(key, (Boolean) value);
} else if (value instanceof byte[]) {
((ObjectNode) jsonNode).put(key, (byte[]) value);
} else {
((ObjectNode) jsonNode).put(key, to(value));
}
}
/**
* 除去json中的某个属性
*
* @return json
*/
public static String remove(String json, String key) {
try {
JsonNode node = mapper.readTree(json);
((ObjectNode) node).remove(key);
return node.toString();
} catch (IOException e) {
throw new JacksonException(StrUtil.format("jackson remove error, json: {}, key: {}", json, key), e);
}
}
/**
* 修改json中的属性
*/
public static <V> String update(String json, String key, V value) {
try {
JsonNode node = mapper.readTree(json);
((ObjectNode) node).remove(key);
add(node, key, value);
return node.toString();
} catch (IOException e) {
throw new JacksonException(
StrUtil.format("jackson update error, json: {}, key: {}, value: {}", json, key, value), e);
}
}
/**
* 格式化Json(美化)
*
* @return json
*/
public static String format(String json) {
try {
JsonNode node = mapper.readTree(json);
return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(node);
} catch (IOException e) {
throw new JacksonException(StrUtil.format("jackson format json error, json: {}", json), e);
}
}
/**
* 判断字符串是否是json
*
* @return json
*/
public static boolean isJson(String json) {
try {
mapper.readTree(json);
return true;
} catch (Exception e) {
return false;
}
}
}

View File

@ -0,0 +1,86 @@
package com.agileboot.common.utils.poi;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.agileboot.common.annotation.ExcelColumn;
import com.agileboot.common.annotation.ExcelSheet;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.multipart.MultipartFile;
/**
* @author valarchie
*/
public class CustomExcelUtil {
public static void writeToResponse(List<?> list, Class clazz, HttpServletResponse response) {
// 通过工具类创建writer
ExcelWriter writer = ExcelUtil.getWriter();
ExcelSheet sheetAnno = (ExcelSheet)clazz.getAnnotation(ExcelSheet.class);
if (sheetAnno != null) {
// 默认的sheetName是 sheet1
writer.renameSheet(sheetAnno.name());
}
Field[] fields = clazz.getDeclaredFields();
//自定义标题别名
for (Field field : fields) {
ExcelColumn annotation = field.getAnnotation(ExcelColumn.class);
if (annotation != null) {
writer.addHeaderAlias(field.getName(), annotation.name());
}
}
// 默认的未添加alias的属性也会写出如果想只写出加了别名的字段可以调用此方法排除之
writer.setOnlyAlias(true);
// 合并单元格后的标题行使用默认标题样式
// writer.merge(4, "一班成绩单");
// 一次性写出内容使用默认样式强制输出标题
writer.write(list, true);
try {
writer.flush(response.getOutputStream(), true);
} catch (IOException e) {
writer.close();
e.printStackTrace();
}
}
public static List<?> readFromResponse(Class clazz, MultipartFile file) {
ExcelReader reader = null;
try {
reader = ExcelUtil.getReader(file.getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
Field[] fields = clazz.getDeclaredFields();
//自定义标题别名
if (fields != null) {
for (Field field : fields) {
ExcelColumn annotation = field.getAnnotation(ExcelColumn.class);
if (annotation != null) {
reader.addHeaderAlias(annotation.name(), field.getName());
}
}
}
return reader.read(0, 1, clazz);
}
}

View File

@ -0,0 +1,42 @@
package com.agileboot.common.utils.time;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import java.util.Date;
import lombok.extern.slf4j.Slf4j;
/**
* @author valarchie
*/
@Slf4j
public class DatePicker {
public static Date getBeginOfTheDay(Object date) {
if (date == null) {
return null;
}
try {
DateTime parse = DateUtil.parse(date.toString());
return DateUtil.beginOfDay(parse);
} catch (Exception e) {
log.error("pick begin of day failed, due to: ", e);
}
return null;
}
public static Date getEndOfTheDay(Object date) {
if (date == null) {
return null;
}
try {
DateTime parse = DateUtil.parse(date.toString());
return DateUtil.endOfDay(parse);
} catch (Exception e) {
log.error("pick end of day failed, due to: ", e);
}
return null;
}
}

Binary file not shown.

View File

@ -0,0 +1,24 @@
package com.agileboot.common.core.exception;
import org.junit.Assert;
import org.junit.Test;
public class ApiExceptionTest {
@Test
public void testVarargsWithArrayArgs() {
String errorMsg = "these parameters are null: %s, %s, %s.";
Object[] array = new Object[] { "param1" , "param2" , "param3"};
String format1 = String.format(errorMsg, array);
String format2 = String.format(errorMsg, "param1", "param2", "param3");
System.out.println(format1);
System.out.println(format2);
Assert.assertEquals(format1, format2);
}
}

View File

@ -0,0 +1,62 @@
package com.agileboot.common.utils;
import cn.hutool.core.date.DateUtil;
import com.agileboot.common.utils.jackson.JacksonUtil;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.junit.Assert;
import org.junit.Test;
/**
* @author duanxinyuan 2019/1/21 18:17
*/
public class JacksonUtilTest {
@Test
public void testObjectToJson() {
Person person = Person.newPerson();
String jacksonStr = JacksonUtil.to(person);
Assert.assertEquals(DateUtil.formatDateTime(person.getDate()), JacksonUtil.getAsString(jacksonStr, "date"));
Assert.assertEquals(DateUtil.formatLocalDateTime(person.getLocalDateTime()),
JacksonUtil.getAsString(jacksonStr, "localDateTime"));
Assert.assertEquals(person.getName(), JacksonUtil.getAsString(jacksonStr, "name"));
Assert.assertEquals(person.getAge(), JacksonUtil.getAsInt(jacksonStr, "age"));
Assert.assertEquals(person.isMan(), JacksonUtil.getAsBoolean(jacksonStr, "man"));
Assert.assertEquals(person.getMoney(), JacksonUtil.getAsBigDecimal(jacksonStr, "money"));
Assert.assertEquals(person.getTrait(), JacksonUtil.getAsList(jacksonStr, "trait", String.class));
Assert.assertNotNull(JacksonUtil.getAsString(jacksonStr, "name"));
}
/**
* 测试兼容情况
*/
@Test
public void testAllPrimitiveTypeToJson() {
String json = "{\n"
+ "\"code\": \"200\",\n"
+ "\"id\": \"2001215464647687987\",\n"
+ "\"message\": \"success\",\n"
+ "\"amount\": \"1.12345\",\n"
+ "\"amount1\": \"0.12345\",\n"
+ "\"isSuccess\": \"true\",\n"
+ "\"isSuccess1\": \"1\",\n"
+ "\"key\": \"8209167202090377654857374178856064487200234961995543450245362822537162918731039965956758726661669012305745755921310000297396309887550627402157318910581311\"\n"
+ "}";
Assert.assertEquals(200, JacksonUtil.getAsInt(json, "code"));
Assert.assertEquals(2001215464647687987L,JacksonUtil.getAsLong(json, "id"));
Assert.assertEquals("success", JacksonUtil.getAsString(json, "message"));
Assert.assertEquals(new BigDecimal("1.12345"), JacksonUtil.getAsBigDecimal(json, "amount"));
Assert.assertEquals(new BigDecimal("0.12345"), JacksonUtil.getAsBigDecimal(json, "amount1"));
Assert.assertEquals(1.12345d, JacksonUtil.getAsDouble(json, "amount"), 0.00001);
Assert.assertEquals(0.12345d, JacksonUtil.getAsDouble(json, "amount1"), 0.00001);
Assert.assertEquals(true, JacksonUtil.getAsBoolean(json, "isSuccess"));
Assert.assertEquals(true, JacksonUtil.getAsBoolean(json, "isSuccess1"));
Assert.assertEquals(new BigInteger(
"8209167202090377654857374178856064487200234961995543450245362822537162918731039965956758726661669012305745755921310000297396309887550627402157318910581311"),
JacksonUtil.getAsBigInteger(json, "key"));
Assert.assertEquals("1", JacksonUtil.getAsString(json, "isSuccess1"));
}
}

View File

@ -0,0 +1,42 @@
package com.agileboot.common.utils;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import lombok.Data;
/**
* @author duanxinyuan
* 2018/6/29 14:17
*/
@Data
public class Person {
public String name;
public Date date;
public LocalDateTime localDateTime;
public int age;
public BigDecimal money;
public boolean man;
public ArrayList<String> trait;
public HashMap<String, String> cards;
public static Person newPerson() {
Person person = new Person();
person.name = "张三";
person.date = new Date();
person.localDateTime = LocalDateTime.now();
person.age = 100;
person.money = BigDecimal.valueOf(500.21);
person.man = true;
person.trait = new ArrayList<>();
person.trait.add("淡然");
person.trait.add("温和");
person.cards = new HashMap<>();
person.cards.put("身份证", "4a6d456as");
person.cards.put("建行卡", "649874545");
return person;
}
}

View File

@ -0,0 +1,16 @@
package com.agileboot.common.utils.file;
import org.junit.Assert;
import org.junit.Test;
public class FileUploadUtilsTest {
@Test
public void isAllowedExtension() {
String[] imageTypes = new String[]{"img", "gif"};
boolean isAllow = FileUploadUtils.isAllowedExtension("img", imageTypes);
boolean isNotAllow = FileUploadUtils.isAllowedExtension("png", imageTypes);
Assert.assertTrue(isAllow);
Assert.assertFalse(isNotAllow);
}
}

View File

@ -0,0 +1,16 @@
package com.agileboot.common.utils.ip;
import org.junit.Assert;
import org.junit.Test;
public class OfflineIpRegionUtilTest {
@Test
public void getIpRegion() {
IpRegion ipRegion = OfflineIpRegionUtil.getIpRegion("110.81.189.80");
Assert.assertEquals("中国", ipRegion.getCountry());
Assert.assertEquals("福建省", ipRegion.getProvince());
Assert.assertEquals("泉州市", ipRegion.getCity());
}
}

47
agileboot-domain/pom.xml Normal file
View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>agileboot</artifactId>
<groupId>com.agileboot</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>agileboot-domain</artifactId>
<description>
领域核心代码 放在这个包
generator代码生成
</description>
<dependencies>
<!-- 通用工具-->
<dependency>
<groupId>com.agileboot</groupId>
<artifactId>agileboot-common</artifactId>
</dependency>
<!-- 通用工具-->
<dependency>
<groupId>com.agileboot</groupId>
<artifactId>agileboot-orm</artifactId>
</dependency>
<dependency>
<groupId>com.agileboot</groupId>
<artifactId>agileboot-infrastructure</artifactId>
</dependency>
<!--
&lt;!&ndash; 核心模块&ndash;&gt;
<dependency>
<groupId>com.agileboot</groupId>
<artifactId>agileboot-infrastructure</artifactId>
</dependency>-->
</dependencies>
</project>

View File

@ -0,0 +1,22 @@
package com.agileboot.domain.common;
import cn.hutool.core.collection.CollUtil;
import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode;
import java.util.List;
import lombok.Data;
@Data
public class BulkOperationCommand<T> {
public BulkOperationCommand(List<T> idList) {
if (CollUtil.isEmpty(idList)) {
throw new ApiException(ErrorCode.Business.BULK_DELETE_IDS_IS_INVALID);
}
this.ids = idList;
}
private List<T> ids;
}

View File

@ -0,0 +1,16 @@
package com.agileboot.domain.common;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class UploadFileDTO {
public UploadFileDTO(String imgUrl) {
this.imgUrl = imgUrl;
}
private String imgUrl;
}

View File

@ -0,0 +1,16 @@
package com.agileboot.domain.system;
import cn.hutool.core.lang.tree.Tree;
import java.util.List;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class TreeSelectedDTO {
private List<Long> checkedKeys;
private List<Tree<Long>> menus;
private List<Tree<Long>> depts;
}

View File

@ -0,0 +1,43 @@
package com.agileboot.domain.system.config;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.json.JSONUtil;
import com.agileboot.orm.entity.SysConfigEntity;
import com.agileboot.orm.enums.dictionary.CommonAnswerEnum;
import com.agileboot.orm.enums.interfaces.BasicEnumUtil;
import java.util.Date;
import java.util.List;
import lombok.Data;
@Data
public class ConfigDTO {
public ConfigDTO(SysConfigEntity entity) {
if (entity != null) {
configId = entity.getConfigId() + "";
configName = entity.getConfigName();
configKey = entity.getConfigKey();
configValue = entity.getConfigValue();
configOptions =
JSONUtil.isTypeJSONArray(entity.getConfigOptions()) ? JSONUtil.toList(entity.getConfigOptions(),
String.class) : ListUtil.empty();
isAllowChange = Convert.toInt(entity.getIsAllowChange()) + "";
isAllowChangeStr = BasicEnumUtil.getDescriptionByBool(CommonAnswerEnum.class, entity.getIsAllowChange());
remark = entity.getRemark();
createTime = entity.getCreateTime();
}
}
private String configId;
private String configName;
private String configKey;
private String configValue;
private List<String> configOptions;
private String isAllowChange;
private String isAllowChangeStr;
private String remark;
private Date createTime;
}

View File

@ -0,0 +1,58 @@
package com.agileboot.domain.system.config;
import com.agileboot.common.core.page.PageDTO;
import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode;
import com.agileboot.infrastructure.web.domain.login.LoginUser;
import com.agileboot.orm.entity.SysConfigEntity;
import com.agileboot.orm.service.ISysConfigService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* @author valarchie
*/
@Service
public class ConfigDomainService {
@Autowired
private ISysConfigService configService;
public PageDTO getConfigList(ConfigQuery query) {
Page<SysConfigEntity> page = configService.page(query.toPage(), query.toQueryWrapper());
List<ConfigDTO> records = page.getRecords().stream().map(ConfigDTO::new).collect(Collectors.toList());
return new PageDTO(records, page.getTotal());
}
public ConfigDTO getConfigInfo(Long id) {
SysConfigEntity byId = configService.getById(id);
return new ConfigDTO(byId);
}
@Transactional
public void updateConfig(ConfigUpdateCommand updateCommand, LoginUser loginUser) {
ConfigModel configModel = getConfigModel(updateCommand.getConfigId());
configModel.setConfigValue(updateCommand.getConfigValue());
configModel.checkCanBeEdit();
configModel.logUpdater(loginUser);
configModel.updateById();
}
public ConfigModel getConfigModel(Long id) {
SysConfigEntity byId = configService.getById(id);
if (byId == null) {
throw new ApiException(ErrorCode.Business.OBJECT_NOT_FOUND, id, "参数配置");
}
return new ConfigModel(byId);
}
}

View File

@ -0,0 +1,42 @@
package com.agileboot.domain.system.config;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode;
import com.agileboot.orm.entity.SysConfigEntity;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import lombok.Data;
@Data
public class ConfigModel extends SysConfigEntity {
private Set<String> configOptionSet;
public ConfigModel(SysConfigEntity entity) {
BeanUtil.copyProperties(entity, this);
List<String> options =
JSONUtil.isTypeJSONArray(entity.getConfigOptions()) ? JSONUtil.toList(entity.getConfigOptions(),
String.class) : ListUtil.empty();
this.configOptionSet = new HashSet<>(options);
}
public void checkCanBeEdit() {
if (StrUtil.isBlank(getConfigValue())) {
throw new ApiException(ErrorCode.Business.CONFIG_VALUE_IS_NOT_ALLOW_TO_EMPTY);
}
if(!configOptionSet.isEmpty()&& !configOptionSet.contains(getConfigValue())) {
throw new ApiException(ErrorCode.Business.CONFIG_VALUE_IS_NOT_IN_OPTIONS);
}
}
}

View File

@ -0,0 +1,34 @@
package com.agileboot.domain.system.config;
import cn.hutool.core.util.StrUtil;
import com.agileboot.orm.entity.SysConfigEntity;
import com.agileboot.orm.query.AbstractPageQuery;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class ConfigQuery extends AbstractPageQuery {
private String configName;
private String configKey;
private Boolean isAllowChange;
@SuppressWarnings("rawtypes")
@Override
public QueryWrapper toQueryWrapper() {
QueryWrapper<SysConfigEntity> sysNoticeWrapper = new QueryWrapper<>();
sysNoticeWrapper.like(StrUtil.isNotEmpty(configName), "config_name", configName);
sysNoticeWrapper.eq(StrUtil.isNotEmpty(configKey), "config_key", configKey);
sysNoticeWrapper.eq(isAllowChange != null, "is_allow_change", isAllowChange);
addSortCondition(sysNoticeWrapper);
addTimeCondition(sysNoticeWrapper, "create_time");
return sysNoticeWrapper;
}
}

View File

@ -0,0 +1,18 @@
package com.agileboot.domain.system.config;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Positive;
import lombok.Data;
@Data
public class ConfigUpdateCommand {
@NotNull
@Positive
private Long configId;
@NotNull
@NotEmpty
private String configValue;
}

View File

@ -0,0 +1,65 @@
package com.agileboot.domain.system.dept;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import lombok.Data;
@Data
public class AddDeptCommand {
/**
* 父部门ID
*/
private Long parentId;
/**
* 祖级列表
*/
private String ancestors;
/**
* 部门名称
*/
@NotBlank(message = "部门名称不能为空")
@Size(max = 30, message = "部门名称长度不能超过30个字符")
private String deptName;
/**
* 显示顺序
*/
@NotNull(message = "显示顺序不能为空")
private Integer orderNum;
/**
* 负责人
*/
private String leaderName;
/**
* 联系电话
*/
@Size(max = 11, message = "联系电话长度不能超过11个字符")
private String phone;
/**
* 邮箱
*/
@Email(message = "邮箱格式不正确")
@Size(max = 50, message = "邮箱长度不能超过50个字符")
private String email;
public DeptModel toModel() {
DeptModel model = new DeptModel();
model.setParentId(parentId);
model.setAncestors(ancestors);
model.setDeptName(deptName);
model.setOrderNum(orderNum);
model.setLeaderName(leaderName);
model.setPhone(phone);
model.setEmail(email);
return model;
}
}

View File

@ -0,0 +1,44 @@
package com.agileboot.domain.system.dept;
import com.agileboot.orm.entity.SysDeptEntity;
import com.agileboot.orm.enums.dictionary.CommonStatusEnum;
import com.agileboot.orm.enums.interfaces.BasicEnumUtil;
import lombok.Data;
@Data
public class DeptDTO {
public DeptDTO(SysDeptEntity entity) {
if (entity != null) {
this.deptId = entity.getDeptId();
this.parentId = entity.getParentId();
this.deptName = entity.getDeptName();
this.orderNum = entity.getOrderNum();
this.leaderName = entity.getLeaderName();
this.email = entity.getEmail();
this.phone = entity.getPhone();
this.status = entity.getStatus() + "";
this.statusStr = BasicEnumUtil.getDescriptionByValue(CommonStatusEnum.class, entity.getStatus());
}
}
private Long deptId;
private Long parentId;
private String deptName;
private Integer orderNum;
private String leaderName;
private String phone;
private String email;
private String status;
private String statusStr;
}

View File

@ -0,0 +1,126 @@
package com.agileboot.domain.system.dept;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.StrUtil;
import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode;
import com.agileboot.domain.system.TreeSelectedDTO;
import com.agileboot.infrastructure.web.domain.login.LoginUser;
import com.agileboot.orm.entity.SysDeptEntity;
import com.agileboot.orm.entity.SysRoleEntity;
import com.agileboot.orm.service.ISysDeptService;
import com.agileboot.orm.service.ISysRoleService;
import com.agileboot.orm.service.ISysUserService;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* @author valarchie
*/
@SuppressWarnings("AlibabaTransactionMustHaveRollback")
@Service
public class DeptDomainService {
@Autowired
private ISysDeptService deptService;
@Autowired
private ISysRoleService roleService;
@Autowired
private ISysUserService userService;
public List<DeptDTO> getDeptList(DeptQuery query) {
List<SysDeptEntity> list = deptService.list(query.toQueryWrapper());
return list.stream().map(DeptDTO::new).collect(Collectors.toList());
}
public DeptDTO getDeptInfo(Long id) {
SysDeptEntity byId = deptService.getById(id);
return new DeptDTO(byId);
}
public List<Tree<Long>> getDeptTree() {
List<SysDeptEntity> list = deptService.list();
return TreeUtil.build(list, 0L, (dept, tree) -> {
tree.setId(dept.getDeptId());
tree.setParentId(dept.getParentId());
tree.putExtra("label", dept.getDeptName());
});
}
public TreeSelectedDTO getDeptTreeForRole(Long roleId) {
List<Long> checkedKeys = new ArrayList<>();
SysRoleEntity role = roleService.getById(roleId);
if (role != null && StrUtil.isNotEmpty(role.getDeptIdSet())) {
checkedKeys = StrUtil.split(role.getDeptIdSet(), ",")
.stream().map(Long::new).collect(Collectors.toList());
}
TreeSelectedDTO selectedDTO = new TreeSelectedDTO();
selectedDTO.setDepts(getDeptTree());
selectedDTO.setCheckedKeys(checkedKeys);
return selectedDTO;
}
@Transactional
public void addDept(AddDeptCommand addCommand, LoginUser loginUser) {
DeptModel deptModel = addCommand.toModel();
if (deptService.checkDeptNameUnique(deptModel.getDeptName(), null, deptModel.getParentId())) {
throw new ApiException(ErrorCode.Business.DEPT_NAME_IS_NOT_UNIQUE, deptModel.getDeptName());
}
deptModel.generateAncestors(deptService);
deptModel.logCreator(loginUser);
deptModel.insert();
}
@Transactional
public void updateDept(UpdateDeptCommand updateCommand, LoginUser loginUser) {
// TODO 需要再调整一下
getDeptModel(updateCommand.getDeptId());
DeptModel deptModel = updateCommand.toModel();
if (deptService.checkDeptNameUnique(deptModel.getDeptName(), deptModel.getDeptId(), deptModel.getParentId())) {
throw new ApiException(ErrorCode.Business.DEPT_NAME_IS_NOT_UNIQUE, deptModel.getDeptName());
}
deptModel.checkParentId();
deptModel.checkStatusAllowChange(deptService);
deptModel.generateAncestors(deptService);
deptModel.logUpdater(loginUser);
deptModel.updateById();
}
@Transactional
public void removeDept(Long deptId) {
DeptModel deptModel = getDeptModel(deptId);
deptModel.checkExistChildDept(deptService);
deptModel.checkExistLinkedUsers(userService);
deptService.removeById(deptId);
}
public DeptModel getDeptModel(Long id) {
SysDeptEntity byId = deptService.getById(id);
if (byId == null) {
throw new ApiException(ErrorCode.Business.OBJECT_NOT_FOUND, id, "参数配置");
}
return new DeptModel(byId);
}
}

View File

@ -0,0 +1,67 @@
package com.agileboot.domain.system.dept;
import cn.hutool.core.bean.BeanUtil;
import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode;
import com.agileboot.orm.entity.SysDeptEntity;
import com.agileboot.orm.enums.dictionary.CommonStatusEnum;
import com.agileboot.orm.enums.interfaces.BasicEnumUtil;
import com.agileboot.orm.service.ISysDeptService;
import com.agileboot.orm.service.ISysUserService;
import java.util.Objects;
import lombok.NoArgsConstructor;
@NoArgsConstructor
public class DeptModel extends SysDeptEntity {
public DeptModel(SysDeptEntity entity) {
if (entity != null) {
BeanUtil.copyProperties(entity, this);
}
}
public void checkParentId() {
if (Objects.equals(getParentId(), getDeptId())) {
throw new ApiException(ErrorCode.Business.DEPT_PARENT_ID_IS_NOT_ALLOWED_SELF);
}
}
public void checkExistChildDept(ISysDeptService deptService) {
if (deptService.hasChildDeptById(getDeptId())) {
throw new ApiException(ErrorCode.Business.DEPT_EXIST_CHILD_DEPT_NOT_ALLOW_DELETE);
}
}
public void checkExistLinkedUsers(ISysUserService userService) {
if (userService.checkDeptExistUser(getDeptId())) {
throw new ApiException(ErrorCode.Business.DEPT_EXIST_LINK_USER_NOT_ALLOW_DELETE);
}
}
public void generateAncestors(ISysDeptService deptService) {
SysDeptEntity parentDept = deptService.getById(getParentId());
if (parentDept == null || CommonStatusEnum.DISABLE.equals(
BasicEnumUtil.fromValue(CommonStatusEnum.class, parentDept.getStatus()))) {
throw new ApiException(ErrorCode.Business.DEPT_PARENT_DEPT_NO_EXIST_OR_DISABLED);
}
setAncestors(parentDept.getAncestors() + "," + getParentId());
}
/**
* DDD 有些阻抗 如果为了追求性能的话 还是得通过 数据库的方式来判断
* @param deptService
*/
public void checkStatusAllowChange(ISysDeptService deptService) {
if (CommonStatusEnum.DISABLE.getValue().equals(getStatus()) &&
deptService.existChildrenDeptById(getDeptId(), true)) {
throw new ApiException(ErrorCode.Business.DEPT_STATUS_ID_IS_NOT_ALLOWED_CHANGE);
}
}
}

View File

@ -0,0 +1,39 @@
package com.agileboot.domain.system.dept;
import cn.hutool.core.util.StrUtil;
import com.agileboot.orm.entity.SysDeptEntity;
import com.agileboot.orm.query.AbstractQuery;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class DeptQuery extends AbstractQuery {
private Long deptId;
private Integer status;
private Long parentId;
private String deptName;
private boolean isExcludeCurrentDept;
@Override
public QueryWrapper toQueryWrapper() {
QueryWrapper<SysDeptEntity> queryWrapper = new QueryWrapper<>();
queryWrapper.eq(status != null, "status", status)
.eq(parentId != null, "parent_id", parentId)
.like(StrUtil.isNotEmpty(deptName), "dept_name", deptName)
.and(deptId != null && isExcludeCurrentDept, o ->
o.ne("dept_id", deptId)
.or()
.apply("FIND_IN_SET ( dept_id , ancestors)")
);
return queryWrapper;
}
}

View File

@ -0,0 +1,26 @@
package com.agileboot.domain.system.dept;
import cn.hutool.core.convert.Convert;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.PositiveOrZero;
import lombok.Data;
@Data
public class UpdateDeptCommand extends AddDeptCommand{
@NotNull
@PositiveOrZero
private Long deptId;
@PositiveOrZero
private String status;
@Override
public DeptModel toModel() {
DeptModel deptModel = super.toModel();
deptModel.setDeptId(this.deptId);
deptModel.setStatus(Convert.toInt(status, 0));
return deptModel;
}
}

View File

@ -0,0 +1,60 @@
package com.agileboot.domain.system.loginInfo;
import com.agileboot.common.annotation.ExcelColumn;
import com.agileboot.common.annotation.ExcelSheet;
import com.agileboot.orm.entity.SysLoginInfoEntity;
import com.agileboot.orm.enums.LoginStatusEnum;
import com.agileboot.orm.enums.interfaces.BasicEnumUtil;
import java.util.Date;
import lombok.Data;
@Data
@ExcelSheet(name = "登录日志")
public class LoginInfoDTO {
public LoginInfoDTO(SysLoginInfoEntity entity) {
if (entity != null) {
infoId = entity.getInfoId() + "";
username = entity.getUsername();
ipAddress = entity.getIpAddress();
loginLocation = entity.getLoginLocation();
operationSystem = entity.getOperationSystem();
browser = entity.getBrowser();
status = entity.getStatus() + "";
statusStr = BasicEnumUtil.getDescriptionByValue(LoginStatusEnum.class, entity.getStatus());
msg = entity.getMsg();
loginTime = entity.getLoginTime();
}
}
@ExcelColumn(name = "ID")
private String infoId;
@ExcelColumn(name = "用户名")
private String username;
@ExcelColumn(name = "ip地址")
private String ipAddress;
@ExcelColumn(name = "登录地点")
private String loginLocation;
@ExcelColumn(name = "操作系统")
private String operationSystem;
@ExcelColumn(name = "浏览器")
private String browser;
private String status;
@ExcelColumn(name = "状态")
private String statusStr;
@ExcelColumn(name = "描述")
private String msg;
@ExcelColumn(name = "登录时间")
private Date loginTime;
}

View File

@ -0,0 +1,35 @@
package com.agileboot.domain.system.loginInfo;
import com.agileboot.common.core.page.PageDTO;
import com.agileboot.domain.common.BulkOperationCommand;
import com.agileboot.orm.entity.SysLoginInfoEntity;
import com.agileboot.orm.service.ISysLoginInfoService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author valarchie
*/
@Service
public class LoginInfoDomainService {
@Autowired
private ISysLoginInfoService loginInfoService;
public PageDTO getLoginInfoList(LoginInfoQuery query) {
Page<SysLoginInfoEntity> page = loginInfoService.page(query.toPage(), query.toQueryWrapper());
List<LoginInfoDTO> records = page.getRecords().stream().map(LoginInfoDTO::new).collect(Collectors.toList());
return new PageDTO(records, page.getTotal());
}
public void deleteLoginInfo(BulkOperationCommand<Long> deleteCommand) {
QueryWrapper<SysLoginInfoEntity> queryWrapper = new QueryWrapper<>();
queryWrapper.in("info_id", deleteCommand.getIds());
loginInfoService.remove(queryWrapper);
}
}

View File

@ -0,0 +1,32 @@
package com.agileboot.domain.system.loginInfo;
import cn.hutool.core.util.StrUtil;
import com.agileboot.orm.entity.SysLoginInfoEntity;
import com.agileboot.orm.query.AbstractPageQuery;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class LoginInfoQuery extends AbstractPageQuery {
private String ipaddr;
private String status;
private String username;
@Override
public QueryWrapper toQueryWrapper() {
QueryWrapper<SysLoginInfoEntity> queryWrapper = new QueryWrapper<>();
queryWrapper.like(StrUtil.isNotEmpty(ipaddr), "ip_address", ipaddr)
.eq(StrUtil.isNotEmpty(status), "status", status)
.like(StrUtil.isNotEmpty(username), "username", username);
addSortCondition(queryWrapper);
addTimeCondition(queryWrapper, "login_time");
return queryWrapper;
}
}

View File

@ -0,0 +1,37 @@
package com.agileboot.domain.system.loginInfo;
import cn.hutool.core.util.StrUtil;
import com.agileboot.orm.query.AbstractPageQuery;
import com.agileboot.orm.result.SearchUserDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.Data;
@Data
public class SearchUserQuery extends AbstractPageQuery {
private Long userId;
private String username;
private Integer status;
private String phoneNumber;
private Long deptId;
@Override
public QueryWrapper<SearchUserDO> toQueryWrapper() {
QueryWrapper<SearchUserDO> queryWrapper = new QueryWrapper<>();
queryWrapper.like(StrUtil.isNotEmpty(username), "username", username)
.like(StrUtil.isNotEmpty(phoneNumber), "u.phone_number", phoneNumber)
.eq(userId != null, "u.user_id", userId)
.eq(status != null, "u.status", status)
.eq("u.deleted", 0)
.and(deptId != null, o ->
o.eq("u.dept_id", deptId)
.or()
.apply("u.dept_id IN ( SELECT t.dept_id FROM sys_dept t WHERE find_in_set(" + deptId
+ ", ancestors))"));
this.addTimeCondition(queryWrapper, "u.create_time");
return queryWrapper;
}
}

View File

@ -0,0 +1,50 @@
package com.agileboot.domain.system.menu;
import cn.hutool.core.bean.BeanUtil;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import lombok.Data;
@Data
public class AddMenuCommand {
@NotBlank(message = "菜单名称不能为空")
@Size(max = 50, message = "菜单名称长度不能超过50个字符")
private String menuName;
private Long parentId;
@NotNull(message = "显示顺序不能为空")
private Integer orderNum;
@Size(max = 200, message = "路由地址不能超过200个字符")
private String path;
@Size(max = 200, message = "组件路径不能超过255个字符")
private String component;
private Integer isExternal;
private Integer isCache;
private Integer menuType;
private Integer isVisible;
private Integer status;
private String query;
@Size(min = 0, max = 100, message = "权限标识长度不能超过100个字符")
private String perms;
private String icon;
public MenuModel toModel() {
MenuModel model = new MenuModel();
BeanUtil.copyProperties(this, model);
return model;
}
}

View File

@ -0,0 +1,51 @@
package com.agileboot.domain.system.menu;
import cn.hutool.core.util.BooleanUtil;
import com.agileboot.orm.entity.SysMenuEntity;
import com.agileboot.orm.enums.dictionary.CommonStatusEnum;
import com.agileboot.orm.enums.interfaces.BasicEnumUtil;
import java.util.Date;
import lombok.Data;
@Data
public class MenuDTO {
public MenuDTO(SysMenuEntity entity) {
if (entity != null) {
this.menuId = entity.getMenuId();
this.parentId = entity.getParentId();
this.menuName = entity.getMenuName();
this.menuType = entity.getMenuType() + "";
this.icon = entity.getIcon();
this.orderNum = entity.getOrderNum() + "";
this.component = entity.getComponent();
this.perms = entity.getPerms();
this.path = entity.getPath();
this.status = entity.getStatus() + "";
this.statusStr = BasicEnumUtil.getDescriptionByValue(CommonStatusEnum.class, entity.getStatus());
this.createTime = entity.getCreateTime();
this.isExternal = BooleanUtil.toInt(entity.getIsExternal()) + "";
this.isCache = BooleanUtil.toInt(entity.getIsCache()) + "";
this.isVisible = BooleanUtil.toInt(entity.getIsVisible()) + "";
this.query = entity.getQuery();
}
}
private Long menuId;
private Long parentId;
private String menuType;
private String menuName;
private String icon;
private String orderNum;
private String component;
private String path;
private String perms;
private String status;
private String statusStr;
private Date createTime;
private String isExternal;
private String isCache;
private String isVisible;
private String query;
}

View File

@ -0,0 +1,190 @@
package com.agileboot.domain.system.menu;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNodeConfig;
import cn.hutool.core.lang.tree.TreeUtil;
import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode;
import com.agileboot.domain.system.TreeSelectedDTO;
import com.agileboot.infrastructure.web.domain.login.LoginUser;
import com.agileboot.infrastructure.web.util.AuthenticationUtils;
import com.agileboot.orm.entity.SysMenuEntity;
import com.agileboot.orm.service.ISysMenuService;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author valarchie
*/
@Service
public class MenuDomainService {
@Autowired
private ISysMenuService menuService;
public List<MenuDTO> getMenuList(MenuQuery query) {
List<SysMenuEntity> list = menuService.list(query.toQueryWrapper());
return list.stream().map(MenuDTO::new).collect(Collectors.toList());
}
public MenuDTO getMenuInfo(Long menuId) {
SysMenuEntity byId = menuService.getById(menuId);
return new MenuDTO(byId);
}
public List<Tree<Long>> getDropdownList(LoginUser loginUser) {
List<SysMenuEntity> menuEntityList =
loginUser.isAdmin() ? menuService.list() : menuService.selectMenuListByUserId(loginUser.getUserId());
return buildMenuTreeSelect(menuEntityList);
}
public TreeSelectedDTO getRoleDropdownList(LoginUser loginUser, Long roleId) {
List<SysMenuEntity> menus = loginUser.isAdmin() ?
menuService.list() : menuService.selectMenuListByUserId(loginUser.getUserId());
TreeSelectedDTO tree = new TreeSelectedDTO();
tree.setMenus(buildMenuTreeSelect(menus));
tree.setCheckedKeys(menuService.selectMenuListByRoleId(roleId));
return tree;
}
public void addMenu(AddMenuCommand addCommand, LoginUser loginUser) {
MenuModel model = addCommand.toModel();
model.checkMenuNameUnique(menuService);
model.checkExternalLink();
model.logCreator(loginUser);
model.insert();
}
public void updateMenu(UpdateMenuCommand updateCommand, LoginUser loginUser) {
MenuModel model = updateCommand.toModel();
model.checkMenuNameUnique(menuService);
model.checkExternalLink();
model.checkParentId();
model.logUpdater(loginUser);
model.updateById();
}
public void remove(Long menuId) {
MenuModel menuModel = getMenuModel(menuId);
menuModel.checkHasChildMenus(menuService);
menuModel.checkMenuAlreadyAssignToRole(menuService);
menuModel.deleteById();
}
public MenuModel getMenuModel(Long menuId) {
SysMenuEntity byId = menuService.getById(menuId);
if (byId == null) {
throw new ApiException(ErrorCode.Business.OBJECT_NOT_FOUND, menuId, "菜单");
}
return new MenuModel(byId);
}
/**
* 构建前端所需要树结构
*
* @param menus 菜单列表
* @return 树结构列表
*/
public List<Tree<Long>> buildMenuTreeSelect(List<SysMenuEntity> menus) {
TreeNodeConfig config = new TreeNodeConfig();
//默认为id可以不设置
config.setIdKey("menuId");
return TreeUtil.build(menus, 0L, config, (menu, tree) -> {
// 也可以使用 tree.setId(dept.getId());等一些默认值
tree.setId(menu.getMenuId());
tree.setParentId(menu.getParentId());
tree.putExtra("label", menu.getMenuName());
});
}
public List<Tree<Long>> buildMenuEntityTree(Long userId) {
List<SysMenuEntity> menus = null;
if (AuthenticationUtils.isAdmin(userId)) {
menus = menuService.listMenuListWithoutButton();
} else {
menus = menuService.listMenuListWithoutButtonByUserId(userId);
}
TreeNodeConfig config = new TreeNodeConfig();
//默认为id可以不设置
config.setIdKey("menuId");
return TreeUtil.build(menus, 0L, config, (menu, tree) -> {
// 也可以使用 tree.setId(dept.getId());等一些默认值
tree.setId(menu.getMenuId());
tree.setParentId(menu.getParentId());
tree.putExtra("entity", menu);
});
}
public List<RouterVo> buildRouterTree(List<Tree<Long>> trees) {
List<RouterVo> routers = new LinkedList<RouterVo>();
if (CollUtil.isNotEmpty(trees)) {
for (Tree<Long> tree : trees) {
RouterVo routerVo = null;
Object entity = tree.get("entity");
if (entity != null) {
RouterModel model = new RouterModel();
BeanUtil.copyProperties(entity, model, true);
routerVo = model.produceDefaultRouterVO();
if(model.isMultipleLevelMenu(tree)) {
routerVo = model.produceDirectoryRouterVO(buildRouterTree(tree.getChildren()));
}
if(model.isSingleLevelMenu()) {
routerVo = model.produceMenuFrameRouterVO();
}
if(model.getParentId() == 0L && model.isInnerLink()) {
routerVo = model.produceInnerLinkRouterVO();
}
routers.add(routerVo);
}
}
}
return routers;
}
public List<RouterVo> getRouterTree(Long userId) {
List<Tree<Long>> trees = buildMenuEntityTree(userId);
return buildRouterTree(trees);
}
}

View File

@ -0,0 +1,54 @@
package com.agileboot.domain.system.menu;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.http.HttpUtil;
import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode;
import com.agileboot.orm.entity.SysMenuEntity;
import com.agileboot.orm.service.ISysMenuService;
import lombok.NoArgsConstructor;
@NoArgsConstructor
public class MenuModel extends SysMenuEntity {
public MenuModel(SysMenuEntity entity) {
if (entity != null) {
BeanUtil.copyProperties(entity, this);
}
}
public void checkMenuNameUnique(ISysMenuService menuService) {
if (menuService.checkMenuNameUnique(getMenuName(), getMenuId(), getParentId())) {
throw new ApiException(ErrorCode.Business.MENU_NAME_IS_NOT_UNIQUE);
}
}
public void checkExternalLink() {
if (getIsExternal() && !HttpUtil.isHttp(getPath()) && !HttpUtil.isHttps(getPath())) {
throw new ApiException(ErrorCode.Business.MENU_EXTERNAL_LINK_MUST_BE_HTTP);
}
}
public void checkParentId() {
if (getMenuId().equals(getParentId())) {
throw new ApiException(ErrorCode.Business.MENU_PARENT_ID_NOT_ALLOW_SELF);
}
}
public void checkHasChildMenus(ISysMenuService menuService) {
if (menuService.hasChildByMenuId(getMenuId())) {
throw new ApiException(ErrorCode.Business.MENU_EXIST_CHILD_MENU_NOT_ALLOW_DELETE);
}
}
public void checkMenuAlreadyAssignToRole(ISysMenuService menuService) {
if (menuService.checkMenuExistRole(getMenuId())) {
throw new ApiException(ErrorCode.Business.MENU_ALREADY_ASSIGN_TO_ROLE_NOT_ALLOW_DELETE);
}
}
}

View File

@ -0,0 +1,29 @@
package com.agileboot.domain.system.menu;
import cn.hutool.core.util.StrUtil;
import com.agileboot.orm.entity.SysMenuEntity;
import com.agileboot.orm.query.AbstractQuery;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import java.util.Arrays;
import lombok.Data;
@Data
public class MenuQuery extends AbstractQuery {
private String menuName;
private Boolean isVisible;
private Integer status;
@Override
public QueryWrapper<SysMenuEntity> toQueryWrapper() {
QueryWrapper<SysMenuEntity> queryWrapper = new QueryWrapper<>();
queryWrapper.like(StrUtil.isNotEmpty(menuName), columnName("menu_name"), menuName)
.eq(isVisible!=null, "is_visible", isVisible)
.eq(status!=null, "status", status);
queryWrapper.orderBy(true, true, Arrays.asList("parent_id", "order_num"));
return queryWrapper;
}
}

View File

@ -0,0 +1,59 @@
package com.agileboot.domain.system.menu;
import cn.hutool.http.HttpUtil;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 路由显示信息
*
* @author ruoyi
*/
@Data
@NoArgsConstructor
public class MetaVo {
/**
* 设置该路由在侧边栏和面包屑中展示的名字
*/
private String title;
/**
* 设置该路由的图标对应路径src/assets/icons/svg
*/
private String icon;
/**
* 设置为true则不会被 <keep-alive>缓存
*/
private boolean noCache;
/**
* 内链地址http(s)://开头
*/
private String link;
public MetaVo(String title, String icon) {
this.title = title;
this.icon = icon;
}
public MetaVo(String title, String icon, String link) {
this.title = title;
this.icon = icon;
if (HttpUtil.isHttp(link)) {
this.link = link;
}
}
public MetaVo(String title, String icon, boolean noCache, String link) {
this.title = title;
this.icon = icon;
this.noCache = noCache;
if (HttpUtil.isHttp(link)) {
this.link = link;
}
}
}

View File

@ -0,0 +1,197 @@
package com.agileboot.domain.system.menu;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.agileboot.common.constant.Constants;
import com.agileboot.orm.entity.SysMenuEntity;
import com.agileboot.orm.enums.MenuComponentEnum;
import com.agileboot.orm.enums.MenuTypeEnum;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class RouterModel extends SysMenuEntity {
public RouterVo produceDirectoryRouterVO(List<RouterVo> children) {
RouterVo router = produceDefaultRouterVO();
if (CollUtil.isNotEmpty(children) && Objects.equals(MenuTypeEnum.DIRECTORY.getValue(), getMenuType())) {
router.setAlwaysShow(true);
router.setRedirect("noRedirect");
router.setChildren(children);
}
return router;
}
public RouterVo produceMenuFrameRouterVO() {
RouterVo router = new RouterVo();
router.setMeta(null);
List<RouterVo> childrenList = new ArrayList<>();
RouterVo children = new RouterVo();
children.setPath(getPath());
children.setComponent(getComponent());
children.setName(StrUtil.upperFirst(getPath()));
children.setMeta(new MetaVo(getMenuName(), getIcon(), !getIsCache(), getPath()));
children.setQuery(getQuery());
childrenList.add(children);
router.setChildren(childrenList);
return router;
}
public RouterVo produceInnerLinkRouterVO() {
RouterVo router = new RouterVo();
router.setMeta(new MetaVo(getMenuName(), getIcon()));
router.setPath("/");
List<RouterVo> childrenList = new ArrayList<>();
RouterVo children = new RouterVo();
String routerPath = trimHttpPrefixForInnerLink(getPath());
children.setPath(routerPath);
children.setComponent(MenuComponentEnum.INNER_LINK.description());
children.setName(StrUtil.upperFirst(routerPath));
children.setMeta(new MetaVo(getMenuName(), getIcon(), getPath()));
childrenList.add(children);
router.setChildren(childrenList);
return router;
}
public RouterVo produceDefaultRouterVO() {
RouterVo router = new RouterVo();
router.setHidden(!getIsVisible());
router.setName(getRouteName());
router.setPath(getRouterPath());
router.setComponent(getComponentTypeForFrontEnd());
router.setQuery(getQuery());
router.setMeta(new MetaVo(getMenuName(), getIcon(), !getIsCache(), getPath()));
return router;
}
/**
* 获取路由名称
* @return 路由名称
*/
public String getRouteName() {
String routerName = StrUtil.upperFirst(getPath());
// 非外链并且是一级目录类型为目录
if (isSingleLevelMenu()) {
routerName = StrUtil.EMPTY;
}
return routerName;
}
/**
* 是否为单个一级菜单
*
* @return 结果
*/
public boolean isSingleLevelMenu() {
return getParentId().intValue() == 0
&& MenuTypeEnum.MENU.getValue().equals(getMenuType())
&& !getIsExternal();
}
/**
* 是否为菜单内部跳转
*
* @return 结果
*/
public boolean isMultipleLevelMenu(Tree<Long> tree) {
return MenuTypeEnum.DIRECTORY.getValue().equals(getMenuType()) && tree.hasChild();
}
/**
* 获取路由地址
* @return 路由地址
*/
public String getRouterPath() {
String routerPath = getPath();
// 内链打开外网方式
if (getParentId().intValue() != 0 && isInnerLink()) {
routerPath = trimHttpPrefixForInnerLink(routerPath);
}
// 非外链并且是一级目录类型为目录
if (0L == getParentId() && Objects.equals(MenuTypeEnum.DIRECTORY.getValue(), getMenuType()) && !getIsExternal()) {
routerPath = "/" + getPath();
// 非外链并且是一级目录类型为菜单
} else if (isSingleLevelMenu()) {
routerPath = "/";
}
return routerPath;
}
/**
* 是否为内链组件
*
* @return 结果
*/
public boolean isInnerLink() {
return !getIsExternal() && (HttpUtil.isHttp(getPath()) || HttpUtil.isHttps(getPath()));
}
/**
* 内链域名特殊字符替换
*/
public String trimHttpPrefixForInnerLink(String path) {
if (HttpUtil.isHttp(path)) {
return StrUtil.stripIgnoreCase(path, Constants.HTTP, "");
}
if (HttpUtil.isHttps(path)) {
return StrUtil.stripIgnoreCase(path, Constants.HTTPS, "");
}
return path;
}
/**
* 获取组件信息
*
* @return 组件信息
*/
public String getComponentTypeForFrontEnd() {
String component = MenuComponentEnum.LAYOUT.description();
if (StrUtil.isNotEmpty(getComponent()) && !isSingleLevelMenu()) {
component = getComponent();
} else if (isInnerLinkView()) {
component = MenuComponentEnum.INNER_LINK.description();
} else if (isParentView()) {
component = MenuComponentEnum.PARENT_VIEW.description();
}
return component;
}
/**
* 是否为inner_link_view组件
*
* @return 结果
*/
public boolean isInnerLinkView() {
return StrUtil.isEmpty(getComponent()) && getParentId().intValue() != 0 && isInnerLink();
}
/**
* 是否为parent_view组件
*
* @return 结果
*/
public boolean isParentView() {
return StrUtil.isEmpty(getComponent()) && getParentId().intValue() != 0 && MenuTypeEnum.DIRECTORY.getValue() == getMenuType();
}
}

View File

@ -0,0 +1,61 @@
package com.agileboot.domain.system.menu;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.util.List;
import lombok.Data;
/**
* 路由配置信息
*
* @author ruoyi
*/
@JsonInclude(JsonInclude.Include.NON_EMPTY)
@Data
public class RouterVo {
/**
* 路由名字
*/
private String name;
/**
* 路由地址
*/
private String path;
/**
* 是否隐藏路由当设置 true 的时候该路由不会再侧边栏出现
*/
private boolean hidden;
/**
* 重定向地址当设置 noRedirect 的时候该路由在面包屑导航中不可被点击
*/
private String redirect;
/**
* 组件地址
*/
private String component;
/**
* 路由参数 {"id": 1, "name": "agileBoot"}
*/
private String query;
/**
* 当你一个路由下面的 children 声明的路由大于1个时自动会变成嵌套的模式--如组件页面
*/
private Boolean alwaysShow;
/**
* 其他元素
*/
private MetaVo meta;
/**
* 子路由
*/
private List<RouterVo> children;
}

View File

@ -0,0 +1,19 @@
package com.agileboot.domain.system.menu;
import javax.validation.constraints.NotNull;
import lombok.Data;
@Data
public class UpdateMenuCommand extends AddMenuCommand{
@NotNull
private Long menuId;
@Override
public MenuModel toModel() {
MenuModel model = super.toModel();
model.setMenuId(menuId);
return model;
}
}

View File

@ -0,0 +1,124 @@
package com.agileboot.domain.system.monitor;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.agileboot.domain.system.monitor.dto.RedisCacheInfoDTO;
import com.agileboot.domain.system.monitor.dto.RedisCacheInfoDTO.CommonStatusDTO;
import com.agileboot.infrastructure.cache.RedisUtil;
import com.agileboot.infrastructure.cache.guava.GuavaCacheService;
import com.agileboot.infrastructure.cache.redis.CacheKeyEnum;
import com.agileboot.infrastructure.cache.redis.RedisCacheService;
import com.agileboot.infrastructure.web.domain.OnlineUser;
import com.agileboot.infrastructure.web.domain.login.LoginUser;
import com.agileboot.infrastructure.web.domain.server.ServerInfo;
import com.agileboot.orm.entity.SysDeptEntity;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisServerCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
@Service
public class MonitorDomainService {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private RedisUtil redisUtil;
@Autowired
private RedisCacheService redisCacheService;
public RedisCacheInfoDTO getRedisCacheInfo() {
Properties info = (Properties) redisTemplate.execute((RedisCallback<Object>) RedisServerCommands::info);
Properties commandStats = (Properties) redisTemplate.execute(
(RedisCallback<Object>) connection -> connection.info("commandstats"));
Object dbSize = redisTemplate.execute((RedisCallback<Object>) RedisServerCommands::dbSize);
if(commandStats == null) {
throw new RuntimeException("找不到对应的redis信息。");
}
RedisCacheInfoDTO cacheInfo = new RedisCacheInfoDTO();
cacheInfo.setInfo(info);
cacheInfo.setDbSize(dbSize);
cacheInfo.setCommandStats(new ArrayList<>());
commandStats.stringPropertyNames().forEach(key -> {
String property = commandStats.getProperty(key);
RedisCacheInfoDTO.CommonStatusDTO commonStatus = new CommonStatusDTO();
commonStatus.setName(StrUtil.removePrefix(key, "cmdstat_"));
commonStatus.setValue(StrUtil.subBetween(property, "calls=", ",usec"));
cacheInfo.getCommandStats().add(commonStatus);
});
return cacheInfo;
}
public List<OnlineUser> getOnlineUserList(String userName, String ipaddr) {
Collection<String> keys = redisUtil.keys(CacheKeyEnum.LOGIN_USER_KEY.key() + "*");
List<OnlineUser> allOnlineUsers = keys.stream().map(
o -> mapLoginUserToUserOnline(redisCacheService.loginUserCache.getCachedObjectByKey(o)))
.filter(Objects::nonNull)
.collect(Collectors.toList());
List<OnlineUser> filteredOnlineUsers = allOnlineUsers.stream()
.filter(o ->
StrUtil.isEmpty(userName) || userName.equals(o.getUserName())
).filter( o ->
StrUtil.isEmpty(ipaddr) || ipaddr.equals(o.getIpaddr())
).collect(Collectors.toList());
Collections.reverse(filteredOnlineUsers);
return filteredOnlineUsers;
}
public ServerInfo getServerInfo() {
return ServerInfo.fillInfo();
}
/**
* 设置在线用户信息
*
* @param user 用户信息
* @return 在线用户
*/
public OnlineUser mapLoginUserToUserOnline(LoginUser user) {
if (user == null) {
return null;
}
OnlineUser onlineUser = new OnlineUser();
onlineUser.setTokenId(user.getToken());
onlineUser.setUserName(user.getUsername());
onlineUser.setIpaddr(user.getLoginInfo().getIpAddress());
onlineUser.setLoginLocation(user.getLoginInfo().getLocation());
onlineUser.setBrowser(user.getLoginInfo().getBrowser());
onlineUser.setOs(user.getLoginInfo().getOperationSystem());
onlineUser.setLoginTime(user.getLoginTime());
GuavaCacheService cacheService = SpringUtil.getBean(GuavaCacheService.class);
SysDeptEntity deptEntity = cacheService.deptCache.get(user.getDeptId() + "");
if (deptEntity != null) {
onlineUser.setDeptName(deptEntity.getDeptName());
}
return onlineUser;
}
}

View File

@ -0,0 +1,24 @@
package com.agileboot.domain.system.monitor.dto;
import java.util.List;
import java.util.Properties;
import lombok.Data;
/**
* @author valarchie
*/
@Data
public class RedisCacheInfoDTO {
private Properties info;
private Object dbSize;
private List<CommonStatusDTO> commandStats;
@Data
public static class CommonStatusDTO {
private String name;
private String value;
}
}

View File

@ -0,0 +1,33 @@
package com.agileboot.domain.system.notice;
import cn.hutool.core.convert.Convert;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import lombok.Data;
@Data
public class NoticeAddCommand {
@NotBlank(message = "公告标题不能为空")
@Size(max = 50, message = "公告标题不能超过50个字符")
protected String noticeTitle;
protected String noticeType;
@NotBlank
protected String noticeContent;
protected String status;
public NoticeModel toModel() {
NoticeModel model = new NoticeModel();
model.setNoticeTitle(noticeTitle);
model.setNoticeType(Convert.toInt(noticeType));
model.setNoticeContent(noticeContent);
model.setStatus(Convert.toInt(status));
return model;
}
}

View File

@ -0,0 +1,36 @@
package com.agileboot.domain.system.notice;
import com.agileboot.orm.entity.SysNoticeEntity;
import java.util.Date;
import lombok.Data;
@Data
public class NoticeDTO {
public NoticeDTO(SysNoticeEntity entity) {
if (entity != null) {
this.noticeId = entity.getNoticeId() + "";
this.noticeTitle = entity.getNoticeTitle() + "";
this.noticeType = entity.getNoticeType() + "";
this.noticeContent = entity.getNoticeContent();
this.status = entity.getStatus() + "";
this.createTime = entity.getCreateTime();
this.creatorName = entity.getCreatorName();
}
}
private String noticeId;
private String noticeTitle;
private String noticeType;
private String noticeContent;
private String status;
private Date createTime;
private String creatorName;
}

View File

@ -0,0 +1,72 @@
package com.agileboot.domain.system.notice;
import com.agileboot.common.core.page.PageDTO;
import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode;
import com.agileboot.domain.common.BulkOperationCommand;
import com.agileboot.infrastructure.web.domain.login.LoginUser;
import com.agileboot.orm.entity.SysNoticeEntity;
import com.agileboot.orm.service.ISysNoticeService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author valarchie
*/
@Service
public class NoticeDomainService {
@Autowired
private ISysNoticeService noticeService;
public PageDTO getNoticeList(NoticeQuery query) {
Page<SysNoticeEntity> page = noticeService.page(query.toPage(), query.toQueryWrapper());
List<NoticeDTO> records = page.getRecords().stream().map(NoticeDTO::new).collect(Collectors.toList());
return new PageDTO(records, page.getTotal());
}
public NoticeDTO getNoticeInfo(Long id) {
SysNoticeEntity byId = noticeService.getById(id);
return new NoticeDTO(byId);
}
public void addNotice(NoticeAddCommand addCommand, LoginUser loginUser) {
NoticeModel noticeModel = addCommand.toModel();
noticeModel.checkFields();
noticeModel.logCreator(loginUser);
noticeModel.insert();
}
public void updateNotice(NoticeUpdateCommand updateCommand, LoginUser loginUser) {
SysNoticeEntity byId = noticeService.getById(updateCommand.getNoticeId());
if (byId == null) {
throw new ApiException(ErrorCode.Business.OBJECT_NOT_FOUND, updateCommand.getNoticeId(), "通知公告");
}
NoticeModel noticeModel = updateCommand.toModel();
noticeModel.checkFields();
noticeModel.logUpdater(loginUser);
noticeModel.updateById();
}
public void deleteNotice(BulkOperationCommand<Long> deleteCommand) {
noticeService.removeBatchByIds(deleteCommand.getIds());
}
}

View File

@ -0,0 +1,23 @@
package com.agileboot.domain.system.notice;
import com.agileboot.orm.entity.SysNoticeEntity;
import com.agileboot.orm.enums.dictionary.CommonStatusEnum;
import com.agileboot.orm.enums.dictionary.NoticeTypeEnum;
import com.agileboot.orm.enums.interfaces.BasicEnumUtil;
import lombok.Data;
@Data
public class NoticeModel extends SysNoticeEntity {
public void checkFields() {
Integer noticeType = this.getNoticeType();
BasicEnumUtil.fromValue(NoticeTypeEnum.class, noticeType);
Integer status = this.getStatus();
BasicEnumUtil.fromValue(CommonStatusEnum.class, status);
}
}

View File

@ -0,0 +1,33 @@
package com.agileboot.domain.system.notice;
import cn.hutool.core.util.StrUtil;
import com.agileboot.orm.entity.SysNoticeEntity;
import com.agileboot.orm.query.AbstractPageQuery;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@EqualsAndHashCode(callSuper = true)
@Data
@NoArgsConstructor
public class NoticeQuery extends AbstractPageQuery {
private String noticeType;
private String noticeTitle;
private String creatorName;
@SuppressWarnings("rawtypes")
@Override
public QueryWrapper toQueryWrapper() {
QueryWrapper<SysNoticeEntity> sysNoticeWrapper = new QueryWrapper<>();
sysNoticeWrapper.like(StrUtil.isNotEmpty(noticeTitle), "notice_title", noticeTitle)
.eq(noticeType != null, "notice_type", noticeType)
.like(StrUtil.isNotEmpty(creatorName), "creator_name", creatorName);
return sysNoticeWrapper;
}
}

View File

@ -0,0 +1,23 @@
package com.agileboot.domain.system.notice;
import cn.hutool.core.convert.Convert;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Positive;
import lombok.Data;
@Data
public class NoticeUpdateCommand extends NoticeAddCommand{
@NotNull
@Positive
protected String noticeId;
@Override
public NoticeModel toModel() {
NoticeModel noticeModel = super.toModel();
noticeModel.setNoticeId(Convert.toInt(noticeId));
return noticeModel;
}
}

View File

@ -0,0 +1,92 @@
package com.agileboot.domain.system.operationLog;
import cn.hutool.core.bean.BeanUtil;
import com.agileboot.common.annotation.ExcelColumn;
import com.agileboot.common.annotation.ExcelSheet;
import com.agileboot.orm.entity.SysOperationLogEntity;
import com.agileboot.orm.enums.OperatorTypeEnum;
import com.agileboot.orm.enums.RequestMethodEnum;
import com.agileboot.orm.enums.dictionary.BusinessTypeEnum;
import com.agileboot.orm.enums.dictionary.OperationStatusEnum;
import com.agileboot.orm.enums.interfaces.BasicEnumUtil;
import java.util.Date;
import lombok.Data;
@Data
@ExcelSheet(name = "操作日志")
public class OperationLogDTO {
public OperationLogDTO(SysOperationLogEntity entity) {
if (entity != null) {
BeanUtil.copyProperties(entity, this);
this.requestMethod = BasicEnumUtil.getDescriptionByValue(RequestMethodEnum.class,
entity.getRequestMethod());
this.statusStr = BasicEnumUtil.getDescriptionByValue(OperationStatusEnum.class, entity.getStatus());
businessTypeStr = BasicEnumUtil.getDescriptionByValue(BusinessTypeEnum.class, entity.getBusinessType());
operatorTypeStr = BasicEnumUtil.getDescriptionByValue(OperatorTypeEnum.class, entity.getOperatorType());
}
}
@ExcelColumn(name = "ID")
private Long operationId;
private Integer businessType;
@ExcelColumn(name = "操作类型")
private String businessTypeStr;
@ExcelColumn(name = "操作类型")
private String requestMethod;
@ExcelColumn(name = "操作类型")
private String requestModule;
@ExcelColumn(name = "操作类型")
private String requestUrl;
@ExcelColumn(name = "操作类型")
private String calledMethod;
private Integer operatorType;
@ExcelColumn(name = "操作人类型")
private String operatorTypeStr;
@ExcelColumn(name = "用户ID")
private Long userId;
@ExcelColumn(name = "用户名")
private String username;
@ExcelColumn(name = "ip地址")
private String operatorIp;
@ExcelColumn(name = "ip地点")
private String operatorLocation;
@ExcelColumn(name = "部门ID")
private Long deptId;
@ExcelColumn(name = "部门")
private String deptName;
@ExcelColumn(name = "操作参数")
private String operationParam;
@ExcelColumn(name = "操作结果")
private String operationResult;
private Integer status;
@ExcelColumn(name = "状态")
private String statusStr;
@ExcelColumn(name = "错误堆栈")
private String errorStack;
@ExcelColumn(name = "操作时间")
private Date operationTime;
}

View File

@ -0,0 +1,32 @@
package com.agileboot.domain.system.operationLog;
import com.agileboot.common.core.page.PageDTO;
import com.agileboot.domain.common.BulkOperationCommand;
import com.agileboot.orm.entity.SysOperationLogEntity;
import com.agileboot.orm.service.ISysOperationLogService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author valarchie
*/
@Service
public class OperationLogDomainService {
@Autowired
private ISysOperationLogService operationLogService;
public PageDTO getOperationLogList(OperationLogQuery query) {
Page<SysOperationLogEntity> page = operationLogService.page(query.toPage(), query.toQueryWrapper());
List<OperationLogDTO> records = page.getRecords().stream().map(OperationLogDTO::new).collect(Collectors.toList());
return new PageDTO(records, page.getTotal());
}
public void deleteOperationLog(BulkOperationCommand<Long> deleteCommand) {
operationLogService.removeBatchByIds(deleteCommand.getIds());
}
}

View File

@ -0,0 +1,34 @@
package com.agileboot.domain.system.operationLog;
import cn.hutool.core.util.StrUtil;
import com.agileboot.orm.entity.SysLoginInfoEntity;
import com.agileboot.orm.query.AbstractPageQuery;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class OperationLogQuery extends AbstractPageQuery {
private String businessType;
private String status;
private String username;
private String requestModule;
@Override
public QueryWrapper toQueryWrapper() {
QueryWrapper<SysLoginInfoEntity> queryWrapper = new QueryWrapper<>();
queryWrapper.like(businessType!=null, "business_type", businessType)
.eq(status != null, "status", status)
.like(StrUtil.isNotEmpty(username), "username", username)
.like(StrUtil.isNotEmpty(requestModule), "request_module", requestModule);
addSortCondition(queryWrapper);
addTimeCondition(queryWrapper, "operation_time");
return queryWrapper;
}
}

View File

@ -0,0 +1,47 @@
package com.agileboot.domain.system.post;
import cn.hutool.core.convert.Convert;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.PositiveOrZero;
import javax.validation.constraints.Size;
import lombok.Data;
@Data
public class AddPostCommand {
@NotBlank(message = "岗位编码不能为空")
@Size(max = 64, message = "岗位编码长度不能超过64个字符")
protected String postCode;
/**
* 岗位名称
*/
@NotBlank(message = "岗位名称不能为空")
@Size(max = 64, message = "岗位名称长度不能超过64个字符")
protected String postName;
/**
* 岗位排序
*/
@NotNull(message = "显示顺序不能为空")
protected Integer postSort;
protected String remark;
@PositiveOrZero
protected String status;
public PostModel toModel() {
PostModel model = new PostModel();
model.setPostCode(postCode);
model.setPostName(postName);
model.setPostSort(postSort);
model.setRemark(remark);
model.setStatus(Convert.toInt(status));
return model;
}
}

View File

@ -0,0 +1,42 @@
package com.agileboot.domain.system.post;
import cn.hutool.core.bean.BeanUtil;
import com.agileboot.common.annotation.ExcelColumn;
import com.agileboot.orm.entity.SysPostEntity;
import com.agileboot.orm.enums.dictionary.CommonStatusEnum;
import com.agileboot.orm.enums.interfaces.BasicEnumUtil;
import lombok.Data;
@Data
public class PostDTO {
public PostDTO(SysPostEntity entity) {
if (entity != null) {
BeanUtil.copyProperties(entity, this);
statusStr = BasicEnumUtil.getDescriptionByValue(CommonStatusEnum.class, entity.getStatus());
}
}
@ExcelColumn(name = "岗位ID")
private Long postId;
@ExcelColumn(name = "岗位编码")
private String postCode;
@ExcelColumn(name = "岗位名称")
private String postName;
@ExcelColumn(name = "岗位排序")
private String postSort;
@ExcelColumn(name = "备注")
private String remark;
private String status;
@ExcelColumn(name = "状态")
private String statusStr;
}

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