280 Commits

Author SHA1 Message Date
RuoYi
af1b557bc8 若依 3.2.0 2021-10-12 09:01:05 +08:00
RuoYi
e6c3bd1ce5 升级spring-boot-admin到最新版2.5.2 2021-10-11 18:57:21 +08:00
RuoYi
046d25f35d 升级spring-boot到最新版本2.5.5 2021-10-10 16:07:43 +08:00
RuoYi
d20d4ffa16 菜单子项添加路由参数 2021-10-10 16:07:06 +08:00
RuoYi
76f2273d0b Excel导入支持@Excels注解 2021-10-10 16:02:54 +08:00
RuoYi
dda78c95b7 升级pagehelper到最新版1.4.0 2021-10-10 16:01:07 +08:00
RuoYi
ebbeb047be 升级druid到最新版1.2.8 2021-10-10 09:46:21 +08:00
RuoYi
ebd7fd59d0 升级element-ui到最新版本2.15.6 2021-10-09 11:54:18 +08:00
RuoYi
c20c2c221f 导入模板标题默认空参数 2021-10-09 11:53:36 +08:00
RuoYi
ee33dbdc0e 升级sass-loader到最新版本10.1.1 2021-09-27 19:06:57 +08:00
RuoYi
5d8d4fa530 升级dart-sass到版本1.32.13 2021-09-27 19:06:43 +08:00
RuoYi
2021028cad 升级file-saver到最新版本2.0.5 2021-09-27 19:06:23 +08:00
RuoYi
7fcb1b2e0a 新增通用方法简化下载使用 2021-09-27 12:10:39 +08:00
RuoYi
f4a2f909a7 Excel注解支持导入导出标题信息 2021-09-26 09:03:28 +08:00
RuoYi
328630fe5d 修复xss过滤后格式出现的异常 2021-09-25 17:16:42 +08:00
RuoYi
f7ae7e29d1 修正代码生成编辑页面单词拼写错误 2021-09-24 14:56:08 +08:00
RuoYi
e46ea3ebac 自动生成代码漏掉 this.#[[$modal]]# 2021-09-24 14:55:11 +08:00
RuoYi
d565fa4883 升级springcloud到最新版2020.0.4 2021-09-24 09:54:05 +08:00
RuoYi
a627108dfe 升级fastjson到最新版1.2.78 2021-09-24 09:53:31 +08:00
RuoYi
829451f05e 新增通用方法简化模态/缓存使用 2021-09-23 09:59:17 +08:00
RuoYi
cae41a8da2 新增通用方法简化模态/缓存使用 2021-09-23 09:35:59 +08:00
RuoYi
a4099cf645 Excel注解支持自定义数据处理器 2021-09-22 09:06:51 +08:00
RuoYi
0a104689ed Excel注解支持自定义数据处理器 2021-09-22 09:03:21 +08:00
RuoYi
97d0226c78 升级spring-boot-admin到最新版2.5.1 2021-09-18 21:34:55 +08:00
RuoYi
3a04dda55d 升级spring-boot到最新版本2.5.4 2021-09-18 21:34:31 +08:00
RuoYi
94310c4f3d 代码生成点击预览重置激活tab 2021-09-18 21:20:45 +08:00
RuoYi
39e7d8a84b 优化aop语法 使用spring自动注入注解 基于注解拦截的aop注解不可能为空 2021-09-18 21:20:18 +08:00
RuoYi
e1037ac125 Cron表达式生成器关闭时销毁,避免再次打开时存在上一次修改的数据 2021-09-18 21:19:52 +08:00
RuoYi
a10384c009 使用vue-data-dict简化数据字典使用 2021-09-17 15:43:02 +08:00
RuoYi
ab8215d1ce 日志注解新增是否保存响应参数 2021-09-16 16:19:23 +08:00
RuoYi
524982c7bd 禁用el-tag组件的渐变动画 2021-09-16 16:19:11 +08:00
RuoYi
d6188aa009 修复后端主子表代码模板方法名生成错误问题 2021-09-16 16:18:48 +08:00
RuoYi
7e8b08a58c 修复多图组件验证失败被删除问题 2021-09-10 11:08:23 +08:00
RuoYi
14771f0ab4 修复代码生成页面数据编辑保存之后总是跳转第一页的问题 2021-09-08 11:29:20 +08:00
RuoYi
97c0603269 优化提示 2021-09-08 11:28:54 +08:00
RuoYi
a58c430858 菜单管理支持配置路由参数 2021-09-08 10:04:23 +08:00
RuoYi
cd4119b26a 修正单词拼写错误 2021-09-08 09:31:37 +08:00
RuoYi
ea20fa3ce2 页签新增关闭左侧 2021-09-05 13:28:32 +08:00
RuoYi
3b54208ac9 页签右键按钮添加图标 2021-09-05 13:27:20 +08:00
RuoYi
d2de744c3f 菜单&部门新增展开/折叠功能 2021-09-04 12:21:33 +08:00
RuoYi
655249d67a 新增暗色菜单风格主题 2021-09-04 12:21:18 +08:00
RuoYi
80a890549d 修复保存配置主题颜色失效问题 2021-09-03 16:52:14 +08:00
RuoYi
5da1979ce6 自定义弹层溢出滚动样式 2021-09-03 13:28:37 +08:00
RuoYi
642512aa91 定时任务支持在线生成cron表达式 2021-09-03 09:53:26 +08:00
RuoYi
4a32a8e725 代码生成导入表按创建时间排序 2021-09-03 09:52:49 +08:00
RuoYi
bb30ae7086 防止表格最后页最后项删除变成暂无数据 2021-09-02 10:46:16 +08:00
RuoYi
34a6f2e1f1 查询列表设置数据字典样式回显 2021-09-01 14:03:59 +08:00
RuoYi
0e51d1e7b1 根据用户ID查询菜单条件加别名 2021-08-30 17:08:55 +08:00
RuoYi
40bdf7b100 修复字典组件值为整形不显示问题 2021-08-29 17:39:43 +08:00
RuoYi
57f51249b6 验证码默认20s超时 2021-08-29 17:39:25 +08:00
RuoYi
dcc5dc0d16 修复带utc日期格式 yyyy-MM-dd'T'HH:mm:ss.SSS 在safari浏览器中无法正确格式化的问题 2021-08-29 17:39:10 +08:00
RuoYi
e87a818706 添加日期范围支持重复添加多组日期范围 2021-08-29 17:38:36 +08:00
RuoYi
67df97d5a7 修改时检查用户数据权限范围 2021-08-24 16:24:25 +08:00
RuoYi
3f8fd0a0f8 定时任务对检查异常进行事务回滚 2021-08-24 11:19:09 +08:00
RuoYi
961fe9a5f1 自定义可拖动弹窗宽高指令 2021-08-20 21:25:53 +08:00
RuoYi
22f156bbb7 补充定时任务表字段注释 2021-08-20 11:07:38 +08:00
RuoYi
22c22c4246 定时任务屏蔽ldap远程调用 2021-08-19 15:34:13 +08:00
RuoYi
994cd62c7a 删掉此处代码,使右边栏动画生效。 2021-08-19 15:32:01 +08:00
RuoYi
1e4ed04a65 优化异常信息 2021-08-16 22:26:29 +08:00
RuoYi
139b25639e 日期范围支持添加多组 2021-08-16 22:26:20 +08:00
RuoYi
13800738cb 修改Dashboard名称 2021-08-16 22:25:09 +08:00
RuoYi
98c538c9e3 默认首页使用keep-alive缓存 2021-08-13 16:41:56 +08:00
RuoYi
0e7c45173c 代码生成主子表多选行数据 2021-08-13 16:41:41 +08:00
RuoYi
132b23dc33 补充遗漏的@Override注解 2021-08-13 16:37:47 +08:00
RuoYi
c3f1dd846c update bin 2021-08-13 16:32:40 +08:00
RuoYi
7b7abde756 添加新群号:201705537 2021-08-08 19:55:51 +08:00
RuoYi
aeeb0eb26b 字典工具类更换路径 2021-08-06 17:52:44 +08:00
RuoYi
265a181891 http请求默认外链打开 2021-08-06 17:02:37 +08:00
RuoYi
22663e1b43 升级element-ui到最新版本2.15.5 2021-08-06 14:33:23 +08:00
RuoYi
24be1a436e 若依 3.1.0 2021-08-01 09:01:15 +08:00
RuoYi
370c53edfa 文件服务本地资源允许跨域访问 2021-07-31 21:51:40 +08:00
RuoYi
f112133ddf 是否开启用户注册功能sql脚本 2021-07-31 21:49:44 +08:00
RuoYi
93ee021b6e XSS过滤排除非json类型 2021-07-31 12:18:24 +08:00
RuoYi
e57d2ea17c 升级nacos到最新版2.0.3 2021-07-31 09:54:28 +08:00
RuoYi
ea038a5437 优化代码生成模板 2021-07-30 22:33:09 +08:00
RuoYi
329aa68644 新增是否开启用户注册功能 2021-07-30 20:03:59 +08:00
Ricky
887430874d 优化代码生成 2021-07-30 18:41:46 +08:00
Ricky
52f8693e1d 优化代码生成 2021-07-30 18:35:52 +08:00
RuoYi
8057dcc4fc 定时任务屏蔽http(s)远程调用 2021-07-30 11:06:11 +08:00
RuoYi
66e0f9a53d 启用部门状态排除顶级节点 2021-07-30 11:05:54 +08:00
RuoYi
7a35c474d6 统一网关错误码响应 2021-07-29 14:51:27 +08:00
RuoYi
892065003d 修复导出含params属性对象参数问题 2021-07-29 10:38:34 +08:00
RuoYi
ede8353503 升级common-pool到最新版本2.10.0 2021-07-28 20:34:41 +08:00
RuoYi
60796ca8fb rollback pr 2021-07-28 20:33:46 +08:00
RuoYi
25e9112ccc 升级minio到最新版本8.2.2 2021-07-28 20:12:50 +08:00
RuoYi
f6c5c91eb1 升级commons.io到最新版本v2.11.0 2021-07-28 14:00:32 +08:00
若依
740da06972 !91 【轻量级PR】 [SysUser] 返回给前端数据的时候,隐藏密码和加盐,防止别人猜测加密方式 或者 暴力破击
Merge pull request !91 from dazer007/master
2021-07-28 05:51:09 +00:00
RuoYi
2a363f9c17 升级tobato到最新版本1.27.2 2021-07-28 13:33:49 +08:00
RuoYi
3c649a8814 升级minio到最新版本8.3.0 2021-07-28 13:30:46 +08:00
RuoYi
a144bf2bff 升级dynamic-ds到最新版本3.4.1 2021-07-28 13:28:50 +08:00
RuoYi
83ee4223be 升级spring-boot-mybatis到最新版2.2.0 2021-07-28 13:24:29 +08:00
RuoYi
8f6c864e96 升级spring-boot-admin到最新版2.4.3 2021-07-28 13:19:50 +08:00
RuoYi
12209ae5a4 升级spring-boot到最新版本2.5.3 2021-07-28 13:17:20 +08:00
duandazhi
4e0301a7b2 [SysUser] 返回给前端数据的时候,隐藏密码和加盐,防止别人猜测加密方式 或者 暴力破击 2021-07-28 13:07:06 +08:00
RuoYi
00fa1c3158 支持配置XSS跨站脚本过滤 2021-07-28 13:05:18 +08:00
若依
04edd66199 !90 【轻量级PR】SysUserController remove 解决把自己删除的bug
Merge pull request !90 from dazer007/secerity-fix-remove-self-ok
2021-07-28 05:01:49 +00:00
若依
d8896c9054 !89 update ruoyi-ui/src/utils/zipdownload.js.
Merge pull request !89 from ytzjf/N/A
2021-07-28 05:01:46 +00:00
duandazhi
dd70c1950e sysuercontroller remove self 问题修复 2021-07-28 10:24:14 +08:00
RuoYi
954d208ac6 支持配置XSS跨站脚本过滤 2021-07-28 09:58:59 +08:00
ytzjf
98d25fa16e update ruoyi-ui/src/utils/zipdownload.js.
BLOB下载时清除URL对象引用
2021-07-28 01:57:11 +00:00
RuoYi
3af7af265b 验证码配置 2021-07-27 20:47:57 +08:00
RuoYi
436c2154ad 支持配置验证码开关&类型 2021-07-27 20:39:46 +08:00
RuoYi
42e8baa85c 添加新群号 2021-07-27 20:38:30 +08:00
RuoYi
0dff71bd4d 跳转路由高亮相对应的菜单栏 2021-07-27 13:09:30 +08:00
RuoYi
20ce9da509 修复任意账户越权问题 2021-07-27 13:08:37 +08:00
RuoYi
a044b0d205 升级element-ui到最新版本2.15.3 2021-07-26 10:11:08 +08:00
RuoYi
698200ecc2 角色&菜单新增字段属性提示信息 2021-07-26 10:10:39 +08:00
RuoYi
883d89ee0b 内链设置meta信息 2021-07-26 10:08:42 +08:00
RuoYi
fb9a480f3c 密码框新增显示切换密码图标 2021-07-26 10:08:28 +08:00
RuoYi
e288710251 导入用户样式调整 2021-07-26 10:06:08 +08:00
RuoYi
1019aa58ce 顶部菜单样式调整 2021-07-26 10:03:52 +08:00
RuoYi
e9621469b2 更多操作按钮添加权限控制 2021-07-25 11:47:55 +08:00
RuoYi
57c4910605 富文本新增上传文件大小限制 2021-07-25 11:39:12 +08:00
RuoYi
9920957796 状态码401返回Promise.reject 2021-07-25 11:36:31 +08:00
RuoYi
e798f46876 顶部菜单排除隐藏的默认路由 2021-07-24 19:15:16 +08:00
RuoYi
caa7537607 修复定时任务日志执行状态显示 2021-07-17 16:51:37 +08:00
若依
8b23c97fdb !83 【轻量级PR】RuoYiFileApplication 重命名为RuoYiFileApplication
Merge pull request !83 from dazer007/RuoYFileApplication-rename-
2021-07-17 08:48:40 +00:00
duandazhi
af49ad54f6 RuoYFileApplication-rename 2021-07-16 12:26:07 +08:00
RuoYi
816479e092 定时任务新增更多操作 2021-07-15 17:36:14 +08:00
RuoYi
7e72849d05 修复多图时无法删除相应图片问题 2021-07-13 10:32:48 +08:00
RuoYi
99206a5d65 删除富文本video事件 2021-07-13 10:32:23 +08:00
RuoYi
114c9f3af6 菜单路由配置支持内链访问 2021-07-11 19:30:24 +08:00
RuoYi
e14fb70c26 自定义弹窗拖拽指令 2021-07-09 21:14:16 +08:00
RuoYi
201149a131 富文本默认上传返回url类型 2021-07-09 21:13:54 +08:00
RuoYi
2bc80c4c07 授权用户添加事务 2021-07-09 21:13:09 +08:00
RuoYi
ee7607bcca 全局注册通用组件 2021-07-09 21:12:37 +08:00
RuoYi
7e2dbc5608 ImageUpload组件支持多图片上传 2021-07-09 21:11:07 +08:00
RuoYi
8eac239e48 FileUpload组件支持多文件上传 2021-07-09 21:10:08 +08:00
若依
6650397e38 !79 【轻量级 PR】:修改登录失效返回值code401
Merge pull request !79 from bug制造者/master
2021-07-09 12:57:28 +00:00
bug制造者
1ca4c26d81 修改登录失效返回值code401
之前返回的code200,前端就会$message一下错误信息,不会弹出401重新登录跳转的弹框。
2021-07-07 09:16:05 +00:00
RuoYi
0b0da91139 角色管理新增分配用户功能 2021-07-06 14:02:37 +08:00
RuoYi
3d47ec2e73 用户管理新增分配角色功能 2021-07-02 10:59:22 +08:00
RuoYi
1f21102204 升级pagehelper到最新版1.3.1 2021-06-25 17:42:41 +08:00
RuoYi
58b4600144 用户信息长度校验限制 2021-06-25 17:41:40 +08:00
RuoYi
6146d63f2c 修复日志列表取消字段排序时的报错问题 2021-06-25 17:40:05 +08:00
RuoYi
dbf42739ca 全局挂载字典标签组件 2021-06-25 17:39:43 +08:00
Ricky
57f56c2769 增加字典标签样式回显 2021-06-22 14:47:28 +08:00
RuoYi
5c130cfda6 封装iframe组件 2021-06-17 20:19:31 +08:00
RuoYi
df284ff6f5 日志列表支持排序操作 2021-06-17 20:18:59 +08:00
RuoYi
1b7550f9a2 升级commons.fileupload到最新版本v1.4 2021-06-16 10:06:09 +08:00
RuoYi
195df24b4b 升级commons.io到最新版本v2.10.0 2021-06-16 10:05:53 +08:00
RuoYi
68de85aa5f 升级nacos到最新版2.0.2 2021-06-15 10:42:03 +08:00
RuoYi
8b6784d8e2 升级element-ui到最新版本2.15.2 2021-06-15 10:41:31 +08:00
RuoYi
b73c14400c 升级spring-boot到最新版本2.5.1 2021-06-12 14:05:27 +08:00
RuoYi
54922af7db 升级dynamic-ds到最新版本3.4.0 2021-06-12 14:05:07 +08:00
RuoYi
23944b2f9c 系统布局配置支持动态标题开关 2021-06-11 11:04:06 +08:00
RuoYi
9d0240b831 分页组件新增pagerCount属性 2021-06-11 11:03:54 +08:00
RuoYi
1aef90d506 修复用户搜索分页变量错误 2021-06-11 11:03:33 +08:00
RuoYi
41b7e62b02 优化部门父级启用状态 2021-06-11 10:49:26 +08:00
RuoYi
70a5e47b66 若依 3.0.0 2021-06-10 09:19:42 +08:00
RuoYi
8722e184d6 升级swagger到最新版本v3.0.0 2021-06-08 20:03:37 +08:00
RuoYi
819ad04e3e 优化Excel导入增加空行判断 2021-06-08 19:55:09 +08:00
RuoYi
ab89a5efcc 富文本工具栏配置视频 2021-06-03 13:58:05 +08:00
RuoYi
2fbb10bfd2 修复导出角色数据范围翻译缺少仅本人 2021-06-03 13:57:52 +08:00
RuoYi
3fba7d5d92 开启nacos/gRpc端口 2021-06-03 13:56:21 +08:00
RuoYi
d8d8758cfa 修复关闭confirm提示框控制台报错问题 2021-06-03 13:55:22 +08:00
RuoYi
5ba3e031f2 升级springcloud到最新版2020.0.3 2021-06-03 10:35:47 +08:00
若依
cc658b9ece !66 代码生成优化
Merge pull request !66 from 雪忆天堂/N/A
2021-06-03 10:35:16 +08:00
RuoYi
1ed627798e 修复表单构建选择下拉选择控制台报错问题 2021-06-02 21:39:43 +08:00
RuoYi
bc4c7a02d3 菜单权限标识长度限制100 2021-06-02 21:39:31 +08:00
RuoYi
17493a2a15 优化图片工具类读取文件 2021-06-02 12:00:20 +08:00
RuoYi
63d7e816ff 添加docker支持 2021-06-02 09:58:36 +08:00
RuoYi
fc7bd9a35d 升级nacos到最新版2.0.1 性能提升 2021-05-31 14:53:50 +08:00
RuoYi
7ebcb4bf8a 修复升级cloud引起的代码生成报错 2021-05-30 18:20:35 +08:00
RuoYi
67feb9947e 优化参数&字典缓存操作 2021-05-27 21:13:48 +08:00
RuoYi
af479c7838 修复两处存在SQL注入漏洞问题 2021-05-27 21:13:01 +08:00
RuoYi
2f3949d732 升级Spring Cloud相关组件到最新版 2021-05-27 09:46:19 +08:00
RuoYi
79c8b17bee Redis设置HashKey序列化 2021-05-26 10:58:57 +08:00
RuoYi
12d3e3d0d8 新增IE浏览器版本过低提示页面 2021-05-26 10:57:11 +08:00
RuoYi
fc33d88334 去除job多余的Serializable 2021-05-26 10:56:26 +08:00
RuoYi
09b49f46e8 升级fastjson到最新版1.2.76 2021-05-26 10:55:34 +08:00
RuoYi
379ea3f6bc 升级druid到最新版本v1.2.6 2021-05-26 10:53:36 +08:00
RuoYi
84b975ac7f 修复请求形参未传值记录日志异常问题 2021-05-26 10:52:38 +08:00
RuoYi
c7b6858433 修正方法名单词拼写错误 2021-05-26 10:47:43 +08:00
RuoYi
33bae9509b 修正mapper模板注释 2021-05-26 10:46:10 +08:00
雪忆天堂
d0cc511bbc 代码生成优化 2021-05-25 16:27:41 +08:00
RuoYi
d855fd67b4 删除操作日志记录日志 2021-05-11 16:52:21 +08:00
RuoYi
7f802e8274 树级结构更新子节点使用replaceFirst 2021-05-11 16:52:07 +08:00
RuoYi
b48e6fb19a 修正代码生成导入表权限标识 2021-04-26 18:19:38 +08:00
若依
de4dd01d97 !61 优化注释
Merge pull request !61 from mask/master
2021-04-26 18:14:25 +08:00
RuoYi
d04a93d75d 修复开启TopNav后,左侧打开外链问题 2021-04-21 16:33:05 +08:00
Mask
fb8eac7064 优化注释 2021-04-21 16:31:21 +08:00
RuoYi
f26c32881c 修复一级菜单包屑显示重复问题 2021-04-21 15:49:16 +08:00
Ricky
2419815bc1 优化ExcelUtil空值处理 2021-04-21 09:59:06 +08:00
RuoYi
cb9fa33b5a 主题颜色保存配置 2021-04-19 17:05:20 +08:00
RuoYi
eb69f8e614 过滤BindingResult对象,防止异常 2021-04-18 22:32:19 +08:00
RuoYi
1eb9581d36 兼容顶部栏一级菜单内部跳转 2021-04-18 22:31:30 +08:00
RuoYi
7789620a15 修正模板字符编码 2021-04-18 22:31:03 +08:00
RuoYi
c3ba1a092c 升级mybatis到最新版2.1.4 2021-04-18 22:30:07 +08:00
Ricky
3ef167e416 优化树表代码生成模板 2021-04-15 10:51:03 +08:00
RuoYi
8e1a9a2433 固定顶部导航栏&窗口大小改变实时更新栏数 2021-04-14 11:02:43 +08:00
RuoYi
cd73787dc7 数据监控默认地址修改 2021-04-13 18:14:53 +08:00
RuoYi
1624a63cff 恢复tansParams方法 2021-04-13 15:36:05 +08:00
RuoYi
db4a8b0e90 布局设置支持保存&重置配置 2021-04-13 15:10:33 +08:00
RuoYi
d989e4a717 富文本编辑器支持自定义上传地址 2021-04-13 15:08:07 +08:00
RuoYi
a664602e28 优化代码生成导出模板名称 2021-04-13 14:54:54 +08:00
RuoYi
f44b453d76 优化主子表代码生成模板 2021-04-13 14:53:50 +08:00
RuoYi
1298d02379 新增菜单导航显示风格TopNav(false为左侧导航菜单,true为顶部导航菜单) 2021-04-12 10:20:25 +08:00
RuoYi
92a93f7cf8 修正通知公告日志记录类型 2021-04-12 10:19:13 +08:00
RuoYi
94a6a5a7d9 页签新增关闭右侧 2021-04-10 21:24:28 +08:00
RuoYi
2ca3f9b640 修复树表数据显示不全&加载慢问题 2021-04-09 10:36:09 +08:00
RuoYi
286bedaa40 删除多余的初始字典数据 2021-04-08 22:21:06 +08:00
RuoYi
edd1cfaf43 Merge branch 'master' of https://gitee.com/y_project/RuoYi-Cloud 2021-04-08 22:19:54 +08:00
RuoYi
accd7ebc93 修改主题后mini类型按钮无效问题 2021-04-08 22:19:43 +08:00
RuoYi
b7b755f0c6 通用下载完成后删除节点 2021-04-08 22:19:16 +08:00
RuoYi
87fcfe0c56 优化注释 2021-04-08 22:19:05 +08:00
RuoYi
89a9188dd4 用户&角色单条删除时使其逻辑删除 2021-04-08 22:18:18 +08:00
若依
45b0e82432 !58 取消了druid的wall配置
Merge pull request !58 from 啦啦啦少年/master
2021-04-08 22:17:16 +08:00
liuxingxing
5d3125463c druid 配置了wall 2021-04-06 15:35:40 +08:00
RuoYi
1ab84cb0e5 修复firefox下表单构建拖拽会新打卡一个选项卡 2021-03-30 17:46:43 +08:00
RuoYi
e58c159225 添加新群号 2021-03-29 18:15:44 +08:00
若依
2edb885ae5 !55 页面超链接修改
Merge pull request !55 from 零度/master
2021-03-25 17:27:57 +08:00
lingdu
b037ee9caf 页面源码地址修改,页面文档地址修改 2021-03-24 21:48:00 +08:00
RuoYi
27d137a4ae 显隐列初始默认隐藏列 2021-03-24 17:33:44 +08:00
RuoYi
25f94db9c5 关闭用户头像上传窗口时恢复原图 2021-03-24 17:33:29 +08:00
RuoYi
457ec516f7 个人信息添加手机&邮箱重复验证 2021-03-21 11:09:44 +08:00
若依
29cd58d3f4 !53 修复异步操作导致的request null,IP获取错误
Merge pull request !53 from TwelveT/N/A
2021-03-20 08:59:05 +08:00
TwelveT
262d06ea3a 修复异步操作导致的request null,IP获取错误 2021-03-19 14:46:06 +08:00
RuoYi
0f733b89dc 通用Controller添加响应返回消息 2021-03-16 16:27:34 +08:00
RuoYi
265b680a61 调整sql默认为当前时间 2021-03-16 16:27:09 +08:00
若依
2b7bb59d12 !49 修复Byte[]类型参数死循环的问题
Merge pull request !49 from 周艺峰/master
2021-03-14 22:07:45 +08:00
RuoYi
41fa57d778 velocity剔除commons-collections版本,防止3.2.1版本的反序列化漏洞 2021-03-14 21:53:51 +08:00
若依
6ad1df166b !51 增加feign客户端IP头部信息
Merge pull request !51 from TwelveT/N/A
2021-03-14 21:31:40 +08:00
TwelveT
24e7ed38a5 增加feign客户端IP头部信息 2021-03-07 23:47:43 +08:00
RuoYi
fd0e9202d8 seata-boot-starter替换为cloud-alibaba-seata 2021-03-07 20:26:59 +08:00
RuoYi
0922410a22 富文本编辑组件支持只读 2021-03-07 15:21:48 +08:00
RuoYi
4edaa14e28 删除多余的代码 2021-03-07 15:21:33 +08:00
周艺峰
a2dede8448 修复Byte[]类型参数死循环的问题 2021-03-04 09:28:51 +08:00
RuoYi
dc83ba0356 更新脚本&优化代码 2021-02-28 21:33:50 +08:00
JuJu
60c815ab76 修复部分更新方法过滤失败与单*匹配不精确问题 2021-02-28 17:21:02 +08:00
DokiYoloo
48cf4250b4 修复AuthFilter白名单过滤匹配不精准 2021-02-25 23:35:22 +08:00
RuoYi
ede8456ea0 修复网关黑名单过滤器中文乱码问题 2021-02-23 18:05:41 +08:00
RuoYi
4dc51fae53 修正注释 2021-02-23 10:27:54 +08:00
RuoYi
192f58e690 修改ip字段长度防止ipv6地址长度不够 2021-02-10 12:53:25 +08:00
RuoYi
852b32eabb 角色非自定义权限范围清空选择值 2021-02-06 10:35:44 +08:00
RuoYi
5e167eb6e1 修复四级菜单无法显示问题 2021-02-06 09:33:45 +08:00
RuoYi
31aefd15f7 若依 2.5.0 2021-02-02 13:31:07 +08:00
RuoYi
716405d22b update README.md 2021-02-02 11:40:58 +08:00
RuoYi
3c33fe21fa 升级spring-boot-alibaba到最新版2.2.5 2021-02-02 11:38:53 +08:00
DokiYoloo
e1e05b761a !43 角色管理-编辑角色-功能权限显示异常
Merge pull request !43 from wihi/master
2021-01-30 17:19:23 +08:00
wanghuan
1804d4fce6 修复角色管理-编辑角色-功能权限显示异常 2021-01-30 16:35:11 +08:00
RuoYi
0dcc955ced 更换过期的共享配置属性 2021-01-27 18:00:50 +08:00
RuoYi
53076612f8 增加分布式事务seata支持 2021-01-27 10:47:01 +08:00
RuoYi
f0f2cde0f9 update swagger 2021-01-26 10:52:31 +08:00
若依
0546504cae !39 修复sentinel流量告警无提示
Merge pull request !39 from 9527/master
2021-01-26 10:50:09 +08:00
RuoYi
17652c3a2f 升级element-ui到最新版本2.15.0 2021-01-24 12:07:17 +08:00
ysj
e96d12975f 修复sentinel流量告警前端不响应 2021-01-21 10:06:29 +08:00
RuoYi
a9b51070ed 添加启动执行脚本 2021-01-17 12:06:33 +08:00
若依
a8c7b9102b !38 返回绝对路径
Merge pull request !38 from xlongwei/N/A
2021-01-17 12:02:21 +08:00
xlongwei
ca04b583a9 返回绝对路径 2021-01-16 13:25:39 +08:00
RuoYi
99390fda05 update README.md 2021-01-14 13:22:53 +08:00
RuoYi
be12108139 修复生成树表代码异常 2021-01-13 17:53:28 +08:00
RuoYi
1dec234174 升级spring-boot到最新版本2.3.7 2021-01-13 17:46:26 +08:00
RuoYi
3fc684b346 升级spring-cloud到Hoxton.SR9 2021-01-13 17:45:30 +08:00
RuoYi
12d8add3e4 升级spring-boot-admin到最新版2.3.1 2021-01-13 17:43:49 +08:00
RuoYi
372b77c662 升级fastjson到最新版1.2.75 2021-01-13 17:24:26 +08:00
RuoYi
5891960756 代码生成模板支持主子表 2021-01-08 11:08:59 +08:00
RuoYi
c5899bfbf0 修复导入数据为负浮点数时丢失精度问题 2021-01-08 11:08:43 +08:00
RuoYi
4125f44179 用户显隐列添加不同key防止被复用 2021-01-07 13:37:33 +08:00
RuoYi
09a0058379 表格右侧工具栏组件支持显隐列 2021-01-06 17:50:01 +08:00
RuoYi
3fdcac939a 升级druid到最新版本v1.2.4 2021-01-06 12:01:05 +08:00
RuoYi
4cc4e8a8fa 编码解码用户名,防止中文出现乱码 2021-01-06 12:00:41 +08:00
RuoYi
dee4653b9b 代码生成支持文件上传组件 2021-01-06 11:59:13 +08:00
若依
2c610dc465 !37 update ruoyi-ui/src/components/FileUpload/index.vue.
Merge pull request !37 from ouwei2020/N/A
2021-01-06 11:57:57 +08:00
ouwei2020
2c323ca3ff update ruoyi-ui/src/components/FileUpload/index.vue. 2021-01-06 11:09:23 +08:00
DokiYolo
a95be9d418 解决header获取username中文情况下乱码 2021-01-06 10:06:57 +08:00
RuoYi
a445462153 代码生成支持文件上传组件 2021-01-05 21:34:27 +08:00
RuoYi
843f08984b 图片组件添加预览&移除功能 2021-01-05 21:08:01 +08:00
RuoYi
2b3820223c 修复IE11浏览器报错问题 2021-01-05 10:30:05 +08:00
RuoYi
a5d0028b39 操作按钮组调整为朴素按钮样式 2021-01-05 10:20:26 +08:00
RuoYi
449704180b Update copyright 2021-01-04 17:54:40 +08:00
JuJu
8a18873b81 修正操作日志删除接口路径 2021-01-01 12:47:46 +08:00
JuJu
199228f6cb spring.factories增加RemoteFileFallbackFactory 2021-01-01 12:41:28 +08:00
RuoYi
6d0b4f5d16 用户手机邮箱&菜单组件修改允许空字符串 2020-12-29 17:29:22 +08:00
RuoYi
4cc0e2650c 代码生成数据库文本类型生成表单文本域 2020-12-29 11:27:58 +08:00
RuoYi
06074571f5 Excel注解支持Image图片导出 2020-12-27 10:05:28 +08:00
RuoYi
2698ea58d4 代码生成日期控件区分范围 2020-12-25 09:36:22 +08:00
RuoYi
8c9eb7d6b6 修正侧边栏静态路由丢失问题 2020-12-24 19:33:09 +08:00
RuoYi
79472708d9 防止get请求参数值为false或0等特殊值会导致无法正确的传参 2020-12-22 16:25:16 +08:00
RuoYi
586908b4d7 若依 2.4.0 2020-12-22 10:40:42 +08:00
324 changed files with 11614 additions and 3101 deletions

View File

@@ -5,7 +5,7 @@
* 采用前后端分离的模式,微服务版本前端(基于 [RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue))。 * 采用前后端分离的模式,微服务版本前端(基于 [RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue))。
* 后端采用Spring Boot、Spring Cloud & Alibaba。 * 后端采用Spring Boot、Spring Cloud & Alibaba。
* 注册中心、配置中心选型Nacos权限认证使用Redis。 * 注册中心、配置中心选型Nacos权限认证使用Redis。
* 流量控制框架选型Sentinel。 * 流量控制框架选型Sentinel分布式事务选型Seata
* 如需不分离应用,请移步 [RuoYi](https://gitee.com/y_project/RuoYi),如需分离应用,请移步 [RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue) * 如需不分离应用,请移步 [RuoYi](https://gitee.com/y_project/RuoYi),如需分离应用,请移步 [RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue)
* 阿里云折扣场:[点我进入](http://aly.ruoyi.vip),腾讯云秒杀场:[点我进入](http://txy.ruoyi.vip)   * 阿里云折扣场:[点我进入](http://aly.ruoyi.vip),腾讯云秒杀场:[点我进入](http://txy.ruoyi.vip)  
* 阿里云优惠券:[点我领取](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)   * 阿里云优惠券:[点我领取](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)  
@@ -24,6 +24,7 @@ com.ruoyi
├── ruoyi-common // 通用模块 ├── ruoyi-common // 通用模块
│ └── ruoyi-common-core // 核心模块 │ └── ruoyi-common-core // 核心模块
│ └── ruoyi-common-datascope // 权限范围 │ └── ruoyi-common-datascope // 权限范围
│ └── ruoyi-common-datasource // 多数据源
│ └── ruoyi-common-log // 日志记录 │ └── ruoyi-common-log // 日志记录
│ └── ruoyi-common-redis // 缓存服务 │ └── ruoyi-common-redis // 缓存服务
│ └── ruoyi-common-security // 安全模块 │ └── ruoyi-common-security // 安全模块
@@ -40,7 +41,7 @@ com.ruoyi
## 架构图 ## 架构图
<img src="https://oscimg.oschina.net/oscnet/up-63c1c1dd2dc2b91d498164d9ee33682a32a.png"/> <img src="https://oscimg.oschina.net/oscnet/up-82e9722ecb846786405a904bafcf19f73f3.png"/>
## 内置功能 ## 内置功能
@@ -107,11 +108,11 @@ com.ruoyi
</tr> </tr>
<tr> <tr>
<td><img src="https://oscimg.oschina.net/oscnet/up-ff9e3066561574aca73005c5730c6a41f15.png"/></td> <td><img src="https://oscimg.oschina.net/oscnet/up-ff9e3066561574aca73005c5730c6a41f15.png"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/up-6d73c2140ce694e3de4c05035fdc1868d4c.png"/></td> <td><img src="https://oscimg.oschina.net/oscnet/up-5e4daac0bb59612c5038448acbcef235e3a.png"/></td>
</tr> </tr>
</table> </table>
## 若依微服务交流群 ## 若依微服务交流群
QQ群 [![加入QQ群](https://img.shields.io/badge/已满-42799195-blue.svg)](https://jq.qq.com/?_wv=1027&k=yqInfq0S) [![加入QQ群](https://img.shields.io/badge/已满-170157040-blue.svg)](https://jq.qq.com/?_wv=1027&k=Oy1mb3p8) [![加入QQ群](https://img.shields.io/badge/130643120-blue.svg)](https://jq.qq.com/?_wv=1027&k=rvxkJtXK) 点击按钮入群。 QQ群 [![加入QQ群](https://img.shields.io/badge/已满-42799195-blue.svg)](https://jq.qq.com/?_wv=1027&k=yqInfq0S) [![加入QQ群](https://img.shields.io/badge/已满-170157040-blue.svg)](https://jq.qq.com/?_wv=1027&k=Oy1mb3p8) [![加入QQ群](https://img.shields.io/badge/已满-130643120-blue.svg)](https://jq.qq.com/?_wv=1027&k=rvxkJtXK) [![加入QQ群](https://img.shields.io/badge/已满-225920371-blue.svg)](https://jq.qq.com/?_wv=1027&k=0Ck3PvTe) [![加入QQ群](https://img.shields.io/badge/201705537-blue.svg)](https://jq.qq.com/?_wv=1027&k=FnHHP4TT) 点击按钮入群。

View File

@@ -1,6 +1,6 @@
@echo off @echo off
echo. echo.
echo [信息] 清理生成路径。 echo [信息] 清理工程target生成路径。
echo. echo.
%~d0 %~d0

14
bin/run-auth.bat Normal file
View File

@@ -0,0 +1,14 @@
@echo off
echo.
echo [信息] 使用Jar命令运行Auth工程。
echo.
cd %~dp0
cd ../ruoyi-auth/target
set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
java -Dfile.encoding=utf-8 -jar %JAVA_OPTS% ruoyi-auth.jar
cd bin
pause

14
bin/run-gateway.bat Normal file
View File

@@ -0,0 +1,14 @@
@echo off
echo.
echo [信息] 使用Jar命令运行Gateway工程。
echo.
cd %~dp0
cd ../ruoyi-gateway/target
set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
java -Dfile.encoding=utf-8 -jar %JAVA_OPTS% ruoyi-gateway.jar
cd bin
pause

14
bin/run-modules-file.bat Normal file
View File

@@ -0,0 +1,14 @@
@echo off
echo.
echo [信息] 使用Jar命令运行Modules-File工程。
echo.
cd %~dp0
cd ../ruoyi-modules/ruoyi-file/target
set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
java -Dfile.encoding=utf-8 -jar %JAVA_OPTS% ruoyi-modules-file.jar
cd bin
pause

14
bin/run-modules-gen.bat Normal file
View File

@@ -0,0 +1,14 @@
@echo off
echo.
echo [信息] 使用Jar命令运行Modules-Gen工程。
echo.
cd %~dp0
cd ../ruoyi-modules/ruoyi-gen/target
set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
java -Dfile.encoding=utf-8 -jar %JAVA_OPTS% ruoyi-modules-gen.jar
cd bin
pause

14
bin/run-modules-job.bat Normal file
View File

@@ -0,0 +1,14 @@
@echo off
echo.
echo [信息] 使用Jar命令运行Modules-Job工程。
echo.
cd %~dp0
cd ../ruoyi-modules/ruoyi-job/target
set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
java -Dfile.encoding=utf-8 -jar %JAVA_OPTS% ruoyi-modules-job.jar
cd bin
pause

View File

@@ -0,0 +1,14 @@
@echo off
echo.
echo [信息] 使用Jar命令运行Modules-System工程。
echo.
cd %~dp0
cd ../ruoyi-modules/ruoyi-system/target
set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
java -Dfile.encoding=utf-8 -jar %JAVA_OPTS% ruoyi-modules-system.jar
cd bin
pause

14
bin/run-monitor.bat Normal file
View File

@@ -0,0 +1,14 @@
@echo off
echo.
echo [信息] 使用Jar命令运行Monitor工程。
echo.
cd %~dp0
cd ../ruoyi-visual/ruoyi-monitor/target
set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
java -Dfile.encoding=utf-8 -jar %JAVA_OPTS% ruoyi-visual-monitor.jar
cd bin
pause

67
docker/deploy.sh Normal file
View File

@@ -0,0 +1,67 @@
#!/bin/sh
# 使用说明,用来提示输入参数
usage() {
echo "Usage: sh 执行脚本.sh [port|base|modules|stop|rm]"
exit 1
}
# 开启所需端口
port(){
firewall-cmd --add-port=80/tcp --permanent
firewall-cmd --add-port=8080/tcp --permanent
firewall-cmd --add-port=8848/tcp --permanent
firewall-cmd --add-port=9848/tcp --permanent
firewall-cmd --add-port=9849/tcp --permanent
firewall-cmd --add-port=6379/tcp --permanent
firewall-cmd --add-port=3306/tcp --permanent
firewall-cmd --add-port=9100/tcp --permanent
firewall-cmd --add-port=9200/tcp --permanent
firewall-cmd --add-port=9201/tcp --permanent
firewall-cmd --add-port=9202/tcp --permanent
firewall-cmd --add-port=9203/tcp --permanent
firewall-cmd --add-port=9300/tcp --permanent
service firewalld restart
}
# 启动基础环境(必须)
base(){
docker-compose up -d ruoyi-mysql ruoyi-redis ruoyi-nacos ruoyi-nginx
}
# 启动程序模块(必须)
modules(){
docker-compose up -d ruoyi-gateway ruoyi-auth ruoyi-modules-system
}
# 关闭所有环境/模块
stop(){
docker-compose stop
}
# 删除所有环境/模块
rm(){
docker-compose rm
}
# 根据输入参数,选择执行对应方法,不输入则执行使用说明
case "$1" in
"port")
port
;;
"base")
base
;;
"modules")
modules
;;
"stop")
stop
;;
"rm")
rm
;;
*)
usage
;;
esac

140
docker/docker-compose.yml Normal file
View File

@@ -0,0 +1,140 @@
version : '3.8'
services:
ruoyi-nacos:
container_name: ruoyi-nacos
image: nacos/nacos-server
build:
context: ./nacos
environment:
- MODE=standalone
volumes:
- ./nacos/logs/:/home/nacos/logs
- ./nacos/conf/application.properties:/home/nacos/conf/application.properties
ports:
- "8848:8848"
- "9848:9848"
- "9849:9849"
depends_on:
- ruoyi-mysql
ruoyi-mysql:
container_name: ruoyi-mysql
image: mysql:5.7
build:
context: ./mysql
ports:
- "3306:3306"
volumes:
- ./mysql/conf:/etc/mysql/conf.d
- ./mysql/logs:/logs
- ./mysql/data:/var/lib/mysql
command: [
'mysqld',
'--innodb-buffer-pool-size=80M',
'--character-set-server=utf8mb4',
'--collation-server=utf8mb4_unicode_ci',
'--default-time-zone=+8:00',
'--lower-case-table-names=1'
]
environment:
MYSQL_DATABASE: 'ry-cloud'
MYSQL_ROOT_PASSWORD: password
ruoyi-redis:
container_name: ruoyi-redis
image: redis
build:
context: ./redis
ports:
- "6379:6379"
volumes:
- ./redis/conf/redis.conf:/home/ruoyi/redis/redis.conf
- ./redis/data:/data
command: redis-server /home/ruoyi/redis/redis.conf
ruoyi-nginx:
container_name: ruoyi-nginx
image: nginx
build:
context: ./nginx
ports:
- "80:80"
volumes:
- ./nginx/html/dist:/home/ruoyi/projects/ruoyi-ui
- ./nginx/conf/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/logs:/var/log/nginx
- ./nginx/conf.d:/etc/nginx/conf.d
depends_on:
- ruoyi-gateway
links:
- ruoyi-gateway
ruoyi-gateway:
container_name: ruoyi-gateway
build:
context: ./ruoyi/gateway
dockerfile: dockerfile
ports:
- "8080:8080"
depends_on:
- ruoyi-redis
links:
- ruoyi-redis
ruoyi-auth:
container_name: ruoyi-auth
build:
context: ./ruoyi/auth
dockerfile: dockerfile
ports:
- "9200:9200"
depends_on:
- ruoyi-redis
links:
- ruoyi-redis
ruoyi-modules-system:
container_name: ruoyi-modules-system
build:
context: ./ruoyi/modules/system
dockerfile: dockerfile
ports:
- "9201:9201"
depends_on:
- ruoyi-redis
- ruoyi-mysql
links:
- ruoyi-redis
- ruoyi-mysql
ruoyi-modules-gen:
container_name: ruoyi-modules-gen
build:
context: ./ruoyi/modules/gen
dockerfile: dockerfile
ports:
- "9202:9202"
depends_on:
- ruoyi-mysql
links:
- ruoyi-mysql
ruoyi-modules-job:
container_name: ruoyi-modules-job
build:
context: ./ruoyi/modules/job
dockerfile: dockerfile
ports:
- "9203:9203"
depends_on:
- ruoyi-mysql
links:
- ruoyi-mysql
ruoyi-modules-file:
container_name: ruoyi-modules-file
build:
context: ./ruoyi/modules/file
dockerfile: dockerfile
ports:
- "9300:9300"
volumes:
- ./ruoyi/uploadPath:/home/ruoyi/uploadPath
ruoyi-visual-monitor:
container_name: ruoyi-visual-monitor
build:
context: ./ruoyi/visual/monitor
dockerfile: dockerfile
ports:
- "9100:9100"

View File

@@ -0,0 +1 @@
存放sql目录下的所有脚本用于docker自动执行。

7
docker/mysql/dockerfile Normal file
View File

@@ -0,0 +1,7 @@
# 基础镜像
FROM mysql:5.7
# author
MAINTAINER ruoyi
# 执行sql脚本
ADD ./db/*.sql /docker-entrypoint-initdb.d/

View File

@@ -0,0 +1,32 @@
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://ruoyi-mysql:3306/ry-config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user=root
db.password=password
nacos.naming.empty-service.auto-clean=true
nacos.naming.empty-service.clean.initial-delay-ms=50000
nacos.naming.empty-service.clean.period-time-ms=30000
management.endpoints.web.exposure.include=*
management.metrics.export.elastic.enabled=false
management.metrics.export.influx.enabled=false
server.tomcat.accesslog.enabled=true
server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D %{User-Agent}i %{Request-Source}i
server.tomcat.basedir=
nacos.security.ignore.urls=/,/error,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-ui/public/**,/v1/auth/**,/v1/console/health/**,/actuator/**,/v1/console/server/**
nacos.core.auth.system.type=nacos
nacos.core.auth.enabled=false
nacos.core.auth.default.token.expire.seconds=18000
nacos.core.auth.default.token.secret.key=SecretKey012345678901234567890123456789012345678901234567890123456789
nacos.core.auth.caching.enabled=true
nacos.core.auth.enable.userAgentAuthWhite=false
nacos.core.auth.server.identity.key=serverIdentity
nacos.core.auth.server.identity.value=security
nacos.istio.mcp.server.enabled=false

7
docker/nacos/dockerfile Normal file
View File

@@ -0,0 +1,7 @@
# 基础镜像
FROM nacos/nacos-server
# author
MAINTAINER ruoyi
# 复制conf文件到路径
COPY ./conf/application.properties /home/nacos/conf/application.properties

View File

@@ -0,0 +1,36 @@
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
location / {
root /home/ruoyi/projects/ruoyi-ui;
try_files $uri $uri/ /index.html;
index index.html index.htm;
}
location /prod-api/{
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://ruoyi-gateway:8080/;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}# requirepass 123456

15
docker/nginx/dockerfile Normal file
View File

@@ -0,0 +1,15 @@
# 基础镜像
FROM nginx
# author
MAINTAINER ruoyi
# 挂载目录
VOLUME /home/ruoyi/projects/ruoyi-ui
# 创建目录
RUN mkdir -p /home/ruoyi/projects/ruoyi-ui
# 指定路径
WORKDIR /home/ruoyi/projects/ruoyi-ui
# 复制conf文件到路径
COPY ./conf/nginx.conf /etc/nginx/nginx.conf
# 复制html文件到路径
COPY ./html/dist /home/ruoyi/projects/ruoyi-ui

1
docker/nginx/html/dist/readme.txt vendored Normal file
View File

@@ -0,0 +1 @@
存放前端ruoyi-ui构建好的静态文件用于nginx请求访问。

View File

@@ -0,0 +1 @@
# requirepass 123456

13
docker/redis/dockerfile Normal file
View File

@@ -0,0 +1,13 @@
# 基础镜像
FROM redis
# author
MAINTAINER ruoyi
# 挂载目录
VOLUME /home/ruoyi/redis
# 创建目录
RUN mkdir -p /home/ruoyi/redis
# 指定路径
WORKDIR /home/ruoyi/redis
# 复制conf文件到路径
COPY ./conf/redis.conf /home/ruoyi/redis/redis.conf

View File

@@ -0,0 +1,15 @@
# 基础镜像
FROM openjdk:8-jre
# author
MAINTAINER ruoyi
# 挂载目录
VOLUME /home/ruoyi
# 创建目录
RUN mkdir -p /home/ruoyi
# 指定路径
WORKDIR /home/ruoyi
# 复制jar文件到路径
COPY ./jar/ruoyi-auth.jar /home/ruoyi/ruoyi-auth.jar
# 启动认证服务
ENTRYPOINT ["java","-jar","ruoyi-auth.jar"]

View File

@@ -0,0 +1 @@
存放认证中心打包好的jar文件用于docker启动应用。

View File

@@ -0,0 +1,15 @@
# 基础镜像
FROM openjdk:8-jre
# author
MAINTAINER ruoyi
# 挂载目录
VOLUME /home/ruoyi
# 创建目录
RUN mkdir -p /home/ruoyi
# 指定路径
WORKDIR /home/ruoyi
# 复制jar文件到路径
COPY ./jar/ruoyi-gateway.jar /home/ruoyi/ruoyi-gateway.jar
# 启动网关服务
ENTRYPOINT ["java","-jar","ruoyi-gateway.jar"]

View File

@@ -0,0 +1 @@
存放网关模块打包好的jar文件用于docker启动应用。

View File

@@ -0,0 +1,15 @@
# 基础镜像
FROM openjdk:8-jre
# author
MAINTAINER ruoyi
# 挂载目录
VOLUME /home/ruoyi
# 创建目录
RUN mkdir -p /home/ruoyi
# 指定路径
WORKDIR /home/ruoyi
# 复制jar文件到路径
COPY ./jar/ruoyi-modules-file.jar /home/ruoyi/ruoyi-modules-file.jar
# 启动文件服务
ENTRYPOINT ["java","-jar","ruoyi-modules-file.jar"]

View File

@@ -0,0 +1 @@
存放文件服务打包好的jar文件用于docker启动应用。

View File

@@ -0,0 +1,15 @@
# 基础镜像
FROM openjdk:8-jre
# author
MAINTAINER ruoyi
# 挂载目录
VOLUME /home/ruoyi
# 创建目录
RUN mkdir -p /home/ruoyi
# 指定路径
WORKDIR /home/ruoyi
# 复制jar文件到路径
COPY ./jar/ruoyi-modules-gen.jar /home/ruoyi/ruoyi-modules-gen.jar
# 启动代码生成服务
ENTRYPOINT ["java","-jar","ruoyi-modules-gen.jar"]

View File

@@ -0,0 +1 @@
存放代码生成打包好的jar文件用于docker启动应用。

View File

@@ -0,0 +1,15 @@
# 基础镜像
FROM openjdk:8-jre
# author
MAINTAINER ruoyi
# 挂载目录
VOLUME /home/ruoyi
# 创建目录
RUN mkdir -p /home/ruoyi
# 指定路径
WORKDIR /home/ruoyi
# 复制jar文件到路径
COPY ./jar/ruoyi-modules-job.jar /home/ruoyi/ruoyi-modules-job.jar
# 启动定时任务服务
ENTRYPOINT ["java","-jar","ruoyi-modules-job.jar"]

View File

@@ -0,0 +1 @@
存放定时任务打包好的jar文件用于docker启动应用。

View File

@@ -0,0 +1,15 @@
# 基础镜像
FROM openjdk:8-jre
# author
MAINTAINER ruoyi
# 挂载目录
VOLUME /home/ruoyi
# 创建目录
RUN mkdir -p /home/ruoyi
# 指定路径
WORKDIR /home/ruoyi
# 复制jar文件到路径
COPY ./jar/ruoyi-modules-system.jar /home/ruoyi/ruoyi-modules-system.jar
# 启动系统服务
ENTRYPOINT ["java","-jar","ruoyi-modules-system.jar"]

View File

@@ -0,0 +1 @@
存放系统模块打包好的jar文件用于docker启动应用。

View File

@@ -0,0 +1,15 @@
# 基础镜像
FROM openjdk:8-jre
# author
MAINTAINER ruoyi
# 挂载目录
VOLUME /home/ruoyi
# 创建目录
RUN mkdir -p /home/ruoyi
# 指定路径
WORKDIR /home/ruoyi
# 复制jar文件到路径
COPY ./jar/ruoyi-visual-monitor.jar /home/ruoyi/ruoyi-visual-monitor.jar
# 启动系统服务
ENTRYPOINT ["java","-jar","ruoyi-visual-monitor.jar"]

View File

@@ -0,0 +1 @@
存放监控中心打包好的jar文件用于docker启动应用。

64
pom.xml
View File

@@ -6,36 +6,38 @@
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi</artifactId> <artifactId>ruoyi</artifactId>
<version>2.4.0</version> <version>3.2.0</version>
<name>ruoyi</name> <name>ruoyi</name>
<url>http://www.ruoyi.vip</url> <url>http://www.ruoyi.vip</url>
<description>若依微服务系统</description> <description>若依微服务系统</description>
<properties> <properties>
<ruoyi.version>2.4.0</ruoyi.version> <ruoyi.version>3.2.0</ruoyi.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version> <java.version>1.8</java.version>
<spring-boot.version>2.3.4.RELEASE</spring-boot.version> <spring-boot.version>2.5.5</spring-boot.version>
<spring-cloud.version>Hoxton.SR8</spring-cloud.version> <spring-cloud.version>2020.0.4</spring-cloud.version>
<spring-cloud-alibaba.version>2.2.3.RELEASE</spring-cloud-alibaba.version> <spring-cloud-alibaba.version>2021.1</spring-cloud-alibaba.version>
<spring-boot-admin.version>2.3.0</spring-boot-admin.version> <alibaba.nacos.version>2.0.3</alibaba.nacos.version>
<spring-boot.mybatis>2.1.3</spring-boot.mybatis> <spring-boot-admin.version>2.5.2</spring-boot-admin.version>
<swagger.fox.version>2.9.2</swagger.fox.version> <spring-boot.mybatis>2.2.0</spring-boot.mybatis>
<swagger.core.version>1.5.24</swagger.core.version> <swagger.fox.version>3.0.0</swagger.fox.version>
<tobato.version>1.26.5</tobato.version> <swagger.core.version>1.6.2</swagger.core.version>
<tobato.version>1.27.2</tobato.version>
<kaptcha.version>2.3.2</kaptcha.version> <kaptcha.version>2.3.2</kaptcha.version>
<pagehelper.boot.version>1.3.0</pagehelper.boot.version> <pagehelper.boot.version>1.4.0</pagehelper.boot.version>
<druid.version>1.2.2</druid.version> <druid.version>1.2.8</druid.version>
<dynamic-ds.version>3.2.1</dynamic-ds.version> <dynamic-ds.version>3.4.1</dynamic-ds.version>
<commons.io.version>2.5</commons.io.version> <commons.io.version>2.11.0</commons.io.version>
<commons.fileupload.version>1.3.3</commons.fileupload.version> <commons.fileupload.version>1.4</commons.fileupload.version>
<velocity.version>1.7</velocity.version> <velocity.version>1.7</velocity.version>
<fastjson.version>1.2.74</fastjson.version> <fastjson.version>1.2.78</fastjson.version>
<minio.version>8.0.3</minio.version> <minio.version>8.2.2</minio.version>
<poi.version>4.1.2</poi.version> <poi.version>4.1.2</poi.version>
<common-pool.version>2.6.2</common-pool.version> <common-pool.version>2.10.0</common-pool.version>
<commons-collections.version>3.2.2</commons-collections.version>
</properties> </properties>
<!-- 依赖声明 --> <!-- 依赖声明 -->
@@ -60,6 +62,13 @@
<scope>import</scope> <scope>import</scope>
</dependency> </dependency>
<!-- Alibaba Nacos 配置 -->
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>${alibaba.nacos.version}</version>
</dependency>
<!-- SpringBoot 依赖配置 --> <!-- SpringBoot 依赖配置 -->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
@@ -142,6 +151,19 @@
<groupId>org.apache.velocity</groupId> <groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId> <artifactId>velocity</artifactId>
<version>${velocity.version}</version> <version>${velocity.version}</version>
<exclusions>
<exclusion>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Collection 增强Java集合框架 -->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>${commons-collections.version}</version>
</dependency> </dependency>
<!-- JSON 解析器和生成器 --> <!-- JSON 解析器和生成器 -->
@@ -228,7 +250,11 @@
<packaging>pom</packaging> <packaging>pom</packaging>
<dependencies> <dependencies>
<!-- bootstrap 启动器 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi</artifactId> <artifactId>ruoyi</artifactId>
<version>2.4.0</version> <version>3.2.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-api</artifactId> <artifactId>ruoyi-api</artifactId>
<version>2.4.0</version> <version>3.2.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -3,9 +3,11 @@ package com.ruoyi.system.api;
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestHeader;
import com.ruoyi.common.core.constant.SecurityConstants;
import com.ruoyi.common.core.constant.ServiceNameConstants; import com.ruoyi.common.core.constant.ServiceNameConstants;
import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.domain.R;
import com.ruoyi.system.api.domain.SysLogininfor;
import com.ruoyi.system.api.domain.SysOperLog; import com.ruoyi.system.api.domain.SysOperLog;
import com.ruoyi.system.api.factory.RemoteLogFallbackFactory; import com.ruoyi.system.api.factory.RemoteLogFallbackFactory;
@@ -21,20 +23,19 @@ public interface RemoteLogService
* 保存系统日志 * 保存系统日志
* *
* @param sysOperLog 日志实体 * @param sysOperLog 日志实体
* @param source 请求来源
* @return 结果 * @return 结果
*/ */
@PostMapping("/operlog") @PostMapping("/operlog")
R<Boolean> saveLog(@RequestBody SysOperLog sysOperLog); public R<Boolean> saveLog(@RequestBody SysOperLog sysOperLog, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/** /**
* 保存访问记录 * 保存访问记录
* *
* @param username 用户名称 * @param sysLogininfor 访问实体
* @param status 状态 * @param source 请求来源
* @param message 消息
* @return 结果 * @return 结果
*/ */
@PostMapping("/logininfor") @PostMapping("/logininfor")
R<Boolean> saveLogininfor(@RequestParam("username") String username, @RequestParam("status") String status, public R<Boolean> saveLogininfor(@RequestBody SysLogininfor sysLogininfor, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
@RequestParam("message") String message);
} }

View File

@@ -3,8 +3,13 @@ package com.ruoyi.system.api;
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import com.ruoyi.common.core.constant.SecurityConstants;
import com.ruoyi.common.core.constant.ServiceNameConstants; import com.ruoyi.common.core.constant.ServiceNameConstants;
import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.domain.R;
import com.ruoyi.system.api.domain.SysUser;
import com.ruoyi.system.api.factory.RemoteUserFallbackFactory; import com.ruoyi.system.api.factory.RemoteUserFallbackFactory;
import com.ruoyi.system.api.model.LoginUser; import com.ruoyi.system.api.model.LoginUser;
@@ -20,8 +25,19 @@ public interface RemoteUserService
* 通过用户名查询用户信息 * 通过用户名查询用户信息
* *
* @param username 用户名 * @param username 用户名
* @param source 请求来源
* @return 结果 * @return 结果
*/ */
@GetMapping(value = "/user/info/{username}") @GetMapping("/user/info/{username}")
public R<LoginUser> getUserInfo(@PathVariable("username") String username); public R<LoginUser> getUserInfo(@PathVariable("username") String username, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/**
* 注册用户信息
*
* @param sysUser 用户信息
* @param source 请求来源
* @return 结果
*/
@PostMapping("/user/register")
public R<Boolean> registerUserInfo(@RequestBody SysUser sysUser, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
} }

View File

@@ -1,4 +1,4 @@
package com.ruoyi.system.domain; package com.ruoyi.system.api.domain;
import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size; import javax.validation.constraints.Size;

View File

@@ -1,4 +1,4 @@
package com.ruoyi.system.domain; package com.ruoyi.system.api.domain;
import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size; import javax.validation.constraints.Size;

View File

@@ -1,4 +1,4 @@
package com.ruoyi.system.domain; package com.ruoyi.system.api.domain;
import java.util.Date; import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;

View File

@@ -33,8 +33,8 @@ public class SysRole extends BaseEntity
@Excel(name = "角色排序") @Excel(name = "角色排序")
private String roleSort; private String roleSort;
/** 数据范围1所有数据权限2自定义数据权限3本部门数据权限4本部门及以下数据权限 */ /** 数据范围1所有数据权限2自定义数据权限3本部门数据权限4本部门及以下数据权限5仅本人数据权限 */
@Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限") @Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限")
private String dataScope; private String dataScope;
/** 菜单树选择项是否关联显示( 0父子不互相关联显示 1父子互相关联显示 */ /** 菜单树选择项是否关联显示( 0父子不互相关联显示 1父子互相关联显示 */
@@ -204,6 +204,7 @@ public class SysRole extends BaseEntity
this.deptIds = deptIds; this.deptIds = deptIds;
} }
@Override
public String toString() { public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("roleId", getRoleId()) .append("roleId", getRoleId())

View File

@@ -57,9 +57,6 @@ public class SysUser extends BaseEntity
/** 密码 */ /** 密码 */
private String password; private String password;
/** 盐加密 */
private String salt;
/** 帐号状态0正常 1停用 */ /** 帐号状态0正常 1停用 */
@Excel(name = "帐号状态", readConverterExp = "0=正常,1=停用") @Excel(name = "帐号状态", readConverterExp = "0=正常,1=停用")
private String status; private String status;
@@ -91,6 +88,9 @@ public class SysUser extends BaseEntity
/** 岗位组 */ /** 岗位组 */
private Long[] postIds; private Long[] postIds;
/** 角色ID */
private Long roleId;
public SysUser() public SysUser()
{ {
@@ -208,16 +208,6 @@ public class SysUser extends BaseEntity
this.password = password; this.password = password;
} }
public String getSalt()
{
return salt;
}
public void setSalt(String salt)
{
this.salt = salt;
}
public String getStatus() public String getStatus()
{ {
return status; return status;
@@ -298,6 +288,15 @@ public class SysUser extends BaseEntity
this.postIds = postIds; this.postIds = postIds;
} }
public Long getRoleId()
{
return roleId;
}
public void setRoleId(Long roleId)
{
this.roleId = roleId;
}
@Override @Override
public String toString() { public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
@@ -310,7 +309,6 @@ public class SysUser extends BaseEntity
.append("sex", getSex()) .append("sex", getSex())
.append("avatar", getAvatar()) .append("avatar", getAvatar())
.append("password", getPassword()) .append("password", getPassword())
.append("salt", getSalt())
.append("status", getStatus()) .append("status", getStatus())
.append("delFlag", getDelFlag()) .append("delFlag", getDelFlag())
.append("loginIp", getLoginIp()) .append("loginIp", getLoginIp())

View File

@@ -2,12 +2,12 @@ package com.ruoyi.system.api.factory;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.domain.R;
import com.ruoyi.system.api.RemoteFileService; import com.ruoyi.system.api.RemoteFileService;
import com.ruoyi.system.api.domain.SysFile; import com.ruoyi.system.api.domain.SysFile;
import feign.hystrix.FallbackFactory;
/** /**
* 文件服务降级处理 * 文件服务降级处理

View File

@@ -2,11 +2,12 @@ package com.ruoyi.system.api.factory;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.domain.R;
import com.ruoyi.system.api.RemoteLogService; import com.ruoyi.system.api.RemoteLogService;
import com.ruoyi.system.api.domain.SysLogininfor;
import com.ruoyi.system.api.domain.SysOperLog; import com.ruoyi.system.api.domain.SysOperLog;
import feign.hystrix.FallbackFactory;
/** /**
* 日志服务降级处理 * 日志服务降级处理
@@ -25,13 +26,13 @@ public class RemoteLogFallbackFactory implements FallbackFactory<RemoteLogServic
return new RemoteLogService() return new RemoteLogService()
{ {
@Override @Override
public R<Boolean> saveLog(SysOperLog sysOperLog) public R<Boolean> saveLog(SysOperLog sysOperLog, String source)
{ {
return null; return null;
} }
@Override @Override
public R<Boolean> saveLogininfor(String username, String status, String message) public R<Boolean> saveLogininfor(SysLogininfor sysLogininfor, String source)
{ {
return null; return null;
} }

View File

@@ -2,11 +2,12 @@ package com.ruoyi.system.api.factory;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.domain.R;
import com.ruoyi.system.api.RemoteUserService; import com.ruoyi.system.api.RemoteUserService;
import com.ruoyi.system.api.domain.SysUser;
import com.ruoyi.system.api.model.LoginUser; import com.ruoyi.system.api.model.LoginUser;
import feign.hystrix.FallbackFactory;
/** /**
* 用户服务降级处理 * 用户服务降级处理
@@ -25,10 +26,16 @@ public class RemoteUserFallbackFactory implements FallbackFactory<RemoteUserServ
return new RemoteUserService() return new RemoteUserService()
{ {
@Override @Override
public R<LoginUser> getUserInfo(String username) public R<LoginUser> getUserInfo(String username, String source)
{ {
return R.fail("获取用户失败:" + throwable.getMessage()); return R.fail("获取用户失败:" + throwable.getMessage());
} }
@Override
public R<Boolean> registerUserInfo(SysUser sysUser, String source)
{
return R.fail("注册用户失败:" + throwable.getMessage());
}
}; };
} }
} }

View File

@@ -1,3 +1,4 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.ruoyi.system.api.factory.RemoteUserFallbackFactory,\ com.ruoyi.system.api.factory.RemoteUserFallbackFactory,\
com.ruoyi.system.api.factory.RemoteLogFallbackFactory com.ruoyi.system.api.factory.RemoteLogFallbackFactory, \
com.ruoyi.system.api.factory.RemoteFileFallbackFactory

View File

@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi</artifactId> <artifactId>ruoyi</artifactId>
<version>2.4.0</version> <version>3.2.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -16,19 +16,19 @@
<dependencies> <dependencies>
<!-- SpringCloud Ailibaba Nacos --> <!-- SpringCloud Alibaba Nacos -->
<dependency> <dependency>
<groupId>com.alibaba.cloud</groupId> <groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency> </dependency>
<!-- SpringCloud Ailibaba Nacos Config --> <!-- SpringCloud Alibaba Nacos Config -->
<dependency> <dependency>
<groupId>com.alibaba.cloud</groupId> <groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency> </dependency>
<!-- SpringCloud Ailibaba Sentinel --> <!-- SpringCloud Alibaba Sentinel -->
<dependency> <dependency>
<groupId>com.alibaba.cloud</groupId> <groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
@@ -46,12 +46,6 @@
<artifactId>spring-boot-starter-actuator</artifactId> <artifactId>spring-boot-starter-actuator</artifactId>
</dependency> </dependency>
<!-- Mysql Connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- RuoYi Common Security--> <!-- RuoYi Common Security-->
<dependency> <dependency>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
@@ -61,6 +55,7 @@
</dependencies> </dependencies>
<build> <build>
<finalName>${project.artifactId}</finalName>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>

View File

@@ -1,7 +1,8 @@
package com.ruoyi.auth; package com.ruoyi.auth;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import com.ruoyi.common.security.annotation.EnableRyFeignClients; import com.ruoyi.common.security.annotation.EnableRyFeignClients;
/** /**
@@ -10,7 +11,7 @@ import com.ruoyi.common.security.annotation.EnableRyFeignClients;
* @author ruoyi * @author ruoyi
*/ */
@EnableRyFeignClients @EnableRyFeignClients
@SpringCloudApplication @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class })
public class RuoYiAuthApplication public class RuoYiAuthApplication
{ {
public static void main(String[] args) public static void main(String[] args)

View File

@@ -7,6 +7,7 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.auth.form.LoginBody; import com.ruoyi.auth.form.LoginBody;
import com.ruoyi.auth.form.RegisterBody;
import com.ruoyi.auth.service.SysLoginService; import com.ruoyi.auth.service.SysLoginService;
import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.utils.StringUtils;
@@ -63,4 +64,12 @@ public class TokenController
} }
return R.ok(); return R.ok();
} }
@PostMapping("register")
public R<?> register(@RequestBody RegisterBody registerBody)
{
// 用户注册
sysLoginService.register(registerBody.getUsername(), registerBody.getPassword());
return R.ok();
}
} }

View File

@@ -17,16 +17,6 @@ public class LoginBody
*/ */
private String password; private String password;
/**
* 验证码
*/
private String code;
/**
* 唯一标识
*/
private String uuid = "";
public String getUsername() public String getUsername()
{ {
return username; return username;
@@ -46,24 +36,4 @@ public class LoginBody
{ {
this.password = password; this.password = password;
} }
public String getCode()
{
return code;
}
public void setCode(String code)
{
this.code = code;
}
public String getUuid()
{
return uuid;
}
public void setUuid(String uuid)
{
this.uuid = uuid;
}
} }

View File

@@ -0,0 +1,11 @@
package com.ruoyi.auth.form;
/**
* 用户注册对象
*
* @author ruoyi
*/
public class RegisterBody extends LoginBody
{
}

View File

@@ -3,14 +3,18 @@ package com.ruoyi.auth.service;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.ruoyi.common.core.constant.Constants; import com.ruoyi.common.core.constant.Constants;
import com.ruoyi.common.core.constant.SecurityConstants;
import com.ruoyi.common.core.constant.UserConstants; import com.ruoyi.common.core.constant.UserConstants;
import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.enums.UserStatus; import com.ruoyi.common.core.enums.UserStatus;
import com.ruoyi.common.core.exception.BaseException; import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.core.utils.SecurityUtils; import com.ruoyi.common.core.utils.SecurityUtils;
import com.ruoyi.common.core.utils.ServletUtils;
import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.utils.ip.IpUtils;
import com.ruoyi.system.api.RemoteLogService; import com.ruoyi.system.api.RemoteLogService;
import com.ruoyi.system.api.RemoteUserService; import com.ruoyi.system.api.RemoteUserService;
import com.ruoyi.system.api.domain.SysLogininfor;
import com.ruoyi.system.api.domain.SysUser; import com.ruoyi.system.api.domain.SysUser;
import com.ruoyi.system.api.model.LoginUser; import com.ruoyi.system.api.model.LoginUser;
@@ -36,60 +40,120 @@ public class SysLoginService
// 用户名或密码为空 错误 // 用户名或密码为空 错误
if (StringUtils.isAnyBlank(username, password)) if (StringUtils.isAnyBlank(username, password))
{ {
remoteLogService.saveLogininfor(username, Constants.LOGIN_FAIL, "用户/密码必须填写"); recordLogininfor(username, Constants.LOGIN_FAIL, "用户/密码必须填写");
throw new BaseException("用户/密码必须填写"); throw new ServiceException("用户/密码必须填写");
} }
// 密码如果不在指定范围内 错误 // 密码如果不在指定范围内 错误
if (password.length() < UserConstants.PASSWORD_MIN_LENGTH if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
|| password.length() > UserConstants.PASSWORD_MAX_LENGTH) || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
{ {
remoteLogService.saveLogininfor(username, Constants.LOGIN_FAIL, "用户密码不在指定范围"); recordLogininfor(username, Constants.LOGIN_FAIL, "用户密码不在指定范围");
throw new BaseException("用户密码不在指定范围"); throw new ServiceException("用户密码不在指定范围");
} }
// 用户名不在指定范围内 错误 // 用户名不在指定范围内 错误
if (username.length() < UserConstants.USERNAME_MIN_LENGTH if (username.length() < UserConstants.USERNAME_MIN_LENGTH
|| username.length() > UserConstants.USERNAME_MAX_LENGTH) || username.length() > UserConstants.USERNAME_MAX_LENGTH)
{ {
remoteLogService.saveLogininfor(username, Constants.LOGIN_FAIL, "用户名不在指定范围"); recordLogininfor(username, Constants.LOGIN_FAIL, "用户名不在指定范围");
throw new BaseException("用户名不在指定范围"); throw new ServiceException("用户名不在指定范围");
} }
// 查询用户信息 // 查询用户信息
R<LoginUser> userResult = remoteUserService.getUserInfo(username); R<LoginUser> userResult = remoteUserService.getUserInfo(username, SecurityConstants.INNER);
if (R.FAIL == userResult.getCode()) if (R.FAIL == userResult.getCode())
{ {
throw new BaseException(userResult.getMsg()); throw new ServiceException(userResult.getMsg());
} }
if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData())) if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData()))
{ {
remoteLogService.saveLogininfor(username, Constants.LOGIN_FAIL, "登录用户不存在"); recordLogininfor(username, Constants.LOGIN_FAIL, "登录用户不存在");
throw new BaseException("登录用户:" + username + " 不存在"); throw new ServiceException("登录用户:" + username + " 不存在");
} }
LoginUser userInfo = userResult.getData(); LoginUser userInfo = userResult.getData();
SysUser user = userResult.getData().getSysUser(); SysUser user = userResult.getData().getSysUser();
if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
{ {
remoteLogService.saveLogininfor(username, Constants.LOGIN_FAIL, "对不起,您的账号已被删除"); recordLogininfor(username, Constants.LOGIN_FAIL, "对不起,您的账号已被删除");
throw new ServiceException("对不起,您的账号:" + username + " 已被删除");
throw new BaseException("对不起,您的账号:" + username + " 已被删除");
} }
if (UserStatus.DISABLE.getCode().equals(user.getStatus())) if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
{ {
remoteLogService.saveLogininfor(username, Constants.LOGIN_FAIL, "用户已停用,请联系管理员"); recordLogininfor(username, Constants.LOGIN_FAIL, "用户已停用,请联系管理员");
throw new BaseException("对不起,您的账号:" + username + " 已停用"); throw new ServiceException("对不起,您的账号:" + username + " 已停用");
} }
if (!SecurityUtils.matchesPassword(password, user.getPassword())) if (!SecurityUtils.matchesPassword(password, user.getPassword()))
{ {
remoteLogService.saveLogininfor(username, Constants.LOGIN_FAIL, "用户密码错误"); recordLogininfor(username, Constants.LOGIN_FAIL, "用户密码错误");
throw new BaseException("用户不存在/密码错误"); throw new ServiceException("用户不存在/密码错误");
} }
remoteLogService.saveLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功"); recordLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功");
return userInfo; return userInfo;
} }
public void logout(String loginName) public void logout(String loginName)
{ {
remoteLogService.saveLogininfor(loginName, Constants.LOGOUT, "退出成功"); recordLogininfor(loginName, Constants.LOGOUT, "退出成功");
}
/**
* 注册
*/
public void register(String username, String password)
{
// 用户名或密码为空 错误
if (StringUtils.isAnyBlank(username, password))
{
throw new ServiceException("用户/密码必须填写");
}
if (username.length() < UserConstants.USERNAME_MIN_LENGTH
|| username.length() > UserConstants.USERNAME_MAX_LENGTH)
{
throw new ServiceException("账户长度必须在2到20个字符之间");
}
if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
|| password.length() > UserConstants.PASSWORD_MAX_LENGTH)
{
throw new ServiceException("密码长度必须在5到20个字符之间");
}
// 注册用户信息
SysUser sysUser = new SysUser();
sysUser.setUserName(username);
sysUser.setNickName(username);
sysUser.setPassword(SecurityUtils.encryptPassword(password));
R<?> registerResult = remoteUserService.registerUserInfo(sysUser, SecurityConstants.INNER);
if (R.FAIL == registerResult.getCode())
{
throw new ServiceException(registerResult.getMsg());
}
recordLogininfor(username, Constants.REGISTER, "注册成功");
}
/**
* 记录登录信息
*
* @param username 用户名
* @param status 状态
* @param message 消息内容
* @return
*/
public void recordLogininfor(String username, String status, String message)
{
SysLogininfor logininfor = new SysLogininfor();
logininfor.setUserName(username);
logininfor.setIpaddr(IpUtils.getIpAddr(ServletUtils.getRequest()));
logininfor.setMsg(message);
// 日志状态
if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER))
{
logininfor.setStatus("0");
}
else if (Constants.LOGIN_FAIL.equals(status))
{
logininfor.setStatus("1");
}
remoteLogService.saveLogininfor(logininfor, SecurityConstants.INNER);
} }
} }

View File

@@ -21,4 +21,5 @@ spring:
# 配置文件格式 # 配置文件格式
file-extension: yml file-extension: yml
# 共享配置 # 共享配置
shared-dataids: application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} shared-configs:
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}

View File

@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi</artifactId> <artifactId>ruoyi</artifactId>
<version>2.4.0</version> <version>3.2.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId> <artifactId>ruoyi-common</artifactId>
<version>2.4.0</version> <version>3.2.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -23,6 +23,12 @@
<artifactId>spring-cloud-starter-openfeign</artifactId> <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency> </dependency>
<!-- SpringCloud Loadbalancer -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- Spring Context Support --> <!-- Spring Context Support -->
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>

View File

@@ -5,6 +5,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import java.math.BigDecimal; import java.math.BigDecimal;
import com.ruoyi.common.core.utils.poi.ExcelHandlerAdapter;
/** /**
* 自定义导出Excel数据注解 * 自定义导出Excel数据注解
@@ -103,7 +104,17 @@ public @interface Excel
/** /**
* 导出字段对齐方式0默认1靠左2居中3靠右 * 导出字段对齐方式0默认1靠左2居中3靠右
*/ */
Align align() default Align.AUTO; public Align align() default Align.AUTO;
/**
* 自定义数据处理器
*/
public Class<?> handler() default ExcelHandlerAdapter.class;
/**
* 自定义数据处理器参数
*/
public String[] args() default {};
public enum Align public enum Align
{ {
@@ -144,7 +155,7 @@ public @interface Excel
public enum ColumnType public enum ColumnType
{ {
NUMERIC(0), STRING(1); NUMERIC(0), STRING(1), IMAGE(2);
private final int value; private final int value;
ColumnType(int value) ColumnType(int value)

View File

@@ -7,33 +7,8 @@ package com.ruoyi.common.core.constant;
*/ */
public class CacheConstants public class CacheConstants
{ {
/**
* 令牌自定义标识
*/
public static final String HEADER = "Authorization";
/**
* 令牌前缀
*/
public static final String TOKEN_PREFIX = "Bearer ";
/** /**
* 权限缓存前缀 * 权限缓存前缀
*/ */
public final static String LOGIN_TOKEN_KEY = "login_tokens:"; public final static String LOGIN_TOKEN_KEY = "login_tokens:";
/**
* 用户ID字段
*/
public static final String DETAILS_USER_ID = "user_id";
/**
* 用户名字段
*/
public static final String DETAILS_USERNAME = "username";
/**
* 授权信息字段
*/
public static final String AUTHORIZATION_HEADER = "authorization";
} }

View File

@@ -17,6 +17,16 @@ public class Constants
*/ */
public static final String GBK = "GBK"; public static final String GBK = "GBK";
/**
* RMI 远程方法调用
*/
public static final String LOOKUP_RMI = "rmi://";
/**
* LDAP 远程方法调用
*/
public static final String LOOKUP_LDAP = "ldap://";
/** /**
* http请求 * http请求
*/ */

View File

@@ -13,6 +13,9 @@ public class GenConstants
/** 树表(增删改查) */ /** 树表(增删改查) */
public static final String TPL_TREE = "tree"; public static final String TPL_TREE = "tree";
/** 主子表(增删改查) */
public static final String TPL_SUB = "sub";
/** 树编码字段 */ /** 树编码字段 */
public static final String TREE_CODE = "treeCode"; public static final String TREE_CODE = "treeCode";
@@ -29,8 +32,10 @@ public class GenConstants
public static final String PARENT_MENU_NAME = "parentMenuName"; public static final String PARENT_MENU_NAME = "parentMenuName";
/** 数据库字符串类型 */ /** 数据库字符串类型 */
public static final String[] COLUMNTYPE_STR = { "char", "varchar", "nvarchar", "varchar2", "tinytext", "text", public static final String[] COLUMNTYPE_STR = { "char", "varchar", "nvarchar", "varchar2" };
"mediumtext", "longtext" };
/** 数据库文本类型 */
public static final String[] COLUMNTYPE_TEXT = { "tinytext", "text", "mediumtext", "longtext" };
/** 数据库时间类型 */ /** 数据库时间类型 */
public static final String[] COLUMNTYPE_TIME = { "datetime", "time", "date", "timestamp" }; public static final String[] COLUMNTYPE_TIME = { "datetime", "time", "date", "timestamp" };
@@ -74,8 +79,11 @@ public class GenConstants
/** 日期控件 */ /** 日期控件 */
public static final String HTML_DATETIME = "datetime"; public static final String HTML_DATETIME = "datetime";
/** 上传控件 */ /** 图片上传控件 */
public static final String HTML_UPLOAD_IMAGE = "uploadImage"; public static final String HTML_IMAGE_UPLOAD = "imageUpload";
/** 文件上传控件 */
public static final String HTML_FILE_UPLOAD = "fileUpload";
/** 富文本控件 */ /** 富文本控件 */
public static final String HTML_EDITOR = "editor"; public static final String HTML_EDITOR = "editor";

View File

@@ -0,0 +1,44 @@
package com.ruoyi.common.core.constant;
/**
* 权限相关通用常量
*
* @author ruoyi
*/
public class SecurityConstants
{
/**
* 令牌自定义标识
*/
public static final String TOKEN_AUTHENTICATION = "Authorization";
/**
* 令牌前缀
*/
public static final String TOKEN_PREFIX = "Bearer ";
/**
* 用户ID字段
*/
public static final String DETAILS_USER_ID = "user_id";
/**
* 用户名字段
*/
public static final String DETAILS_USERNAME = "username";
/**
* 授权信息字段
*/
public static final String AUTHORIZATION_HEADER = "authorization";
/**
* 请求来源
*/
public static final String FROM_SOURCE = "from-source";
/**
* 内部请求
*/
public static final String INNER = "inner";
}

View File

@@ -57,6 +57,9 @@ public class UserConstants
/** ParentView组件标识 */ /** ParentView组件标识 */
public final static String PARENT_VIEW = "ParentView"; public final static String PARENT_VIEW = "ParentView";
/** InnerLink组件标识 */
public final static String INNER_LINK = "InnerLink";
/** 校验返回结果码 */ /** 校验返回结果码 */
public final static String UNIQUE = "0"; public final static String UNIQUE = "0";

View File

@@ -1,43 +0,0 @@
package com.ruoyi.common.core.exception;
/**
* 自定义异常
*
* @author ruoyi
*/
public class CustomException extends RuntimeException
{
private static final long serialVersionUID = 1L;
private Integer code;
private String message;
public CustomException(String message)
{
this.message = message;
}
public CustomException(String message, Integer code)
{
this.message = message;
this.code = code;
}
public CustomException(String message, Throwable e)
{
super(message, e);
this.message = message;
}
@Override
public String getMessage()
{
return message;
}
public Integer getCode()
{
return code;
}
}

View File

@@ -0,0 +1,58 @@
package com.ruoyi.common.core.exception;
/**
* 全局异常
*
* @author ruoyi
*/
public class GlobalException extends RuntimeException
{
private static final long serialVersionUID = 1L;
/**
* 错误提示
*/
private String message;
/**
* 错误明细,内部调试错误
*
* 和 {@link CommonResult#getDetailMessage()} 一致的设计
*/
private String detailMessage;
/**
* 空构造方法,避免反序列化问题
*/
public GlobalException()
{
}
public GlobalException(String message)
{
this.message = message;
}
public String getDetailMessage()
{
return detailMessage;
}
public GlobalException setDetailMessage(String detailMessage)
{
this.detailMessage = detailMessage;
return this;
}
public String getMessage()
{
return message;
}
public GlobalException setMessage(String message)
{
this.message = message;
return this;
}
}

View File

@@ -0,0 +1,16 @@
package com.ruoyi.common.core.exception;
/**
* 内部认证异常
*
* @author ruoyi
*/
public class InnerAuthException extends RuntimeException
{
private static final long serialVersionUID = 1L;
public InnerAuthException(String message)
{
super(message);
}
}

View File

@@ -0,0 +1,73 @@
package com.ruoyi.common.core.exception;
/**
* 业务异常
*
* @author ruoyi
*/
public final class ServiceException extends RuntimeException
{
private static final long serialVersionUID = 1L;
/**
* 错误码
*/
private Integer code;
/**
* 错误提示
*/
private String message;
/**
* 错误明细,内部调试错误
*
* 和 {@link CommonResult#getDetailMessage()} 一致的设计
*/
private String detailMessage;
/**
* 空构造方法,避免反序列化问题
*/
public ServiceException()
{
}
public ServiceException(String message)
{
this.message = message;
}
public ServiceException(String message, Integer code)
{
this.message = message;
this.code = code;
}
public String getDetailMessage()
{
return detailMessage;
}
public String getMessage()
{
return message;
}
public Integer getCode()
{
return code;
}
public ServiceException setMessage(String message)
{
this.message = message;
return this;
}
public ServiceException setDetailMessage(String detailMessage)
{
this.detailMessage = detailMessage;
return this;
}
}

View File

@@ -1,4 +1,4 @@
package com.ruoyi.common.core.exception; package com.ruoyi.common.core.exception.base;
/** /**
* 基础异常 * 基础异常

View File

@@ -1,6 +1,6 @@
package com.ruoyi.common.core.exception.file; package com.ruoyi.common.core.exception.file;
import com.ruoyi.common.core.exception.BaseException; import com.ruoyi.common.core.exception.base.BaseException;
/** /**
* 文件信息异常类 * 文件信息异常类

View File

@@ -1,6 +1,6 @@
package com.ruoyi.common.core.exception.user; package com.ruoyi.common.core.exception.user;
import com.ruoyi.common.core.exception.BaseException; import com.ruoyi.common.core.exception.base.BaseException;
/** /**
* 用户信息异常类 * 用户信息异常类

View File

@@ -797,7 +797,21 @@ public class Convert
} }
else if (obj instanceof byte[] || obj instanceof Byte[]) else if (obj instanceof byte[] || obj instanceof Byte[])
{ {
return str((Byte[]) obj, charset); if (obj instanceof byte[])
{
return str((byte[]) obj, charset);
}
else
{
Byte[] bytes = (Byte[]) obj;
int length = bytes.length;
byte[] dest = new byte[length];
for (int i = 0; i < length; i++)
{
dest[i] = bytes[i];
}
return str(dest, charset);
}
} }
else if (obj instanceof ByteBuffer) else if (obj instanceof ByteBuffer)
{ {

View File

@@ -22,7 +22,7 @@ public class ExceptionUtil
return str; return str;
} }
public static String getRootErrorMseeage(Exception e) public static String getRootErrorMessage(Exception e)
{ {
Throwable root = ExceptionUtils.getRootCause(e); Throwable root = ExceptionUtils.getRootCause(e);
root = (root == null ? e : root); root = (root == null ? e : root);

View File

@@ -2,7 +2,7 @@ package com.ruoyi.common.core.utils;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import com.ruoyi.common.core.constant.CacheConstants; import com.ruoyi.common.core.constant.SecurityConstants;
import com.ruoyi.common.core.text.Convert; import com.ruoyi.common.core.text.Convert;
/** /**
@@ -17,7 +17,8 @@ public class SecurityUtils
*/ */
public static String getUsername() public static String getUsername()
{ {
return ServletUtils.getRequest().getHeader(CacheConstants.DETAILS_USERNAME); String username = ServletUtils.getRequest().getHeader(SecurityConstants.DETAILS_USERNAME);
return ServletUtils.urlDecode(username);
} }
/** /**
@@ -25,7 +26,7 @@ public class SecurityUtils
*/ */
public static Long getUserId() public static Long getUserId()
{ {
return Convert.toLong(ServletUtils.getRequest().getHeader(CacheConstants.DETAILS_USER_ID)); return Convert.toLong(ServletUtils.getRequest().getHeader(SecurityConstants.DETAILS_USER_ID));
} }
/** /**
@@ -41,10 +42,18 @@ public class SecurityUtils
*/ */
public static String getToken(HttpServletRequest request) public static String getToken(HttpServletRequest request)
{ {
String token = ServletUtils.getRequest().getHeader(CacheConstants.HEADER); String token = request.getHeader(SecurityConstants.TOKEN_AUTHENTICATION);
if (StringUtils.isNotEmpty(token) && token.startsWith(CacheConstants.TOKEN_PREFIX)) return replaceTokenPrefix(token);
}
/**
* 替换token前缀
*/
public static String replaceTokenPrefix(String token)
{ {
token = token.replace(CacheConstants.TOKEN_PREFIX, ""); if (StringUtils.isNotEmpty(token) && token.startsWith(SecurityConstants.TOKEN_PREFIX))
{
token = token.replace(SecurityConstants.TOKEN_PREFIX, "");
} }
return token; return token;
} }

View File

@@ -1,16 +1,28 @@
package com.ruoyi.common.core.utils; package com.ruoyi.common.core.utils;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.context.request.ServletRequestAttributes;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.core.constant.Constants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.text.Convert; import com.ruoyi.common.core.text.Convert;
import reactor.core.publisher.Mono;
/** /**
* 客户端工具类 * 客户端工具类
@@ -51,6 +63,22 @@ public class ServletUtils
return Convert.toInt(getRequest().getParameter(name), defaultValue); return Convert.toInt(getRequest().getParameter(name), defaultValue);
} }
/**
* 获取Boolean参数
*/
public static Boolean getParameterToBool(String name)
{
return Convert.toBool(getRequest().getParameter(name));
}
/**
* 获取Boolean参数
*/
public static Boolean getParameterToBool(String name, Boolean defaultValue)
{
return Convert.toBool(getRequest().getParameter(name), defaultValue);
}
/** /**
* 获取request * 获取request
*/ */
@@ -173,4 +201,98 @@ public class ServletUtils
} }
return false; return false;
} }
/**
* 内容编码
*
* @param str 内容
* @return 编码后的内容
*/
public static String urlEncode(String str)
{
try
{
return URLEncoder.encode(str, Constants.UTF8);
}
catch (UnsupportedEncodingException e)
{
return "";
}
}
/**
* 内容解码
*
* @param str 内容
* @return 解码后的内容
*/
public static String urlDecode(String str)
{
try
{
return URLDecoder.decode(str, Constants.UTF8);
}
catch (UnsupportedEncodingException e)
{
return "";
}
}
/**
* 设置webflux模型响应
*
* @param response ServerHttpResponse
* @param value 响应内容
* @return Mono<Void>
*/
public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, Object value)
{
return webFluxResponseWriter(response, HttpStatus.OK, value, R.FAIL);
}
/**
* 设置webflux模型响应
*
* @param response ServerHttpResponse
* @param code 响应状态码
* @param value 响应内容
* @return Mono<Void>
*/
public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, Object value, int code)
{
return webFluxResponseWriter(response, HttpStatus.OK, value, code);
}
/**
* 设置webflux模型响应
*
* @param response ServerHttpResponse
* @param status http状态码
* @param code 响应状态码
* @param value 响应内容
* @return Mono<Void>
*/
public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, HttpStatus status, Object value, int code)
{
return webFluxResponseWriter(response, MediaType.APPLICATION_JSON_VALUE, status, value, code);
}
/**
* 设置webflux模型响应
*
* @param response ServerHttpResponse
* @param contentType content-type
* @param status http状态码
* @param code 响应状态码
* @param value 响应内容
* @return Mono<Void>
*/
public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, String contentType, HttpStatus status, Object value, int code)
{
response.setStatusCode(status);
response.getHeaders().add(HttpHeaders.CONTENT_TYPE, contentType);
R<?> result = R.fail(code, value.toString());
DataBuffer dataBuffer = response.bufferFactory().wrap(JSONObject.toJSONString(result).getBytes());
return response.writeWith(Mono.just(dataBuffer));
}
} }

View File

@@ -3,6 +3,8 @@ package com.ruoyi.common.core.utils;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.springframework.util.AntPathMatcher;
import com.ruoyi.common.core.constant.Constants;
import com.ruoyi.common.core.text.StrFormatter; import com.ruoyi.common.core.text.StrFormatter;
/** /**
@@ -18,9 +20,6 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
/** 下划线 */ /** 下划线 */
private static final char SEPARATOR = '_'; private static final char SEPARATOR = '_';
/** 星号 */
private static final String START = "*";
/** /**
* 获取参数不为空值 * 获取参数不为空值
* *
@@ -238,6 +237,30 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
return str.substring(start, end); return str.substring(start, end);
} }
/**
* 判断是否为空,并且不是空白字符
*
* @param str 要判断的value
* @return 结果
*/
public static boolean hasText(String str)
{
return (str != null && !str.isEmpty() && containsText(str));
}
private static boolean containsText(CharSequence str)
{
int strLen = str.length();
for (int i = 0; i < strLen; i++)
{
if (!Character.isWhitespace(str.charAt(i)))
{
return true;
}
}
return false;
}
/** /**
* 格式化文本, {} 表示占位符<br> * 格式化文本, {} 表示占位符<br>
* 此方法只是简单将占位符 {} 按照顺序替换为参数<br> * 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
@@ -261,7 +284,18 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
} }
/** /**
* 下划线转驼峰命名 * 是否为http(s)://开头
*
* @param link 链接
* @return 结果
*/
public static boolean ishttp(String link)
{
return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS);
}
/**
* 驼峰转下划线命名
*/ */
public static String toUnderScoreCase(String str) public static String toUnderScoreCase(String str)
{ {
@@ -413,9 +447,9 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
{ {
return false; return false;
} }
for (String testStr : strs) for (String pattern : strs)
{ {
if (matches(str, testStr)) if (isMatch(pattern, str))
{ {
return true; return true;
} }
@@ -424,95 +458,19 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
} }
/** /**
* 查找指定字符串是否匹配指定字符串数组中的任意一个字符串 * 判断url是否与规则配置:
* ? 表示单个字符;
* * 表示一层路径内的任意字符串,不可跨层级;
* ** 表示任意层路径;
* *
* @param str 指定字符串 * @param pattern 匹配规则
* @param strs 需要检查的字符串数组 * @param url 需要匹配的url
* @return 是否匹配 * @return
*/ */
public static boolean matches(String str, String... strs) public static boolean isMatch(String pattern, String url)
{ {
if (isEmpty(str) || isEmpty(strs)) AntPathMatcher matcher = new AntPathMatcher();
{ return matcher.match(pattern, url);
return false;
}
for (String testStr : strs)
{
if (matches(str, testStr))
{
return true;
}
}
return false;
}
/**
* 查找指定字符串是否匹配
*
* @param str 指定字符串
* @param pattern 需要检查的字符串
* @return 是否匹配
*/
public static boolean matches(String str, String pattern)
{
if (isEmpty(pattern) || isEmpty(str))
{
return false;
}
pattern = pattern.replaceAll("\\s*", ""); // 替换空格
int beginOffset = 0; // pattern截取开始位置
int formerStarOffset = -1; // 前星号的偏移位置
int latterStarOffset = -1; // 后星号的偏移位置
String remainingURI = str;
String prefixPattern = "";
String suffixPattern = "";
boolean result = false;
do
{
formerStarOffset = indexOf(pattern, START, beginOffset);
prefixPattern = substring(pattern, beginOffset, formerStarOffset > -1 ? formerStarOffset : pattern.length());
// 匹配前缀Pattern
result = remainingURI.contains(prefixPattern);
// 已经没有星号,直接返回
if (formerStarOffset == -1)
{
return result;
}
// 匹配失败,直接返回
if (!result)
return false;
if (!isEmpty(prefixPattern))
{
remainingURI = substringAfter(str, prefixPattern);
}
// 匹配后缀Pattern
latterStarOffset = indexOf(pattern, START, formerStarOffset + 1);
suffixPattern = substring(pattern, formerStarOffset + 1, latterStarOffset > -1 ? latterStarOffset : pattern.length());
result = remainingURI.contains(suffixPattern);
// 匹配失败,直接返回
if (!result)
return false;
if (!isEmpty(suffixPattern))
{
remainingURI = substringAfter(str, suffixPattern);
}
// 移动指针
beginOffset = latterStarOffset + 1;
}
while (!isEmpty(suffixPattern) && !isEmpty(remainingURI));
return true;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")

View File

@@ -44,4 +44,33 @@ public class FileTypeUtils
} }
return fileName.substring(separatorIndex + 1).toLowerCase(); return fileName.substring(separatorIndex + 1).toLowerCase();
} }
/**
* 获取文件类型
*
* @param photoByte 文件字节码
* @return 后缀(不含".")
*/
public static String getFileExtendName(byte[] photoByte)
{
String strFileExtendName = "JPG";
if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
&& ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97))
{
strFileExtendName = "GIF";
}
else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70))
{
strFileExtendName = "JPG";
}
else if ((photoByte[0] == 66) && (photoByte[1] == 77))
{
strFileExtendName = "BMP";
}
else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71))
{
strFileExtendName = "PNG";
}
return strFileExtendName;
}
} }

View File

@@ -18,7 +18,7 @@ import com.ruoyi.common.core.utils.StringUtils;
* *
* @author ruoyi * @author ruoyi
*/ */
public class FileUtils extends org.apache.commons.io.FileUtils public class FileUtils
{ {
/** 字符常量:斜杠 {@code '/'} */ /** 字符常量:斜杠 {@code '/'} */
public static final char SLASH = '/'; public static final char SLASH = '/';
@@ -244,6 +244,7 @@ public class FileUtils extends org.apache.commons.io.FileUtils
.append(percentEncodedFileName); .append(percentEncodedFileName);
response.setHeader("Content-disposition", contentDispositionValue.toString()); response.setHeader("Content-disposition", contentDispositionValue.toString());
response.setHeader("download-filename", percentEncodedFileName);
} }
/** /**

View File

@@ -0,0 +1,87 @@
package com.ruoyi.common.core.utils.file;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Arrays;
import org.apache.poi.util.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 图片处理工具类
*
* @author ruoyi
*/
public class ImageUtils
{
private static final Logger log = LoggerFactory.getLogger(ImageUtils.class);
public static byte[] getImage(String imagePath)
{
InputStream is = getFile(imagePath);
try
{
return IOUtils.toByteArray(is);
}
catch (Exception e)
{
log.error("图片加载异常 {}", e);
return null;
}
finally
{
IOUtils.closeQuietly(is);
}
}
public static InputStream getFile(String imagePath)
{
try
{
byte[] result = readFile(imagePath);
result = Arrays.copyOf(result, result.length);
return new ByteArrayInputStream(result);
}
catch (Exception e)
{
log.error("获取图片异常 {}", e);
}
return null;
}
/**
* 读取文件为字节数据
*
* @param key 地址
* @return 字节数据
*/
public static byte[] readFile(String url)
{
InputStream in = null;
ByteArrayOutputStream baos = null;
try
{
// 网络地址
URL urlObj = new URL(url);
URLConnection urlConnection = urlObj.openConnection();
urlConnection.setConnectTimeout(30 * 1000);
urlConnection.setReadTimeout(60 * 1000);
urlConnection.setDoInput(true);
in = urlConnection.getInputStream();
return IOUtils.toByteArray(in);
}
catch (Exception e)
{
log.error("访问文件异常 {}", e);
return null;
}
finally
{
IOUtils.closeQuietly(in);
IOUtils.closeQuietly(baos);
}
}
}

View File

@@ -0,0 +1,155 @@
package com.ruoyi.common.core.utils.html;
import com.ruoyi.common.core.utils.StringUtils;
/**
* 转义和反转义工具类
*
* @author ruoyi
*/
public class EscapeUtil
{
public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)";
private static final char[][] TEXT = new char[64][];
static
{
for (int i = 0; i < 64; i++)
{
TEXT[i] = new char[] { (char) i };
}
// special HTML characters
TEXT['\''] = "&#039;".toCharArray(); // 单引号
TEXT['"'] = "&#34;".toCharArray(); // 双引号
TEXT['&'] = "&#38;".toCharArray(); // &符
TEXT['<'] = "&#60;".toCharArray(); // 小于号
TEXT['>'] = "&#62;".toCharArray(); // 大于号
}
/**
* 转义文本中的HTML字符为安全的字符
*
* @param text 被转义的文本
* @return 转义后的文本
*/
public static String escape(String text)
{
return encode(text);
}
/**
* 还原被转义的HTML特殊字符
*
* @param content 包含转义符的HTML内容
* @return 转换后的字符串
*/
public static String unescape(String content)
{
return decode(content);
}
/**
* 清除所有HTML标签但是不删除标签内的内容
*
* @param content 文本
* @return 清除标签后的文本
*/
public static String clean(String content)
{
return new HTMLFilter().filter(content);
}
/**
* Escape编码
*
* @param text 被编码的文本
* @return 编码后的字符
*/
private static String encode(String text)
{
int len;
if ((text == null) || ((len = text.length()) == 0))
{
return StringUtils.EMPTY;
}
StringBuilder buffer = new StringBuilder(len + (len >> 2));
char c;
for (int i = 0; i < len; i++)
{
c = text.charAt(i);
if (c < 64)
{
buffer.append(TEXT[c]);
}
else
{
buffer.append(c);
}
}
return buffer.toString();
}
/**
* Escape解码
*
* @param content 被转义的内容
* @return 解码后的字符串
*/
public static String decode(String content)
{
if (StringUtils.isEmpty(content))
{
return content;
}
StringBuilder tmp = new StringBuilder(content.length());
int lastPos = 0, pos = 0;
char ch;
while (lastPos < content.length())
{
pos = content.indexOf("%", lastPos);
if (pos == lastPos)
{
if (content.charAt(pos + 1) == 'u')
{
ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16);
tmp.append(ch);
lastPos = pos + 6;
}
else
{
ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16);
tmp.append(ch);
lastPos = pos + 3;
}
}
else
{
if (pos == -1)
{
tmp.append(content.substring(lastPos));
lastPos = content.length();
}
else
{
tmp.append(content.substring(lastPos, pos));
lastPos = pos;
}
}
}
return tmp.toString();
}
public static void main(String[] args)
{
String html = "<script>alert(1);</script>";
// String html = "<scr<script>ipt>alert(\"XSS\")</scr<script>ipt>";
// String html = "<123";
// String html = "123>";
System.out.println(EscapeUtil.clean(html));
System.out.println(EscapeUtil.escape(html));
System.out.println(EscapeUtil.unescape(html));
}
}

View File

@@ -0,0 +1,570 @@
package com.ruoyi.common.core.utils.html;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* HTML过滤器用于去除XSS漏洞隐患。
*
* @author ruoyi
*/
public final class HTMLFilter
{
/**
* regex flag union representing /si modifiers in php
**/
private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL;
private static final Pattern P_COMMENTS = Pattern.compile("<!--(.*?)-->", Pattern.DOTALL);
private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI);
private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL);
private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI);
private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI);
private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI);
private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI);
private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI);
private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?");
private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?");
private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?");
private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))");
private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL);
private static final Pattern P_END_ARROW = Pattern.compile("^>");
private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)");
private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)");
private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)");
private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)");
private static final Pattern P_AMP = Pattern.compile("&");
private static final Pattern P_QUOTE = Pattern.compile("\"");
private static final Pattern P_LEFT_ARROW = Pattern.compile("<");
private static final Pattern P_RIGHT_ARROW = Pattern.compile(">");
private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>");
// @xxx could grow large... maybe use sesat's ReferenceMap
private static final ConcurrentMap<String, Pattern> P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>();
private static final ConcurrentMap<String, Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>();
/**
* set of allowed html elements, along with allowed attributes for each element
**/
private final Map<String, List<String>> vAllowed;
/**
* counts of open tags for each (allowable) html element
**/
private final Map<String, Integer> vTagCounts = new HashMap<>();
/**
* html elements which must always be self-closing (e.g. "<img />")
**/
private final String[] vSelfClosingTags;
/**
* html elements which must always have separate opening and closing tags (e.g. "<b></b>")
**/
private final String[] vNeedClosingTags;
/**
* set of disallowed html elements
**/
private final String[] vDisallowed;
/**
* attributes which should be checked for valid protocols
**/
private final String[] vProtocolAtts;
/**
* allowed protocols
**/
private final String[] vAllowedProtocols;
/**
* tags which should be removed if they contain no content (e.g. "<b></b>" or "<b />")
**/
private final String[] vRemoveBlanks;
/**
* entities allowed within html markup
**/
private final String[] vAllowedEntities;
/**
* flag determining whether comments are allowed in input String.
*/
private final boolean stripComment;
private final boolean encodeQuotes;
/**
* flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "<b text </b>"
* becomes "<b> text </b>"). If set to false, unbalanced angle brackets will be html escaped.
*/
private final boolean alwaysMakeTags;
/**
* Default constructor.
*/
public HTMLFilter()
{
vAllowed = new HashMap<>();
final ArrayList<String> a_atts = new ArrayList<>();
a_atts.add("href");
a_atts.add("target");
vAllowed.put("a", a_atts);
final ArrayList<String> img_atts = new ArrayList<>();
img_atts.add("src");
img_atts.add("width");
img_atts.add("height");
img_atts.add("alt");
vAllowed.put("img", img_atts);
final ArrayList<String> no_atts = new ArrayList<>();
vAllowed.put("b", no_atts);
vAllowed.put("strong", no_atts);
vAllowed.put("i", no_atts);
vAllowed.put("em", no_atts);
vSelfClosingTags = new String[] { "img" };
vNeedClosingTags = new String[] { "a", "b", "strong", "i", "em" };
vDisallowed = new String[] {};
vAllowedProtocols = new String[] { "http", "mailto", "https" }; // no ftp.
vProtocolAtts = new String[] { "src", "href" };
vRemoveBlanks = new String[] { "a", "b", "strong", "i", "em" };
vAllowedEntities = new String[] { "amp", "gt", "lt", "quot" };
stripComment = true;
encodeQuotes = true;
alwaysMakeTags = false;
}
/**
* Map-parameter configurable constructor.
*
* @param conf map containing configuration. keys match field names.
*/
@SuppressWarnings("unchecked")
public HTMLFilter(final Map<String, Object> conf)
{
assert conf.containsKey("vAllowed") : "configuration requires vAllowed";
assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags";
assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags";
assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed";
assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols";
assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts";
assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks";
assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities";
vAllowed = Collections.unmodifiableMap((HashMap<String, List<String>>) conf.get("vAllowed"));
vSelfClosingTags = (String[]) conf.get("vSelfClosingTags");
vNeedClosingTags = (String[]) conf.get("vNeedClosingTags");
vDisallowed = (String[]) conf.get("vDisallowed");
vAllowedProtocols = (String[]) conf.get("vAllowedProtocols");
vProtocolAtts = (String[]) conf.get("vProtocolAtts");
vRemoveBlanks = (String[]) conf.get("vRemoveBlanks");
vAllowedEntities = (String[]) conf.get("vAllowedEntities");
stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true;
encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true;
alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true;
}
private void reset()
{
vTagCounts.clear();
}
// ---------------------------------------------------------------
// my versions of some PHP library functions
public static String chr(final int decimal)
{
return String.valueOf((char) decimal);
}
public static String htmlSpecialChars(final String s)
{
String result = s;
result = regexReplace(P_AMP, "&amp;", result);
result = regexReplace(P_QUOTE, "&quot;", result);
result = regexReplace(P_LEFT_ARROW, "&lt;", result);
result = regexReplace(P_RIGHT_ARROW, "&gt;", result);
return result;
}
// ---------------------------------------------------------------
/**
* given a user submitted input String, filter out any invalid or restricted html.
*
* @param input text (i.e. submitted by a user) than may contain html
* @return "clean" version of input, with only valid, whitelisted html elements allowed
*/
public String filter(final String input)
{
reset();
String s = input;
s = escapeComments(s);
s = balanceHTML(s);
s = checkTags(s);
s = processRemoveBlanks(s);
// s = validateEntities(s);
return s;
}
public boolean isAlwaysMakeTags()
{
return alwaysMakeTags;
}
public boolean isStripComments()
{
return stripComment;
}
private String escapeComments(final String s)
{
final Matcher m = P_COMMENTS.matcher(s);
final StringBuffer buf = new StringBuffer();
if (m.find())
{
final String match = m.group(1); // (.*?)
m.appendReplacement(buf, Matcher.quoteReplacement("<!--" + htmlSpecialChars(match) + "-->"));
}
m.appendTail(buf);
return buf.toString();
}
private String balanceHTML(String s)
{
if (alwaysMakeTags)
{
//
// try and form html
//
s = regexReplace(P_END_ARROW, "", s);
// 不追加结束标签
s = regexReplace(P_BODY_TO_END, "<$1>", s);
s = regexReplace(P_XML_CONTENT, "$1<$2", s);
}
else
{
//
// escape stray brackets
//
s = regexReplace(P_STRAY_LEFT_ARROW, "&lt;$1", s);
s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2&gt;<", s);
//
// the last regexp causes '<>' entities to appear
// (we need to do a lookahead assertion so that the last bracket can
// be used in the next pass of the regexp)
//
s = regexReplace(P_BOTH_ARROWS, "", s);
}
return s;
}
private String checkTags(String s)
{
Matcher m = P_TAGS.matcher(s);
final StringBuffer buf = new StringBuffer();
while (m.find())
{
String replaceStr = m.group(1);
replaceStr = processTag(replaceStr);
m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr));
}
m.appendTail(buf);
// these get tallied in processTag
// (remember to reset before subsequent calls to filter method)
final StringBuilder sBuilder = new StringBuilder(buf.toString());
for (String key : vTagCounts.keySet())
{
for (int ii = 0; ii < vTagCounts.get(key); ii++)
{
sBuilder.append("</").append(key).append(">");
}
}
s = sBuilder.toString();
return s;
}
private String processRemoveBlanks(final String s)
{
String result = s;
for (String tag : vRemoveBlanks)
{
if (!P_REMOVE_PAIR_BLANKS.containsKey(tag))
{
P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?></" + tag + ">"));
}
result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result);
if (!P_REMOVE_SELF_BLANKS.containsKey(tag))
{
P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>"));
}
result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result);
}
return result;
}
private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s)
{
Matcher m = regex_pattern.matcher(s);
return m.replaceAll(replacement);
}
private String processTag(final String s)
{
// ending tags
Matcher m = P_END_TAG.matcher(s);
if (m.find())
{
final String name = m.group(1).toLowerCase();
if (allowed(name))
{
if (false == inArray(name, vSelfClosingTags))
{
if (vTagCounts.containsKey(name))
{
vTagCounts.put(name, vTagCounts.get(name) - 1);
return "</" + name + ">";
}
}
}
}
// starting tags
m = P_START_TAG.matcher(s);
if (m.find())
{
final String name = m.group(1).toLowerCase();
final String body = m.group(2);
String ending = m.group(3);
// debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" );
if (allowed(name))
{
final StringBuilder params = new StringBuilder();
final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body);
final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body);
final List<String> paramNames = new ArrayList<>();
final List<String> paramValues = new ArrayList<>();
while (m2.find())
{
paramNames.add(m2.group(1)); // ([a-z0-9]+)
paramValues.add(m2.group(3)); // (.*?)
}
while (m3.find())
{
paramNames.add(m3.group(1)); // ([a-z0-9]+)
paramValues.add(m3.group(3)); // ([^\"\\s']+)
}
String paramName, paramValue;
for (int ii = 0; ii < paramNames.size(); ii++)
{
paramName = paramNames.get(ii).toLowerCase();
paramValue = paramValues.get(ii);
// debug( "paramName='" + paramName + "'" );
// debug( "paramValue='" + paramValue + "'" );
// debug( "allowed? " + vAllowed.get( name ).contains( paramName ) );
if (allowedAttribute(name, paramName))
{
if (inArray(paramName, vProtocolAtts))
{
paramValue = processParamProtocol(paramValue);
}
params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\"");
}
}
if (inArray(name, vSelfClosingTags))
{
ending = " /";
}
if (inArray(name, vNeedClosingTags))
{
ending = "";
}
if (ending == null || ending.length() < 1)
{
if (vTagCounts.containsKey(name))
{
vTagCounts.put(name, vTagCounts.get(name) + 1);
}
else
{
vTagCounts.put(name, 1);
}
}
else
{
ending = " /";
}
return "<" + name + params + ending + ">";
}
else
{
return "";
}
}
// comments
m = P_COMMENT.matcher(s);
if (!stripComment && m.find())
{
return "<" + m.group() + ">";
}
return "";
}
private String processParamProtocol(String s)
{
s = decodeEntities(s);
final Matcher m = P_PROTOCOL.matcher(s);
if (m.find())
{
final String protocol = m.group(1);
if (!inArray(protocol, vAllowedProtocols))
{
// bad protocol, turn into local anchor link instead
s = "#" + s.substring(protocol.length() + 1);
if (s.startsWith("#//"))
{
s = "#" + s.substring(3);
}
}
}
return s;
}
private String decodeEntities(String s)
{
StringBuffer buf = new StringBuffer();
Matcher m = P_ENTITY.matcher(s);
while (m.find())
{
final String match = m.group(1);
final int decimal = Integer.decode(match).intValue();
m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
}
m.appendTail(buf);
s = buf.toString();
buf = new StringBuffer();
m = P_ENTITY_UNICODE.matcher(s);
while (m.find())
{
final String match = m.group(1);
final int decimal = Integer.valueOf(match, 16).intValue();
m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
}
m.appendTail(buf);
s = buf.toString();
buf = new StringBuffer();
m = P_ENCODE.matcher(s);
while (m.find())
{
final String match = m.group(1);
final int decimal = Integer.valueOf(match, 16).intValue();
m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
}
m.appendTail(buf);
s = buf.toString();
s = validateEntities(s);
return s;
}
private String validateEntities(final String s)
{
StringBuffer buf = new StringBuffer();
// validate entities throughout the string
Matcher m = P_VALID_ENTITIES.matcher(s);
while (m.find())
{
final String one = m.group(1); // ([^&;]*)
final String two = m.group(2); // (?=(;|&|$))
m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two)));
}
m.appendTail(buf);
return encodeQuotes(buf.toString());
}
private String encodeQuotes(final String s)
{
if (encodeQuotes)
{
StringBuffer buf = new StringBuffer();
Matcher m = P_VALID_QUOTES.matcher(s);
while (m.find())
{
final String one = m.group(1); // (>|^)
final String two = m.group(2); // ([^<]+?)
final String three = m.group(3); // (<|$)
// 不替换双引号为&quot;防止json格式无效 regexReplace(P_QUOTE, "&quot;", two)
m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three));
}
m.appendTail(buf);
return buf.toString();
}
else
{
return s;
}
}
private String checkEntity(final String preamble, final String term)
{
return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&amp;" + preamble;
}
private boolean isValidEntity(final String entity)
{
return inArray(entity, vAllowedEntities);
}
private static boolean inArray(final String s, final String[] array)
{
for (String item : array)
{
if (item != null && item.equals(s))
{
return true;
}
}
return false;
}
private boolean allowed(final String name)
{
return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed);
}
private boolean allowedAttribute(final String name, final String paramName)
{
return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName));
}
}

View File

@@ -14,6 +14,11 @@ public class IpUtils
{ {
public static String getIpAddr(HttpServletRequest request) public static String getIpAddr(HttpServletRequest request)
{ {
if (request == null)
{
return null;
}
String ip = null; String ip = null;
// X-Forwarded-ForSquid 服务代理 // X-Forwarded-ForSquid 服务代理

View File

@@ -0,0 +1,19 @@
package com.ruoyi.common.core.utils.poi;
/**
* Excel数据格式处理适配器
*
* @author ruoyi
*/
public interface ExcelHandlerAdapter
{
/**
* 格式化
*
* @param value 单元格数据值
* @param args excel注解args参数组
*
* @return 处理后的值
*/
Object format(Object value, String[] args);
}

View File

@@ -4,6 +4,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.util.ArrayList; import java.util.ArrayList;
@@ -20,10 +21,12 @@ import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.poi.ss.usermodel.DataValidation; import org.apache.poi.ss.usermodel.DataValidation;
import org.apache.poi.ss.usermodel.DataValidationConstraint; import org.apache.poi.ss.usermodel.DataValidationConstraint;
import org.apache.poi.ss.usermodel.DataValidationHelper; import org.apache.poi.ss.usermodel.DataValidationHelper;
import org.apache.poi.ss.usermodel.DateUtil; import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.usermodel.FillPatternType; import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.Font; import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment; import org.apache.poi.ss.usermodel.HorizontalAlignment;
@@ -33,8 +36,11 @@ import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.VerticalAlignment; import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory; import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList; import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.streaming.SXSSFWorkbook; import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFDataValidation; import org.apache.poi.xssf.usermodel.XSSFDataValidation;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -45,6 +51,8 @@ import com.ruoyi.common.core.annotation.Excels;
import com.ruoyi.common.core.text.Convert; import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.core.utils.DateUtils; import com.ruoyi.common.core.utils.DateUtils;
import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.utils.file.FileTypeUtils;
import com.ruoyi.common.core.utils.file.ImageUtils;
import com.ruoyi.common.core.utils.reflect.ReflectUtils; import com.ruoyi.common.core.utils.reflect.ReflectUtils;
/** /**
@@ -96,6 +104,21 @@ public class ExcelUtil<T>
*/ */
private List<Object[]> fields; private List<Object[]> fields;
/**
* 当前行号
*/
private int rownum;
/**
* 标题
*/
private String title;
/**
* 最大高度
*/
private short maxHeight;
/** /**
* 统计列表 * 统计列表
*/ */
@@ -116,7 +139,7 @@ public class ExcelUtil<T>
this.clazz = clazz; this.clazz = clazz;
} }
public void init(List<T> list, String sheetName, Type type) public void init(List<T> list, String sheetName, String title, Type type)
{ {
if (list == null) if (list == null)
{ {
@@ -125,8 +148,27 @@ public class ExcelUtil<T>
this.list = list; this.list = list;
this.sheetName = sheetName; this.sheetName = sheetName;
this.type = type; this.type = type;
this.title = title;
createExcelField(); createExcelField();
createWorkbook(); createWorkbook();
createTitle();
}
/**
* 创建excel第一行标题
*/
public void createTitle()
{
if (StringUtils.isNotEmpty(title))
{
Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0);
titleRow.setHeightInPoints(30);
Cell titleCell = titleRow.createCell(0);
titleCell.setCellStyle(styles.get("title"));
titleCell.setCellValue(title);
sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(),
this.fields.size() - 1));
}
} }
/** /**
@@ -137,46 +179,50 @@ public class ExcelUtil<T>
*/ */
public List<T> importExcel(InputStream is) throws Exception public List<T> importExcel(InputStream is) throws Exception
{ {
return importExcel(StringUtils.EMPTY, is); return importExcel(is, 0);
}
/**
* 对excel表单默认第一个索引名转换成list
*
* @param is 输入流
* @param titleNum 标题占用行数
* @return 转换后集合
*/
public List<T> importExcel(InputStream is, int titleNum) throws Exception
{
return importExcel(StringUtils.EMPTY, is, titleNum);
} }
/** /**
* 对excel表单指定表格索引名转换成list * 对excel表单指定表格索引名转换成list
* *
* @param sheetName 表格索引名 * @param sheetName 表格索引名
* @param titleNum 标题占用行数
* @param is 输入流 * @param is 输入流
* @return 转换后集合 * @return 转换后集合
*/ */
public List<T> importExcel(String sheetName, InputStream is) throws Exception public List<T> importExcel(String sheetName, InputStream is, int titleNum) throws Exception
{ {
this.type = Type.IMPORT; this.type = Type.IMPORT;
this.wb = WorkbookFactory.create(is); this.wb = WorkbookFactory.create(is);
List<T> list = new ArrayList<T>(); List<T> list = new ArrayList<T>();
Sheet sheet = null; // 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheet
if (StringUtils.isNotEmpty(sheetName)) Sheet sheet = StringUtils.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0);
{
// 如果指定sheet名,则取指定sheet中的内容.
sheet = wb.getSheet(sheetName);
}
else
{
// 如果传入的sheet名不存在则默认指向第1个sheet.
sheet = wb.getSheetAt(0);
}
if (sheet == null) if (sheet == null)
{ {
throw new IOException("文件sheet不存在"); throw new IOException("文件sheet不存在");
} }
int rows = sheet.getPhysicalNumberOfRows(); // 获取最后一个非空行的行下标比如总行数为n则返回的为n-1
int rows = sheet.getLastRowNum();
if (rows > 0) if (rows > 0)
{ {
// 定义一个map用于存放excel列的序号和field. // 定义一个map用于存放excel列的序号和field.
Map<String, Integer> cellMap = new HashMap<String, Integer>(); Map<String, Integer> cellMap = new HashMap<String, Integer>();
// 获取表头 // 获取表头
Row heard = sheet.getRow(0); Row heard = sheet.getRow(titleNum);
for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++) for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++)
{ {
Cell cell = heard.getCell(i); Cell cell = heard.getCell(i);
@@ -191,37 +237,36 @@ public class ExcelUtil<T>
} }
} }
// 有数据时才处理 得到类的所有field. // 有数据时才处理 得到类的所有field.
Field[] allFields = clazz.getDeclaredFields(); List<Object[]> fields = this.getFields();
// 定义一个map用于存放列的序号和field. Map<Integer, Object[]> fieldsMap = new HashMap<Integer, Object[]>();
Map<Integer, Field> fieldsMap = new HashMap<Integer, Field>(); for (Object[] objects : fields)
for (int col = 0; col < allFields.length; col++)
{ {
Field field = allFields[col]; Excel attr = (Excel) objects[1];
Excel attr = field.getAnnotation(Excel.class);
if (attr != null && (attr.type() == Type.ALL || attr.type() == type))
{
// 设置类的私有字段属性可访问.
field.setAccessible(true);
Integer column = cellMap.get(attr.name()); Integer column = cellMap.get(attr.name());
if (column != null) if (column != null)
{ {
fieldsMap.put(column, field); fieldsMap.put(column, objects);
} }
} }
} for (int i = titleNum + 1; i <= rows; i++)
for (int i = 1; i < rows; i++)
{ {
// 从第2行开始取数据,默认第一行是表头. // 从第2行开始取数据,默认第一行是表头.
Row row = sheet.getRow(i); Row row = sheet.getRow(i);
// 判断当前行是否是空行
if (isRowEmpty(row))
{
continue;
}
T entity = null; T entity = null;
for (Map.Entry<Integer, Field> entry : fieldsMap.entrySet()) for (Map.Entry<Integer, Object[]> entry : fieldsMap.entrySet())
{ {
Object val = this.getCellValue(row, entry.getKey()); Object val = this.getCellValue(row, entry.getKey());
// 如果不存在实例则新建. // 如果不存在实例则新建.
entity = (entity == null ? clazz.newInstance() : entity); entity = (entity == null ? clazz.newInstance() : entity);
// 从map中得到对应列的field. // 从map中得到对应列的field.
Field field = fieldsMap.get(entry.getKey()); Field field = (Field) entry.getValue()[0];
Excel attr = (Excel) entry.getValue()[1];
// 取得类型,并根据对象类型设置值. // 取得类型,并根据对象类型设置值.
Class<?> fieldType = field.getType(); Class<?> fieldType = field.getType();
if (String.class == fieldType) if (String.class == fieldType)
@@ -232,10 +277,18 @@ public class ExcelUtil<T>
val = StringUtils.substringBefore(s, ".0"); val = StringUtils.substringBefore(s, ".0");
} }
else else
{
String dateFormat = field.getAnnotation(Excel.class).dateFormat();
if (StringUtils.isNotEmpty(dateFormat))
{
val = DateUtils.parseDateToStr(dateFormat, (Date) val);
}
else
{ {
val = Convert.toStr(val); val = Convert.toStr(val);
} }
} }
}
else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val)))
{ {
val = Convert.toInt(val); val = Convert.toInt(val);
@@ -273,7 +326,6 @@ public class ExcelUtil<T>
} }
if (StringUtils.isNotNull(fieldType)) if (StringUtils.isNotNull(fieldType))
{ {
Excel attr = field.getAnnotation(Excel.class);
String propertyName = field.getName(); String propertyName = field.getName();
if (StringUtils.isNotEmpty(attr.targetAttr())) if (StringUtils.isNotEmpty(attr.targetAttr()))
{ {
@@ -283,6 +335,10 @@ public class ExcelUtil<T>
{ {
val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator()); val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator());
} }
else if (!attr.handler().equals(ExcelHandlerAdapter.class))
{
val = dataFormatHandlerAdapter(val, attr);
}
ReflectUtils.invokeSetter(entity, propertyName, val); ReflectUtils.invokeSetter(entity, propertyName, val);
} }
} }
@@ -301,14 +357,35 @@ public class ExcelUtil<T>
* @return 结果 * @return 结果
* @throws IOException * @throws IOException
*/ */
public void exportExcel(HttpServletResponse response, List<T> list, String sheetName) throws IOException public void exportExcel(HttpServletResponse response, List<T> list, String sheetName)throws IOException
{ {
response.setContentType("application/vnd.ms-excel"); exportExcel(response, list, sheetName, StringUtils.EMPTY);
}
/**
* 对list数据源将其里面的数据导入到excel表单
*
* @param response 返回数据
* @param list 导出数据集合
* @param sheetName 工作表的名称
* @param title 标题
* @return 结果
* @throws IOException
*/
public void exportExcel(HttpServletResponse response, List<T> list, String sheetName, String title) throws IOException
{
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8");
this.init(list, sheetName, Type.EXPORT); this.init(list, sheetName, title, Type.EXPORT);
exportExcel(response.getOutputStream()); exportExcel(response.getOutputStream());
} }
/**
* 对list数据源将其里面的数据导入到excel表单
*
* @param sheetName 工作表的名称
* @return 结果
*/
/** /**
* 对list数据源将其里面的数据导入到excel表单 * 对list数据源将其里面的数据导入到excel表单
* *
@@ -317,9 +394,21 @@ public class ExcelUtil<T>
*/ */
public void importTemplateExcel(HttpServletResponse response, String sheetName) throws IOException public void importTemplateExcel(HttpServletResponse response, String sheetName) throws IOException
{ {
response.setContentType("application/vnd.ms-excel"); importTemplateExcel(response, sheetName, StringUtils.EMPTY);
}
/**
* 对list数据源将其里面的数据导入到excel表单
*
* @param sheetName 工作表的名称
* @param title 标题
* @return 结果
*/
public void importTemplateExcel(HttpServletResponse response, String sheetName, String title) throws IOException
{
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8");
this.init(null, sheetName, Type.IMPORT); this.init(null, sheetName, title, Type.IMPORT);
exportExcel(response.getOutputStream()); exportExcel(response.getOutputStream());
} }
@@ -328,18 +417,37 @@ public class ExcelUtil<T>
* *
* @return 结果 * @return 结果
*/ */
public void exportExcel(OutputStream outputStream) public void exportExcel(OutputStream out)
{ {
try try
{
writeSheet();
wb.write(out);
}
catch (Exception e)
{
log.error("导出Excel异常{}", e.getMessage());
}
finally
{
IOUtils.closeQuietly(wb);
IOUtils.closeQuietly(out);
}
}
/**
* 创建写入数据到Sheet
*/
public void writeSheet()
{ {
// 取出一共有多少个sheet. // 取出一共有多少个sheet.
double sheetNo = Math.ceil(list.size() / sheetSize); int sheetNo = Math.max(1, (int) Math.ceil(list.size() * 1.0 / sheetSize));
for (int index = 0; index <= sheetNo; index++) for (int index = 0; index < sheetNo; index++)
{ {
createSheet(sheetNo, index); createSheet(sheetNo, index);
// 产生一行 // 产生一行
Row row = sheet.createRow(0); Row row = sheet.createRow(rownum);
int column = 0; int column = 0;
// 写入各个字段的列头名称 // 写入各个字段的列头名称
for (Object[] os : fields) for (Object[] os : fields)
@@ -353,37 +461,6 @@ public class ExcelUtil<T>
addStatisticsRow(); addStatisticsRow();
} }
} }
wb.write(outputStream);
}
catch (Exception e)
{
log.error("导出Excel异常{}", e.getMessage());
}
finally
{
if (wb != null)
{
try
{
wb.close();
}
catch (IOException e1)
{
e1.printStackTrace();
}
}
if (outputStream != null)
{
try
{
outputStream.close();
}
catch (IOException e1)
{
e1.printStackTrace();
}
}
}
} }
/** /**
@@ -398,7 +475,7 @@ public class ExcelUtil<T>
int endNo = Math.min(startNo + sheetSize, list.size()); int endNo = Math.min(startNo + sheetSize, list.size());
for (int i = startNo; i < endNo; i++) for (int i = startNo; i < endNo; i++)
{ {
row = sheet.createRow(i + 1 - startNo); row = sheet.createRow(i + 1 + rownum - startNo);
// 得到导出对象. // 得到导出对象.
T vo = (T) list.get(i); T vo = (T) list.get(i);
int column = 0; int column = 0;
@@ -406,8 +483,6 @@ public class ExcelUtil<T>
{ {
Field field = (Field) os[0]; Field field = (Field) os[0];
Excel excel = (Excel) os[1]; Excel excel = (Excel) os[1];
// 设置实体类私有属性可访问
field.setAccessible(true);
this.addCell(excel, row, vo, field, column++); this.addCell(excel, row, vo, field, column++);
} }
} }
@@ -426,6 +501,16 @@ public class ExcelUtil<T>
CellStyle style = wb.createCellStyle(); CellStyle style = wb.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER); style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER); style.setVerticalAlignment(VerticalAlignment.CENTER);
Font titleFont = wb.createFont();
titleFont.setFontName("Arial");
titleFont.setFontHeightInPoints((short) 16);
titleFont.setBold(true);
style.setFont(titleFont);
styles.put("title", style);
style = wb.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setBorderRight(BorderStyle.THIN); style.setBorderRight(BorderStyle.THIN);
style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setBorderLeft(BorderStyle.THIN); style.setBorderLeft(BorderStyle.THIN);
@@ -509,10 +594,53 @@ public class ExcelUtil<T>
cell.setCellValue(StringUtils.isNull(value) ? attr.defaultValue() : value + attr.suffix()); cell.setCellValue(StringUtils.isNull(value) ? attr.defaultValue() : value + attr.suffix());
} }
else if (ColumnType.NUMERIC == attr.cellType()) else if (ColumnType.NUMERIC == attr.cellType())
{
if (StringUtils.isNotNull(value))
{ {
cell.setCellValue(StringUtils.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value)); cell.setCellValue(StringUtils.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value));
} }
} }
else if (ColumnType.IMAGE == attr.cellType())
{
ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1), cell.getRow().getRowNum() + 1);
String imagePath = Convert.toStr(value);
if (StringUtils.isNotEmpty(imagePath))
{
byte[] data = ImageUtils.getImage(imagePath);
getDrawingPatriarch(cell.getSheet()).createPicture(anchor,
cell.getSheet().getWorkbook().addPicture(data, getImageType(data)));
}
}
}
/**
* 获取画布
*/
public static Drawing<?> getDrawingPatriarch(Sheet sheet)
{
if (sheet.getDrawingPatriarch() == null)
{
sheet.createDrawingPatriarch();
}
return sheet.getDrawingPatriarch();
}
/**
* 获取图片类型,设置图片插入类型
*/
public int getImageType(byte[] value)
{
String type = FileTypeUtils.getFileExtendName(value);
if ("JPG".equalsIgnoreCase(type))
{
return Workbook.PICTURE_TYPE_JPEG;
}
else if ("PNG".equalsIgnoreCase(type))
{
return Workbook.PICTURE_TYPE_PNG;
}
return Workbook.PICTURE_TYPE_JPEG;
}
/** /**
* 创建表格样式 * 创建表格样式
@@ -527,7 +655,6 @@ public class ExcelUtil<T>
{ {
// 设置列宽 // 设置列宽
sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256)); sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256));
row.setHeight((short) (attr.height() * 20));
} }
// 如果设置了提示信息则鼠标放上去提示. // 如果设置了提示信息则鼠标放上去提示.
if (StringUtils.isNotEmpty(attr.prompt())) if (StringUtils.isNotEmpty(attr.prompt()))
@@ -552,7 +679,7 @@ public class ExcelUtil<T>
try try
{ {
// 设置行高 // 设置行高
row.setHeight((short) (attr.height() * 20)); row.setHeight(maxHeight);
// 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列. // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列.
if (attr.isExport()) if (attr.isExport())
{ {
@@ -578,6 +705,10 @@ public class ExcelUtil<T>
{ {
cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).toString()); cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).toString());
} }
else if (!attr.handler().equals(ExcelHandlerAdapter.class))
{
cell.setCellValue(dataFormatHandlerAdapter(value, attr));
}
else else
{ {
// 设置列类型 // 设置列类型
@@ -724,6 +855,28 @@ public class ExcelUtil<T>
return StringUtils.stripEnd(propertyString.toString(), separator); return StringUtils.stripEnd(propertyString.toString(), separator);
} }
/**
* 数据处理器
*
* @param value 数据值
* @param excel 数据注解
* @return
*/
public String dataFormatHandlerAdapter(Object value, Excel excel)
{
try
{
Object instance = excel.handler().newInstance();
Method formatMethod = excel.handler().getMethod("format", new Class[] { Object.class, String[].class });
value = formatMethod.invoke(instance, value, excel.args());
}
catch (Exception e)
{
log.error("不能格式化数据 " + excel.handler(), e.getMessage());
}
return Convert.toStr(value);
}
/** /**
* 合计统计信息 * 合计统计信息
*/ */
@@ -754,10 +907,9 @@ public class ExcelUtil<T>
{ {
if (statistics.size() > 0) if (statistics.size() > 0)
{ {
Cell cell = null;
Row row = sheet.createRow(sheet.getLastRowNum() + 1); Row row = sheet.createRow(sheet.getLastRowNum() + 1);
Set<Integer> keys = statistics.keySet(); Set<Integer> keys = statistics.keySet();
cell = row.createCell(0); Cell cell = row.createCell(0);
cell.setCellStyle(styles.get("total")); cell.setCellStyle(styles.get("total"));
cell.setCellValue("合计"); cell.setCellValue("合计");
@@ -827,7 +979,17 @@ public class ExcelUtil<T>
*/ */
private void createExcelField() private void createExcelField()
{ {
this.fields = new ArrayList<Object[]>(); this.fields = getFields();
this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList());
this.maxHeight = getRowHeight();
}
/**
* 获取字段注解信息
*/
public List<Object[]> getFields()
{
List<Object[]> fields = new ArrayList<Object[]>();
List<Field> tempFields = new ArrayList<>(); List<Field> tempFields = new ArrayList<>();
tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields())); tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
tempFields.addAll(Arrays.asList(clazz.getDeclaredFields())); tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
@@ -836,7 +998,12 @@ public class ExcelUtil<T>
// 单注解 // 单注解
if (field.isAnnotationPresent(Excel.class)) if (field.isAnnotationPresent(Excel.class))
{ {
putToField(field, field.getAnnotation(Excel.class)); Excel attr = field.getAnnotation(Excel.class);
if (attr != null && (attr.type() == Type.ALL || attr.type() == type))
{
field.setAccessible(true);
fields.add(new Object[] { field, attr });
}
} }
// 多注解 // 多注解
@@ -844,25 +1011,32 @@ public class ExcelUtil<T>
{ {
Excels attrs = field.getAnnotation(Excels.class); Excels attrs = field.getAnnotation(Excels.class);
Excel[] excels = attrs.value(); Excel[] excels = attrs.value();
for (Excel excel : excels) for (Excel attr : excels)
{
putToField(field, excel);
}
}
}
this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList());
}
/**
* 放到字段集合中
*/
private void putToField(Field field, Excel attr)
{ {
if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) if (attr != null && (attr.type() == Type.ALL || attr.type() == type))
{ {
this.fields.add(new Object[] { field, attr }); field.setAccessible(true);
fields.add(new Object[] { field, attr });
} }
} }
}
}
return fields;
}
/**
* 根据注解获取最大行高
*/
public short getRowHeight()
{
double maxHeight = 0;
for (Object[] os : this.fields)
{
Excel excel = (Excel) os[1];
maxHeight = maxHeight > excel.height() ? maxHeight : excel.height();
}
return (short) (maxHeight * 20);
}
/** /**
* 创建一个工作簿 * 创建一个工作簿
@@ -870,6 +1044,9 @@ public class ExcelUtil<T>
public void createWorkbook() public void createWorkbook()
{ {
this.wb = new SXSSFWorkbook(500); this.wb = new SXSSFWorkbook(500);
this.sheet = wb.createSheet();
wb.setSheetName(0, sheetName);
this.styles = createStyles(wb);
} }
/** /**
@@ -878,17 +1055,13 @@ public class ExcelUtil<T>
* @param sheetNo sheet数量 * @param sheetNo sheet数量
* @param index 序号 * @param index 序号
*/ */
public void createSheet(double sheetNo, int index) public void createSheet(int sheetNo, int index)
{
// 设置工作表的名称.
if (sheetNo > 1 && index > 0)
{ {
this.sheet = wb.createSheet(); this.sheet = wb.createSheet();
this.styles = createStyles(wb); this.createTitle();
// 设置工作表的名称.
if (sheetNo == 0)
{
wb.setSheetName(index, sheetName);
}
else
{
wb.setSheetName(index, sheetName + index); wb.setSheetName(index, sheetName + index);
} }
} }
@@ -921,7 +1094,7 @@ public class ExcelUtil<T>
} }
else else
{ {
if ((Double) val % 1 > 0) if ((Double) val % 1 != 0)
{ {
val = new BigDecimal(val.toString()); val = new BigDecimal(val.toString());
} }
@@ -952,4 +1125,27 @@ public class ExcelUtil<T>
} }
return val; return val;
} }
/**
* 判断是否是空行
*
* @param row 判断的行
* @return
*/
private boolean isRowEmpty(Row row)
{
if (row == null)
{
return true;
}
for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++)
{
Cell cell = row.getCell(i);
if (cell != null && cell.getCellType() != CellType.BLANK)
{
return false;
}
}
return true;
}
} }

View File

@@ -1,6 +1,6 @@
package com.ruoyi.common.core.utils.sql; package com.ruoyi.common.core.utils.sql;
import com.ruoyi.common.core.exception.BaseException; import com.ruoyi.common.core.exception.UtilException;
import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.utils.StringUtils;
/** /**
@@ -22,7 +22,7 @@ public class SqlUtil
{ {
if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value))
{ {
throw new BaseException("参数不符合规范,不能进行查询"); throw new UtilException("参数不符合规范,不能进行查询");
} }
return value; return value;
} }

View File

@@ -3,12 +3,10 @@ package com.ruoyi.common.core.web.controller;
import java.beans.PropertyEditorSupport; import java.beans.PropertyEditorSupport;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.InitBinder;
import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
import com.ruoyi.common.core.constant.HttpStatus; import com.ruoyi.common.core.constant.HttpStatus;
@@ -27,7 +25,7 @@ import com.ruoyi.common.core.web.page.TableSupport;
*/ */
public class BaseController public class BaseController
{ {
protected final Logger logger = LoggerFactory.getLogger(BaseController.class); protected final Logger logger = LoggerFactory.getLogger(this.getClass());
/** /**
* 将前台传递过来的日期格式的字符串自动转化为Date类型 * 将前台传递过来的日期格式的字符串自动转化为Date类型
@@ -57,7 +55,8 @@ public class BaseController
if (StringUtils.isNotNull(pageNum) && StringUtils.isNotNull(pageSize)) if (StringUtils.isNotNull(pageNum) && StringUtils.isNotNull(pageSize))
{ {
String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy()); String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
PageHelper.startPage(pageNum, pageSize, orderBy); Boolean reasonable = pageDomain.getReasonable();
PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);
} }
} }
@@ -85,4 +84,47 @@ public class BaseController
{ {
return rows > 0 ? AjaxResult.success() : AjaxResult.error(); return rows > 0 ? AjaxResult.success() : AjaxResult.error();
} }
/**
* 响应返回结果
*
* @param result 结果
* @return 操作结果
*/
protected AjaxResult toAjax(boolean result)
{
return result ? success() : error();
}
/**
* 返回成功
*/
public AjaxResult success()
{
return AjaxResult.success();
}
/**
* 返回失败消息
*/
public AjaxResult error()
{
return AjaxResult.error();
}
/**
* 返回成功消息
*/
public AjaxResult success(String message)
{
return AjaxResult.success(message);
}
/**
* 返回失败消息
*/
public AjaxResult error(String message)
{
return AjaxResult.error(message);
}
} }

View File

@@ -21,6 +21,9 @@ public class PageDomain
/** 排序的方向desc或者asc */ /** 排序的方向desc或者asc */
private String isAsc = "asc"; private String isAsc = "asc";
/** 分页参数合理化 */
private Boolean reasonable = true;
public String getOrderBy() public String getOrderBy()
{ {
if (StringUtils.isEmpty(orderByColumn)) if (StringUtils.isEmpty(orderByColumn))
@@ -67,6 +70,32 @@ public class PageDomain
public void setIsAsc(String isAsc) public void setIsAsc(String isAsc)
{ {
if (StringUtils.isNotEmpty(isAsc))
{
// 兼容前端排序类型
if ("ascending".equals(isAsc))
{
isAsc = "asc";
}
else if ("descending".equals(isAsc))
{
isAsc = "desc";
}
this.isAsc = isAsc; this.isAsc = isAsc;
} }
}
public Boolean getReasonable()
{
if (StringUtils.isNull(reasonable))
{
return Boolean.TRUE;
}
return reasonable;
}
public void setReasonable(Boolean reasonable)
{
this.reasonable = reasonable;
}
} }

View File

@@ -29,6 +29,11 @@ public class TableSupport
*/ */
public static final String IS_ASC = "isAsc"; public static final String IS_ASC = "isAsc";
/**
* 分页参数合理化
*/
public static final String REASONABLE = "reasonable";
/** /**
* 封装分页对象 * 封装分页对象
*/ */
@@ -39,6 +44,7 @@ public class TableSupport
pageDomain.setPageSize(ServletUtils.getParameterToInt(PAGE_SIZE)); pageDomain.setPageSize(ServletUtils.getParameterToInt(PAGE_SIZE));
pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN)); pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN));
pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC)); pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC));
pageDomain.setReasonable(ServletUtils.getParameterToBool(REASONABLE));
return pageDomain; return pageDomain;
} }

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId> <artifactId>ruoyi-common</artifactId>
<version>2.4.0</version> <version>3.2.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -1,12 +1,8 @@
package com.ruoyi.common.datascope.aspect; package com.ruoyi.common.datascope.aspect;
import java.lang.reflect.Method;
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.utils.StringUtils;
@@ -59,26 +55,15 @@ public class DataScopeAspect
@Autowired @Autowired
private TokenService tokenService; private TokenService tokenService;
// 配置织入点 @Before("@annotation(controllerDataScope)")
@Pointcut("@annotation(com.ruoyi.common.datascope.annotation.DataScope)") public void doBefore(JoinPoint point, DataScope controllerDataScope) throws Throwable
public void dataScopePointCut()
{ {
clearDataScope(point);
handleDataScope(point, controllerDataScope);
} }
@Before("dataScopePointCut()") protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope)
public void doBefore(JoinPoint point) throws Throwable
{ {
handleDataScope(point);
}
protected void handleDataScope(final JoinPoint joinPoint)
{
// 获得注解
DataScope controllerDataScope = getAnnotationLog(joinPoint);
if (controllerDataScope == null)
{
return;
}
// 获取当前的用户 // 获取当前的用户
LoginUser loginUser = tokenService.getLoginUser(); LoginUser loginUser = tokenService.getLoginUser();
if (StringUtils.isNotNull(loginUser)) if (StringUtils.isNotNull(loginUser))
@@ -155,18 +140,15 @@ public class DataScopeAspect
} }
/** /**
* 是否存在注解,如果存在就获取 * 拼接权限sql前先清空params.dataScope参数防止注入
*/ */
private DataScope getAnnotationLog(JoinPoint joinPoint) private void clearDataScope(final JoinPoint joinPoint)
{ {
Signature signature = joinPoint.getSignature(); Object params = joinPoint.getArgs()[0];
MethodSignature methodSignature = (MethodSignature) signature; if (StringUtils.isNotNull(params) && params instanceof BaseEntity)
Method method = methodSignature.getMethod();
if (method != null)
{ {
return method.getAnnotation(DataScope.class); BaseEntity baseEntity = (BaseEntity) params;
baseEntity.getParams().put(DATA_SCOPE, "");
} }
return null;
} }
} }

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId> <artifactId>ruoyi-common</artifactId>
<version>2.4.0</version> <version>3.2.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -31,5 +31,11 @@
<version>${dynamic-ds.version}</version> <version>${dynamic-ds.version}</version>
</dependency> </dependency>
<!-- SpringBoot Seata -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId> <artifactId>ruoyi-common</artifactId>
<version>2.4.0</version> <version>3.2.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -38,4 +38,9 @@ public @interface Log
* 是否保存请求的参数 * 是否保存请求的参数
*/ */
public boolean isSaveRequestData() default true; public boolean isSaveRequestData() default true;
/**
* 是否保存响应的参数
*/
public boolean isSaveResponseData() default true;
} }

View File

@@ -1,26 +1,22 @@
package com.ruoyi.common.log.aspect; package com.ruoyi.common.log.aspect;
import java.lang.reflect.Method;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator;
import java.util.Map; import java.util.Map;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.ruoyi.common.core.constant.CacheConstants; import com.ruoyi.common.core.utils.SecurityUtils;
import com.ruoyi.common.core.utils.ServletUtils; import com.ruoyi.common.core.utils.ServletUtils;
import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.utils.ip.IpUtils; import com.ruoyi.common.core.utils.ip.IpUtils;
@@ -43,21 +39,15 @@ public class LogAspect
@Autowired @Autowired
private AsyncLogService asyncLogService; private AsyncLogService asyncLogService;
// 配置织入点
@Pointcut("@annotation(com.ruoyi.common.log.annotation.Log)")
public void logPointCut()
{
}
/** /**
* 处理完请求后执行 * 处理完请求后执行
* *
* @param joinPoint 切点 * @param joinPoint 切点
*/ */
@AfterReturning(pointcut = "logPointCut()", returning = "jsonResult") @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult)
{ {
handleLog(joinPoint, null, jsonResult); handleLog(joinPoint, controllerLog, null, jsonResult);
} }
/** /**
@@ -66,35 +56,24 @@ public class LogAspect
* @param joinPoint 切点 * @param joinPoint 切点
* @param e 异常 * @param e 异常
*/ */
@AfterThrowing(value = "logPointCut()", throwing = "e") @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, Exception e) public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e)
{ {
handleLog(joinPoint, e, null); handleLog(joinPoint, controllerLog, e, null);
} }
protected void handleLog(final JoinPoint joinPoint, final Exception e, Object jsonResult) protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult)
{ {
try try
{ {
// 获得注解
Log controllerLog = getAnnotationLog(joinPoint);
if (controllerLog == null)
{
return;
}
// *========数据库日志=========*// // *========数据库日志=========*//
SysOperLog operLog = new SysOperLog(); SysOperLog operLog = new SysOperLog();
operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
// 请求的地址 // 请求的地址
String ip = IpUtils.getIpAddr(ServletUtils.getRequest()); String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
operLog.setOperIp(ip); operLog.setOperIp(ip);
// 返回参数
operLog.setJsonResult(JSON.toJSONString(jsonResult));
operLog.setOperUrl(ServletUtils.getRequest().getRequestURI()); operLog.setOperUrl(ServletUtils.getRequest().getRequestURI());
HttpServletRequest request = ServletUtils.getRequest(); String username = SecurityUtils.getUsername();
String username = request.getHeader(CacheConstants.DETAILS_USERNAME);
if (StringUtils.isNotBlank(username)) if (StringUtils.isNotBlank(username))
{ {
operLog.setOperName(username); operLog.setOperName(username);
@@ -112,7 +91,7 @@ public class LogAspect
// 设置请求方式 // 设置请求方式
operLog.setRequestMethod(ServletUtils.getRequest().getMethod()); operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
// 处理设置注解上的参数 // 处理设置注解上的参数
getControllerMethodDescription(joinPoint, controllerLog, operLog); getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
// 保存数据库 // 保存数据库
asyncLogService.saveSysLog(operLog); asyncLogService.saveSysLog(operLog);
} }
@@ -132,7 +111,7 @@ public class LogAspect
* @param operLog 操作日志 * @param operLog 操作日志
* @throws Exception * @throws Exception
*/ */
public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog) throws Exception public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception
{ {
// 设置action动作 // 设置action动作
operLog.setBusinessType(log.businessType().ordinal()); operLog.setBusinessType(log.businessType().ordinal());
@@ -146,6 +125,11 @@ public class LogAspect
// 获取参数的信息,传入到数据库中。 // 获取参数的信息,传入到数据库中。
setRequestValue(joinPoint, operLog); setRequestValue(joinPoint, operLog);
} }
// 是否需要保存response参数和值
if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult))
{
operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000));
}
} }
/** /**
@@ -164,22 +148,6 @@ public class LogAspect
} }
} }
/**
* 是否存在注解,如果存在就获取
*/
private Log getAnnotationLog(JoinPoint joinPoint) throws Exception
{
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method != null)
{
return method.getAnnotation(Log.class);
}
return null;
}
/** /**
* 参数拼装 * 参数拼装
*/ */
@@ -188,13 +156,13 @@ public class LogAspect
String params = ""; String params = "";
if (paramsArray != null && paramsArray.length > 0) if (paramsArray != null && paramsArray.length > 0)
{ {
for (int i = 0; i < paramsArray.length; i++) for (Object o : paramsArray)
{ {
if (!isFilterObject(paramsArray[i])) if (StringUtils.isNotNull(o) && !isFilterObject(o))
{ {
try try
{ {
Object jsonObj = JSON.toJSON(paramsArray[i]); Object jsonObj = JSON.toJSON(o);
params += jsonObj.toString() + " "; params += jsonObj.toString() + " ";
} }
catch (Exception e) catch (Exception e)
@@ -223,20 +191,21 @@ public class LogAspect
else if (Collection.class.isAssignableFrom(clazz)) else if (Collection.class.isAssignableFrom(clazz))
{ {
Collection collection = (Collection) o; Collection collection = (Collection) o;
for (Iterator iter = collection.iterator(); iter.hasNext();) for (Object value : collection)
{ {
return iter.next() instanceof MultipartFile; return value instanceof MultipartFile;
} }
} }
else if (Map.class.isAssignableFrom(clazz)) else if (Map.class.isAssignableFrom(clazz))
{ {
Map map = (Map) o; Map map = (Map) o;
for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) for (Object value : map.entrySet())
{ {
Map.Entry entry = (Map.Entry) iter.next(); Map.Entry entry = (Map.Entry) value;
return entry.getValue() instanceof MultipartFile; return entry.getValue() instanceof MultipartFile;
} }
} }
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse; return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
|| o instanceof BindingResult;
} }
} }

View File

@@ -3,6 +3,7 @@ package com.ruoyi.common.log.service;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.ruoyi.common.core.constant.SecurityConstants;
import com.ruoyi.system.api.RemoteLogService; import com.ruoyi.system.api.RemoteLogService;
import com.ruoyi.system.api.domain.SysOperLog; import com.ruoyi.system.api.domain.SysOperLog;
@@ -23,6 +24,6 @@ public class AsyncLogService
@Async @Async
public void saveSysLog(SysOperLog sysOperLog) public void saveSysLog(SysOperLog sysOperLog)
{ {
remoteLogService.saveLog(sysOperLog); remoteLogService.saveLog(sysOperLog, SecurityConstants.INNER);
} }
} }

View File

@@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId> <artifactId>ruoyi-common</artifactId>
<version>2.4.0</version> <version>3.2.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -8,8 +8,10 @@ import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
/** /**
* redis配置 * redis配置
@@ -21,7 +23,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
public class RedisConfig extends CachingConfigurerSupport public class RedisConfig extends CachingConfigurerSupport
{ {
@Bean @Bean
@SuppressWarnings(value = { "unchecked", "rawtypes", "deprecation" }) @SuppressWarnings(value = { "unchecked", "rawtypes" })
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
{ {
RedisTemplate<Object, Object> template = new RedisTemplate<>(); RedisTemplate<Object, Object> template = new RedisTemplate<>();
@@ -31,12 +33,17 @@ public class RedisConfig extends CachingConfigurerSupport
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
serializer.setObjectMapper(mapper); serializer.setObjectMapper(mapper);
template.setValueSerializer(serializer);
// 使用StringRedisSerializer来序列化和反序列化redis的key值 // 使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer()); template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
// Hash的key也采用StringRedisSerializer的序列化方式
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);
template.afterPropertiesSet(); template.afterPropertiesSet();
return template; return template;
} }

View File

@@ -74,6 +74,17 @@ public class RedisService
return redisTemplate.expire(key, timeout, unit); return redisTemplate.expire(key, timeout, unit);
} }
/**
* 判断 key是否存在
*
* @param key 键
* @return true 存在 false不存在
*/
public Boolean hasKey(String key)
{
return redisTemplate.hasKey(key);
}
/** /**
* 获得缓存的基本对象。 * 获得缓存的基本对象。
* *

View File

@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId> <artifactId>ruoyi-common</artifactId>
<version>2.4.0</version> <version>3.2.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -0,0 +1,19 @@
package com.ruoyi.common.security.annotation;
import java.lang.annotation.*;
/**
* 内部认证注解
*
* @author ruoyi
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface InnerAuth
{
/**
* 是否校验用户信息
*/
boolean isUser() default false;
}

View File

@@ -0,0 +1,51 @@
package com.ruoyi.common.security.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import com.ruoyi.common.core.constant.SecurityConstants;
import com.ruoyi.common.core.exception.InnerAuthException;
import com.ruoyi.common.core.utils.ServletUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.security.annotation.InnerAuth;
/**
* 内部服务调用验证处理
*
* @author ruoyi
*/
@Aspect
@Component
public class InnerAuthAspect implements Ordered
{
@Around("@annotation(innerAuth)")
public Object innerAround(ProceedingJoinPoint point, InnerAuth innerAuth) throws Throwable
{
String source = ServletUtils.getRequest().getHeader(SecurityConstants.FROM_SOURCE);
// 内部请求验证
if (!StringUtils.equals(SecurityConstants.INNER, source))
{
throw new InnerAuthException("没有内部访问权限,不允许访问");
}
String userid = ServletUtils.getRequest().getHeader(SecurityConstants.DETAILS_USER_ID);
String username = ServletUtils.getRequest().getHeader(SecurityConstants.DETAILS_USERNAME);
// 用户信息验证
if (innerAuth.isUser() && (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username)))
{
throw new InnerAuthException("没有设置用户信息,不允许访问 ");
}
return point.proceed();
}
/**
* 确保在权限认证aop执行前执行
*/
@Override
public int getOrder()
{
return Ordered.HIGHEST_PRECEDENCE + 1;
}
}

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