mirror of
				https://github.com/dromara/RuoYi-Vue-Plus.git
				synced 2025-10-31 14:23:45 +08:00 
			
		
		
		
	Compare commits
	
		
			405 Commits
		
	
	
		
			v5.2.2
			...
			e1e3843ec0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | e1e3843ec0 | ||
|  | 15905b7022 | ||
|  | 1c5ae2f168 | ||
|  | f29e0223a7 | ||
|  | 9fbe3cf399 | ||
|  | e4f1da30fc | ||
|  | 21deab4bf1 | ||
|  | d7d7dcbcf7 | ||
|  | 3f680385a9 | ||
|  | 7ecf4bbf1c | ||
|  | ae65985fbc | ||
|  | 2a34c3ebb2 | ||
|  | 3b46f8c8cf | ||
|  | 7c2efb1aef | ||
|  | ea25474529 | ||
|  | 33e1d34ce5 | ||
|  | 142fb33d81 | ||
|  | ee6c0388da | ||
|  | c171817d6a | ||
|  | 71dddee146 | ||
|  | d456ff64f1 | ||
|  | 9e78fcccf7 | ||
|  | 878cd7e9f0 | ||
|  | 5c9721cfac | ||
|  | 31502dccc7 | ||
|  | 538aa8d908 | ||
|  | 00003b2c57 | ||
|  | a2c238d466 | ||
|  | d89f147c54 | ||
|  | 53cf1b2013 | ||
|  | 564ab331d7 | ||
|  | a690ece164 | ||
|  | b50904c6ff | ||
|  | 70aa14ecf8 | ||
|  | c37b92978a | ||
|  | ef39ad7107 | ||
|  | 48d3ef9818 | ||
|  | 5bf901cdcd | ||
|  | 8e99dd306a | ||
|  | 07fdc240d7 | ||
|  | 023ceaaf91 | ||
|  | 9e551a0b2a | ||
|  | 7c3f3523ea | ||
|  | 40eac07789 | ||
|  | 5868fadbf5 | ||
|  | 124bcc4bba | ||
|  | e71d6fa983 | ||
|  | 7129ad4fac | ||
|  | 16923cc86a | ||
|  | 3761473967 | ||
|  | 34031cae8d | ||
|  | 26abb98747 | ||
|  | c2f67b4a77 | ||
|  | 4d925a4d62 | ||
|  | 4e62054bd1 | ||
|  | fe40d7db32 | ||
|  | c0eeafb5cd | ||
|  | 5626b97a19 | ||
|  | 0f95e9393d | ||
|  | 5de1ffff90 | ||
|  | 33698ee448 | ||
|  | 5e5d478cf2 | ||
|  | 778096d100 | ||
|  | bba163f7b4 | ||
|  | d4f792810e | ||
|  | d14ee59912 | ||
|  | be5d69d5a5 | ||
|  | 97c36674e4 | ||
|  | 1f1564fad9 | ||
|  | c79e053bea | ||
|  | 92e9ed771b | ||
|  | 800c6c8ff3 | ||
|  | 865627fdad | ||
|  | 192537672e | ||
|  | 384f9528e7 | ||
|  | 5fc76b6426 | ||
|  | 4d8a45204c | ||
|  | 2de9397db8 | ||
|  | bfc73ed214 | ||
|  | 8bf741fd5b | ||
|  | 460545a75e | ||
|  | a93b30ec91 | ||
|  | 34bac1add9 | ||
|  | f028cb76fc | ||
|  | 5a4be5fba1 | ||
|  | 23245b78ca | ||
|  | 13ac302525 | ||
|  | 96a62a3564 | ||
|  | 7adf702283 | ||
|  | 279c8e014a | ||
|  | b7517cbbd4 | ||
|  | 45eac02f4f | ||
|  | a6b7c3afe6 | ||
|  | e99e4f6c58 | ||
|  | bcb97bc406 | ||
|  | ad01406fc1 | ||
|  | 15eb08c065 | ||
|  | 2340556091 | ||
|  | 65c54184e8 | ||
|  | 9dcb7c6a12 | ||
|  | 0c6faa751a | ||
|  | b465cb34de | ||
|  | 21c12a791a | ||
|  | 723a0b6d9c | ||
|  | c4ef053958 | ||
|  | 055d1f3bb2 | ||
|  | fe27d8920a | ||
|  | df65670d3d | ||
|  | 2623d0b343 | ||
|  | c0e0b41d13 | ||
|  | 8763bfa3d3 | ||
|  | 71180584da | ||
|  | 319a89e320 | ||
|  | 0673cf8849 | ||
|  | b537899e62 | ||
|  | 7b679e60e0 | ||
|  | bb475a6088 | ||
|  | a217c495d1 | ||
|  | bdb86e2b3a | ||
|  | e8700ac44b | ||
|  | d80f6ab695 | ||
|  | 381be5a1a1 | ||
|  | 214cbac9a6 | ||
|  | 906a031172 | ||
|  | 236dd6e054 | ||
|  | eb17eb6559 | ||
|  | 2746af21f0 | ||
|  | 78abb617ce | ||
|  | 3c57c0e7f9 | ||
|  | 934bbe8bd7 | ||
|  | 718a010c0f | ||
|  | a87071b834 | ||
|  | 2c598f93ab | ||
|  | 0937093851 | ||
|  | b528f0bd14 | ||
|  | 7c2c82fc0a | ||
|  | ffe8b16ff3 | ||
|  | ecf7ebad53 | ||
|  | 3bf26cd509 | ||
|  | a671d4a8a8 | ||
|  | d9713d0f8c | ||
|  | aeaa33ebd3 | ||
|  | c64de03d27 | ||
|  | 2d99304396 | ||
|  | a22dc9537f | ||
|  | 6c28f8a0dd | ||
|  | c100168374 | ||
|  | 8636d8b3e8 | ||
|  | 37b2d648b1 | ||
|  | 27b4992f6e | ||
|  | 001297ca7a | ||
|  | 4f3cbc4bc2 | ||
|  | 008e02a406 | ||
|  | e8acfac091 | ||
|  | e1a26b0388 | ||
|  | a6fc47b4f6 | ||
|  | ce7f5121b0 | ||
|  | b38ca837d6 | ||
|  | a2714fb9f7 | ||
|  | 8c57d694c5 | ||
|  | 0ae521a7dc | ||
|  | 79ec3fd2c9 | ||
|  | 69b95b3e7a | ||
|  | 9d8b9fabbe | ||
|  | 875c9fa77c | ||
|  | 6bc2d9d4a7 | ||
|  | fe5a1f358d | ||
|  | 1972537176 | ||
|  | 8f21e9e2fd | ||
|  | 089a79002f | ||
|  | ece1dee990 | ||
|  | 652f5161a9 | ||
|  | 4ba4ea4fcc | ||
|  | 3444b50da6 | ||
|  | 62b7d96551 | ||
|  | cb71df8a42 | ||
|  | dd54cc972a | ||
|  | 6f14c91d30 | ||
|  | 6f0dd8dc89 | ||
|  | 7d82f954ac | ||
|  | 69130a76e4 | ||
|  | 9086d32bee | ||
|  | a680310f80 | ||
|  | a07880e1d0 | ||
|  | ae584d54a6 | ||
|  | e36e8f7758 | ||
|  | b47798ef19 | ||
|  | 55b1a67637 | ||
|  | d2b9cd2797 | ||
|  | 9a95c46578 | ||
|  | a58f72868a | ||
|  | 820db87604 | ||
|  | 254e61ab01 | ||
|  | ad53965626 | ||
|  | 4de9fa33b7 | ||
|  | 08e40b611b | ||
|  | 8bd2e27653 | ||
|  | d023510f7e | ||
|  | ec5ca0a08f | ||
|  | a46c798e01 | ||
|  | 41a3bdf73d | ||
|  | a7b83672ba | ||
|  | c2746c2392 | ||
|  | 492e7dab26 | ||
|  | 5480e419b6 | ||
|  | 251a617ecc | ||
|  | 36de389fa4 | ||
|  | f4f052deb4 | ||
|  | 3a0fbd45ae | ||
|  | f20c271972 | ||
|  | ad85fa2016 | ||
|  | 1a403361c9 | ||
|  | 9ad64521d3 | ||
|  | ff76df9ae0 | ||
|  | da1cd55c1d | ||
|  | a65baf5d67 | ||
|  | 489cb52976 | ||
|  | 591f86e4e9 | ||
|  | 9768023d38 | ||
|  | 60af92ed2d | ||
|  | 4d566071db | ||
|  | aface5ded1 | ||
|  | 7f6b71d938 | ||
|  | c8ed71d010 | ||
|  | df6649907d | ||
|  | 2fb1c99f56 | ||
|  | fcebda8987 | ||
|  | 0185a468bd | ||
|  | c566f2ae28 | ||
|  | 29c5ff89ba | ||
|  | ab3e4978b1 | ||
|  | 0027f671d2 | ||
|  | 06a8ab0ab2 | ||
|  | 4352b3fe4a | ||
|  | dd17246086 | ||
|  | d7b0dc91d5 | ||
|  | 4598d4d843 | ||
|  | 8a731efe0d | ||
|  | 59fd7eeeb3 | ||
|  | 552e543471 | ||
|  | b1badca062 | ||
|  | 9f34edee4f | ||
|  | d257de7882 | ||
|  | 4821902fdc | ||
|  | a1f82a7d08 | ||
|  | 9009a90ef2 | ||
|  | fc9c90ad99 | ||
|  | 9e613488f1 | ||
|  | 5e6cb0dd3c | ||
|  | 2bb787886d | ||
|  | ed6f8262c6 | ||
|  | 64d574cf06 | ||
|  | e0b0ffcb28 | ||
|  | c17225abb1 | ||
|  | 19aed0a1e4 | ||
|  | 14c1bde958 | ||
|  | eed929b9fe | ||
|  | eda67dd572 | ||
|  | fef2d5207b | ||
|  | 87294b41af | ||
|  | 99d9c516fc | ||
|  | 476c7a77c8 | ||
|  | 8dc4b5cf6b | ||
|  | eba6b48daf | ||
|  | 1aff46bc1c | ||
|  | e78684886d | ||
|  | 7f35925794 | ||
|  | 71a59d3e5f | ||
|  | 4af46a6045 | ||
|  | d194b39e57 | ||
|  | cd08f66c59 | ||
|  | ef919b9f3d | ||
|  | 847b158283 | ||
|  | dd2abd95c9 | ||
|  | b5908d52d7 | ||
|  | bf515042d0 | ||
|  | e94fccc784 | ||
|  | 6d45199592 | ||
|  | 57b3b329a8 | ||
|  | a0fc268bb9 | ||
|  | 631739733f | ||
|  | 2d2bd48963 | ||
|  | f6d9bec16c | ||
|  | 0f1118e03a | ||
|  | 009ac75229 | ||
|  | b64ac8d7f6 | ||
|  | 41240fc415 | ||
|  | a18e430056 | ||
|  | 1a993a7899 | ||
|  | d3b5220dc3 | ||
|  | 4f92c0317e | ||
|  | a098565c37 | ||
|  | 0d5fe5d91e | ||
|  | 5c66f3b90c | ||
|  | 63d22b9b33 | ||
|  | b4678b74ab | ||
|  | a82ed1e9dd | ||
|  | d2ffbfb80b | ||
|  | 5d61782a6c | ||
|  | 1b8c9fdaa9 | ||
|  | 90328ae79b | ||
|  | 9cdcbbccbf | ||
|  | b820a98c6c | ||
|  | 8e749c472a | ||
|  | 70a5077291 | ||
|  | 8a5d8cc9b9 | ||
|  | 0a0a16f969 | ||
|  | 73d5fbd085 | ||
|  | 9da3d25292 | ||
|  | 0385a444c2 | ||
|  | 7d856b030b | ||
|  | 8c0441b91a | ||
|  | baef55ae1b | ||
|  | 80e6943d2e | ||
|  | 2ffdd56301 | ||
|  | 53635da552 | ||
|  | 5e7fb88762 | ||
|  | d89727725b | ||
|  | 8cd7e3c924 | ||
|  | 0baf2c5861 | ||
|  | b0548f9a56 | ||
|  | 2d190cfb19 | ||
|  | 3b5858b114 | ||
|  | 112157ade0 | ||
|  | 8cd30ae86b | ||
|  | 9b6b288e73 | ||
|  | 1757e5519d | ||
|  | a21fa666fd | ||
|  | df9a57c379 | ||
|  | 5f31efd33e | ||
|  | 2a9f245b39 | ||
|  | 761586cc3c | ||
|  | 6731b7947b | ||
|  | ddc8bd1139 | ||
|  | 6ae9bbdb31 | ||
|  | 6c950c9569 | ||
|  | 3ce3ffca05 | ||
|  | d3ccc43d68 | ||
|  | 4c96440686 | ||
|  | 8d8d76364b | ||
|  | 82af98c205 | ||
|  | fdbe8c2395 | ||
|  | 59715b1e02 | ||
|  | f2ec530065 | ||
|  | ac89cb46f5 | ||
|  | abfa995a32 | ||
|  | 51cfbef887 | ||
|  | 7171ed1508 | ||
|  | 6727f3e6a4 | ||
|  | 81006c758a | ||
|  | 01025c4024 | ||
|  | 517277132e | ||
|  | dc5c92ee2d | ||
|  | 57e142e160 | ||
|  | c563afed59 | ||
|  | 556cf87f0e | ||
|  | e0a00cfe98 | ||
|  | d60774a7b8 | ||
|  | e3d40b75cb | ||
|  | eb4479e940 | ||
|  | 7e6d0a5c64 | ||
|  | 88dd4165cb | ||
|  | e96118c574 | ||
|  | 39c4e5fe55 | ||
|  | 46141dc114 | ||
|  | e3a25f2425 | ||
|  | 64289c16f3 | ||
|  | 6b05ddb576 | ||
|  | 88ee252fce | ||
|  | 2add7291ab | ||
|  | 501be029c6 | ||
|  | d86652dee1 | ||
|  | 272ca613ee | ||
|  | 74af811a3b | ||
|  | fc72b67090 | ||
|  | e33f76d710 | ||
|  | 1c3d594947 | ||
|  | 64d9b27310 | ||
|  | f3f3593cfe | ||
|  | e5e8e3ce7c | ||
|  | a7fd7ba72c | ||
|  | 020f090f4a | ||
|  | 5e3231d59b | ||
|  | b522bc015d | ||
|  | 6e64fd7fd7 | ||
|  | 8b44f5cdbc | ||
|  | f9b7d955aa | ||
|  | 6ea2a2fc51 | ||
|  | 760c8d7200 | ||
|  | 32ad28c3dc | ||
|  | 6886e9fd5b | ||
|  | f20130d3db | ||
|  | df9cc881f1 | ||
|  | 4044988afa | ||
|  | d3360e81b9 | ||
|  | df070b7d78 | ||
|  | 83dd98faf3 | ||
|  | 37f89f560f | ||
|  | 918ed0d6d0 | ||
|  | a3c9edde78 | ||
|  | cac0a4cd16 | ||
|  | 581b6e03d5 | ||
|  | 801cc584e5 | ||
|  | b82ff8617e | ||
|  | c87016c1af | 
| @@ -2,7 +2,7 @@ | |||||||
|   <configuration default="false" name="ruoyi-monitor-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker"> |   <configuration default="false" name="ruoyi-monitor-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker"> | ||||||
|     <deployment type="dockerfile"> |     <deployment type="dockerfile"> | ||||||
|       <settings> |       <settings> | ||||||
|         <option name="imageTag" value="ruoyi/ruoyi-monitor-admin:5.2.2" /> |         <option name="imageTag" value="ruoyi/ruoyi-monitor-admin:5.3.1" /> | ||||||
|         <option name="buildOnly" value="true" /> |         <option name="buildOnly" value="true" /> | ||||||
|         <option name="sourceFilePath" value="ruoyi-extend/ruoyi-monitor-admin/Dockerfile" /> |         <option name="sourceFilePath" value="ruoyi-extend/ruoyi-monitor-admin/Dockerfile" /> | ||||||
|       </settings> |       </settings> | ||||||
|   | |||||||
| @@ -1,8 +1,8 @@ | |||||||
| <component name="ProjectRunConfigurationManager"> | <component name="ProjectRunConfigurationManager"> | ||||||
|   <configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="演示机"> |   <configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker"> | ||||||
|     <deployment type="dockerfile"> |     <deployment type="dockerfile"> | ||||||
|       <settings> |       <settings> | ||||||
|         <option name="imageTag" value="ruoyi/ruoyi-server:5.2.2" /> |         <option name="imageTag" value="ruoyi/ruoyi-server:5.3.1" /> | ||||||
|         <option name="buildOnly" value="true" /> |         <option name="buildOnly" value="true" /> | ||||||
|         <option name="sourceFilePath" value="ruoyi-admin/Dockerfile" /> |         <option name="sourceFilePath" value="ruoyi-admin/Dockerfile" /> | ||||||
|       </settings> |       </settings> | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ | |||||||
|   <configuration default="false" name="ruoyi-snailjob-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker"> |   <configuration default="false" name="ruoyi-snailjob-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker"> | ||||||
|     <deployment type="dockerfile"> |     <deployment type="dockerfile"> | ||||||
|       <settings> |       <settings> | ||||||
|         <option name="imageTag" value="ruoyi/ruoyi-snailjob-server:5.2.2" /> |         <option name="imageTag" value="ruoyi/ruoyi-snailjob-server:5.3.1" /> | ||||||
|         <option name="buildOnly" value="true" /> |         <option name="buildOnly" value="true" /> | ||||||
|         <option name="sourceFilePath" value="ruoyi-extend/ruoyi-snailjob-server/Dockerfile" /> |         <option name="sourceFilePath" value="ruoyi-extend/ruoyi-snailjob-server/Dockerfile" /> | ||||||
|       </settings> |       </settings> | ||||||
|   | |||||||
							
								
								
									
										21
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								README.md
									
									
									
									
									
								
							| @@ -6,22 +6,24 @@ | |||||||
|  |  | ||||||
| [](https://gitee.com/dromara/RuoYi-Vue-Plus) | [](https://gitee.com/dromara/RuoYi-Vue-Plus) | ||||||
| [](https://github.com/dromara/RuoYi-Vue-Plus) | [](https://github.com/dromara/RuoYi-Vue-Plus) | ||||||
| [](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/master/LICENSE) | [](https://gitcode.com/dromara/RuoYi-Vue-Plus) | ||||||
|  | [](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/5.X/LICENSE) | ||||||
| [](https://www.jetbrains.com/?from=RuoYi-Vue-Plus) | [](https://www.jetbrains.com/?from=RuoYi-Vue-Plus) | ||||||
| <br> | <br> | ||||||
| [](https://gitee.com/dromara/RuoYi-Vue-Plus) | [](https://gitee.com/dromara/RuoYi-Vue-Plus) | ||||||
| []() | []() | ||||||
| []() | []() | ||||||
| []() | []() | ||||||
|  |  | ||||||
| > RuoYi-Vue-Plus 是重写 RuoYi-Vue 针对 `分布式集群与多租户` 场景全方位升级(不兼容原框架) | > Dromara RuoYi-Vue-Plus 是重写 RuoYi-Vue 针对 `分布式集群与多租户` 场景全方位升级(不兼容原框架) | ||||||
|  |  | ||||||
| > 项目代码、文档 均开源免费可商用 遵循开源协议在项目中保留开源协议文件即可<br> | > 项目代码、文档 均开源免费可商用 遵循开源协议在项目中保留开源协议文件即可<br> | ||||||
| 活到老写到老 为兴趣而开源 为学习而开源 为让大家真正可以学到技术而开源 | 活到老写到老 为兴趣而开源 为学习而开源 为让大家真正可以学到技术而开源 | ||||||
|  |  | ||||||
| > 系统演示: [传送门](https://plus-doc.dromara.org/#/common/demo_system) | > 系统演示: [传送门](https://plus-doc.dromara.org/#/common/demo_system) | ||||||
|  |  | ||||||
| > 前端项目地址: [plus-ui](https://gitee.com/JavaLionLi/plus-ui) | > 官方前端项目地址: [plus-ui](https://gitee.com/JavaLionLi/plus-ui)<br> | ||||||
|  | > 成员前端项目地址: 基于vben5 [ruoyi-plus-vben5](https://gitee.com/dapppp/ruoyi-plus-vben5) | ||||||
|  |  | ||||||
| > 文档地址: [plus-doc](https://plus-doc.dromara.org) | > 文档地址: [plus-doc](https://plus-doc.dromara.org) | ||||||
|  |  | ||||||
| @@ -31,6 +33,8 @@ MaxKey 业界领先单点登录产品 - https://gitee.com/dromara/MaxKey <br> | |||||||
| CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow <br> | CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow <br> | ||||||
| 数舵科技 软件定制开发APP小程序等 - http://www.shuduokeji.com/ <br> | 数舵科技 软件定制开发APP小程序等 - http://www.shuduokeji.com/ <br> | ||||||
| 引迈信息 软件开发平台 - https://www.jnpfsoft.com/index.html?from=plus-doc <br> | 引迈信息 软件开发平台 - https://www.jnpfsoft.com/index.html?from=plus-doc <br> | ||||||
|  | <font color="red">**启山商城系统 多租户商城源码可免费商用可二次开发 - https://www.73app.cn/** </font><br> | ||||||
|  | Mall4J 高质量Java商城系统 - https://www.mall4j.com/cn/?statId=11 <br> | ||||||
| [如何成为赞助商 加群联系作者详谈](https://plus-doc.dromara.org/#/common/add_group) | [如何成为赞助商 加群联系作者详谈](https://plus-doc.dromara.org/#/common/add_group) | ||||||
|  |  | ||||||
| # 本框架与RuoYi的功能差异 | # 本框架与RuoYi的功能差异 | ||||||
| @@ -72,7 +76,7 @@ CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow <br> | |||||||
| | 邮件          | 采用 mail-api 通用协议支持大部分邮件厂商                                                                                         | 不支持                                                                                | | | 邮件          | 采用 mail-api 通用协议支持大部分邮件厂商                                                                                         | 不支持                                                                                | | ||||||
| | 接口文档        | 采用 SpringDoc、javadoc 无注解零入侵基于java注释<br/>只需把注释写好 无需再写一大堆的文档注解了                                                     | 采用 Springfox 已停止维护 需要编写大量的注解来支持文档生成                                                |  | | 接口文档        | 采用 SpringDoc、javadoc 无注解零入侵基于java注释<br/>只需把注释写好 无需再写一大堆的文档注解了                                                     | 采用 Springfox 已停止维护 需要编写大量的注解来支持文档生成                                                |  | ||||||
| | 校验框架        | 采用 Validation 支持注解与工具类校验 注解支持国际化                                                                                  | 仅支持注解 且注解不支持国际化                                                                    | | | 校验框架        | 采用 Validation 支持注解与工具类校验 注解支持国际化                                                                                  | 仅支持注解 且注解不支持国际化                                                                    | | ||||||
| | Excel框架     | 采用 Alibaba EasyExcel 基于插件化<br/>框架对其增加了很多功能 例如 自动合并相同内容 自动排列布局 字典翻译等                                               | 基于 POI 手写实现 功能有限 复杂 扩展性差                                                           | | | Excel框架     | 采用 FastExcel(原Alibaba EasyExcel) 基于插件化<br/>框架对其增加了很多功能 例如 自动合并相同内容 自动排列布局 字典翻译等                                   | 基于 POI 手写实现 功能有限 复杂 扩展性差                                                           | | ||||||
| | 工作流支持       | 支持各种复杂审批 转办 委派 加减签 会签 或签 票签 等功能                                                                                   | 无                                                                                  | | | 工作流支持       | 支持各种复杂审批 转办 委派 加减签 会签 或签 票签 等功能                                                                                   | 无                                                                                  | | ||||||
| | 工具类框架       | 采用 Hutool、Lombok 上百种工具覆盖90%的使用需求 基于注解自动生成 get set 等简化框架大量代码                                                       | 手写工具稳定性差易出问题 工具数量有限 代码臃肿需自己手写 get set 等                                            |  | | 工具类框架       | 采用 Hutool、Lombok 上百种工具覆盖90%的使用需求 基于注解自动生成 get set 等简化框架大量代码                                                       | 手写工具稳定性差易出问题 工具数量有限 代码臃肿需自己手写 get set 等                                            |  | ||||||
| | 监控框架        | 采用 SpringBoot-Admin 基于SpringBoot官方 actuator 探针机制<br/>实时监控服务状态 框架还为其扩展了在线日志查看监控                                    | 无                                                                                  |  | | 监控框架        | 采用 SpringBoot-Admin 基于SpringBoot官方 actuator 探针机制<br/>实时监控服务状态 框架还为其扩展了在线日志查看监控                                    | 无                                                                                  |  | ||||||
| @@ -110,7 +114,6 @@ CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow <br> | |||||||
| | 系统接口   | 根据业务代码自动生成相关的api接口文档                                                 | 支持  | 支持               | | | 系统接口   | 根据业务代码自动生成相关的api接口文档                                                 | 支持  | 支持               | | ||||||
| | 服务监控   | 监视集群系统CPU、内存、磁盘、堆栈、在线日志、Spring相关配置等                                  | 支持  | 仅支持单机CPU、内存、磁盘监控 | | | 服务监控   | 监视集群系统CPU、内存、磁盘、堆栈、在线日志、Spring相关配置等                                  | 支持  | 仅支持单机CPU、内存、磁盘监控 | | ||||||
| | 缓存监控   | 对系统的缓存信息查询,命令统计等。                                                    | 支持  | 支持               | | | 缓存监控   | 对系统的缓存信息查询,命令统计等。                                                    | 支持  | 支持               | | ||||||
| | 在线构建器  | 拖动表单元素生成相应的HTML代码。                                                   | 支持  | 支持               | |  | ||||||
| | 使用案例   | 系统的一些功能案例                                                            | 支持  | 不支持              | | | 使用案例   | 系统的一些功能案例                                                            | 支持  | 不支持              | | ||||||
|  |  | ||||||
| ## 参考文档 | ## 参考文档 | ||||||
| @@ -165,8 +168,8 @@ CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow <br> | |||||||
| |  |  | | |  |  | | ||||||
| |  |  | | |  |  | | ||||||
| |  |  | | |  |  | | ||||||
| |  |  | | |  |  | | ||||||
| |  |  | | |  |  | | ||||||
| |  |  | | |  |  | | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										119
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										119
									
								
								pom.xml
									
									
									
									
									
								
							| @@ -10,56 +10,55 @@ | |||||||
|  |  | ||||||
|     <name>RuoYi-Vue-Plus</name> |     <name>RuoYi-Vue-Plus</name> | ||||||
|     <url>https://gitee.com/dromara/RuoYi-Vue-Plus</url> |     <url>https://gitee.com/dromara/RuoYi-Vue-Plus</url> | ||||||
|     <description>RuoYi-Vue-Plus多租户管理系统</description> |     <description>Dromara RuoYi-Vue-Plus多租户管理系统</description> | ||||||
|  |  | ||||||
|     <properties> |     <properties> | ||||||
|         <revision>5.2.2</revision> |         <revision>5.3.1</revision> | ||||||
|         <spring-boot.version>3.2.9</spring-boot.version> |         <spring-boot.version>3.4.5</spring-boot.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>17</java.version> |         <java.version>17</java.version> | ||||||
|         <mybatis.version>3.5.16</mybatis.version> |         <mybatis.version>3.5.16</mybatis.version> | ||||||
|         <springdoc.version>2.6.0</springdoc.version> |         <springdoc.version>2.8.5</springdoc.version> | ||||||
|         <therapi-javadoc.version>0.15.0</therapi-javadoc.version> |         <therapi-javadoc.version>0.15.0</therapi-javadoc.version> | ||||||
|         <easyexcel.version>4.0.2</easyexcel.version> |         <fastexcel.version>1.2.0</fastexcel.version> | ||||||
|         <velocity.version>2.3</velocity.version> |         <velocity.version>2.3</velocity.version> | ||||||
|         <satoken.version>1.38.0</satoken.version> |         <satoken.version>1.42.0</satoken.version> | ||||||
|         <mybatis-plus.version>3.5.7</mybatis-plus.version> |         <mybatis-plus.version>3.5.11</mybatis-plus.version> | ||||||
|         <p6spy.version>3.9.1</p6spy.version> |         <p6spy.version>3.9.1</p6spy.version> | ||||||
|         <hutool.version>5.8.31</hutool.version> |         <hutool.version>5.8.35</hutool.version> | ||||||
|         <okhttp.version>4.10.0</okhttp.version> |         <spring-boot-admin.version>3.4.5</spring-boot-admin.version> | ||||||
|         <spring-boot-admin.version>3.2.3</spring-boot-admin.version> |         <redisson.version>3.45.1</redisson.version> | ||||||
|         <redisson.version>3.34.1</redisson.version> |  | ||||||
|         <lock4j.version>2.2.7</lock4j.version> |         <lock4j.version>2.2.7</lock4j.version> | ||||||
|         <dynamic-ds.version>4.3.1</dynamic-ds.version> |         <dynamic-ds.version>4.3.1</dynamic-ds.version> | ||||||
|         <snailjob.version>1.1.2</snailjob.version> |         <snailjob.version>1.4.0</snailjob.version> | ||||||
|         <mapstruct-plus.version>1.4.4</mapstruct-plus.version> |         <mapstruct-plus.version>1.4.6</mapstruct-plus.version> | ||||||
|         <mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version> |         <mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version> | ||||||
|         <lombok.version>1.18.34</lombok.version> |         <lombok.version>1.18.36</lombok.version> | ||||||
|         <bouncycastle.version>1.76</bouncycastle.version> |         <bouncycastle.version>1.80</bouncycastle.version> | ||||||
|         <justauth.version>1.16.6</justauth.version> |         <justauth.version>1.16.7</justauth.version> | ||||||
|         <!-- 离线IP地址定位库 --> |         <!-- 离线IP地址定位库 --> | ||||||
|         <ip2region.version>2.7.0</ip2region.version> |         <ip2region.version>2.7.0</ip2region.version> | ||||||
|         <undertow.version>2.3.15.Final</undertow.version> |  | ||||||
|  |  | ||||||
|         <!-- OSS 配置 --> |         <!-- OSS 配置 --> | ||||||
|         <aws.sdk.version>2.25.15</aws.sdk.version> |         <aws.sdk.version>2.28.22</aws.sdk.version> | ||||||
|         <aws.crt.version>0.29.13</aws.crt.version> |  | ||||||
|         <!-- SMS 配置 --> |         <!-- SMS 配置 --> | ||||||
|         <sms4j.version>3.3.2</sms4j.version> |         <sms4j.version>3.3.4</sms4j.version> | ||||||
|         <!-- 限制框架中的fastjson版本 --> |         <!-- 限制框架中的fastjson版本 --> | ||||||
|         <fastjson.version>1.2.83</fastjson.version> |         <fastjson.version>1.2.83</fastjson.version> | ||||||
|         <!-- 面向运行时的D-ORM依赖 --> |         <!-- 面向运行时的D-ORM依赖 --> | ||||||
|         <anyline.version>8.7.2-20240808</anyline.version> |         <anyline.version>8.7.2-20250101</anyline.version> | ||||||
|         <!--工作流配置--> |         <!--工作流配置--> | ||||||
|         <flowable.version>7.0.1</flowable.version> |         <warm-flow.version>1.6.10</warm-flow.version> | ||||||
|  |  | ||||||
|         <!-- 插件版本 --> |         <!-- 插件版本 --> | ||||||
|         <maven-jar-plugin.version>3.2.2</maven-jar-plugin.version> |         <maven-jar-plugin.version>3.2.2</maven-jar-plugin.version> | ||||||
|         <maven-war-plugin.version>3.2.2</maven-war-plugin.version> |         <maven-war-plugin.version>3.2.2</maven-war-plugin.version> | ||||||
|         <maven-compiler-plugin.verison>3.11.0</maven-compiler-plugin.verison> |         <maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version> | ||||||
|         <maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version> |         <maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version> | ||||||
|         <flatten-maven-plugin.version>1.3.0</flatten-maven-plugin.version> |         <flatten-maven-plugin.version>1.3.0</flatten-maven-plugin.version> | ||||||
|  |         <!-- 打包默认跳过测试 --> | ||||||
|  |         <skipTests>true</skipTests> | ||||||
|     </properties> |     </properties> | ||||||
|  |  | ||||||
|     <profiles> |     <profiles> | ||||||
| @@ -69,6 +68,8 @@ | |||||||
|                 <!-- 环境标识,需要与配置文件的名称相对应 --> |                 <!-- 环境标识,需要与配置文件的名称相对应 --> | ||||||
|                 <profiles.active>local</profiles.active> |                 <profiles.active>local</profiles.active> | ||||||
|                 <logging.level>info</logging.level> |                 <logging.level>info</logging.level> | ||||||
|  |                 <monitor.username>ruoyi</monitor.username> | ||||||
|  |                 <monitor.password>123456</monitor.password> | ||||||
|             </properties> |             </properties> | ||||||
|         </profile> |         </profile> | ||||||
|         <profile> |         <profile> | ||||||
| @@ -77,6 +78,8 @@ | |||||||
|                 <!-- 环境标识,需要与配置文件的名称相对应 --> |                 <!-- 环境标识,需要与配置文件的名称相对应 --> | ||||||
|                 <profiles.active>dev</profiles.active> |                 <profiles.active>dev</profiles.active> | ||||||
|                 <logging.level>info</logging.level> |                 <logging.level>info</logging.level> | ||||||
|  |                 <monitor.username>ruoyi</monitor.username> | ||||||
|  |                 <monitor.password>123456</monitor.password> | ||||||
|             </properties> |             </properties> | ||||||
|             <activation> |             <activation> | ||||||
|                 <!-- 默认环境 --> |                 <!-- 默认环境 --> | ||||||
| @@ -88,6 +91,8 @@ | |||||||
|             <properties> |             <properties> | ||||||
|                 <profiles.active>prod</profiles.active> |                 <profiles.active>prod</profiles.active> | ||||||
|                 <logging.level>warn</logging.level> |                 <logging.level>warn</logging.level> | ||||||
|  |                 <monitor.username>ruoyi</monitor.username> | ||||||
|  |                 <monitor.password>123456</monitor.password> | ||||||
|             </properties> |             </properties> | ||||||
|         </profile> |         </profile> | ||||||
|     </profiles> |     </profiles> | ||||||
| @@ -114,12 +119,16 @@ | |||||||
|                 <scope>import</scope> |                 <scope>import</scope> | ||||||
|             </dependency> |             </dependency> | ||||||
|  |  | ||||||
|  |             <!-- Warm-Flow国产工作流引擎, 在线文档:http://warm-flow.cn/ --> | ||||||
|             <dependency> |             <dependency> | ||||||
|                 <groupId>org.flowable</groupId> |                 <groupId>org.dromara.warm</groupId> | ||||||
|                 <artifactId>flowable-bom</artifactId> |                 <artifactId>warm-flow-mybatis-plus-sb3-starter</artifactId> | ||||||
|                 <version>${flowable.version}</version> |                 <version>${warm-flow.version}</version> | ||||||
|                 <type>pom</type> |             </dependency> | ||||||
|                 <scope>import</scope> |             <dependency> | ||||||
|  |                 <groupId>org.dromara.warm</groupId> | ||||||
|  |                 <artifactId>warm-flow-plugin-ui-sb-web</artifactId> | ||||||
|  |                 <version>${warm-flow.version}</version> | ||||||
|             </dependency> |             </dependency> | ||||||
|  |  | ||||||
|             <!-- JustAuth 的依赖配置--> |             <!-- JustAuth 的依赖配置--> | ||||||
| @@ -157,9 +166,9 @@ | |||||||
|             </dependency> |             </dependency> | ||||||
|  |  | ||||||
|             <dependency> |             <dependency> | ||||||
|                 <groupId>com.alibaba</groupId> |                 <groupId>cn.idev.excel</groupId> | ||||||
|                 <artifactId>easyexcel</artifactId> |                 <artifactId>fastexcel</artifactId> | ||||||
|                 <version>${easyexcel.version}</version> |                 <version>${fastexcel.version}</version> | ||||||
|             </dependency> |             </dependency> | ||||||
|  |  | ||||||
|             <!-- velocity代码生成使用模板 --> |             <!-- velocity代码生成使用模板 --> | ||||||
| @@ -212,6 +221,12 @@ | |||||||
|                 <version>${mybatis-plus.version}</version> |                 <version>${mybatis-plus.version}</version> | ||||||
|             </dependency> |             </dependency> | ||||||
|  |  | ||||||
|  |             <dependency> | ||||||
|  |                 <groupId>com.baomidou</groupId> | ||||||
|  |                 <artifactId>mybatis-plus-jsqlparser</artifactId> | ||||||
|  |                 <version>${mybatis-plus.version}</version> | ||||||
|  |             </dependency> | ||||||
|  |  | ||||||
|             <dependency> |             <dependency> | ||||||
|                 <groupId>com.baomidou</groupId> |                 <groupId>com.baomidou</groupId> | ||||||
|                 <artifactId>mybatis-plus-annotation</artifactId> |                 <artifactId>mybatis-plus-annotation</artifactId> | ||||||
| @@ -225,30 +240,24 @@ | |||||||
|                 <version>${p6spy.version}</version> |                 <version>${p6spy.version}</version> | ||||||
|             </dependency> |             </dependency> | ||||||
|  |  | ||||||
|             <dependency> |  | ||||||
|                 <groupId>com.squareup.okhttp3</groupId> |  | ||||||
|                 <artifactId>okhttp</artifactId> |  | ||||||
|                 <version>${okhttp.version}</version> |  | ||||||
|             </dependency> |  | ||||||
|  |  | ||||||
|             <!--  AWS SDK for Java 2.x  --> |             <!--  AWS SDK for Java 2.x  --> | ||||||
|             <dependency> |             <dependency> | ||||||
|                 <groupId>software.amazon.awssdk</groupId> |                 <groupId>software.amazon.awssdk</groupId> | ||||||
|                 <artifactId>s3</artifactId> |                 <artifactId>s3</artifactId> | ||||||
|                 <version>${aws.sdk.version}</version> |                 <version>${aws.sdk.version}</version> | ||||||
|             </dependency> |             </dependency> | ||||||
|             <!-- 使用AWS基于 CRT 的 S3 客户端 --> |  | ||||||
|             <dependency> |  | ||||||
|                 <groupId>software.amazon.awssdk.crt</groupId> |  | ||||||
|                 <artifactId>aws-crt</artifactId> |  | ||||||
|                 <version>${aws.crt.version}</version> |  | ||||||
|             </dependency> |  | ||||||
|             <!-- 基于 AWS CRT 的 S3 客户端的性能增强的 S3 传输管理器 --> |             <!-- 基于 AWS CRT 的 S3 客户端的性能增强的 S3 传输管理器 --> | ||||||
|             <dependency> |             <dependency> | ||||||
|                 <groupId>software.amazon.awssdk</groupId> |                 <groupId>software.amazon.awssdk</groupId> | ||||||
|                 <artifactId>s3-transfer-manager</artifactId> |                 <artifactId>s3-transfer-manager</artifactId> | ||||||
|                 <version>${aws.sdk.version}</version> |                 <version>${aws.sdk.version}</version> | ||||||
|             </dependency> |             </dependency> | ||||||
|  |             <!-- 将基于 Netty 的 HTTP 客户端从类路径中移除 --> | ||||||
|  |             <dependency> | ||||||
|  |                 <groupId>software.amazon.awssdk</groupId> | ||||||
|  |                 <artifactId>netty-nio-client</artifactId> | ||||||
|  |                 <version>${aws.sdk.version}</version> | ||||||
|  |             </dependency> | ||||||
|             <!--短信sms4j--> |             <!--短信sms4j--> | ||||||
|             <dependency> |             <dependency> | ||||||
|                 <groupId>org.dromara.sms4j</groupId> |                 <groupId>org.dromara.sms4j</groupId> | ||||||
| @@ -312,28 +321,6 @@ | |||||||
|                 <version>${ip2region.version}</version> |                 <version>${ip2region.version}</version> | ||||||
|             </dependency> |             </dependency> | ||||||
|  |  | ||||||
|             <dependency> |  | ||||||
|                 <groupId>io.undertow</groupId> |  | ||||||
|                 <artifactId>undertow-core</artifactId> |  | ||||||
|                 <version>${undertow.version}</version> |  | ||||||
|             </dependency> |  | ||||||
|             <dependency> |  | ||||||
|                 <groupId>io.undertow</groupId> |  | ||||||
|                 <artifactId>undertow-servlet</artifactId> |  | ||||||
|                 <version>${undertow.version}</version> |  | ||||||
|             </dependency> |  | ||||||
|             <dependency> |  | ||||||
|                 <groupId>io.undertow</groupId> |  | ||||||
|                 <artifactId>undertow-websockets-jsr</artifactId> |  | ||||||
|                 <version>${undertow.version}</version> |  | ||||||
|             </dependency> |  | ||||||
|  |  | ||||||
|             <dependency> |  | ||||||
|                 <artifactId>commons-compress</artifactId> |  | ||||||
|                 <groupId>org.apache.commons</groupId> |  | ||||||
|                 <version>1.26.2</version> |  | ||||||
|             </dependency> |  | ||||||
|  |  | ||||||
|             <dependency> |             <dependency> | ||||||
|                 <groupId>com.alibaba</groupId> |                 <groupId>com.alibaba</groupId> | ||||||
|                 <artifactId>fastjson</artifactId> |                 <artifactId>fastjson</artifactId> | ||||||
| @@ -387,7 +374,7 @@ | |||||||
|             <plugin> |             <plugin> | ||||||
|                 <groupId>org.apache.maven.plugins</groupId> |                 <groupId>org.apache.maven.plugins</groupId> | ||||||
|                 <artifactId>maven-compiler-plugin</artifactId> |                 <artifactId>maven-compiler-plugin</artifactId> | ||||||
|                 <version>${maven-compiler-plugin.verison}</version> |                 <version>${maven-compiler-plugin.version}</version> | ||||||
|                 <configuration> |                 <configuration> | ||||||
|                     <source>${java.version}</source> |                     <source>${java.version}</source> | ||||||
|                     <target>${java.version}</target> |                     <target>${java.version}</target> | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| # 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/ | # 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/ | ||||||
| FROM bellsoft/liberica-openjdk-debian:17.0.11-cds | FROM bellsoft/liberica-openjdk-debian:17.0.11-cds | ||||||
| #FROM bellsoft/liberica-openjdk-debian:21.0.3-cds | #FROM bellsoft/liberica-openjdk-debian:21.0.5-cds | ||||||
| #FROM findepi/graalvm:java17-native | #FROM findepi/graalvm:java17-native | ||||||
|  |  | ||||||
| LABEL maintainer="Lion Li" | LABEL maintainer="Lion Li" | ||||||
| @@ -11,13 +11,20 @@ RUN mkdir -p /ruoyi/server/logs \ | |||||||
|  |  | ||||||
| WORKDIR /ruoyi/server | WORKDIR /ruoyi/server | ||||||
|  |  | ||||||
| ENV SERVER_PORT=8080 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS="" | ENV SERVER_PORT=8080 SNAIL_PORT=28080 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS="" | ||||||
|  |  | ||||||
| EXPOSE ${SERVER_PORT} | EXPOSE ${SERVER_PORT} | ||||||
|  | # 暴露 snail job 客户端端口 用于定时任务调度中心通信 | ||||||
|  | EXPOSE ${SNAIL_PORT} | ||||||
|  |  | ||||||
| ADD ./target/ruoyi-admin.jar ./app.jar | ADD ./target/ruoyi-admin.jar ./app.jar | ||||||
|  | # 工作流字体文件 | ||||||
|  | ADD ./zhFonts/ /usr/share/fonts/zhFonts/ | ||||||
|  |  | ||||||
|  | SHELL ["/bin/bash", "-c"] | ||||||
|  |  | ||||||
| ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \ | ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \ | ||||||
|  |            -Dsnail-job.port=${SNAIL_PORT} \ | ||||||
|            # 应用名称 如果想区分集群节点监控 改成不同的名称即可 |            # 应用名称 如果想区分集群节点监控 改成不同的名称即可 | ||||||
|            #-Dskywalking.agent.service_name=ruoyi-server \ |            #-Dskywalking.agent.service_name=ruoyi-server \ | ||||||
|            #-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar \ |            #-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar \ | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ package org.dromara.web.controller; | |||||||
|  |  | ||||||
| import cn.dev33.satoken.annotation.SaIgnore; | import cn.dev33.satoken.annotation.SaIgnore; | ||||||
| import cn.dev33.satoken.exception.NotLoginException; | import cn.dev33.satoken.exception.NotLoginException; | ||||||
|  | import cn.dev33.satoken.stp.StpUtil; | ||||||
| import cn.hutool.core.codec.Base64; | import cn.hutool.core.codec.Base64; | ||||||
| import cn.hutool.core.collection.CollUtil; | import cn.hutool.core.collection.CollUtil; | ||||||
| import cn.hutool.core.util.ObjectUtil; | import cn.hutool.core.util.ObjectUtil; | ||||||
| @@ -12,7 +13,7 @@ import me.zhyd.oauth.model.AuthResponse; | |||||||
| import me.zhyd.oauth.model.AuthUser; | import me.zhyd.oauth.model.AuthUser; | ||||||
| import me.zhyd.oauth.request.AuthRequest; | import me.zhyd.oauth.request.AuthRequest; | ||||||
| import me.zhyd.oauth.utils.AuthStateUtils; | import me.zhyd.oauth.utils.AuthStateUtils; | ||||||
| import org.dromara.common.core.constant.UserConstants; | import org.dromara.common.core.constant.SystemConstants; | ||||||
| import org.dromara.common.core.domain.R; | import org.dromara.common.core.domain.R; | ||||||
| import org.dromara.common.core.domain.model.LoginBody; | import org.dromara.common.core.domain.model.LoginBody; | ||||||
| import org.dromara.common.core.domain.model.RegisterBody; | import org.dromara.common.core.domain.model.RegisterBody; | ||||||
| @@ -92,7 +93,7 @@ public class AuthController { | |||||||
|         if (ObjectUtil.isNull(client) || !StringUtils.contains(client.getGrantType(), grantType)) { |         if (ObjectUtil.isNull(client) || !StringUtils.contains(client.getGrantType(), grantType)) { | ||||||
|             log.info("客户端id: {} 认证类型:{} 异常!.", clientId, grantType); |             log.info("客户端id: {} 认证类型:{} 异常!.", clientId, grantType); | ||||||
|             return R.fail(MessageUtils.message("auth.grant.type.error")); |             return R.fail(MessageUtils.message("auth.grant.type.error")); | ||||||
|         } else if (!UserConstants.NORMAL.equals(client.getStatus())) { |         } else if (!SystemConstants.NORMAL.equals(client.getStatus())) { | ||||||
|             return R.fail(MessageUtils.message("auth.grant.type.blocked")); |             return R.fail(MessageUtils.message("auth.grant.type.blocked")); | ||||||
|         } |         } | ||||||
|         // 校验租户 |         // 校验租户 | ||||||
| @@ -111,7 +112,7 @@ public class AuthController { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 第三方登录请求 |      * 获取跳转URL | ||||||
|      * |      * | ||||||
|      * @param source 登录来源 |      * @param source 登录来源 | ||||||
|      * @return 结果 |      * @return 结果 | ||||||
| @@ -133,13 +134,15 @@ public class AuthController { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 第三方登录回调业务处理 绑定授权 |      * 前端回调绑定授权(需要token) | ||||||
|      * |      * | ||||||
|      * @param loginBody 请求体 |      * @param loginBody 请求体 | ||||||
|      * @return 结果 |      * @return 结果 | ||||||
|      */ |      */ | ||||||
|     @PostMapping("/social/callback") |     @PostMapping("/social/callback") | ||||||
|     public R<Void> socialCallback(@RequestBody SocialLoginBody loginBody) { |     public R<Void> socialCallback(@RequestBody SocialLoginBody loginBody) { | ||||||
|  |         // 校验token | ||||||
|  |         StpUtil.checkLogin(); | ||||||
|         // 获取第三方登录信息 |         // 获取第三方登录信息 | ||||||
|         AuthResponse<AuthUser> response = SocialUtils.loginAuth( |         AuthResponse<AuthUser> response = SocialUtils.loginAuth( | ||||||
|                 loginBody.getSource(), loginBody.getSocialCode(), |                 loginBody.getSource(), loginBody.getSocialCode(), | ||||||
| @@ -155,12 +158,14 @@ public class AuthController { | |||||||
|  |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 取消授权 |      * 取消授权(需要token) | ||||||
|      * |      * | ||||||
|      * @param socialId socialId |      * @param socialId socialId | ||||||
|      */ |      */ | ||||||
|     @DeleteMapping(value = "/unlock/{socialId}") |     @DeleteMapping(value = "/unlock/{socialId}") | ||||||
|     public R<Void> unlockSocial(@PathVariable Long socialId) { |     public R<Void> unlockSocial(@PathVariable Long socialId) { | ||||||
|  |         // 校验token | ||||||
|  |         StpUtil.checkLogin(); | ||||||
|         Boolean rows = socialUserService.deleteWithValidById(socialId); |         Boolean rows = socialUserService.deleteWithValidById(socialId); | ||||||
|         return rows ? R.ok() : R.fail("取消授权失败"); |         return rows ? R.ok() : R.fail("取消授权失败"); | ||||||
|     } |     } | ||||||
| @@ -226,7 +231,7 @@ public class AuthController { | |||||||
|         } |         } | ||||||
|         // 根据域名进行筛选 |         // 根据域名进行筛选 | ||||||
|         List<TenantListVo> list = StreamUtils.filter(voList, vo -> |         List<TenantListVo> list = StreamUtils.filter(voList, vo -> | ||||||
|                 StringUtils.equals(vo.getDomain(), host)); |             StringUtils.equalsIgnoreCase(vo.getDomain(), host)); | ||||||
|         result.setVoList(CollUtil.isNotEmpty(list) ? list : voList); |         result.setVoList(CollUtil.isNotEmpty(list) ? list : voList); | ||||||
|         return R.ok(result); |         return R.ok(result); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ import lombok.extern.slf4j.Slf4j; | |||||||
| import org.dromara.common.core.constant.Constants; | import org.dromara.common.core.constant.Constants; | ||||||
| import org.dromara.common.core.constant.GlobalConstants; | import org.dromara.common.core.constant.GlobalConstants; | ||||||
| import org.dromara.common.core.domain.R; | import org.dromara.common.core.domain.R; | ||||||
|  | import org.dromara.common.core.exception.ServiceException; | ||||||
| import org.dromara.common.core.utils.SpringUtils; | import org.dromara.common.core.utils.SpringUtils; | ||||||
| import org.dromara.common.core.utils.StringUtils; | import org.dromara.common.core.utils.StringUtils; | ||||||
| import org.dromara.common.core.utils.reflect.ReflectUtils; | import org.dromara.common.core.utils.reflect.ReflectUtils; | ||||||
| @@ -79,12 +80,21 @@ public class CaptchaController { | |||||||
|      * |      * | ||||||
|      * @param email 邮箱 |      * @param email 邮箱 | ||||||
|      */ |      */ | ||||||
|     @RateLimiter(key = "#email", time = 60, count = 1) |  | ||||||
|     @GetMapping("/resource/email/code") |     @GetMapping("/resource/email/code") | ||||||
|     public R<Void> emailCode(@NotBlank(message = "{user.email.not.blank}") String email) { |     public R<Void> emailCode(@NotBlank(message = "{user.email.not.blank}") String email) { | ||||||
|         if (!mailProperties.getEnabled()) { |         if (!mailProperties.getEnabled()) { | ||||||
|             return R.fail("当前系统没有开启邮箱功能!"); |             return R.fail("当前系统没有开启邮箱功能!"); | ||||||
|         } |         } | ||||||
|  |         SpringUtils.getAopProxy(this).emailCodeImpl(email); | ||||||
|  |         return R.ok(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 邮箱验证码 | ||||||
|  |      * 独立方法避免验证码关闭之后仍然走限流 | ||||||
|  |      */ | ||||||
|  |     @RateLimiter(key = "#email", time = 60, count = 1) | ||||||
|  |     public void emailCodeImpl(String email) { | ||||||
|         String key = GlobalConstants.CAPTCHA_CODE_KEY + email; |         String key = GlobalConstants.CAPTCHA_CODE_KEY + email; | ||||||
|         String code = RandomUtil.randomNumbers(4); |         String code = RandomUtil.randomNumbers(4); | ||||||
|         RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); |         RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); | ||||||
| @@ -92,23 +102,30 @@ public class CaptchaController { | |||||||
|             MailUtils.sendText(email, "登录验证码", "您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。"); |             MailUtils.sendText(email, "登录验证码", "您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。"); | ||||||
|         } catch (Exception e) { |         } catch (Exception e) { | ||||||
|             log.error("验证码短信发送异常 => {}", e.getMessage()); |             log.error("验证码短信发送异常 => {}", e.getMessage()); | ||||||
|             return R.fail(e.getMessage()); |             throw new ServiceException(e.getMessage()); | ||||||
|         } |         } | ||||||
|         return R.ok(); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 生成验证码 |      * 生成验证码 | ||||||
|      */ |      */ | ||||||
|     @RateLimiter(time = 60, count = 10, limitType = LimitType.IP) |  | ||||||
|     @GetMapping("/auth/code") |     @GetMapping("/auth/code") | ||||||
|     public R<CaptchaVo> getCode() { |     public R<CaptchaVo> getCode() { | ||||||
|         CaptchaVo captchaVo = new CaptchaVo(); |  | ||||||
|         boolean captchaEnabled = captchaProperties.getEnable(); |         boolean captchaEnabled = captchaProperties.getEnable(); | ||||||
|         if (!captchaEnabled) { |         if (!captchaEnabled) { | ||||||
|  |             CaptchaVo captchaVo = new CaptchaVo(); | ||||||
|             captchaVo.setCaptchaEnabled(false); |             captchaVo.setCaptchaEnabled(false); | ||||||
|             return R.ok(captchaVo); |             return R.ok(captchaVo); | ||||||
|         } |         } | ||||||
|  |         return R.ok(SpringUtils.getAopProxy(this).getCodeImpl()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 生成验证码 | ||||||
|  |      * 独立方法避免验证码关闭之后仍然走限流 | ||||||
|  |      */ | ||||||
|  |     @RateLimiter(time = 60, count = 10, limitType = LimitType.IP) | ||||||
|  |     public CaptchaVo getCodeImpl() { | ||||||
|         // 保存验证码信息 |         // 保存验证码信息 | ||||||
|         String uuid = IdUtil.simpleUUID(); |         String uuid = IdUtil.simpleUUID(); | ||||||
|         String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + uuid; |         String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + uuid; | ||||||
| @@ -128,9 +145,10 @@ public class CaptchaController { | |||||||
|             code = exp.getValue(String.class); |             code = exp.getValue(String.class); | ||||||
|         } |         } | ||||||
|         RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); |         RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); | ||||||
|  |         CaptchaVo captchaVo = new CaptchaVo(); | ||||||
|         captchaVo.setUuid(uuid); |         captchaVo.setUuid(uuid); | ||||||
|         captchaVo.setImg(captcha.getImageBase64()); |         captchaVo.setImg(captcha.getImageBase64()); | ||||||
|         return R.ok(captchaVo); |         return captchaVo; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,9 +1,9 @@ | |||||||
| package org.dromara.web.controller; | package org.dromara.web.controller; | ||||||
|  |  | ||||||
| import cn.dev33.satoken.annotation.SaIgnore; | import cn.dev33.satoken.annotation.SaIgnore; | ||||||
| import org.dromara.common.core.config.RuoYiConfig; |  | ||||||
| import org.dromara.common.core.utils.StringUtils; |  | ||||||
| import lombok.RequiredArgsConstructor; | import lombok.RequiredArgsConstructor; | ||||||
|  | import org.dromara.common.core.utils.SpringUtils; | ||||||
|  | import org.dromara.common.core.utils.StringUtils; | ||||||
| import org.springframework.web.bind.annotation.GetMapping; | import org.springframework.web.bind.annotation.GetMapping; | ||||||
| import org.springframework.web.bind.annotation.RestController; | import org.springframework.web.bind.annotation.RestController; | ||||||
|  |  | ||||||
| @@ -17,16 +17,12 @@ import org.springframework.web.bind.annotation.RestController; | |||||||
| @RestController | @RestController | ||||||
| public class IndexController { | public class IndexController { | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 系统基础配置 |  | ||||||
|      */ |  | ||||||
|     private final RuoYiConfig ruoyiConfig; |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 访问首页,提示语 |      * 访问首页,提示语 | ||||||
|      */ |      */ | ||||||
|     @GetMapping("/") |     @GetMapping("/") | ||||||
|     public String index() { |     public String index() { | ||||||
|         return StringUtils.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion()); |         return StringUtils.format("欢迎使用{}后台管理框架,请通过前端地址访问。", SpringUtils.getApplicationName()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,9 +1,8 @@ | |||||||
| package org.dromara.web.listener; | package org.dromara.web.listener; | ||||||
|  |  | ||||||
| import cn.dev33.satoken.config.SaTokenConfig; |  | ||||||
| import cn.dev33.satoken.listener.SaTokenListener; | import cn.dev33.satoken.listener.SaTokenListener; | ||||||
| import cn.dev33.satoken.stp.SaLoginModel; |  | ||||||
| import cn.dev33.satoken.stp.StpUtil; | import cn.dev33.satoken.stp.StpUtil; | ||||||
|  | import cn.dev33.satoken.stp.parameter.SaLoginParameter; | ||||||
| import cn.hutool.core.convert.Convert; | import cn.hutool.core.convert.Convert; | ||||||
| import cn.hutool.http.useragent.UserAgent; | import cn.hutool.http.useragent.UserAgent; | ||||||
| import cn.hutool.http.useragent.UserAgentUtil; | import cn.hutool.http.useragent.UserAgentUtil; | ||||||
| @@ -35,14 +34,13 @@ import java.time.Duration; | |||||||
| @Slf4j | @Slf4j | ||||||
| public class UserActionListener implements SaTokenListener { | public class UserActionListener implements SaTokenListener { | ||||||
|  |  | ||||||
|     private final SaTokenConfig tokenConfig; |  | ||||||
|     private final SysLoginService loginService; |     private final SysLoginService loginService; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 每次登录时触发 |      * 每次登录时触发 | ||||||
|      */ |      */ | ||||||
|     @Override |     @Override | ||||||
|     public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) { |     public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginParameter loginParameter) { | ||||||
|         UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent")); |         UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent")); | ||||||
|         String ip = ServletUtils.getClientIP(); |         String ip = ServletUtils.getClientIP(); | ||||||
|         UserOnlineDTO dto = new UserOnlineDTO(); |         UserOnlineDTO dto = new UserOnlineDTO(); | ||||||
| @@ -52,17 +50,17 @@ public class UserActionListener implements SaTokenListener { | |||||||
|         dto.setOs(userAgent.getOs().getName()); |         dto.setOs(userAgent.getOs().getName()); | ||||||
|         dto.setLoginTime(System.currentTimeMillis()); |         dto.setLoginTime(System.currentTimeMillis()); | ||||||
|         dto.setTokenId(tokenValue); |         dto.setTokenId(tokenValue); | ||||||
|         String username = (String) loginModel.getExtra(LoginHelper.USER_NAME_KEY); |         String username = (String) loginParameter.getExtra(LoginHelper.USER_NAME_KEY); | ||||||
|         String tenantId = (String) loginModel.getExtra(LoginHelper.TENANT_KEY); |         String tenantId = (String) loginParameter.getExtra(LoginHelper.TENANT_KEY); | ||||||
|         dto.setUserName(username); |         dto.setUserName(username); | ||||||
|         dto.setClientKey((String) loginModel.getExtra(LoginHelper.CLIENT_KEY)); |         dto.setClientKey((String) loginParameter.getExtra(LoginHelper.CLIENT_KEY)); | ||||||
|         dto.setDeviceType(loginModel.getDevice()); |         dto.setDeviceType(loginParameter.getDeviceType()); | ||||||
|         dto.setDeptName((String) loginModel.getExtra(LoginHelper.DEPT_NAME_KEY)); |         dto.setDeptName((String) loginParameter.getExtra(LoginHelper.DEPT_NAME_KEY)); | ||||||
|         TenantHelper.dynamic(tenantId, () -> { |         TenantHelper.dynamic(tenantId, () -> { | ||||||
|             if(tokenConfig.getTimeout() == -1) { |             if(loginParameter.getTimeout() == -1) { | ||||||
|                 RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto); |                 RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto); | ||||||
|             } else { |             } else { | ||||||
|                 RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(tokenConfig.getTimeout())); |                 RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(loginParameter.getTimeout())); | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|         // 记录登录日志 |         // 记录登录日志 | ||||||
| @@ -74,7 +72,7 @@ public class UserActionListener implements SaTokenListener { | |||||||
|         logininforEvent.setRequest(ServletUtils.getRequest()); |         logininforEvent.setRequest(ServletUtils.getRequest()); | ||||||
|         SpringUtils.context().publishEvent(logininforEvent); |         SpringUtils.context().publishEvent(logininforEvent); | ||||||
|         // 更新登录信息 |         // 更新登录信息 | ||||||
|         loginService.recordLoginInfo((Long) loginModel.getExtra(LoginHelper.USER_KEY), ip); |         loginService.recordLoginInfo((Long) loginParameter.getExtra(LoginHelper.USER_KEY), ip); | ||||||
|         log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue); |         log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -12,11 +12,12 @@ import lombok.extern.slf4j.Slf4j; | |||||||
| import me.zhyd.oauth.model.AuthUser; | import me.zhyd.oauth.model.AuthUser; | ||||||
| import org.dromara.common.core.constant.CacheConstants; | import org.dromara.common.core.constant.CacheConstants; | ||||||
| import org.dromara.common.core.constant.Constants; | import org.dromara.common.core.constant.Constants; | ||||||
|  | import org.dromara.common.core.constant.SystemConstants; | ||||||
| import org.dromara.common.core.constant.TenantConstants; | import org.dromara.common.core.constant.TenantConstants; | ||||||
|  | import org.dromara.common.core.domain.dto.PostDTO; | ||||||
| import org.dromara.common.core.domain.dto.RoleDTO; | import org.dromara.common.core.domain.dto.RoleDTO; | ||||||
| import org.dromara.common.core.domain.model.LoginUser; | import org.dromara.common.core.domain.model.LoginUser; | ||||||
| import org.dromara.common.core.enums.LoginType; | import org.dromara.common.core.enums.LoginType; | ||||||
| import org.dromara.common.core.enums.TenantStatus; |  | ||||||
| import org.dromara.common.core.exception.ServiceException; | import org.dromara.common.core.exception.ServiceException; | ||||||
| import org.dromara.common.core.exception.user.UserException; | import org.dromara.common.core.exception.user.UserException; | ||||||
| import org.dromara.common.core.utils.*; | import org.dromara.common.core.utils.*; | ||||||
| @@ -60,6 +61,7 @@ public class SysLoginService { | |||||||
|     private final ISysSocialService sysSocialService; |     private final ISysSocialService sysSocialService; | ||||||
|     private final ISysRoleService roleService; |     private final ISysRoleService roleService; | ||||||
|     private final ISysDeptService deptService; |     private final ISysDeptService deptService; | ||||||
|  |     private final ISysPostService postService; | ||||||
|     private final SysUserMapper userMapper; |     private final SysUserMapper userMapper; | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -148,21 +150,24 @@ public class SysLoginService { | |||||||
|      */ |      */ | ||||||
|     public LoginUser buildLoginUser(SysUserVo user) { |     public LoginUser buildLoginUser(SysUserVo user) { | ||||||
|         LoginUser loginUser = new LoginUser(); |         LoginUser loginUser = new LoginUser(); | ||||||
|  |         Long userId = user.getUserId(); | ||||||
|         loginUser.setTenantId(user.getTenantId()); |         loginUser.setTenantId(user.getTenantId()); | ||||||
|         loginUser.setUserId(user.getUserId()); |         loginUser.setUserId(userId); | ||||||
|         loginUser.setDeptId(user.getDeptId()); |         loginUser.setDeptId(user.getDeptId()); | ||||||
|         loginUser.setUsername(user.getUserName()); |         loginUser.setUsername(user.getUserName()); | ||||||
|         loginUser.setNickname(user.getNickName()); |         loginUser.setNickname(user.getNickName()); | ||||||
|         loginUser.setUserType(user.getUserType()); |         loginUser.setUserType(user.getUserType()); | ||||||
|         loginUser.setMenuPermission(permissionService.getMenuPermission(user.getUserId())); |         loginUser.setMenuPermission(permissionService.getMenuPermission(userId)); | ||||||
|         loginUser.setRolePermission(permissionService.getRolePermission(user.getUserId())); |         loginUser.setRolePermission(permissionService.getRolePermission(userId)); | ||||||
|         if (ObjectUtil.isNotNull(user.getDeptId())) { |         if (ObjectUtil.isNotNull(user.getDeptId())) { | ||||||
|             Opt<SysDeptVo> deptOpt = Opt.of(user.getDeptId()).map(deptService::selectDeptById); |             Opt<SysDeptVo> deptOpt = Opt.of(user.getDeptId()).map(deptService::selectDeptById); | ||||||
|             loginUser.setDeptName(deptOpt.map(SysDeptVo::getDeptName).orElse(StringUtils.EMPTY)); |             loginUser.setDeptName(deptOpt.map(SysDeptVo::getDeptName).orElse(StringUtils.EMPTY)); | ||||||
|             loginUser.setDeptCategory(deptOpt.map(SysDeptVo::getDeptCategory).orElse(StringUtils.EMPTY)); |             loginUser.setDeptCategory(deptOpt.map(SysDeptVo::getDeptCategory).orElse(StringUtils.EMPTY)); | ||||||
|         } |         } | ||||||
|         List<SysRoleVo> roles = roleService.selectRolesByUserId(user.getUserId()); |         List<SysRoleVo> roles = roleService.selectRolesByUserId(userId); | ||||||
|  |         List<SysPostVo> posts = postService.selectPostsByUserId(userId); | ||||||
|         loginUser.setRoles(BeanUtil.copyToList(roles, RoleDTO.class)); |         loginUser.setRoles(BeanUtil.copyToList(roles, RoleDTO.class)); | ||||||
|  |         loginUser.setPosts(BeanUtil.copyToList(posts, PostDTO.class)); | ||||||
|         return loginUser; |         return loginUser; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -223,17 +228,17 @@ public class SysLoginService { | |||||||
|         if (!TenantHelper.isEnable()) { |         if (!TenantHelper.isEnable()) { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|         if (TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|         if (StringUtils.isBlank(tenantId)) { |         if (StringUtils.isBlank(tenantId)) { | ||||||
|             throw new TenantException("tenant.number.not.blank"); |             throw new TenantException("tenant.number.not.blank"); | ||||||
|         } |         } | ||||||
|  |         if (TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|         SysTenantVo tenant = tenantService.queryByTenantId(tenantId); |         SysTenantVo tenant = tenantService.queryByTenantId(tenantId); | ||||||
|         if (ObjectUtil.isNull(tenant)) { |         if (ObjectUtil.isNull(tenant)) { | ||||||
|             log.info("登录租户:{} 不存在.", tenantId); |             log.info("登录租户:{} 不存在.", tenantId); | ||||||
|             throw new TenantException("tenant.not.exists"); |             throw new TenantException("tenant.not.exists"); | ||||||
|         } else if (TenantStatus.DISABLE.getCode().equals(tenant.getStatus())) { |         } else if (SystemConstants.DISABLE.equals(tenant.getStatus())) { | ||||||
|             log.info("登录租户:{} 已被停用.", tenantId); |             log.info("登录租户:{} 已被停用.", tenantId); | ||||||
|             throw new TenantException("tenant.blocked"); |             throw new TenantException("tenant.blocked"); | ||||||
|         } else if (ObjectUtil.isNotNull(tenant.getExpireTime()) |         } else if (ObjectUtil.isNotNull(tenant.getExpireTime()) | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| package org.dromara.web.service; | package org.dromara.web.service; | ||||||
|  |  | ||||||
| import cn.dev33.satoken.secure.BCrypt; | import cn.hutool.crypto.digest.BCrypt; | ||||||
| import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | ||||||
| import lombok.RequiredArgsConstructor; | import lombok.RequiredArgsConstructor; | ||||||
| import org.dromara.common.core.constant.Constants; | import org.dromara.common.core.constant.Constants; | ||||||
| @@ -84,11 +84,11 @@ public class SysRegisterService { | |||||||
|         String captcha = RedisUtils.getCacheObject(verifyKey); |         String captcha = RedisUtils.getCacheObject(verifyKey); | ||||||
|         RedisUtils.deleteObject(verifyKey); |         RedisUtils.deleteObject(verifyKey); | ||||||
|         if (captcha == null) { |         if (captcha == null) { | ||||||
|             recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.expire")); |             recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); | ||||||
|             throw new CaptchaExpireException(); |             throw new CaptchaExpireException(); | ||||||
|         } |         } | ||||||
|         if (!code.equalsIgnoreCase(captcha)) { |         if (!code.equalsIgnoreCase(captcha)) { | ||||||
|             recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.error")); |             recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")); | ||||||
|             throw new CaptchaException(); |             throw new CaptchaException(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -1,17 +1,17 @@ | |||||||
| package org.dromara.web.service.impl; | package org.dromara.web.service.impl; | ||||||
|  |  | ||||||
| import cn.dev33.satoken.stp.SaLoginModel; |  | ||||||
| import cn.dev33.satoken.stp.StpUtil; | import cn.dev33.satoken.stp.StpUtil; | ||||||
|  | import cn.dev33.satoken.stp.parameter.SaLoginParameter; | ||||||
| import cn.hutool.core.util.ObjectUtil; | import cn.hutool.core.util.ObjectUtil; | ||||||
| import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | ||||||
| import lombok.RequiredArgsConstructor; | import lombok.RequiredArgsConstructor; | ||||||
| import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||||
| import org.dromara.common.core.constant.Constants; | import org.dromara.common.core.constant.Constants; | ||||||
| import org.dromara.common.core.constant.GlobalConstants; | import org.dromara.common.core.constant.GlobalConstants; | ||||||
|  | import org.dromara.common.core.constant.SystemConstants; | ||||||
| import org.dromara.common.core.domain.model.EmailLoginBody; | import org.dromara.common.core.domain.model.EmailLoginBody; | ||||||
| import org.dromara.common.core.domain.model.LoginUser; | import org.dromara.common.core.domain.model.LoginUser; | ||||||
| import org.dromara.common.core.enums.LoginType; | import org.dromara.common.core.enums.LoginType; | ||||||
| import org.dromara.common.core.enums.UserStatus; |  | ||||||
| import org.dromara.common.core.exception.user.CaptchaExpireException; | import org.dromara.common.core.exception.user.CaptchaExpireException; | ||||||
| import org.dromara.common.core.exception.user.UserException; | import org.dromara.common.core.exception.user.UserException; | ||||||
| import org.dromara.common.core.utils.MessageUtils; | import org.dromara.common.core.utils.MessageUtils; | ||||||
| @@ -58,8 +58,8 @@ public class EmailAuthStrategy implements IAuthStrategy { | |||||||
|         }); |         }); | ||||||
|         loginUser.setClientKey(client.getClientKey()); |         loginUser.setClientKey(client.getClientKey()); | ||||||
|         loginUser.setDeviceType(client.getDeviceType()); |         loginUser.setDeviceType(client.getDeviceType()); | ||||||
|         SaLoginModel model = new SaLoginModel(); |         SaLoginParameter model = new SaLoginParameter(); | ||||||
|         model.setDevice(client.getDeviceType()); |         model.setDeviceType(client.getDeviceType()); | ||||||
|         // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 |         // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 | ||||||
|         // 例如: 后台用户30分钟过期 app用户1天过期 |         // 例如: 后台用户30分钟过期 app用户1天过期 | ||||||
|         model.setTimeout(client.getTimeout()); |         model.setTimeout(client.getTimeout()); | ||||||
| @@ -92,7 +92,7 @@ public class EmailAuthStrategy implements IAuthStrategy { | |||||||
|         if (ObjectUtil.isNull(user)) { |         if (ObjectUtil.isNull(user)) { | ||||||
|             log.info("登录用户:{} 不存在.", email); |             log.info("登录用户:{} 不存在.", email); | ||||||
|             throw new UserException("user.not.exists", email); |             throw new UserException("user.not.exists", email); | ||||||
|         } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { |         } else if (SystemConstants.DISABLE.equals(user.getStatus())) { | ||||||
|             log.info("登录用户:{} 已被停用.", email); |             log.info("登录用户:{} 已被停用.", email); | ||||||
|             throw new UserException("user.blocked", email); |             throw new UserException("user.blocked", email); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -1,18 +1,18 @@ | |||||||
| package org.dromara.web.service.impl; | package org.dromara.web.service.impl; | ||||||
|  |  | ||||||
| import cn.dev33.satoken.secure.BCrypt; |  | ||||||
| import cn.dev33.satoken.stp.SaLoginModel; |  | ||||||
| import cn.dev33.satoken.stp.StpUtil; | import cn.dev33.satoken.stp.StpUtil; | ||||||
|  | import cn.dev33.satoken.stp.parameter.SaLoginParameter; | ||||||
| import cn.hutool.core.util.ObjectUtil; | import cn.hutool.core.util.ObjectUtil; | ||||||
|  | import cn.hutool.crypto.digest.BCrypt; | ||||||
| import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | ||||||
| import lombok.RequiredArgsConstructor; | import lombok.RequiredArgsConstructor; | ||||||
| import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||||
| import org.dromara.common.core.constant.Constants; | import org.dromara.common.core.constant.Constants; | ||||||
| import org.dromara.common.core.constant.GlobalConstants; | import org.dromara.common.core.constant.GlobalConstants; | ||||||
|  | import org.dromara.common.core.constant.SystemConstants; | ||||||
| import org.dromara.common.core.domain.model.LoginUser; | import org.dromara.common.core.domain.model.LoginUser; | ||||||
| import org.dromara.common.core.domain.model.PasswordLoginBody; | import org.dromara.common.core.domain.model.PasswordLoginBody; | ||||||
| import org.dromara.common.core.enums.LoginType; | import org.dromara.common.core.enums.LoginType; | ||||||
| import org.dromara.common.core.enums.UserStatus; |  | ||||||
| import org.dromara.common.core.exception.user.CaptchaException; | import org.dromara.common.core.exception.user.CaptchaException; | ||||||
| import org.dromara.common.core.exception.user.CaptchaExpireException; | import org.dromara.common.core.exception.user.CaptchaExpireException; | ||||||
| import org.dromara.common.core.exception.user.UserException; | import org.dromara.common.core.exception.user.UserException; | ||||||
| @@ -70,8 +70,8 @@ public class PasswordAuthStrategy implements IAuthStrategy { | |||||||
|         }); |         }); | ||||||
|         loginUser.setClientKey(client.getClientKey()); |         loginUser.setClientKey(client.getClientKey()); | ||||||
|         loginUser.setDeviceType(client.getDeviceType()); |         loginUser.setDeviceType(client.getDeviceType()); | ||||||
|         SaLoginModel model = new SaLoginModel(); |         SaLoginParameter model = new SaLoginParameter(); | ||||||
|         model.setDevice(client.getDeviceType()); |         model.setDeviceType(client.getDeviceType()); | ||||||
|         // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 |         // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 | ||||||
|         // 例如: 后台用户30分钟过期 app用户1天过期 |         // 例如: 后台用户30分钟过期 app用户1天过期 | ||||||
|         model.setTimeout(client.getTimeout()); |         model.setTimeout(client.getTimeout()); | ||||||
| @@ -113,7 +113,7 @@ public class PasswordAuthStrategy implements IAuthStrategy { | |||||||
|         if (ObjectUtil.isNull(user)) { |         if (ObjectUtil.isNull(user)) { | ||||||
|             log.info("登录用户:{} 不存在.", username); |             log.info("登录用户:{} 不存在.", username); | ||||||
|             throw new UserException("user.not.exists", username); |             throw new UserException("user.not.exists", username); | ||||||
|         } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { |         } else if (SystemConstants.DISABLE.equals(user.getStatus())) { | ||||||
|             log.info("登录用户:{} 已被停用.", username); |             log.info("登录用户:{} 已被停用.", username); | ||||||
|             throw new UserException("user.blocked", username); |             throw new UserException("user.blocked", username); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -1,17 +1,17 @@ | |||||||
| package org.dromara.web.service.impl; | package org.dromara.web.service.impl; | ||||||
|  |  | ||||||
| import cn.dev33.satoken.stp.SaLoginModel; |  | ||||||
| import cn.dev33.satoken.stp.StpUtil; | import cn.dev33.satoken.stp.StpUtil; | ||||||
|  | import cn.dev33.satoken.stp.parameter.SaLoginParameter; | ||||||
| import cn.hutool.core.util.ObjectUtil; | import cn.hutool.core.util.ObjectUtil; | ||||||
| import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | ||||||
| import lombok.RequiredArgsConstructor; | import lombok.RequiredArgsConstructor; | ||||||
| import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||||
| import org.dromara.common.core.constant.Constants; | import org.dromara.common.core.constant.Constants; | ||||||
| import org.dromara.common.core.constant.GlobalConstants; | import org.dromara.common.core.constant.GlobalConstants; | ||||||
|  | import org.dromara.common.core.constant.SystemConstants; | ||||||
| import org.dromara.common.core.domain.model.LoginUser; | import org.dromara.common.core.domain.model.LoginUser; | ||||||
| import org.dromara.common.core.domain.model.SmsLoginBody; | import org.dromara.common.core.domain.model.SmsLoginBody; | ||||||
| import org.dromara.common.core.enums.LoginType; | import org.dromara.common.core.enums.LoginType; | ||||||
| import org.dromara.common.core.enums.UserStatus; |  | ||||||
| import org.dromara.common.core.exception.user.CaptchaExpireException; | import org.dromara.common.core.exception.user.CaptchaExpireException; | ||||||
| import org.dromara.common.core.exception.user.UserException; | import org.dromara.common.core.exception.user.UserException; | ||||||
| import org.dromara.common.core.utils.MessageUtils; | import org.dromara.common.core.utils.MessageUtils; | ||||||
| @@ -58,8 +58,8 @@ public class SmsAuthStrategy implements IAuthStrategy { | |||||||
|         }); |         }); | ||||||
|         loginUser.setClientKey(client.getClientKey()); |         loginUser.setClientKey(client.getClientKey()); | ||||||
|         loginUser.setDeviceType(client.getDeviceType()); |         loginUser.setDeviceType(client.getDeviceType()); | ||||||
|         SaLoginModel model = new SaLoginModel(); |         SaLoginParameter model = new SaLoginParameter(); | ||||||
|         model.setDevice(client.getDeviceType()); |         model.setDeviceType(client.getDeviceType()); | ||||||
|         // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 |         // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 | ||||||
|         // 例如: 后台用户30分钟过期 app用户1天过期 |         // 例如: 后台用户30分钟过期 app用户1天过期 | ||||||
|         model.setTimeout(client.getTimeout()); |         model.setTimeout(client.getTimeout()); | ||||||
| @@ -92,7 +92,7 @@ public class SmsAuthStrategy implements IAuthStrategy { | |||||||
|         if (ObjectUtil.isNull(user)) { |         if (ObjectUtil.isNull(user)) { | ||||||
|             log.info("登录用户:{} 不存在.", phonenumber); |             log.info("登录用户:{} 不存在.", phonenumber); | ||||||
|             throw new UserException("user.not.exists", phonenumber); |             throw new UserException("user.not.exists", phonenumber); | ||||||
|         } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { |         } else if (SystemConstants.DISABLE.equals(user.getStatus())) { | ||||||
|             log.info("登录用户:{} 已被停用.", phonenumber); |             log.info("登录用户:{} 已被停用.", phonenumber); | ||||||
|             throw new UserException("user.blocked", phonenumber); |             throw new UserException("user.blocked", phonenumber); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| package org.dromara.web.service.impl; | package org.dromara.web.service.impl; | ||||||
|  |  | ||||||
| import cn.dev33.satoken.stp.SaLoginModel; |  | ||||||
| import cn.dev33.satoken.stp.StpUtil; | import cn.dev33.satoken.stp.StpUtil; | ||||||
|  | import cn.dev33.satoken.stp.parameter.SaLoginParameter; | ||||||
| import cn.hutool.core.collection.CollUtil; | import cn.hutool.core.collection.CollUtil; | ||||||
| import cn.hutool.core.map.MapUtil; | import cn.hutool.core.map.MapUtil; | ||||||
| import cn.hutool.core.util.ObjectUtil; | import cn.hutool.core.util.ObjectUtil; | ||||||
| @@ -11,9 +11,9 @@ import lombok.RequiredArgsConstructor; | |||||||
| import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||||
| import me.zhyd.oauth.model.AuthResponse; | import me.zhyd.oauth.model.AuthResponse; | ||||||
| import me.zhyd.oauth.model.AuthUser; | import me.zhyd.oauth.model.AuthUser; | ||||||
|  | import org.dromara.common.core.constant.SystemConstants; | ||||||
| import org.dromara.common.core.domain.model.LoginUser; | import org.dromara.common.core.domain.model.LoginUser; | ||||||
| import org.dromara.common.core.domain.model.SocialLoginBody; | import org.dromara.common.core.domain.model.SocialLoginBody; | ||||||
| import org.dromara.common.core.enums.UserStatus; |  | ||||||
| import org.dromara.common.core.exception.ServiceException; | import org.dromara.common.core.exception.ServiceException; | ||||||
| import org.dromara.common.core.exception.user.UserException; | import org.dromara.common.core.exception.user.UserException; | ||||||
| import org.dromara.common.core.utils.StreamUtils; | import org.dromara.common.core.utils.StreamUtils; | ||||||
| @@ -99,8 +99,8 @@ public class SocialAuthStrategy implements IAuthStrategy { | |||||||
|         }); |         }); | ||||||
|         loginUser.setClientKey(client.getClientKey()); |         loginUser.setClientKey(client.getClientKey()); | ||||||
|         loginUser.setDeviceType(client.getDeviceType()); |         loginUser.setDeviceType(client.getDeviceType()); | ||||||
|         SaLoginModel model = new SaLoginModel(); |         SaLoginParameter model = new SaLoginParameter(); | ||||||
|         model.setDevice(client.getDeviceType()); |         model.setDeviceType(client.getDeviceType()); | ||||||
|         // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 |         // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 | ||||||
|         // 例如: 后台用户30分钟过期 app用户1天过期 |         // 例如: 后台用户30分钟过期 app用户1天过期 | ||||||
|         model.setTimeout(client.getTimeout()); |         model.setTimeout(client.getTimeout()); | ||||||
| @@ -121,7 +121,7 @@ public class SocialAuthStrategy implements IAuthStrategy { | |||||||
|         if (ObjectUtil.isNull(user)) { |         if (ObjectUtil.isNull(user)) { | ||||||
|             log.info("登录用户:{} 不存在.", ""); |             log.info("登录用户:{} 不存在.", ""); | ||||||
|             throw new UserException("user.not.exists", ""); |             throw new UserException("user.not.exists", ""); | ||||||
|         } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { |         } else if (SystemConstants.DISABLE.equals(user.getStatus())) { | ||||||
|             log.info("登录用户:{} 已被停用.", ""); |             log.info("登录用户:{} 已被停用.", ""); | ||||||
|             throw new UserException("user.blocked", ""); |             throw new UserException("user.blocked", ""); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -1,17 +1,24 @@ | |||||||
| package org.dromara.web.service.impl; | package org.dromara.web.service.impl; | ||||||
|  |  | ||||||
| import cn.dev33.satoken.stp.SaLoginModel; |  | ||||||
| import cn.dev33.satoken.stp.StpUtil; | import cn.dev33.satoken.stp.StpUtil; | ||||||
|  | import cn.dev33.satoken.stp.parameter.SaLoginParameter; | ||||||
| import cn.hutool.core.util.ObjectUtil; | import cn.hutool.core.util.ObjectUtil; | ||||||
| import lombok.RequiredArgsConstructor; | import lombok.RequiredArgsConstructor; | ||||||
| import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||||
|  | import me.zhyd.oauth.config.AuthConfig; | ||||||
|  | import me.zhyd.oauth.model.AuthCallback; | ||||||
|  | import me.zhyd.oauth.model.AuthResponse; | ||||||
|  | import me.zhyd.oauth.model.AuthToken; | ||||||
|  | import me.zhyd.oauth.model.AuthUser; | ||||||
|  | import me.zhyd.oauth.request.AuthRequest; | ||||||
|  | import me.zhyd.oauth.request.AuthWechatMiniProgramRequest; | ||||||
|  | import org.dromara.common.core.constant.SystemConstants; | ||||||
| import org.dromara.common.core.domain.model.XcxLoginBody; | import org.dromara.common.core.domain.model.XcxLoginBody; | ||||||
| import org.dromara.common.core.domain.model.XcxLoginUser; | import org.dromara.common.core.domain.model.XcxLoginUser; | ||||||
| import org.dromara.common.core.enums.UserStatus; | import org.dromara.common.core.exception.ServiceException; | ||||||
| import org.dromara.common.core.utils.ValidatorUtils; | import org.dromara.common.core.utils.ValidatorUtils; | ||||||
| import org.dromara.common.json.utils.JsonUtils; | import org.dromara.common.json.utils.JsonUtils; | ||||||
| import org.dromara.common.satoken.utils.LoginHelper; | import org.dromara.common.satoken.utils.LoginHelper; | ||||||
| import org.dromara.system.domain.SysClient; |  | ||||||
| import org.dromara.system.domain.vo.SysClientVo; | import org.dromara.system.domain.vo.SysClientVo; | ||||||
| import org.dromara.system.domain.vo.SysUserVo; | import org.dromara.system.domain.vo.SysUserVo; | ||||||
| import org.dromara.web.domain.vo.LoginVo; | import org.dromara.web.domain.vo.LoginVo; | ||||||
| @@ -40,12 +47,24 @@ public class XcxAuthStrategy implements IAuthStrategy { | |||||||
|         // 多个小程序识别使用 |         // 多个小程序识别使用 | ||||||
|         String appid = loginBody.getAppid(); |         String appid = loginBody.getAppid(); | ||||||
|  |  | ||||||
|         // todo 以下自行实现 |  | ||||||
|         // 校验 appid + appsrcret + xcxCode 调用登录凭证校验接口 获取 session_key 与 openid |         // 校验 appid + appsrcret + xcxCode 调用登录凭证校验接口 获取 session_key 与 openid | ||||||
|         String openid = ""; |         AuthRequest authRequest = new AuthWechatMiniProgramRequest(AuthConfig.builder() | ||||||
|  |             .clientId(appid).clientSecret("自行填写密钥 可根据不同appid填入不同密钥") | ||||||
|  |             .ignoreCheckRedirectUri(true).ignoreCheckState(true).build()); | ||||||
|  |         AuthCallback authCallback = new AuthCallback(); | ||||||
|  |         authCallback.setCode(xcxCode); | ||||||
|  |         AuthResponse<AuthUser> resp = authRequest.login(authCallback); | ||||||
|  |         String openid, unionId; | ||||||
|  |         if (resp.ok()) { | ||||||
|  |             AuthToken token = resp.getData().getToken(); | ||||||
|  |             openid = token.getOpenId(); | ||||||
|  |             // 微信小程序只有关联到微信开放平台下之后才能获取到 unionId,因此unionId不一定能返回。 | ||||||
|  |             unionId = token.getUnionId(); | ||||||
|  |         } else { | ||||||
|  |             throw new ServiceException(resp.getMsg()); | ||||||
|  |         } | ||||||
|         // 框架登录不限制从什么表查询 只要最终构建出 LoginUser 即可 |         // 框架登录不限制从什么表查询 只要最终构建出 LoginUser 即可 | ||||||
|         SysUserVo user = loadUserByOpenid(openid); |         SysUserVo user = loadUserByOpenid(openid); | ||||||
|  |  | ||||||
|         // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 |         // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 | ||||||
|         XcxLoginUser loginUser = new XcxLoginUser(); |         XcxLoginUser loginUser = new XcxLoginUser(); | ||||||
|         loginUser.setTenantId(user.getTenantId()); |         loginUser.setTenantId(user.getTenantId()); | ||||||
| @@ -57,8 +76,8 @@ public class XcxAuthStrategy implements IAuthStrategy { | |||||||
|         loginUser.setDeviceType(client.getDeviceType()); |         loginUser.setDeviceType(client.getDeviceType()); | ||||||
|         loginUser.setOpenid(openid); |         loginUser.setOpenid(openid); | ||||||
|  |  | ||||||
|         SaLoginModel model = new SaLoginModel(); |         SaLoginParameter model = new SaLoginParameter(); | ||||||
|         model.setDevice(client.getDeviceType()); |         model.setDeviceType(client.getDeviceType()); | ||||||
|         // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 |         // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 | ||||||
|         // 例如: 后台用户30分钟过期 app用户1天过期 |         // 例如: 后台用户30分钟过期 app用户1天过期 | ||||||
|         model.setTimeout(client.getTimeout()); |         model.setTimeout(client.getTimeout()); | ||||||
| @@ -82,7 +101,7 @@ public class XcxAuthStrategy implements IAuthStrategy { | |||||||
|         if (ObjectUtil.isNull(user)) { |         if (ObjectUtil.isNull(user)) { | ||||||
|             log.info("登录用户:{} 不存在.", openid); |             log.info("登录用户:{} 不存在.", openid); | ||||||
|             // todo 用户不存在 业务逻辑自行实现 |             // todo 用户不存在 业务逻辑自行实现 | ||||||
|         } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { |         } else if (SystemConstants.DISABLE.equals(user.getStatus())) { | ||||||
|             log.info("登录用户:{} 已被停用.", openid); |             log.info("登录用户:{} 已被停用.", openid); | ||||||
|             // todo 用户已被停用 业务逻辑自行实现 |             // todo 用户已被停用 业务逻辑自行实现 | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -8,23 +8,27 @@ spring.boot.admin.client: | |||||||
|     metadata: |     metadata: | ||||||
|       username: ${spring.boot.admin.client.username} |       username: ${spring.boot.admin.client.username} | ||||||
|       userpassword: ${spring.boot.admin.client.password} |       userpassword: ${spring.boot.admin.client.password} | ||||||
|   username: ruoyi |   username: @monitor.username@ | ||||||
|   password: 123456 |   password: @monitor.password@ | ||||||
|  |  | ||||||
| --- # snail-job 配置 | --- # snail-job 配置 | ||||||
| snail-job: | snail-job: | ||||||
|   enabled: true |   enabled: true | ||||||
|   # 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务 |   # 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务 | ||||||
|   group: "ruoyi_group" |   group: "ruoyi_group" | ||||||
|   # SnailJob 接入验证令牌 详见 script/sql/snail_job.sql `sj_group_config` 表 |   # SnailJob 接入验证令牌 详见 script/sql/ry_job.sql `sj_group_config` 表 | ||||||
|   token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT" |   token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT" | ||||||
|   server: |   server: | ||||||
|     host: 127.0.0.1 |     host: 127.0.0.1 | ||||||
|     port: 17888 |     port: 17888 | ||||||
|   # 详见 script/sql/snail_job.sql `sj_namespace` 表 |   # 命名空间UUID 详见 script/sql/ry_job.sql `sj_namespace`表`unique_id`字段 | ||||||
|   namespace: ${spring.profiles.active} |   namespace: ${spring.profiles.active} | ||||||
|   # 随主应用端口飘逸 |   # 随主应用端口漂移 | ||||||
|   port: 2${server.port} |   port: 2${server.port} | ||||||
|  |   # 客户端ip指定 | ||||||
|  |   host: | ||||||
|  |   # RPC类型: netty, grpc | ||||||
|  |   rpc-type: grpc | ||||||
|  |  | ||||||
| --- # 数据源配置 | --- # 数据源配置 | ||||||
| spring: | spring: | ||||||
| @@ -48,14 +52,14 @@ spring: | |||||||
|           url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true |           url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true | ||||||
|           username: root |           username: root | ||||||
|           password: root |           password: root | ||||||
|         # 从库数据源 | #        # 从库数据源 | ||||||
|         slave: | #        slave: | ||||||
|           lazy: true | #          lazy: true | ||||||
|           type: ${spring.datasource.type} | #          type: ${spring.datasource.type} | ||||||
|           driverClassName: com.mysql.cj.jdbc.Driver | #          driverClassName: com.mysql.cj.jdbc.Driver | ||||||
|           url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true | #          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true | ||||||
|           username: | #          username: | ||||||
|           password: | #          password: | ||||||
| #        oracle: | #        oracle: | ||||||
| #          type: ${spring.datasource.type} | #          type: ${spring.datasource.type} | ||||||
| #          driverClassName: oracle.jdbc.OracleDriver | #          driverClassName: oracle.jdbc.OracleDriver | ||||||
| @@ -116,8 +120,8 @@ redisson: | |||||||
|   nettyThreads: 8 |   nettyThreads: 8 | ||||||
|   # 单节点配置 |   # 单节点配置 | ||||||
|   singleServerConfig: |   singleServerConfig: | ||||||
|     # 客户端名称 |     # 客户端名称 不能用中文 | ||||||
|     clientName: ${ruoyi.name} |     clientName: RuoYi-Vue-Plus | ||||||
|     # 最小空闲连接数 |     # 最小空闲连接数 | ||||||
|     connectionMinimumIdleSize: 8 |     connectionMinimumIdleSize: 8 | ||||||
|     # 连接池大小 |     # 连接池大小 | ||||||
| @@ -198,7 +202,7 @@ justauth: | |||||||
|       redirect-uri: ${justauth.address}/social-callback?source=maxkey |       redirect-uri: ${justauth.address}/social-callback?source=maxkey | ||||||
|     topiam: |     topiam: | ||||||
|       # topiam 服务器地址 |       # topiam 服务器地址 | ||||||
|       server-url: http://127.0.0.1:1989/api/v1/authorize/y0q************spq***********8ol |       server-url: http://127.0.0.1:1898/api/v1/authorize/y0q************spq***********8ol | ||||||
|       client-id: 449c4*********937************759 |       client-id: 449c4*********937************759 | ||||||
|       client-secret: ac7***********1e0************28d |       client-secret: ac7***********1e0************28d | ||||||
|       redirect-uri: ${justauth.address}/social-callback?source=topiam |       redirect-uri: ${justauth.address}/social-callback?source=topiam | ||||||
|   | |||||||
| @@ -11,23 +11,27 @@ spring.boot.admin.client: | |||||||
|     metadata: |     metadata: | ||||||
|       username: ${spring.boot.admin.client.username} |       username: ${spring.boot.admin.client.username} | ||||||
|       userpassword: ${spring.boot.admin.client.password} |       userpassword: ${spring.boot.admin.client.password} | ||||||
|   username: ruoyi |   username: @monitor.username@ | ||||||
|   password: 123456 |   password: @monitor.password@ | ||||||
|  |  | ||||||
| --- # snail-job 配置 | --- # snail-job 配置 | ||||||
| snail-job: | snail-job: | ||||||
|   enabled: true |   enabled: true | ||||||
|   # 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务 |   # 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务 | ||||||
|   group: "ruoyi_group" |   group: "ruoyi_group" | ||||||
|   # SnailJob 接入验证令牌 详见 script/sql/snail_job.sql `sj_group_config` 表 |   # SnailJob 接入验证令牌 详见 script/sql/ry_job.sql `sj_group_config`表 | ||||||
|   token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT" |   token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT" | ||||||
|   server: |   server: | ||||||
|     host: 127.0.0.1 |     host: 127.0.0.1 | ||||||
|     port: 17888 |     port: 17888 | ||||||
|   # 详见 script/sql/snail_job.sql `sj_namespace` 表 |   # 命名空间UUID 详见 script/sql/ry_job.sql `sj_namespace`表`unique_id`字段 | ||||||
|   namespace: ${spring.profiles.active} |   namespace: ${spring.profiles.active} | ||||||
|   # 随主应用端口飘逸 |   # 随主应用端口漂移 | ||||||
|   port: 2${server.port} |   port: 2${server.port} | ||||||
|  |   # 客户端ip指定 | ||||||
|  |   host: | ||||||
|  |   # RPC类型: netty, grpc | ||||||
|  |   rpc-type: grpc | ||||||
|  |  | ||||||
| --- # 数据源配置 | --- # 数据源配置 | ||||||
| spring: | spring: | ||||||
| @@ -51,14 +55,14 @@ spring: | |||||||
|           url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true |           url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true | ||||||
|           username: root |           username: root | ||||||
|           password: root |           password: root | ||||||
|         # 从库数据源 | #        # 从库数据源 | ||||||
|         slave: | #        slave: | ||||||
|           lazy: true | #          lazy: true | ||||||
|           type: ${spring.datasource.type} | #          type: ${spring.datasource.type} | ||||||
|           driverClassName: com.mysql.cj.jdbc.Driver | #          driverClassName: com.mysql.cj.jdbc.Driver | ||||||
|           url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true | #          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true | ||||||
|           username: | #          username: | ||||||
|           password: | #          password: | ||||||
| #        oracle: | #        oracle: | ||||||
| #          type: ${spring.datasource.type} | #          type: ${spring.datasource.type} | ||||||
| #          driverClassName: oracle.jdbc.OracleDriver | #          driverClassName: oracle.jdbc.OracleDriver | ||||||
| @@ -119,8 +123,8 @@ redisson: | |||||||
|   nettyThreads: 32 |   nettyThreads: 32 | ||||||
|   # 单节点配置 |   # 单节点配置 | ||||||
|   singleServerConfig: |   singleServerConfig: | ||||||
|     # 客户端名称 |     # 客户端名称 不能用中文 | ||||||
|     clientName: ${ruoyi.name} |     clientName: RuoYi-Vue-Plus | ||||||
|     # 最小空闲连接数 |     # 最小空闲连接数 | ||||||
|     connectionMinimumIdleSize: 32 |     connectionMinimumIdleSize: 32 | ||||||
|     # 连接池大小 |     # 连接池大小 | ||||||
|   | |||||||
| @@ -1,24 +1,3 @@ | |||||||
| # 项目相关配置 |  | ||||||
| ruoyi: |  | ||||||
|   # 名称 |  | ||||||
|   name: RuoYi-Vue-Plus |  | ||||||
|   # 版本 |  | ||||||
|   version: ${revision} |  | ||||||
|   # 版权年份 |  | ||||||
|   copyrightYear: 2024 |  | ||||||
|  |  | ||||||
| captcha: |  | ||||||
|   enable: true |  | ||||||
|   # 页面 <参数设置> 可开启关闭 验证码校验 |  | ||||||
|   # 验证码类型 math 数组计算 char 字符验证 |  | ||||||
|   type: MATH |  | ||||||
|   # line 线段干扰 circle 圆圈干扰 shear 扭曲干扰 |  | ||||||
|   category: CIRCLE |  | ||||||
|   # 数字验证码位数 |  | ||||||
|   numberLength: 1 |  | ||||||
|   # 字符验证码长度 |  | ||||||
|   charLength: 4 |  | ||||||
|  |  | ||||||
| # 开发环境配置 | # 开发环境配置 | ||||||
| server: | server: | ||||||
|   # 服务器的HTTP端口,默认为8080 |   # 服务器的HTTP端口,默认为8080 | ||||||
| @@ -41,12 +20,25 @@ server: | |||||||
|       # 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载 |       # 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载 | ||||||
|       worker: 256 |       worker: 256 | ||||||
|  |  | ||||||
|  | captcha: | ||||||
|  |   enable: true | ||||||
|  |   # 页面 <参数设置> 可开启关闭 验证码校验 | ||||||
|  |   # 验证码类型 math 数组计算 char 字符验证 | ||||||
|  |   type: MATH | ||||||
|  |   # line 线段干扰 circle 圆圈干扰 shear 扭曲干扰 | ||||||
|  |   category: CIRCLE | ||||||
|  |   # 数字验证码位数 | ||||||
|  |   numberLength: 1 | ||||||
|  |   # 字符验证码长度 | ||||||
|  |   charLength: 4 | ||||||
|  |  | ||||||
| # 日志配置 | # 日志配置 | ||||||
| logging: | logging: | ||||||
|   level: |   level: | ||||||
|     org.dromara: @logging.level@ |     org.dromara: @logging.level@ | ||||||
|     org.springframework: warn |     org.springframework: warn | ||||||
|     org.mybatis.spring.mapper: error |     org.mybatis.spring.mapper: error | ||||||
|  |     org.apache.fury: warn | ||||||
|   config: classpath:logback-plus.xml |   config: classpath:logback-plus.xml | ||||||
|  |  | ||||||
| # 用户配置 | # 用户配置 | ||||||
| @@ -60,7 +52,7 @@ user: | |||||||
| # Spring配置 | # Spring配置 | ||||||
| spring: | spring: | ||||||
|   application: |   application: | ||||||
|     name: ${ruoyi.name} |     name: RuoYi-Vue-Plus | ||||||
|   threads: |   threads: | ||||||
|     # 开启虚拟线程 仅jdk21可用 |     # 开启虚拟线程 仅jdk21可用 | ||||||
|     virtual: |     virtual: | ||||||
| @@ -110,17 +102,15 @@ sa-token: | |||||||
| security: | security: | ||||||
|   # 排除路径 |   # 排除路径 | ||||||
|   excludes: |   excludes: | ||||||
|     # 静态资源 |  | ||||||
|     - /*.html |     - /*.html | ||||||
|     - /**/*.html |     - /**/*.html | ||||||
|     - /**/*.css |     - /**/*.css | ||||||
|     - /**/*.js |     - /**/*.js | ||||||
|     # 公共路径 |  | ||||||
|     - /favicon.ico |     - /favicon.ico | ||||||
|     - /error |     - /error | ||||||
|     # swagger 文档配置 |  | ||||||
|     - /*/api-docs |     - /*/api-docs | ||||||
|     - /*/api-docs/** |     - /*/api-docs/** | ||||||
|  |     - /warm-flow-ui/token-name | ||||||
|  |  | ||||||
| # 多租户配置 | # 多租户配置 | ||||||
| tenant: | tenant: | ||||||
| @@ -141,6 +131,8 @@ tenant: | |||||||
| # MyBatisPlus配置 | # MyBatisPlus配置 | ||||||
| # https://baomidou.com/config/ | # https://baomidou.com/config/ | ||||||
| mybatis-plus: | mybatis-plus: | ||||||
|  |   # 自定义配置 是否全局开启逻辑删除 关闭后 所有逻辑删除功能将失效 | ||||||
|  |   enableLogicDelete: true | ||||||
|   # 多包名使用 例如 org.dromara.**.mapper,org.xxx.**.mapper |   # 多包名使用 例如 org.dromara.**.mapper,org.xxx.**.mapper | ||||||
|   mapperPackage: org.dromara.**.mapper |   mapperPackage: org.dromara.**.mapper | ||||||
|   # 对应的 XML 文件位置 |   # 对应的 XML 文件位置 | ||||||
| @@ -185,12 +177,9 @@ springdoc: | |||||||
|   api-docs: |   api-docs: | ||||||
|     # 是否开启接口文档 |     # 是否开启接口文档 | ||||||
|     enabled: true |     enabled: true | ||||||
| #  swagger-ui: |  | ||||||
| #    # 持久化认证数据 |  | ||||||
| #    persistAuthorization: true |  | ||||||
|   info: |   info: | ||||||
|     # 标题 |     # 标题 | ||||||
|     title: '标题:${ruoyi.name}多租户管理系统_接口文档' |     title: '标题:RuoYi-Vue-Plus多租户管理系统_接口文档' | ||||||
|     # 描述 |     # 描述 | ||||||
|     description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...' |     description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...' | ||||||
|     # 版本 |     # 版本 | ||||||
| @@ -217,15 +206,16 @@ springdoc: | |||||||
|       packages-to-scan: org.dromara.system |       packages-to-scan: org.dromara.system | ||||||
|     - group: 4.代码生成模块 |     - group: 4.代码生成模块 | ||||||
|       packages-to-scan: org.dromara.generator |       packages-to-scan: org.dromara.generator | ||||||
|  |     - group: 5.工作流模块 | ||||||
|  |       packages-to-scan: org.dromara.workflow | ||||||
|  |  | ||||||
| # 防止XSS攻击 | # 防止XSS攻击 | ||||||
| xss: | xss: | ||||||
|   # 过滤开关 |   # 过滤开关 | ||||||
|   enabled: true |   enabled: true | ||||||
|   # 排除链接(多个用逗号分隔) |   # 排除链接(多个用逗号分隔) | ||||||
|   excludes: /system/notice |   excludeUrls: | ||||||
|   # 匹配链接 |     - /system/notice | ||||||
|   urlPatterns: /system/*,/monitor/*,/tool/* |  | ||||||
|  |  | ||||||
| # 全局线程池相关配置 | # 全局线程池相关配置 | ||||||
| # 如使用JDK21请直接使用虚拟线程 不要开启此配置 | # 如使用JDK21请直接使用虚拟线程 不要开启此配置 | ||||||
| @@ -270,24 +260,19 @@ websocket: | |||||||
|   # 设置访问源地址 |   # 设置访问源地址 | ||||||
|   allowedOrigins: '*' |   allowedOrigins: '*' | ||||||
|  |  | ||||||
| --- #flowable配置 | --- # warm-flow工作流配置 | ||||||
| flowable: | warm-flow: | ||||||
|   # 开关 用于启动/停用工作流 |   # 是否开启工作流,默认true | ||||||
|   enabled: true |   enabled: true | ||||||
|   process.enabled: ${flowable.enabled} |   # 是否开启设计器ui | ||||||
|   eventregistry.enabled: ${flowable.enabled} |   ui: true | ||||||
|   async-executor-activate: false #关闭定时任务JOB |   # 默认Authorization,如果有多个token,用逗号分隔 | ||||||
|   #  将databaseSchemaUpdate设置为true。当Flowable发现库与数据库表结构不一致时,会自动将数据库表结构升级至新版本。 |   token-name: ${sa-token.token-name},clientid | ||||||
|   database-schema-update: true |   # 流程状态对应的三元色 | ||||||
|   activity-font-name: 宋体 |   chart-status-color: | ||||||
|   label-font-name: 宋体 |     ## 未办理 | ||||||
|   annotation-font-name: 宋体 |     - 62,62,62 | ||||||
|   # 关闭各个模块生成表,目前只使用工作流基础表 |     ## 待办理 | ||||||
|   idm: |     - 255,205,23 | ||||||
|     enabled: false |     ## 已办理 | ||||||
|   cmmn: |     - 157,255,0 | ||||||
|     enabled: false |  | ||||||
|   dmn: |  | ||||||
|     enabled: false |  | ||||||
|   app: |  | ||||||
|     enabled: false |  | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ | |||||||
| <configuration> | <configuration> | ||||||
|     <property name="log.path" value="./logs"/> |     <property name="log.path" value="./logs"/> | ||||||
|     <property name="console.log.pattern" |     <property name="console.log.pattern" | ||||||
|               value="%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/> |               value="%cyan(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/> | ||||||
|     <property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/> |     <property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/> | ||||||
|  |  | ||||||
|     <!-- 控制台输出 --> |     <!-- 控制台输出 --> | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| package org.dromara.test; | package org.dromara.test; | ||||||
|  |  | ||||||
| import org.dromara.common.core.config.RuoYiConfig; | import org.dromara.common.web.config.properties.CaptchaProperties; | ||||||
| import org.junit.jupiter.api.*; | import org.junit.jupiter.api.*; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; | import org.springframework.beans.factory.annotation.Autowired; | ||||||
| import org.springframework.boot.test.context.SpringBootTest; | import org.springframework.boot.test.context.SpringBootTest; | ||||||
| @@ -17,19 +17,19 @@ import java.util.concurrent.TimeUnit; | |||||||
| public class DemoUnitTest { | public class DemoUnitTest { | ||||||
|  |  | ||||||
|     @Autowired |     @Autowired | ||||||
|     private RuoYiConfig ruoYiConfig; |     private CaptchaProperties captchaProperties; | ||||||
|  |  | ||||||
|     @DisplayName("测试 @SpringBootTest @Test @DisplayName 注解") |     @DisplayName("测试 @SpringBootTest @Test @DisplayName 注解") | ||||||
|     @Test |     @Test | ||||||
|     public void testTest() { |     public void testTest() { | ||||||
|         System.out.println(ruoYiConfig); |         System.out.println(captchaProperties); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Disabled |     @Disabled | ||||||
|     @DisplayName("测试 @Disabled 注解") |     @DisplayName("测试 @Disabled 注解") | ||||||
|     @Test |     @Test | ||||||
|     public void testDisabled() { |     public void testDisabled() { | ||||||
|         System.out.println(ruoYiConfig); |         System.out.println(captchaProperties); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Timeout(value = 2L, unit = TimeUnit.SECONDS) |     @Timeout(value = 2L, unit = TimeUnit.SECONDS) | ||||||
| @@ -37,7 +37,7 @@ public class DemoUnitTest { | |||||||
|     @Test |     @Test | ||||||
|     public void testTimeout() throws InterruptedException { |     public void testTimeout() throws InterruptedException { | ||||||
|         Thread.sleep(3000); |         Thread.sleep(3000); | ||||||
|         System.out.println(ruoYiConfig); |         System.out.println(captchaProperties); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								ruoyi-admin/zhFonts/.uuid
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								ruoyi-admin/zhFonts/.uuid
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | 3f2ee348-0303-40ca-bf03-03f48d2d2141 | ||||||
							
								
								
									
										
											BIN
										
									
								
								ruoyi-admin/zhFonts/SIMSUN.TTC
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								ruoyi-admin/zhFonts/SIMSUN.TTC
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										4
									
								
								ruoyi-admin/zhFonts/fonts.dir
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								ruoyi-admin/zhFonts/fonts.dir
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | 3 | ||||||
|  | SIMSUN.TTC -misc-simsun-medium-r-normal--0-0-0-0-p-0-iso10646-1 | ||||||
|  | SIMSUN.TTC -misc-simsun-medium-r-normal--0-0-0-0-p-0-iso8859-1 | ||||||
|  | SIMSUN.TTC -misc-simsun-medium-r-normal--0-0-0-0-p-0-koi8-r | ||||||
							
								
								
									
										4
									
								
								ruoyi-admin/zhFonts/fonts.scale
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								ruoyi-admin/zhFonts/fonts.scale
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | 3 | ||||||
|  | SIMSUN.TTC -misc-simsun-medium-r-normal--0-0-0-0-p-0-iso10646-1 | ||||||
|  | SIMSUN.TTC -misc-simsun-medium-r-normal--0-0-0-0-p-0-iso8859-1 | ||||||
|  | SIMSUN.TTC -misc-simsun-medium-r-normal--0-0-0-0-p-0-koi8-r | ||||||
| @@ -14,7 +14,7 @@ | |||||||
|     </description> |     </description> | ||||||
|  |  | ||||||
|     <properties> |     <properties> | ||||||
|         <revision>5.2.2</revision> |         <revision>5.3.1</revision> | ||||||
|     </properties> |     </properties> | ||||||
|  |  | ||||||
|     <dependencyManagement> |     <dependencyManagement> | ||||||
|   | |||||||
| @@ -1,33 +0,0 @@ | |||||||
| package org.dromara.common.core.config; |  | ||||||
|  |  | ||||||
| import lombok.Data; |  | ||||||
| import org.springframework.boot.context.properties.ConfigurationProperties; |  | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * 读取项目相关配置 |  | ||||||
|  * |  | ||||||
|  * @author Lion Li |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| @Data |  | ||||||
| @Component |  | ||||||
| @ConfigurationProperties(prefix = "ruoyi") |  | ||||||
| public class RuoYiConfig { |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 项目名称 |  | ||||||
|      */ |  | ||||||
|     private String name; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 版本 |  | ||||||
|      */ |  | ||||||
|     private String version; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 版权年份 |  | ||||||
|      */ |  | ||||||
|     private String copyrightYear; |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @@ -4,11 +4,13 @@ import jakarta.annotation.PreDestroy; | |||||||
| import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||||
| import org.apache.commons.lang3.concurrent.BasicThreadFactory; | import org.apache.commons.lang3.concurrent.BasicThreadFactory; | ||||||
| import org.dromara.common.core.config.properties.ThreadPoolProperties; | import org.dromara.common.core.config.properties.ThreadPoolProperties; | ||||||
|  | import org.dromara.common.core.utils.SpringUtils; | ||||||
| import org.dromara.common.core.utils.Threads; | import org.dromara.common.core.utils.Threads; | ||||||
| import org.springframework.boot.autoconfigure.AutoConfiguration; | import org.springframework.boot.autoconfigure.AutoConfiguration; | ||||||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||||||
| import org.springframework.boot.context.properties.EnableConfigurationProperties; | import org.springframework.boot.context.properties.EnableConfigurationProperties; | ||||||
| import org.springframework.context.annotation.Bean; | import org.springframework.context.annotation.Bean; | ||||||
|  | import org.springframework.core.task.VirtualThreadTaskExecutor; | ||||||
| import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; | import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; | ||||||
|  |  | ||||||
| import java.util.concurrent.ScheduledExecutorService; | import java.util.concurrent.ScheduledExecutorService; | ||||||
| @@ -49,8 +51,15 @@ public class ThreadPoolConfig { | |||||||
|      */ |      */ | ||||||
|     @Bean(name = "scheduledExecutorService") |     @Bean(name = "scheduledExecutorService") | ||||||
|     protected ScheduledExecutorService scheduledExecutorService() { |     protected ScheduledExecutorService scheduledExecutorService() { | ||||||
|  |         // daemon 必须为 true | ||||||
|  |         BasicThreadFactory.Builder builder = new BasicThreadFactory.Builder().daemon(true); | ||||||
|  |         if (SpringUtils.isVirtual()) { | ||||||
|  |             builder.namingPattern("virtual-schedule-pool-%d").wrappedFactory(new VirtualThreadTaskExecutor().getVirtualThreadFactory()); | ||||||
|  |         } else { | ||||||
|  |             builder.namingPattern("schedule-pool-%d"); | ||||||
|  |         } | ||||||
|         ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(core, |         ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(core, | ||||||
|             new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(), |             builder.build(), | ||||||
|             new ThreadPoolExecutor.CallerRunsPolicy()) { |             new ThreadPoolExecutor.CallerRunsPolicy()) { | ||||||
|             @Override |             @Override | ||||||
|             protected void afterExecute(Runnable r, Throwable t) { |             protected void afterExecute(Runnable r, Throwable t) { | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ package org.dromara.common.core.config; | |||||||
| import jakarta.validation.Validator; | import jakarta.validation.Validator; | ||||||
| import org.hibernate.validator.HibernateValidator; | import org.hibernate.validator.HibernateValidator; | ||||||
| import org.springframework.boot.autoconfigure.AutoConfiguration; | import org.springframework.boot.autoconfigure.AutoConfiguration; | ||||||
|  | import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration; | ||||||
| import org.springframework.context.MessageSource; | import org.springframework.context.MessageSource; | ||||||
| import org.springframework.context.annotation.Bean; | import org.springframework.context.annotation.Bean; | ||||||
| import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; | import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; | ||||||
| @@ -14,11 +15,11 @@ import java.util.Properties; | |||||||
|  * |  * | ||||||
|  * @author Lion Li |  * @author Lion Li | ||||||
|  */ |  */ | ||||||
| @AutoConfiguration | @AutoConfiguration(before = ValidationAutoConfiguration.class) | ||||||
| public class ValidatorConfig { | public class ValidatorConfig { | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 配置校验框架 快速返回模式 |      * 配置校验框架 快速失败模式 | ||||||
|      */ |      */ | ||||||
|     @Bean |     @Bean | ||||||
|     public Validator validator(MessageSource messageSource) { |     public Validator validator(MessageSource messageSource) { | ||||||
| @@ -28,7 +29,7 @@ public class ValidatorConfig { | |||||||
|             // 设置使用 HibernateValidator 校验器 |             // 设置使用 HibernateValidator 校验器 | ||||||
|             factoryBean.setProviderClass(HibernateValidator.class); |             factoryBean.setProviderClass(HibernateValidator.class); | ||||||
|             Properties properties = new Properties(); |             Properties properties = new Properties(); | ||||||
|             // 设置 快速异常返回 |             // 设置快速失败模式(fail-fast),即校验过程中一旦遇到失败,立即停止并返回错误 | ||||||
|             properties.setProperty("hibernate.validator.fail_fast", "true"); |             properties.setProperty("hibernate.validator.fail_fast", "true"); | ||||||
|             factoryBean.setValidationProperties(properties); |             factoryBean.setValidationProperties(properties); | ||||||
|             // 加载配置 |             // 加载配置 | ||||||
|   | |||||||
| @@ -3,13 +3,14 @@ package org.dromara.common.core.constant; | |||||||
| /** | /** | ||||||
|  * 缓存组名称常量 |  * 缓存组名称常量 | ||||||
|  * <p> |  * <p> | ||||||
|  * key 格式为 cacheNames#ttl#maxIdleTime#maxSize |  * key 格式为 cacheNames#ttl#maxIdleTime#maxSize#local | ||||||
|  * <p> |  * <p> | ||||||
|  * ttl 过期时间 如果设置为0则不过期 默认为0 |  * ttl 过期时间 如果设置为0则不过期 默认为0 | ||||||
|  * maxIdleTime 最大空闲时间 根据LRU算法清理空闲数据 如果设置为0则不检测 默认为0 |  * maxIdleTime 最大空闲时间 根据LRU算法清理空闲数据 如果设置为0则不检测 默认为0 | ||||||
|  * maxSize 组最大长度 根据LRU算法清理溢出数据 如果设置为0则无限长 默认为0 |  * maxSize 组最大长度 根据LRU算法清理溢出数据 如果设置为0则无限长 默认为0 | ||||||
|  |  * local 默认开启本地缓存为1 关闭本地缓存为0 | ||||||
|  * <p> |  * <p> | ||||||
|  * 例子: test#60s、test#0#60s、test#0#1m#1000、test#1h#0#500 |  * 例子: test#60s、test#0#60s、test#0#1m#1000、test#1h#0#500、test#1h#0#500#0 | ||||||
|  * |  * | ||||||
|  * @author Lion Li |  * @author Lion Li | ||||||
|  */ |  */ | ||||||
| @@ -30,6 +31,11 @@ public interface CacheNames { | |||||||
|      */ |      */ | ||||||
|     String SYS_DICT = "sys_dict"; |     String SYS_DICT = "sys_dict"; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 数据字典类型 | ||||||
|  |      */ | ||||||
|  |     String SYS_DICT_TYPE = "sys_dict_type"; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 租户 |      * 租户 | ||||||
|      */ |      */ | ||||||
| @@ -60,6 +66,16 @@ public interface CacheNames { | |||||||
|      */ |      */ | ||||||
|     String SYS_OSS = "sys_oss#30d"; |     String SYS_OSS = "sys_oss#30d"; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 角色自定义权限 | ||||||
|  |      */ | ||||||
|  |     String SYS_ROLE_CUSTOM = "sys_role_custom#30d"; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 部门及以下权限 | ||||||
|  |      */ | ||||||
|  |     String SYS_DEPT_AND_CHILD = "sys_dept_and_child#30d"; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * OSS配置 |      * OSS配置 | ||||||
|      */ |      */ | ||||||
|   | |||||||
| @@ -68,12 +68,7 @@ public interface Constants { | |||||||
|     Integer CAPTCHA_EXPIRATION = 2; |     Integer CAPTCHA_EXPIRATION = 2; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 令牌 |      * 顶级父级id | ||||||
|      */ |  | ||||||
|     String TOKEN = "token"; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 顶级部门id |  | ||||||
|      */ |      */ | ||||||
|     Long TOP_PARENT_ID = 0L; |     Long TOP_PARENT_ID = 0L; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -17,9 +17,14 @@ public interface RegexConstants extends RegexPool { | |||||||
|     String DICTIONARY_TYPE = "^[a-z][a-z0-9_]*$"; |     String DICTIONARY_TYPE = "^[a-z][a-z0-9_]*$"; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 权限标识必须符合 tool:build:list 格式,或者空字符串 |      * 权限标识必须符合以下格式: | ||||||
|  |      * 1. 标准格式:xxx:yyy:zzz | ||||||
|  |      * - 第一部分(xxx):只能包含字母、数字和下划线(_),不能使用 `*` | ||||||
|  |      * - 第二部分(yyy):可以包含字母、数字、下划线(_)和 `*` | ||||||
|  |      * - 第三部分(zzz):可以包含字母、数字、下划线(_)和 `*` | ||||||
|  |      * 2. 允许空字符串(""),表示没有权限标识 | ||||||
|      */ |      */ | ||||||
|     String PERMISSION_STRING = "^(|^[a-zA-Z0-9_]+:[a-zA-Z0-9_]+:[a-zA-Z0-9_]+)$"; |     String PERMISSION_STRING = "^$|^[a-zA-Z0-9_]+:[a-zA-Z0-9_*]+:[a-zA-Z0-9_*]+$"; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 身份证号码(后6位) |      * 身份证号码(后6位) | ||||||
|   | |||||||
| @@ -0,0 +1,80 @@ | |||||||
|  | package org.dromara.common.core.constant; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 系统常量信息 | ||||||
|  |  * | ||||||
|  |  * @author Lion Li | ||||||
|  |  */ | ||||||
|  | public interface SystemConstants { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 正常状态 | ||||||
|  |      */ | ||||||
|  |     String NORMAL = "0"; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 异常状态 | ||||||
|  |      */ | ||||||
|  |     String DISABLE = "1"; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 是否为系统默认(是) | ||||||
|  |      */ | ||||||
|  |     String YES = "Y"; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 是否为系统默认(否) | ||||||
|  |      */ | ||||||
|  |     String NO = "N"; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 是否菜单外链(是) | ||||||
|  |      */ | ||||||
|  |     String YES_FRAME = "0"; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 是否菜单外链(否) | ||||||
|  |      */ | ||||||
|  |     String NO_FRAME = "1"; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 菜单类型(目录) | ||||||
|  |      */ | ||||||
|  |     String TYPE_DIR = "M"; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 菜单类型(菜单) | ||||||
|  |      */ | ||||||
|  |     String TYPE_MENU = "C"; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 菜单类型(按钮) | ||||||
|  |      */ | ||||||
|  |     String TYPE_BUTTON = "F"; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Layout组件标识 | ||||||
|  |      */ | ||||||
|  |     String LAYOUT = "Layout"; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * ParentView组件标识 | ||||||
|  |      */ | ||||||
|  |     String PARENT_VIEW = "ParentView"; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * InnerLink组件标识 | ||||||
|  |      */ | ||||||
|  |     String INNER_LINK = "InnerLink"; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 超级管理员ID | ||||||
|  |      */ | ||||||
|  |     Long SUPER_ADMIN_ID = 1L; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 根部门祖级列表 | ||||||
|  |      */ | ||||||
|  |     String ROOT_DEPT_ANCESTORS = "0"; | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -7,16 +7,6 @@ package org.dromara.common.core.constant; | |||||||
|  */ |  */ | ||||||
| public interface TenantConstants { | public interface TenantConstants { | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 租户正常状态 |  | ||||||
|      */ |  | ||||||
|     String NORMAL = "0"; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 租户封禁状态 |  | ||||||
|      */ |  | ||||||
|     String DISABLE = "1"; |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 超级管理员ID |      * 超级管理员ID | ||||||
|      */ |      */ | ||||||
|   | |||||||
| @@ -1,152 +0,0 @@ | |||||||
| package org.dromara.common.core.constant; |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * 用户常量信息 |  | ||||||
|  * |  | ||||||
|  * @author ruoyi |  | ||||||
|  */ |  | ||||||
| public interface UserConstants { |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 平台内系统用户的唯一标志 |  | ||||||
|      */ |  | ||||||
|     String SYS_USER = "SYS_USER"; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 正常状态 |  | ||||||
|      */ |  | ||||||
|     String NORMAL = "0"; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 异常状态 |  | ||||||
|      */ |  | ||||||
|     String EXCEPTION = "1"; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 用户正常状态 |  | ||||||
|      */ |  | ||||||
|     String USER_NORMAL = "0"; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 用户封禁状态 |  | ||||||
|      */ |  | ||||||
|     String USER_DISABLE = "1"; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 角色正常状态 |  | ||||||
|      */ |  | ||||||
|     String ROLE_NORMAL = "0"; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 角色封禁状态 |  | ||||||
|      */ |  | ||||||
|     String ROLE_DISABLE = "1"; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 部门正常状态 |  | ||||||
|      */ |  | ||||||
|     String DEPT_NORMAL = "0"; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 部门停用状态 |  | ||||||
|      */ |  | ||||||
|     String DEPT_DISABLE = "1"; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 岗位正常状态 |  | ||||||
|      */ |  | ||||||
|     String POST_NORMAL = "0"; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 岗位停用状态 |  | ||||||
|      */ |  | ||||||
|     String POST_DISABLE = "1"; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 字典正常状态 |  | ||||||
|      */ |  | ||||||
|     String DICT_NORMAL = "0"; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 通用存在标志 |  | ||||||
|      */ |  | ||||||
|     String DEL_FLAG_NORMAL = "0"; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 通用删除标志 |  | ||||||
|      */ |  | ||||||
|     String DEL_FLAG_REMOVED  = "2"; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 是否为系统默认(是) |  | ||||||
|      */ |  | ||||||
|     String YES = "Y"; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 是否菜单外链(是) |  | ||||||
|      */ |  | ||||||
|     String YES_FRAME = "0"; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 是否菜单外链(否) |  | ||||||
|      */ |  | ||||||
|     String NO_FRAME = "1"; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 菜单正常状态 |  | ||||||
|      */ |  | ||||||
|     String MENU_NORMAL = "0"; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 菜单停用状态 |  | ||||||
|      */ |  | ||||||
|     String MENU_DISABLE = "1"; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 菜单类型(目录) |  | ||||||
|      */ |  | ||||||
|     String TYPE_DIR = "M"; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 菜单类型(菜单) |  | ||||||
|      */ |  | ||||||
|     String TYPE_MENU = "C"; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 菜单类型(按钮) |  | ||||||
|      */ |  | ||||||
|     String TYPE_BUTTON = "F"; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Layout组件标识 |  | ||||||
|      */ |  | ||||||
|     String LAYOUT = "Layout"; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * ParentView组件标识 |  | ||||||
|      */ |  | ||||||
|     String PARENT_VIEW = "ParentView"; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * InnerLink组件标识 |  | ||||||
|      */ |  | ||||||
|     String INNER_LINK = "InnerLink"; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 用户名长度限制 |  | ||||||
|      */ |  | ||||||
|     int USERNAME_MIN_LENGTH = 2; |  | ||||||
|     int USERNAME_MAX_LENGTH = 20; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 密码长度限制 |  | ||||||
|      */ |  | ||||||
|     int PASSWORD_MIN_LENGTH = 5; |  | ||||||
|     int PASSWORD_MAX_LENGTH = 20; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 超级管理员ID |  | ||||||
|      */ |  | ||||||
|     Long SUPER_ADMIN_ID = 1L; |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @@ -0,0 +1,71 @@ | |||||||
|  | package org.dromara.common.core.domain.dto; | ||||||
|  |  | ||||||
|  | import lombok.Data; | ||||||
|  |  | ||||||
|  | import java.io.Serial; | ||||||
|  | import java.io.Serializable; | ||||||
|  | import java.util.HashMap; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.Map; | ||||||
|  | import java.util.Objects; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 办理任务请求对象 | ||||||
|  |  * | ||||||
|  |  * @author may | ||||||
|  |  */ | ||||||
|  | @Data | ||||||
|  | public class CompleteTaskDTO implements Serializable { | ||||||
|  |  | ||||||
|  |     @Serial | ||||||
|  |     private static final long serialVersionUID = 1L; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 任务id | ||||||
|  |      */ | ||||||
|  |     private Long taskId; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 附件id | ||||||
|  |      */ | ||||||
|  |     private String fileId; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 抄送人员 | ||||||
|  |      */ | ||||||
|  |     private List<FlowCopyDTO> flowCopyList; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 消息类型 | ||||||
|  |      */ | ||||||
|  |     private List<String> messageType; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 办理意见 | ||||||
|  |      */ | ||||||
|  |     private String message; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 消息通知 | ||||||
|  |      */ | ||||||
|  |     private String notice; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 流程变量 | ||||||
|  |      */ | ||||||
|  |     private Map<String, Object> variables; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 扩展变量(此处为逗号分隔的ossId) | ||||||
|  |      */ | ||||||
|  |     private String ext; | ||||||
|  |  | ||||||
|  |     public Map<String, Object> getVariables() { | ||||||
|  |         if (variables == null) { | ||||||
|  |             return new HashMap<>(16); | ||||||
|  |         } | ||||||
|  |         variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue())); | ||||||
|  |         return variables; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,36 @@ | |||||||
|  | package org.dromara.common.core.domain.dto; | ||||||
|  |  | ||||||
|  | import lombok.Data; | ||||||
|  | import lombok.NoArgsConstructor; | ||||||
|  |  | ||||||
|  | import java.io.Serial; | ||||||
|  | import java.io.Serializable; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 部门 | ||||||
|  |  * | ||||||
|  |  * @author AprilWind | ||||||
|  |  */ | ||||||
|  | @Data | ||||||
|  | @NoArgsConstructor | ||||||
|  | public class DeptDTO implements Serializable { | ||||||
|  |  | ||||||
|  |     @Serial | ||||||
|  |     private static final long serialVersionUID = 1L; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 部门ID | ||||||
|  |      */ | ||||||
|  |     private Long deptId; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 父部门ID | ||||||
|  |      */ | ||||||
|  |     private Long parentId; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 部门名称 | ||||||
|  |      */ | ||||||
|  |     private String deptName; | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,41 @@ | |||||||
|  | package org.dromara.common.core.domain.dto; | ||||||
|  |  | ||||||
|  | import lombok.Data; | ||||||
|  | import lombok.NoArgsConstructor; | ||||||
|  |  | ||||||
|  | import java.io.Serial; | ||||||
|  | import java.io.Serializable; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 字典数据DTO | ||||||
|  |  * | ||||||
|  |  * @author AprilWind | ||||||
|  |  */ | ||||||
|  | @Data | ||||||
|  | @NoArgsConstructor | ||||||
|  | public class DictDataDTO implements Serializable { | ||||||
|  |  | ||||||
|  |     @Serial | ||||||
|  |     private static final long serialVersionUID = 1L; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 字典标签 | ||||||
|  |      */ | ||||||
|  |     private String dictLabel; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 字典键值 | ||||||
|  |      */ | ||||||
|  |     private String dictValue; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 是否默认(Y是 N否) | ||||||
|  |      */ | ||||||
|  |     private String isDefault; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 备注 | ||||||
|  |      */ | ||||||
|  |     private String remark; | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,41 @@ | |||||||
|  | package org.dromara.common.core.domain.dto; | ||||||
|  |  | ||||||
|  | import lombok.Data; | ||||||
|  | import lombok.NoArgsConstructor; | ||||||
|  |  | ||||||
|  | import java.io.Serial; | ||||||
|  | import java.io.Serializable; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 字典类型DTO | ||||||
|  |  * | ||||||
|  |  * @author AprilWind | ||||||
|  |  */ | ||||||
|  | @Data | ||||||
|  | @NoArgsConstructor | ||||||
|  | public class DictTypeDTO implements Serializable { | ||||||
|  |  | ||||||
|  |     @Serial | ||||||
|  |     private static final long serialVersionUID = 1L; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 字典主键 | ||||||
|  |      */ | ||||||
|  |     private Long dictId; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 字典名称 | ||||||
|  |      */ | ||||||
|  |     private String dictName; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 字典类型 | ||||||
|  |      */ | ||||||
|  |     private String dictType; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 备注 | ||||||
|  |      */ | ||||||
|  |     private String remark; | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -1,28 +1,30 @@ | |||||||
| package org.dromara.workflow.domain.vo; | package org.dromara.common.core.domain.dto; | ||||||
| 
 | 
 | ||||||
| import lombok.Data; | import lombok.Data; | ||||||
| 
 | 
 | ||||||
| import java.io.Serial; | import java.io.Serial; | ||||||
| import java.io.Serializable; | import java.io.Serializable; | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * 流程变量 |  * 抄送 | ||||||
|  * |  * | ||||||
|  * @author may |  * @author may | ||||||
|  */ |  */ | ||||||
| @Data | @Data | ||||||
| public class VariableVo implements Serializable { | public class FlowCopyDTO implements Serializable { | ||||||
| 
 | 
 | ||||||
|     @Serial |     @Serial | ||||||
|     private static final long serialVersionUID = 1L; |     private static final long serialVersionUID = 1L; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * 变量key |      * 用户id | ||||||
|      */ |      */ | ||||||
|     private String key; |     private Long userId; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * 变量值 |      * 用户名称 | ||||||
|      */ |      */ | ||||||
|     private String value; |     private String userName; | ||||||
|  | 
 | ||||||
| } | } | ||||||
| @@ -0,0 +1,46 @@ | |||||||
|  | package org.dromara.common.core.domain.dto; | ||||||
|  |  | ||||||
|  | import lombok.Data; | ||||||
|  | import lombok.NoArgsConstructor; | ||||||
|  |  | ||||||
|  | import java.io.Serial; | ||||||
|  | import java.io.Serializable; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 岗位 | ||||||
|  |  * | ||||||
|  |  * @author AprilWind | ||||||
|  |  */ | ||||||
|  | @Data | ||||||
|  | @NoArgsConstructor | ||||||
|  | public class PostDTO implements Serializable { | ||||||
|  |  | ||||||
|  |     @Serial | ||||||
|  |     private static final long serialVersionUID = 1L; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 岗位ID | ||||||
|  |      */ | ||||||
|  |     private Long postId; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 部门id | ||||||
|  |      */ | ||||||
|  |     private Long deptId; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 岗位编码 | ||||||
|  |      */ | ||||||
|  |     private String postCode; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 岗位名称 | ||||||
|  |      */ | ||||||
|  |     private String postName; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 岗位类别编码 | ||||||
|  |      */ | ||||||
|  |     private String postCategory; | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -35,7 +35,7 @@ public class RoleDTO implements Serializable { | |||||||
|     private String roleKey; |     private String roleKey; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) |      * 数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限 5:仅本人数据权限 6:部门及以下或本人数据权限) | ||||||
|      */ |      */ | ||||||
|     private String dataScope; |     private String dataScope; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -0,0 +1,45 @@ | |||||||
|  | package org.dromara.common.core.domain.dto; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | import lombok.Data; | ||||||
|  |  | ||||||
|  | import java.io.Serial; | ||||||
|  | import java.io.Serializable; | ||||||
|  | import java.util.HashMap; | ||||||
|  | import java.util.Map; | ||||||
|  | import java.util.Objects; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 启动流程对象 | ||||||
|  |  * | ||||||
|  |  * @author may | ||||||
|  |  */ | ||||||
|  | @Data | ||||||
|  | public class StartProcessDTO implements Serializable { | ||||||
|  |  | ||||||
|  |     @Serial | ||||||
|  |     private static final long serialVersionUID = 1L; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 业务唯一值id | ||||||
|  |      */ | ||||||
|  |     private String businessId; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 流程定义编码 | ||||||
|  |      */ | ||||||
|  |     private String flowCode; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 流程变量,前端会提交一个元素{'entity': {业务详情数据对象}} | ||||||
|  |      */ | ||||||
|  |     private Map<String, Object> variables; | ||||||
|  |  | ||||||
|  |     public Map<String, Object> getVariables() { | ||||||
|  |         if (variables == null) { | ||||||
|  |             return new HashMap<>(16); | ||||||
|  |         } | ||||||
|  |         variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue())); | ||||||
|  |         return variables; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,30 @@ | |||||||
|  | package org.dromara.common.core.domain.dto; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | import lombok.Data; | ||||||
|  |  | ||||||
|  | import java.io.Serial; | ||||||
|  | import java.io.Serializable; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 启动流程返回对象 | ||||||
|  |  * | ||||||
|  |  * @author Lion Li | ||||||
|  |  */ | ||||||
|  | @Data | ||||||
|  | public class StartProcessReturnDTO implements Serializable { | ||||||
|  |  | ||||||
|  |     @Serial | ||||||
|  |     private static final long serialVersionUID = 1L; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 流程实例id | ||||||
|  |      */ | ||||||
|  |     private Long processInstanceId; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 任务id | ||||||
|  |      */ | ||||||
|  |     private Long taskId; | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,101 @@ | |||||||
|  | package org.dromara.common.core.domain.dto; | ||||||
|  |  | ||||||
|  | import lombok.AllArgsConstructor; | ||||||
|  | import lombok.Data; | ||||||
|  | import lombok.NoArgsConstructor; | ||||||
|  |  | ||||||
|  | import java.io.Serial; | ||||||
|  | import java.io.Serializable; | ||||||
|  | import java.util.Date; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.function.Function; | ||||||
|  | import java.util.stream.Collectors; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 任务受让人 | ||||||
|  |  * | ||||||
|  |  * @author AprilWind | ||||||
|  |  */ | ||||||
|  | @Data | ||||||
|  | @NoArgsConstructor | ||||||
|  | public class TaskAssigneeDTO implements Serializable { | ||||||
|  |  | ||||||
|  |     @Serial | ||||||
|  |     private static final long serialVersionUID = 1L; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 总大小 | ||||||
|  |      */ | ||||||
|  |     private Long total = 0L; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * | ||||||
|  |      */ | ||||||
|  |     private List<TaskHandler> list; | ||||||
|  |  | ||||||
|  |     public TaskAssigneeDTO(Long total, List<TaskHandler> list) { | ||||||
|  |         this.total = total; | ||||||
|  |         this.list = list; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 将源列表转换为 TaskHandler 列表 | ||||||
|  |      * | ||||||
|  |      * @param <T>              通用类型 | ||||||
|  |      * @param sourceList       待转换的源列表 | ||||||
|  |      * @param storageId        提取 storageId 的函数 | ||||||
|  |      * @param handlerCode      提取 handlerCode 的函数 | ||||||
|  |      * @param handlerName      提取 handlerName 的函数 | ||||||
|  |      * @param groupName        提取 groupName 的函数 | ||||||
|  |      * @param createTimeMapper 提取 createTime 的函数 | ||||||
|  |      * @return 转换后的 TaskHandler 列表 | ||||||
|  |      */ | ||||||
|  |     public static <T> List<TaskHandler> convertToHandlerList( | ||||||
|  |         List<T> sourceList, | ||||||
|  |         Function<T, Long> storageId, | ||||||
|  |         Function<T, String> handlerCode, | ||||||
|  |         Function<T, String> handlerName, | ||||||
|  |         Function<T, Long> groupName, | ||||||
|  |         Function<T, Date> createTimeMapper) { | ||||||
|  |         return sourceList.stream() | ||||||
|  |             .map(item -> new TaskHandler( | ||||||
|  |                 String.valueOf(storageId.apply(item)), | ||||||
|  |                 handlerCode.apply(item), | ||||||
|  |                 handlerName.apply(item), | ||||||
|  |                 groupName != null ? String.valueOf(groupName.apply(item)) : null, | ||||||
|  |                 createTimeMapper.apply(item) | ||||||
|  |             )).collect(Collectors.toList()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Data | ||||||
|  |     @NoArgsConstructor | ||||||
|  |     @AllArgsConstructor | ||||||
|  |     public static class TaskHandler { | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * 主键 | ||||||
|  |          */ | ||||||
|  |         private String storageId; | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * 权限编码 | ||||||
|  |          */ | ||||||
|  |         private String handlerCode; | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * 权限名称 | ||||||
|  |          */ | ||||||
|  |         private String handlerName; | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * 权限分组 | ||||||
|  |          */ | ||||||
|  |         private String groupName; | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * 创建时间 | ||||||
|  |          */ | ||||||
|  |         private Date createTime; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,54 @@ | |||||||
|  | package org.dromara.common.core.domain.event; | ||||||
|  |  | ||||||
|  | import lombok.Data; | ||||||
|  |  | ||||||
|  | import java.io.Serial; | ||||||
|  | import java.io.Serializable; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 流程创建任务监听 | ||||||
|  |  * | ||||||
|  |  * @author may | ||||||
|  |  */ | ||||||
|  | @Data | ||||||
|  | public class ProcessCreateTaskEvent implements Serializable { | ||||||
|  |  | ||||||
|  |     @Serial | ||||||
|  |     private static final long serialVersionUID = 1L; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 租户ID | ||||||
|  |      */ | ||||||
|  |     private String tenantId; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 流程定义编码 | ||||||
|  |      */ | ||||||
|  |     private String flowCode; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关) | ||||||
|  |      */ | ||||||
|  |     private Integer nodeType; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 流程节点编码 | ||||||
|  |      */ | ||||||
|  |     private String nodeCode; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 流程节点名称 | ||||||
|  |      */ | ||||||
|  |     private String nodeName; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 任务id | ||||||
|  |      */ | ||||||
|  |     private Long taskId; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 业务id | ||||||
|  |      */ | ||||||
|  |     private String businessId; | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,34 @@ | |||||||
|  | package org.dromara.common.core.domain.event; | ||||||
|  |  | ||||||
|  | import lombok.Data; | ||||||
|  |  | ||||||
|  | import java.io.Serial; | ||||||
|  | import java.io.Serializable; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 删除流程监听 | ||||||
|  |  * | ||||||
|  |  * @author AprilWind | ||||||
|  |  */ | ||||||
|  | @Data | ||||||
|  | public class ProcessDeleteEvent implements Serializable { | ||||||
|  |  | ||||||
|  |     @Serial | ||||||
|  |     private static final long serialVersionUID = 1L; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 租户ID | ||||||
|  |      */ | ||||||
|  |     private String tenantId; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 流程定义编码 | ||||||
|  |      */ | ||||||
|  |     private String flowCode; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 业务id | ||||||
|  |      */ | ||||||
|  |     private String businessId; | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -4,13 +4,13 @@ import lombok.Data; | |||||||
|  |  | ||||||
| import java.io.Serial; | import java.io.Serial; | ||||||
| import java.io.Serializable; | import java.io.Serializable; | ||||||
|  | import java.util.Map; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * 总体流程监听 |  * 总体流程监听 | ||||||
|  * |  * | ||||||
|  * @author may |  * @author may | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| @Data | @Data | ||||||
| public class ProcessEvent implements Serializable { | public class ProcessEvent implements Serializable { | ||||||
|  |  | ||||||
| @@ -18,24 +18,48 @@ public class ProcessEvent implements Serializable { | |||||||
|     private static final long serialVersionUID = 1L; |     private static final long serialVersionUID = 1L; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 流程定义key |      * 租户ID | ||||||
|      */ |      */ | ||||||
|     private String key; |     private String tenantId; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 流程定义编码 | ||||||
|  |      */ | ||||||
|  |     private String flowCode; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 业务id |      * 业务id | ||||||
|      */ |      */ | ||||||
|     private String businessKey; |     private String businessId; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 状态 |      * 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关) | ||||||
|  |      */ | ||||||
|  |     private Integer nodeType; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 流程节点编码 | ||||||
|  |      */ | ||||||
|  |     private String nodeCode; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 流程节点名称 | ||||||
|  |      */ | ||||||
|  |     private String nodeName; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 流程状态 | ||||||
|      */ |      */ | ||||||
|     private String status; |     private String status; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 办理参数 | ||||||
|  |      */ | ||||||
|  |     private Map<String, Object> params; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 当为true时为申请人节点办理 |      * 当为true时为申请人节点办理 | ||||||
|      */ |      */ | ||||||
|     private boolean submit; |     private Boolean submit; | ||||||
|  |  | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,40 +0,0 @@ | |||||||
| package org.dromara.common.core.domain.event; |  | ||||||
|  |  | ||||||
| import lombok.Data; |  | ||||||
|  |  | ||||||
| import java.io.Serial; |  | ||||||
| import java.io.Serializable; |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * 流程办理监听 |  | ||||||
|  * |  | ||||||
|  * @author may |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| @Data |  | ||||||
| public class ProcessTaskEvent implements Serializable { |  | ||||||
|  |  | ||||||
|     @Serial |  | ||||||
|     private static final long serialVersionUID = 1L; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 流程定义key |  | ||||||
|      */ |  | ||||||
|     private String key; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 审批节点key |  | ||||||
|      */ |  | ||||||
|     private String taskDefinitionKey; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 任务id |  | ||||||
|      */ |  | ||||||
|     private String taskId; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 业务id |  | ||||||
|      */ |  | ||||||
|     private String businessKey; |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @@ -1,8 +1,9 @@ | |||||||
| package org.dromara.common.core.domain.model; | package org.dromara.common.core.domain.model; | ||||||
|  |  | ||||||
| import org.dromara.common.core.domain.dto.RoleDTO; |  | ||||||
| import lombok.Data; | import lombok.Data; | ||||||
| import lombok.NoArgsConstructor; | import lombok.NoArgsConstructor; | ||||||
|  | import org.dromara.common.core.domain.dto.PostDTO; | ||||||
|  | import org.dromara.common.core.domain.dto.RoleDTO; | ||||||
|  |  | ||||||
| import java.io.Serial; | import java.io.Serial; | ||||||
| import java.io.Serializable; | import java.io.Serializable; | ||||||
| @@ -111,6 +112,11 @@ public class LoginUser implements Serializable { | |||||||
|      */ |      */ | ||||||
|     private List<RoleDTO> roles; |     private List<RoleDTO> roles; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 岗位对象 | ||||||
|  |      */ | ||||||
|  |     private List<PostDTO> posts; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 数据权限 当前角色ID |      * 数据权限 当前角色ID | ||||||
|      */ |      */ | ||||||
|   | |||||||
| @@ -5,8 +5,6 @@ import lombok.Data; | |||||||
| import lombok.EqualsAndHashCode; | import lombok.EqualsAndHashCode; | ||||||
| import org.hibernate.validator.constraints.Length; | import org.hibernate.validator.constraints.Length; | ||||||
|  |  | ||||||
| import static org.dromara.common.core.constant.UserConstants.*; |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * 密码登录对象 |  * 密码登录对象 | ||||||
|  * |  * | ||||||
| @@ -20,14 +18,14 @@ public class PasswordLoginBody extends LoginBody { | |||||||
|      * 用户名 |      * 用户名 | ||||||
|      */ |      */ | ||||||
|     @NotBlank(message = "{user.username.not.blank}") |     @NotBlank(message = "{user.username.not.blank}") | ||||||
|     @Length(min = USERNAME_MIN_LENGTH, max = USERNAME_MAX_LENGTH, message = "{user.username.length.valid}") |     @Length(min = 2, max = 30, message = "{user.username.length.valid}") | ||||||
|     private String username; |     private String username; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 用户密码 |      * 用户密码 | ||||||
|      */ |      */ | ||||||
|     @NotBlank(message = "{user.password.not.blank}") |     @NotBlank(message = "{user.password.not.blank}") | ||||||
|     @Length(min = PASSWORD_MIN_LENGTH, max = PASSWORD_MAX_LENGTH, message = "{user.password.length.valid}") |     @Length(min = 5, max = 30, message = "{user.password.length.valid}") | ||||||
|     private String password; |     private String password; | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -5,8 +5,6 @@ import lombok.Data; | |||||||
| import lombok.EqualsAndHashCode; | import lombok.EqualsAndHashCode; | ||||||
| import org.hibernate.validator.constraints.Length; | import org.hibernate.validator.constraints.Length; | ||||||
|  |  | ||||||
| import static org.dromara.common.core.constant.UserConstants.*; |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * 用户注册对象 |  * 用户注册对象 | ||||||
|  * |  * | ||||||
| @@ -20,14 +18,14 @@ public class RegisterBody extends LoginBody { | |||||||
|      * 用户名 |      * 用户名 | ||||||
|      */ |      */ | ||||||
|     @NotBlank(message = "{user.username.not.blank}") |     @NotBlank(message = "{user.username.not.blank}") | ||||||
|     @Length(min = USERNAME_MIN_LENGTH, max = USERNAME_MAX_LENGTH, message = "{user.username.length.valid}") |     @Length(min = 2, max = 30, message = "{user.username.length.valid}") | ||||||
|     private String username; |     private String username; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 用户密码 |      * 用户密码 | ||||||
|      */ |      */ | ||||||
|     @NotBlank(message = "{user.password.not.blank}") |     @NotBlank(message = "{user.password.not.blank}") | ||||||
|     @Length(min = PASSWORD_MIN_LENGTH, max = PASSWORD_MAX_LENGTH, message = "{user.password.length.valid}") |     @Length(min = 5, max = 30, message = "{user.password.length.valid}") | ||||||
|     private String password; |     private String password; | ||||||
|  |  | ||||||
|     private String userType; |     private String userType; | ||||||
|   | |||||||
| @@ -0,0 +1,56 @@ | |||||||
|  | package org.dromara.common.core.domain.model; | ||||||
|  |  | ||||||
|  | import lombok.Data; | ||||||
|  | import lombok.NoArgsConstructor; | ||||||
|  |  | ||||||
|  | import java.io.Serial; | ||||||
|  | import java.io.Serializable; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 任务受让人 | ||||||
|  |  * | ||||||
|  |  * @author AprilWind | ||||||
|  |  */ | ||||||
|  | @Data | ||||||
|  | @NoArgsConstructor | ||||||
|  | public class TaskAssigneeBody implements Serializable { | ||||||
|  |  | ||||||
|  |     @Serial | ||||||
|  |     private static final long serialVersionUID = 1L; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 权限编码 | ||||||
|  |      */ | ||||||
|  |     private String handlerCode; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 权限名称 | ||||||
|  |      */ | ||||||
|  |     private String handlerName; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 权限分组 | ||||||
|  |      */ | ||||||
|  |     private String groupId; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 开始时间 | ||||||
|  |      */ | ||||||
|  |     private String beginTime; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 结束时间 | ||||||
|  |      */ | ||||||
|  |     private String endTime; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 当前页 | ||||||
|  |      */ | ||||||
|  |     private Integer pageNum = 1; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 每页显示条数 | ||||||
|  |      */ | ||||||
|  |     private Integer pageSize = 10; | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -7,6 +7,10 @@ import org.dromara.common.core.exception.ServiceException; | |||||||
| import org.dromara.common.core.utils.StringUtils; | import org.dromara.common.core.utils.StringUtils; | ||||||
|  |  | ||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.Map; | ||||||
|  | import java.util.function.Function; | ||||||
|  | import java.util.stream.Collectors; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * 业务状态枚举 |  * 业务状态枚举 | ||||||
| @@ -16,30 +20,37 @@ import java.util.Arrays; | |||||||
| @Getter | @Getter | ||||||
| @AllArgsConstructor | @AllArgsConstructor | ||||||
| public enum BusinessStatusEnum { | public enum BusinessStatusEnum { | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 已撤销 |      * 已撤销 | ||||||
|      */ |      */ | ||||||
|     CANCEL("cancel", "已撤销"), |     CANCEL("cancel", "已撤销"), | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 草稿 |      * 草稿 | ||||||
|      */ |      */ | ||||||
|     DRAFT("draft", "草稿"), |     DRAFT("draft", "草稿"), | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 待审核 |      * 待审核 | ||||||
|      */ |      */ | ||||||
|     WAITING("waiting", "待审核"), |     WAITING("waiting", "待审核"), | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 已完成 |      * 已完成 | ||||||
|      */ |      */ | ||||||
|     FINISH("finish", "已完成"), |     FINISH("finish", "已完成"), | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 已作废 |      * 已作废 | ||||||
|      */ |      */ | ||||||
|     INVALID("invalid", "已作废"), |     INVALID("invalid", "已作废"), | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 已退回 |      * 已退回 | ||||||
|      */ |      */ | ||||||
|     BACK("back", "已退回"), |     BACK("back", "已退回"), | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 已终止 |      * 已终止 | ||||||
|      */ |      */ | ||||||
| @@ -55,20 +66,72 @@ public enum BusinessStatusEnum { | |||||||
|      */ |      */ | ||||||
|     private final String desc; |     private final String desc; | ||||||
|  |  | ||||||
|  |     private static final Map<String, BusinessStatusEnum> STATUS_MAP = Arrays.stream(BusinessStatusEnum.values()) | ||||||
|  |         .collect(Collectors.toConcurrentMap(BusinessStatusEnum::getStatus, Function.identity())); | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 获取业务状态 |      * 根据状态获取对应的 BusinessStatusEnum 枚举 | ||||||
|      * |      * | ||||||
|      * @param status 状态 |      * @param status 业务状态码 | ||||||
|  |      * @return 对应的 BusinessStatusEnum 枚举,如果找不到则返回 null | ||||||
|  |      */ | ||||||
|  |     public static BusinessStatusEnum getByStatus(String status) { | ||||||
|  |         // 使用 STATUS_MAP 获取对应的枚举,若找不到则返回 null | ||||||
|  |         return STATUS_MAP.get(status); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 根据状态获取对应的业务状态描述信息 | ||||||
|  |      * | ||||||
|  |      * @param status 业务状态码 | ||||||
|  |      * @return 返回业务状态描述,若状态码为空或未找到对应的枚举,返回空字符串 | ||||||
|      */ |      */ | ||||||
|     public static String findByStatus(String status) { |     public static String findByStatus(String status) { | ||||||
|         if (StringUtils.isBlank(status)) { |         if (StringUtils.isBlank(status)) { | ||||||
|             return StrUtil.EMPTY; |             return StrUtil.EMPTY; | ||||||
|         } |         } | ||||||
|         return Arrays.stream(BusinessStatusEnum.values()) |         BusinessStatusEnum statusEnum = STATUS_MAP.get(status); | ||||||
|             .filter(statusEnum -> statusEnum.getStatus().equals(status)) |         return (statusEnum != null) ? statusEnum.getDesc() : StrUtil.EMPTY; | ||||||
|             .findFirst() |     } | ||||||
|             .map(BusinessStatusEnum::getDesc) |  | ||||||
|             .orElse(StrUtil.EMPTY); |     /** | ||||||
|  |      * 判断是否为指定的状态之一:草稿、已撤销或已退回 | ||||||
|  |      * | ||||||
|  |      * @param status 要检查的状态 | ||||||
|  |      * @return 如果状态为草稿、已撤销或已退回之一,则返回 true;否则返回 false | ||||||
|  |      */ | ||||||
|  |     public static boolean isDraftOrCancelOrBack(String status) { | ||||||
|  |         return DRAFT.status.equals(status) || CANCEL.status.equals(status) || BACK.status.equals(status); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 判断是否为撤销,退回,作废,终止 | ||||||
|  |      * | ||||||
|  |      * @param status status | ||||||
|  |      * @return 结果 | ||||||
|  |      */ | ||||||
|  |     public static boolean initialState(String status) { | ||||||
|  |         return CANCEL.status.equals(status) || BACK.status.equals(status) || INVALID.status.equals(status) || TERMINATION.status.equals(status); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 获取运行中的实例状态列表 | ||||||
|  |      * | ||||||
|  |      * @return 包含运行中实例状态的不可变列表 | ||||||
|  |      * (包含 DRAFT、WAITING、BACK 和 CANCEL 状态) | ||||||
|  |      */ | ||||||
|  |     public static List<String> runningStatus() { | ||||||
|  |         return Arrays.asList(DRAFT.status, WAITING.status, BACK.status, CANCEL.status); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 获取结束实例的状态列表 | ||||||
|  |      * | ||||||
|  |      * @return 包含结束实例状态的不可变列表 | ||||||
|  |      * (包含 FINISH、INVALID 和 TERMINATION 状态) | ||||||
|  |      */ | ||||||
|  |     public static List<String> finishStatus() { | ||||||
|  |         return Arrays.asList(FINISH.status, INVALID.status, TERMINATION.status); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -148,5 +211,5 @@ public enum BusinessStatusEnum { | |||||||
|             throw new ServiceException("流程状态为空!"); |             throw new ServiceException("流程状态为空!"); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } |  | ||||||
|  |  | ||||||
|  | } | ||||||
|   | |||||||
| @@ -5,7 +5,6 @@ import lombok.Getter; | |||||||
|  |  | ||||||
| /** | /** | ||||||
|  * 设备类型 |  * 设备类型 | ||||||
|  * 针对一套 用户体系 |  | ||||||
|  * |  * | ||||||
|  * @author Lion Li |  * @author Lion Li | ||||||
|  */ |  */ | ||||||
| @@ -29,9 +28,12 @@ public enum DeviceType { | |||||||
|     XCX("xcx"), |     XCX("xcx"), | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * social第三方端 |      * 第三方社交登录平台 | ||||||
|      */ |      */ | ||||||
|     SOCIAL("social"); |     SOCIAL("social"); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 设备标识 | ||||||
|  |      */ | ||||||
|     private final String device; |     private final String device; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,146 @@ | |||||||
|  | package org.dromara.common.core.enums; | ||||||
|  |  | ||||||
|  | import lombok.AllArgsConstructor; | ||||||
|  | import lombok.Getter; | ||||||
|  | import org.dromara.common.core.utils.StringUtils; | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * 日期格式 | ||||||
|  |  * "yyyy":4位数的年份,例如:2023年表示为"2023"。 | ||||||
|  |  * "yy":2位数的年份,例如:2023年表示为"23"。 | ||||||
|  |  * "MM":2位数的月份,取值范围为01到12,例如:7月表示为"07"。 | ||||||
|  |  * "M":不带前导零的月份,取值范围为1到12,例如:7月表示为"7"。 | ||||||
|  |  * "dd":2位数的日期,取值范围为01到31,例如:22日表示为"22"。 | ||||||
|  |  * "d":不带前导零的日期,取值范围为1到31,例如:22日表示为"22"。 | ||||||
|  |  * "EEEE":星期的全名,例如:星期三表示为"Wednesday"。 | ||||||
|  |  * "E":星期的缩写,例如:星期三表示为"Wed"。 | ||||||
|  |  * "DDD" 或 "D":一年中的第几天,取值范围为001到366,例如:第200天表示为"200"。 | ||||||
|  |  * 时间格式 | ||||||
|  |  * "HH":24小时制的小时数,取值范围为00到23,例如:下午5点表示为"17"。 | ||||||
|  |  * "hh":12小时制的小时数,取值范围为01到12,例如:下午5点表示为"05"。 | ||||||
|  |  * "mm":分钟数,取值范围为00到59,例如:30分钟表示为"30"。 | ||||||
|  |  * "ss":秒数,取值范围为00到59,例如:45秒表示为"45"。 | ||||||
|  |  * "SSS":毫秒数,取值范围为000到999,例如:123毫秒表示为"123"。 | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 日期格式与时间格式枚举 | ||||||
|  |  */ | ||||||
|  | @Getter | ||||||
|  | @AllArgsConstructor | ||||||
|  | public enum FormatsType { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 例如:2023年表示为"23" | ||||||
|  |      */ | ||||||
|  |     YY("yy"), | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 例如:2023年表示为"2023" | ||||||
|  |      */ | ||||||
|  |     YYYY("yyyy"), | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 例例如,2023年7月可以表示为 "2023-07" | ||||||
|  |      */ | ||||||
|  |     YYYY_MM("yyyy-MM"), | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 例如,日期 "2023年7月22日" 可以表示为 "2023-07-22" | ||||||
|  |      */ | ||||||
|  |     YYYY_MM_DD("yyyy-MM-dd"), | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 例如,当前时间如果是 "2023年7月22日下午3点30分",则可以表示为 "2023-07-22 15:30" | ||||||
|  |      */ | ||||||
|  |     YYYY_MM_DD_HH_MM("yyyy-MM-dd HH:mm"), | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023-07-22 15:30:45" | ||||||
|  |      */ | ||||||
|  |     YYYY_MM_DD_HH_MM_SS("yyyy-MM-dd HH:mm:ss"), | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 例如:下午3点30分45秒,表示为 "15:30:45" | ||||||
|  |      */ | ||||||
|  |     HH_MM_SS("HH:mm:ss"), | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 例例如,2023年7月可以表示为 "2023/07" | ||||||
|  |      */ | ||||||
|  |     YYYY_MM_SLASH("yyyy/MM"), | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 例如,日期 "2023年7月22日" 可以表示为 "2023/07/22" | ||||||
|  |      */ | ||||||
|  |     YYYY_MM_DD_SLASH("yyyy/MM/dd"), | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023/07/22 15:30:45" | ||||||
|  |      */ | ||||||
|  |     YYYY_MM_DD_HH_MM_SLASH("yyyy/MM/dd HH:mm"), | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023/07/22 15:30:45" | ||||||
|  |      */ | ||||||
|  |     YYYY_MM_DD_HH_MM_SS_SLASH("yyyy/MM/dd HH:mm:ss"), | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 例例如,2023年7月可以表示为 "2023.07" | ||||||
|  |      */ | ||||||
|  |     YYYY_MM_DOT("yyyy.MM"), | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 例如,日期 "2023年7月22日" 可以表示为 "2023.07.22" | ||||||
|  |      */ | ||||||
|  |     YYYY_MM_DD_DOT("yyyy.MM.dd"), | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 例如,当前时间如果是 "2023年7月22日下午3点30分",则可以表示为 "2023.07.22 15:30" | ||||||
|  |      */ | ||||||
|  |     YYYY_MM_DD_HH_MM_DOT("yyyy.MM.dd HH:mm"), | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023.07.22 15:30:45" | ||||||
|  |      */ | ||||||
|  |     YYYY_MM_DD_HH_MM_SS_DOT("yyyy.MM.dd HH:mm:ss"), | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 例如,2023年7月可以表示为 "202307" | ||||||
|  |      */ | ||||||
|  |     YYYYMM("yyyyMM"), | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 例如,2023年7月22日可以表示为 "20230722" | ||||||
|  |      */ | ||||||
|  |     YYYYMMDD("yyyyMMdd"), | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 例如,2023年7月22日下午3点可以表示为 "2023072215" | ||||||
|  |      */ | ||||||
|  |     YYYYMMDDHH("yyyyMMddHH"), | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 例如,2023年7月22日下午3点30分可以表示为 "202307221530" | ||||||
|  |      */ | ||||||
|  |     YYYYMMDDHHMM("yyyyMMddHHmm"), | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 例如,2023年7月22日下午3点30分45秒可以表示为 "20230722153045" | ||||||
|  |      */ | ||||||
|  |     YYYYMMDDHHMMSS("yyyyMMddHHmmss"); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 时间格式 | ||||||
|  |      */ | ||||||
|  |     private final String timeFormat; | ||||||
|  |  | ||||||
|  |     public static FormatsType getFormatsType(String str) { | ||||||
|  |         for (FormatsType value : values()) { | ||||||
|  |             if (StringUtils.contains(str, value.getTimeFormat())) { | ||||||
|  |                 return value; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         throw new RuntimeException("'FormatsType' not found By " + str); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,30 +0,0 @@ | |||||||
| package org.dromara.common.core.enums; |  | ||||||
|  |  | ||||||
| import lombok.AllArgsConstructor; |  | ||||||
| import lombok.Getter; |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * 用户状态 |  | ||||||
|  * |  | ||||||
|  * @author LionLi |  | ||||||
|  */ |  | ||||||
| @Getter |  | ||||||
| @AllArgsConstructor |  | ||||||
| public enum TenantStatus { |  | ||||||
|     /** |  | ||||||
|      * 正常 |  | ||||||
|      */ |  | ||||||
|     OK("0", "正常"), |  | ||||||
|     /** |  | ||||||
|      * 停用 |  | ||||||
|      */ |  | ||||||
|     DISABLE("1", "停用"), |  | ||||||
|     /** |  | ||||||
|      * 删除 |  | ||||||
|      */ |  | ||||||
|     DELETED("2", "删除"); |  | ||||||
|  |  | ||||||
|     private final String code; |  | ||||||
|     private final String info; |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @@ -1,12 +1,11 @@ | |||||||
| package org.dromara.common.core.enums; | package org.dromara.common.core.enums; | ||||||
|  |  | ||||||
| import org.dromara.common.core.utils.StringUtils; |  | ||||||
| import lombok.AllArgsConstructor; | import lombok.AllArgsConstructor; | ||||||
| import lombok.Getter; | import lombok.Getter; | ||||||
|  | import org.dromara.common.core.utils.StringUtils; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * 设备类型 |  * 用户类型 | ||||||
|  * 针对多套 用户体系 |  | ||||||
|  * |  * | ||||||
|  * @author Lion Li |  * @author Lion Li | ||||||
|  */ |  */ | ||||||
| @@ -15,15 +14,18 @@ import lombok.Getter; | |||||||
| public enum UserType { | public enum UserType { | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * pc端 |      * 后台系统用户 | ||||||
|      */ |      */ | ||||||
|     SYS_USER("sys_user"), |     SYS_USER("sys_user"), | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * app端 |      * 移动客户端用户 | ||||||
|      */ |      */ | ||||||
|     APP_USER("app_user"); |     APP_USER("app_user"); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 用户类型标识(用于 token、权限识别等) | ||||||
|  |      */ | ||||||
|     private final String userType; |     private final String userType; | ||||||
|  |  | ||||||
|     public static UserType getUserType(String str) { |     public static UserType getUserType(String str) { | ||||||
|   | |||||||
| @@ -0,0 +1,62 @@ | |||||||
|  | package org.dromara.common.core.exception; | ||||||
|  |  | ||||||
|  | import lombok.AllArgsConstructor; | ||||||
|  | import lombok.Data; | ||||||
|  | import lombok.EqualsAndHashCode; | ||||||
|  | import lombok.NoArgsConstructor; | ||||||
|  |  | ||||||
|  | import java.io.Serial; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * sse 特制异常 | ||||||
|  |  * | ||||||
|  |  * @author LionLi | ||||||
|  |  */ | ||||||
|  | @Data | ||||||
|  | @EqualsAndHashCode(callSuper = true) | ||||||
|  | @NoArgsConstructor | ||||||
|  | @AllArgsConstructor | ||||||
|  | public final class SseException extends RuntimeException { | ||||||
|  |  | ||||||
|  |     @Serial | ||||||
|  |     private static final long serialVersionUID = 1L; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 错误码 | ||||||
|  |      */ | ||||||
|  |     private Integer code; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 错误提示 | ||||||
|  |      */ | ||||||
|  |     private String message; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 错误明细,内部调试错误 | ||||||
|  |      */ | ||||||
|  |     private String detailMessage; | ||||||
|  |  | ||||||
|  |     public SseException(String message) { | ||||||
|  |         this.message = message; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public SseException(String message, Integer code) { | ||||||
|  |         this.message = message; | ||||||
|  |         this.code = code; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String getMessage() { | ||||||
|  |         return message; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public SseException setMessage(String message) { | ||||||
|  |         this.message = message; | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public SseException setDetailMessage(String detailMessage) { | ||||||
|  |         this.detailMessage = detailMessage; | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,5 +1,9 @@ | |||||||
| package org.dromara.common.core.service; | package org.dromara.common.core.service; | ||||||
|  |  | ||||||
|  | import org.dromara.common.core.domain.dto.DeptDTO; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * 通用 部门服务 |  * 通用 部门服务 | ||||||
|  * |  * | ||||||
| @@ -15,4 +19,19 @@ public interface DeptService { | |||||||
|      */ |      */ | ||||||
|     String selectDeptNameByIds(String deptIds); |     String selectDeptNameByIds(String deptIds); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 根据部门ID查询部门负责人 | ||||||
|  |      * | ||||||
|  |      * @param deptId 部门ID,用于指定需要查询的部门 | ||||||
|  |      * @return 返回该部门的负责人ID | ||||||
|  |      */ | ||||||
|  |     Long selectDeptLeaderById(Long deptId); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 查询部门 | ||||||
|  |      * | ||||||
|  |      * @return 部门列表 | ||||||
|  |      */ | ||||||
|  |     List<DeptDTO> selectDeptsByList(); | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,5 +1,9 @@ | |||||||
| package org.dromara.common.core.service; | package org.dromara.common.core.service; | ||||||
|  |  | ||||||
|  | import org.dromara.common.core.domain.dto.DictDataDTO; | ||||||
|  | import org.dromara.common.core.domain.dto.DictTypeDTO; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -64,4 +68,20 @@ public interface DictService { | |||||||
|      */ |      */ | ||||||
|     Map<String, String> getAllDictByDictType(String dictType); |     Map<String, String> getAllDictByDictType(String dictType); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 根据字典类型查询详细信息 | ||||||
|  |      * | ||||||
|  |      * @param dictType 字典类型 | ||||||
|  |      * @return 字典类型详细信息 | ||||||
|  |      */ | ||||||
|  |     DictTypeDTO getDictType(String dictType); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 根据字典类型查询字典数据列表 | ||||||
|  |      * | ||||||
|  |      * @param dictType 字典类型 | ||||||
|  |      * @return 字典数据列表 | ||||||
|  |      */ | ||||||
|  |     List<DictDataDTO> getDictData(String dictType); | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,10 @@ | |||||||
|  | package org.dromara.common.core.service; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 通用 岗位服务 | ||||||
|  |  * | ||||||
|  |  * @author AprilWind | ||||||
|  |  */ | ||||||
|  | public interface PostService { | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,10 @@ | |||||||
|  | package org.dromara.common.core.service; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 通用 角色服务 | ||||||
|  |  * | ||||||
|  |  * @author AprilWind | ||||||
|  |  */ | ||||||
|  | public interface RoleService { | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,45 @@ | |||||||
|  | package org.dromara.common.core.service; | ||||||
|  |  | ||||||
|  | import org.dromara.common.core.domain.dto.TaskAssigneeDTO; | ||||||
|  | import org.dromara.common.core.domain.model.TaskAssigneeBody; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 工作流设计器获取任务执行人 | ||||||
|  |  * | ||||||
|  |  * @author Lion Li | ||||||
|  |  */ | ||||||
|  | public interface TaskAssigneeService { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 查询角色并返回任务指派的列表,支持分页 | ||||||
|  |      * | ||||||
|  |      * @param taskQuery 查询条件 | ||||||
|  |      * @return 办理人 | ||||||
|  |      */ | ||||||
|  |     TaskAssigneeDTO selectRolesByTaskAssigneeList(TaskAssigneeBody taskQuery); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 查询岗位并返回任务指派的列表,支持分页 | ||||||
|  |      * | ||||||
|  |      * @param taskQuery 查询条件 | ||||||
|  |      * @return 办理人 | ||||||
|  |      */ | ||||||
|  |     TaskAssigneeDTO selectPostsByTaskAssigneeList(TaskAssigneeBody taskQuery); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 查询部门并返回任务指派的列表,支持分页 | ||||||
|  |      * | ||||||
|  |      * @param taskQuery 查询条件 | ||||||
|  |      * @return 办理人 | ||||||
|  |      */ | ||||||
|  |     TaskAssigneeDTO selectDeptsByTaskAssigneeList(TaskAssigneeBody taskQuery); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 查询用户并返回任务指派的列表,支持分页 | ||||||
|  |      * | ||||||
|  |      * @param taskQuery 查询条件 | ||||||
|  |      * @return 办理人 | ||||||
|  |      */ | ||||||
|  |     TaskAssigneeDTO selectUsersByTaskAssigneeList(TaskAssigneeBody taskQuery); | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -82,4 +82,13 @@ public interface UserService { | |||||||
|      * @return 用户 |      * @return 用户 | ||||||
|      */ |      */ | ||||||
|     List<UserDTO> selectUsersByDeptIds(List<Long> deptIds); |     List<UserDTO> selectUsersByDeptIds(List<Long> deptIds); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 通过岗位ID查询用户 | ||||||
|  |      * | ||||||
|  |      * @param postIds 岗位ids | ||||||
|  |      * @return 用户 | ||||||
|  |      */ | ||||||
|  |     List<UserDTO> selectUsersByPostIds(List<Long> postIds); | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,5 +1,9 @@ | |||||||
| package org.dromara.common.core.service; | package org.dromara.common.core.service; | ||||||
|  |  | ||||||
|  | import org.dromara.common.core.domain.dto.CompleteTaskDTO; | ||||||
|  | import org.dromara.common.core.domain.dto.StartProcessDTO; | ||||||
|  | import org.dromara.common.core.domain.dto.StartProcessReturnDTO; | ||||||
|  |  | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
|  |  | ||||||
| @@ -13,64 +17,79 @@ public interface WorkflowService { | |||||||
|     /** |     /** | ||||||
|      * 运行中的实例 删除程实例,删除历史记录,删除业务与流程关联信息 |      * 运行中的实例 删除程实例,删除历史记录,删除业务与流程关联信息 | ||||||
|      * |      * | ||||||
|      * @param businessKeys 业务id |      * @param businessIds 业务id | ||||||
|      * @return 结果 |      * @return 结果 | ||||||
|      */ |      */ | ||||||
|     boolean deleteRunAndHisInstance(List<String> businessKeys); |     boolean deleteInstance(List<Long> businessIds); | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 获取当前流程状态 |      * 获取当前流程状态 | ||||||
|      * |      * | ||||||
|      * @param taskId 任务id |      * @param taskId 任务id | ||||||
|  |      * @return 状态 | ||||||
|      */ |      */ | ||||||
|     String getBusinessStatusByTaskId(String taskId); |     String getBusinessStatusByTaskId(Long taskId); | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 获取当前流程状态 |      * 获取当前流程状态 | ||||||
|      * |      * | ||||||
|      * @param businessKey 业务id |      * @param businessId 业务id | ||||||
|  |      * @return 状态 | ||||||
|      */ |      */ | ||||||
|     String getBusinessStatus(String businessKey); |     String getBusinessStatus(String businessId); | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 设置流程变量(全局变量) |      * 设置流程变量 | ||||||
|      * |      * | ||||||
|      * @param taskId       任务id |      * @param instanceId 流程实例id | ||||||
|      * @param variableName 变量名称 |      * @param variable   流程变量 | ||||||
|      * @param value        变量值 |  | ||||||
|      */ |      */ | ||||||
|     void setVariable(String taskId, String variableName, Object value); |     void setVariable(Long instanceId, Map<String, Object> variable); | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 设置流程变量(全局变量) |      * 获取流程变量 | ||||||
|      * |      * | ||||||
|      * @param taskId    任务id |      * @param instanceId 流程实例id | ||||||
|      * @param variables 流程变量 |  | ||||||
|      */ |      */ | ||||||
|     void setVariables(String taskId, Map<String, Object> variables); |     Map<String, Object> instanceVariable(Long instanceId); | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 设置流程变量(本地变量,非全局变量) |  | ||||||
|      * |  | ||||||
|      * @param taskId       任务id |  | ||||||
|      * @param variableName 变量名称 |  | ||||||
|      * @param value        变量值 |  | ||||||
|      */ |  | ||||||
|     void setVariableLocal(String taskId, String variableName, Object value); |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 设置流程变量(本地变量,非全局变量) |  | ||||||
|      * |  | ||||||
|      * @param taskId    任务id |  | ||||||
|      * @param variables 流程变量 |  | ||||||
|      */ |  | ||||||
|     void setVariablesLocal(String taskId, Map<String, Object> variables); |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 按照业务id查询流程实例id |      * 按照业务id查询流程实例id | ||||||
|      * |      * | ||||||
|      * @param businessKey 业务id |      * @param businessId 业务id | ||||||
|      * @return 结果 |      * @return 结果 | ||||||
|      */ |      */ | ||||||
|     String getInstanceIdByBusinessKey(String businessKey); |     Long getInstanceIdByBusinessId(String businessId); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 新增租户流程定义 | ||||||
|  |      * | ||||||
|  |      * @param tenantId 租户id | ||||||
|  |      */ | ||||||
|  |     void syncDef(String tenantId); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 启动流程 | ||||||
|  |      * | ||||||
|  |      * @param startProcess 参数 | ||||||
|  |      * @return 结果 | ||||||
|  |      */ | ||||||
|  |     StartProcessReturnDTO startWorkFlow(StartProcessDTO startProcess); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 办理任务 | ||||||
|  |      * 系统后台发起审批 无用户信息 需要忽略权限 | ||||||
|  |      * completeTask.getVariables().put("ignore", true); | ||||||
|  |      * | ||||||
|  |      * @param completeTask 参数 | ||||||
|  |      */ | ||||||
|  |     boolean completeTask(CompleteTaskDTO completeTask); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 办理任务 | ||||||
|  |      * | ||||||
|  |      * @param taskId  任务ID | ||||||
|  |      * @param message 办理意见 | ||||||
|  |      */ | ||||||
|  |     boolean completeTask(Long taskId, String message); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,106 +1,157 @@ | |||||||
| package org.dromara.common.core.utils; | package org.dromara.common.core.utils; | ||||||
|  |  | ||||||
| import lombok.AccessLevel; |  | ||||||
| import lombok.NoArgsConstructor; |  | ||||||
| import org.apache.commons.lang3.time.DateFormatUtils; | import org.apache.commons.lang3.time.DateFormatUtils; | ||||||
|  | import org.dromara.common.core.enums.FormatsType; | ||||||
|  | import org.dromara.common.core.exception.ServiceException; | ||||||
|  |  | ||||||
| import java.lang.management.ManagementFactory; | import java.lang.management.ManagementFactory; | ||||||
| import java.text.ParseException; | import java.text.ParseException; | ||||||
| import java.text.SimpleDateFormat; | import java.text.SimpleDateFormat; | ||||||
| import java.time.LocalDate; | import java.time.*; | ||||||
| import java.time.LocalDateTime; |  | ||||||
| import java.time.LocalTime; |  | ||||||
| import java.time.ZoneId; |  | ||||||
| import java.time.ZonedDateTime; |  | ||||||
| import java.util.Date; | import java.util.Date; | ||||||
|  | import java.util.concurrent.TimeUnit; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * 时间工具类 |  * 时间工具类 | ||||||
|  * |  * | ||||||
|  * @author ruoyi |  * @author ruoyi | ||||||
|  */ |  */ | ||||||
| @NoArgsConstructor(access = AccessLevel.PRIVATE) |  | ||||||
| public class DateUtils extends org.apache.commons.lang3.time.DateUtils { | public class DateUtils extends org.apache.commons.lang3.time.DateUtils { | ||||||
|  |  | ||||||
|     public static final String YYYY = "yyyy"; |  | ||||||
|  |  | ||||||
|     public static final String YYYY_MM = "yyyy-MM"; |  | ||||||
|  |  | ||||||
|     public static final String YYYY_MM_DD = "yyyy-MM-dd"; |  | ||||||
|  |  | ||||||
|     public static final String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; |  | ||||||
|  |  | ||||||
|     public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; |  | ||||||
|  |  | ||||||
|     private static final String[] PARSE_PATTERNS = { |     private static final String[] PARSE_PATTERNS = { | ||||||
|         "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", |         "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", | ||||||
|         "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", |         "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", | ||||||
|         "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; |         "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; | ||||||
|  |  | ||||||
|  |     @Deprecated | ||||||
|  |     private DateUtils() { | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 获取当前Date型日期 |      * 获取当前日期和时间 | ||||||
|      * |      * | ||||||
|      * @return Date() 当前日期 |      * @return 当前日期和时间的 Date 对象表示 | ||||||
|      */ |      */ | ||||||
|     public static Date getNowDate() { |     public static Date getNowDate() { | ||||||
|         return new Date(); |         return new Date(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 获取当前日期, 默认格式为yyyy-MM-dd |      * 获取当前日期的字符串表示,格式为YYYY-MM-DD | ||||||
|      * |      * | ||||||
|      * @return String |      * @return 当前日期的字符串表示 | ||||||
|      */ |      */ | ||||||
|     public static String getDate() { |     public static String getDate() { | ||||||
|         return dateTimeNow(YYYY_MM_DD); |         return dateTimeNow(FormatsType.YYYY_MM_DD); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 获取当前日期的字符串表示,格式为yyyyMMdd | ||||||
|  |      * | ||||||
|  |      * @return 当前日期的字符串表示 | ||||||
|  |      */ | ||||||
|  |     public static String getCurrentDate() { | ||||||
|  |         return DateFormatUtils.format(new Date(), FormatsType.YYYYMMDD.getTimeFormat()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 获取当前日期的路径格式字符串,格式为"yyyy/MM/dd" | ||||||
|  |      * | ||||||
|  |      * @return 当前日期的路径格式字符串 | ||||||
|  |      */ | ||||||
|  |     public static String datePath() { | ||||||
|  |         Date now = new Date(); | ||||||
|  |         return DateFormatUtils.format(now, FormatsType.YYYY_MM_DD_SLASH.getTimeFormat()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 获取当前时间的字符串表示,格式为YYYY-MM-DD HH:MM:SS | ||||||
|  |      * | ||||||
|  |      * @return 当前时间的字符串表示 | ||||||
|  |      */ | ||||||
|     public static String getTime() { |     public static String getTime() { | ||||||
|         return dateTimeNow(YYYY_MM_DD_HH_MM_SS); |         return dateTimeNow(FormatsType.YYYY_MM_DD_HH_MM_SS); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 获取当前时间的字符串表示,格式为 "HH:MM:SS" | ||||||
|  |      * | ||||||
|  |      * @return 当前时间的字符串表示,格式为 "HH:MM:SS" | ||||||
|  |      */ | ||||||
|  |     public static String getTimeWithHourMinuteSecond() { | ||||||
|  |         return dateTimeNow(FormatsType.HH_MM_SS); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 获取当前日期和时间的字符串表示,格式为YYYYMMDDHHMMSS | ||||||
|  |      * | ||||||
|  |      * @return 当前日期和时间的字符串表示 | ||||||
|  |      */ | ||||||
|     public static String dateTimeNow() { |     public static String dateTimeNow() { | ||||||
|         return dateTimeNow(YYYYMMDDHHMMSS); |         return dateTimeNow(FormatsType.YYYYMMDDHHMMSS); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static String dateTimeNow(final String format) { |     /** | ||||||
|  |      * 获取当前日期和时间的指定格式的字符串表示 | ||||||
|  |      * | ||||||
|  |      * @param format 日期时间格式,例如"YYYY-MM-DD HH:MM:SS" | ||||||
|  |      * @return 当前日期和时间的字符串表示 | ||||||
|  |      */ | ||||||
|  |     public static String dateTimeNow(final FormatsType format) { | ||||||
|         return parseDateToStr(format, new Date()); |         return parseDateToStr(format, new Date()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static String dateTime(final Date date) { |     /** | ||||||
|         return parseDateToStr(YYYY_MM_DD, date); |      * 将指定日期格式化为 YYYY-MM-DD 格式的字符串 | ||||||
|  |      * | ||||||
|  |      * @param date 要格式化的日期对象 | ||||||
|  |      * @return 格式化后的日期字符串 | ||||||
|  |      */ | ||||||
|  |     public static String formatDate(final Date date) { | ||||||
|  |         return parseDateToStr(FormatsType.YYYY_MM_DD, date); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static String parseDateToStr(final String format, final Date date) { |     /** | ||||||
|         return new SimpleDateFormat(format).format(date); |      * 将指定日期格式化为 YYYY-MM-DD HH:MM:SS 格式的字符串 | ||||||
|  |      * | ||||||
|  |      * @param date 要格式化的日期对象 | ||||||
|  |      * @return 格式化后的日期时间字符串 | ||||||
|  |      */ | ||||||
|  |     public static String formatDateTime(final Date date) { | ||||||
|  |         return parseDateToStr(FormatsType.YYYY_MM_DD_HH_MM_SS, date); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static Date dateTime(final String format, final String ts) { |     /** | ||||||
|  |      * 将指定日期按照指定格式进行格式化 | ||||||
|  |      * | ||||||
|  |      * @param format 要使用的日期时间格式,例如"YYYY-MM-DD HH:MM:SS" | ||||||
|  |      * @param date   要格式化的日期对象 | ||||||
|  |      * @return 格式化后的日期时间字符串 | ||||||
|  |      */ | ||||||
|  |     public static String parseDateToStr(final FormatsType format, final Date date) { | ||||||
|  |         return new SimpleDateFormat(format.getTimeFormat()).format(date); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 将指定格式的日期时间字符串转换为 Date 对象 | ||||||
|  |      * | ||||||
|  |      * @param format 要解析的日期时间格式,例如"YYYY-MM-DD HH:MM:SS" | ||||||
|  |      * @param ts     要解析的日期时间字符串 | ||||||
|  |      * @return 解析后的 Date 对象 | ||||||
|  |      * @throws RuntimeException 如果解析过程中发生异常 | ||||||
|  |      */ | ||||||
|  |     public static Date parseDateTime(final FormatsType format, final String ts) { | ||||||
|         try { |         try { | ||||||
|             return new SimpleDateFormat(format).parse(ts); |             return new SimpleDateFormat(format.getTimeFormat()).parse(ts); | ||||||
|         } catch (ParseException e) { |         } catch (ParseException e) { | ||||||
|             throw new RuntimeException(e); |             throw new RuntimeException(e); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 日期路径 即年/月/日 如2018/08/08 |      * 将对象转换为日期对象 | ||||||
|      */ |      * | ||||||
|     public static String datePath() { |      * @param str 要转换的对象,通常是字符串 | ||||||
|         Date now = new Date(); |      * @return 转换后的日期对象,如果转换失败或输入为null,则返回null | ||||||
|         return DateFormatUtils.format(now, "yyyy/MM/dd"); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 日期路径 即年/月/日 如20180808 |  | ||||||
|      */ |  | ||||||
|     public static String dateTime() { |  | ||||||
|         Date now = new Date(); |  | ||||||
|         return DateFormatUtils.format(now, "yyyyMMdd"); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 日期型字符串转化为日期 格式 |  | ||||||
|      */ |      */ | ||||||
|     public static Date parseDate(Object str) { |     public static Date parseDate(Object str) { | ||||||
|         if (str == null) { |         if (str == null) { | ||||||
| @@ -115,6 +166,8 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils { | |||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 获取服务器启动时间 |      * 获取服务器启动时间 | ||||||
|  |      * | ||||||
|  |      * @return 服务器启动时间的 Date 对象表示 | ||||||
|      */ |      */ | ||||||
|     public static Date getServerStartDate() { |     public static Date getServerStartDate() { | ||||||
|         long time = ManagementFactory.getRuntimeMXBean().getStartTime(); |         long time = ManagementFactory.getRuntimeMXBean().getStartTime(); | ||||||
| @@ -122,35 +175,79 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 计算相差天数 |      * 计算两个时间之间的时间差,并以指定单位返回(绝对值) | ||||||
|  |      * | ||||||
|  |      * @param start 起始时间 | ||||||
|  |      * @param end   结束时间 | ||||||
|  |      * @param unit  所需返回的时间单位(DAYS、HOURS、MINUTES、SECONDS、MILLISECONDS、MICROSECONDS、NANOSECONDS) | ||||||
|  |      * @return 时间差的绝对值,以指定单位表示 | ||||||
|      */ |      */ | ||||||
|     public static int differentDaysByMillisecond(Date date1, Date date2) { |     public static long difference(Date start, Date end, TimeUnit unit) { | ||||||
|         return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24))); |         // 计算时间差,单位为毫秒,取绝对值避免负数 | ||||||
|  |         long diffInMillis = Math.abs(end.getTime() - start.getTime()); | ||||||
|  |  | ||||||
|  |         // 根据目标单位转换时间差 | ||||||
|  |         return switch (unit) { | ||||||
|  |             case DAYS -> diffInMillis / TimeUnit.DAYS.toMillis(1); | ||||||
|  |             case HOURS -> diffInMillis / TimeUnit.HOURS.toMillis(1); | ||||||
|  |             case MINUTES -> diffInMillis / TimeUnit.MINUTES.toMillis(1); | ||||||
|  |             case SECONDS -> diffInMillis / TimeUnit.SECONDS.toMillis(1); | ||||||
|  |             case MILLISECONDS -> diffInMillis; | ||||||
|  |             case MICROSECONDS -> TimeUnit.MILLISECONDS.toMicros(diffInMillis); | ||||||
|  |             case NANOSECONDS -> TimeUnit.MILLISECONDS.toNanos(diffInMillis); | ||||||
|  |         }; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 计算两个时间差 |      * 计算两个日期之间的时间差,并以天、小时和分钟的格式返回 | ||||||
|  |      * | ||||||
|  |      * @param endDate 结束日期 | ||||||
|  |      * @param nowDate 当前日期 | ||||||
|  |      * @return 表示时间差的字符串,格式为"天 小时 分钟" | ||||||
|      */ |      */ | ||||||
|     public static String getDatePoor(Date endDate, Date nowDate) { |     public static String getDatePoor(Date endDate, Date nowDate) { | ||||||
|         long nd = 1000 * 24 * 60 * 60; |         long diffInMillis = endDate.getTime() - nowDate.getTime(); | ||||||
|         long nh = 1000 * 60 * 60; |         long day = TimeUnit.MILLISECONDS.toDays(diffInMillis); | ||||||
|         long nm = 1000 * 60; |         long hour = TimeUnit.MILLISECONDS.toHours(diffInMillis) % 24; | ||||||
|         // long ns = 1000; |         long min = TimeUnit.MILLISECONDS.toMinutes(diffInMillis) % 60; | ||||||
|         // 获得两个时间的毫秒时间差异 |         return String.format("%d天 %d小时 %d分钟", day, hour, min); | ||||||
|         long diff = endDate.getTime() - nowDate.getTime(); |  | ||||||
|         // 计算差多少天 |  | ||||||
|         long day = diff / nd; |  | ||||||
|         // 计算差多少小时 |  | ||||||
|         long hour = diff % nd / nh; |  | ||||||
|         // 计算差多少分钟 |  | ||||||
|         long min = diff % nd % nh / nm; |  | ||||||
|         // 计算差多少秒//输出结果 |  | ||||||
|         // long sec = diff % nd % nh % nm / ns; |  | ||||||
|         return day + "天" + hour + "小时" + min + "分钟"; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 增加 LocalDateTime ==> Date |      * 计算两个时间点的差值(天、小时、分钟、秒),当值为0时不显示该单位 | ||||||
|  |      * | ||||||
|  |      * @param endDate 结束时间 | ||||||
|  |      * @param nowDate 当前时间 | ||||||
|  |      * @return 时间差字符串,格式为 "x天 x小时 x分钟 x秒",若为 0 则不显示 | ||||||
|  |      */ | ||||||
|  |     public static String getTimeDifference(Date endDate, Date nowDate) { | ||||||
|  |         long diffInMillis = endDate.getTime() - nowDate.getTime(); | ||||||
|  |         long day = TimeUnit.MILLISECONDS.toDays(diffInMillis); | ||||||
|  |         long hour = TimeUnit.MILLISECONDS.toHours(diffInMillis) % 24; | ||||||
|  |         long min = TimeUnit.MILLISECONDS.toMinutes(diffInMillis) % 60; | ||||||
|  |         long sec = TimeUnit.MILLISECONDS.toSeconds(diffInMillis) % 60; | ||||||
|  |         // 构建时间差字符串,条件是值不为0才显示 | ||||||
|  |         StringBuilder result = new StringBuilder(); | ||||||
|  |         if (day > 0) { | ||||||
|  |             result.append(String.format("%d天 ", day)); | ||||||
|  |         } | ||||||
|  |         if (hour > 0) { | ||||||
|  |             result.append(String.format("%d小时 ", hour)); | ||||||
|  |         } | ||||||
|  |         if (min > 0) { | ||||||
|  |             result.append(String.format("%d分钟 ", min)); | ||||||
|  |         } | ||||||
|  |         if (sec > 0) { | ||||||
|  |             result.append(String.format("%d秒", sec)); | ||||||
|  |         } | ||||||
|  |         return result.length() > 0 ? result.toString().trim() : "0秒"; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 将 LocalDateTime 对象转换为 Date 对象 | ||||||
|  |      * | ||||||
|  |      * @param temporalAccessor 要转换的 LocalDateTime 对象 | ||||||
|  |      * @return 转换后的 Date 对象 | ||||||
|      */ |      */ | ||||||
|     public static Date toDate(LocalDateTime temporalAccessor) { |     public static Date toDate(LocalDateTime temporalAccessor) { | ||||||
|         ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault()); |         ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault()); | ||||||
| @@ -158,11 +255,46 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 增加 LocalDate ==> Date |      * 将 LocalDate 对象转换为 Date 对象 | ||||||
|  |      * | ||||||
|  |      * @param temporalAccessor 要转换的 LocalDate 对象 | ||||||
|  |      * @return 转换后的 Date 对象 | ||||||
|      */ |      */ | ||||||
|     public static Date toDate(LocalDate temporalAccessor) { |     public static Date toDate(LocalDate temporalAccessor) { | ||||||
|         LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0)); |         LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0)); | ||||||
|         ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault()); |         ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault()); | ||||||
|         return Date.from(zdt.toInstant()); |         return Date.from(zdt.toInstant()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 校验日期范围 | ||||||
|  |      * | ||||||
|  |      * @param startDate 开始日期 | ||||||
|  |      * @param endDate   结束日期 | ||||||
|  |      * @param maxValue  最大时间跨度的限制值 | ||||||
|  |      * @param unit      时间跨度的单位,可选择 "DAYS"、"HOURS" 或 "MINUTES" | ||||||
|  |      */ | ||||||
|  |     public static void validateDateRange(Date startDate, Date endDate, int maxValue, TimeUnit unit) { | ||||||
|  |         // 校验结束日期不能早于开始日期 | ||||||
|  |         if (endDate.before(startDate)) { | ||||||
|  |             throw new ServiceException("结束日期不能早于开始日期"); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // 计算时间跨度 | ||||||
|  |         long diffInMillis = endDate.getTime() - startDate.getTime(); | ||||||
|  |  | ||||||
|  |         // 根据单位转换时间跨度 | ||||||
|  |         long diff = switch (unit) { | ||||||
|  |             case DAYS -> TimeUnit.MILLISECONDS.toDays(diffInMillis); | ||||||
|  |             case HOURS -> TimeUnit.MILLISECONDS.toHours(diffInMillis); | ||||||
|  |             case MINUTES -> TimeUnit.MILLISECONDS.toMinutes(diffInMillis); | ||||||
|  |             default -> throw new IllegalArgumentException("不支持的时间单位"); | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         // 校验时间跨度不超过最大限制 | ||||||
|  |         if (diff > maxValue) { | ||||||
|  |             throw new ServiceException("最大时间跨度为 " + maxValue + " " + unit.toString().toLowerCase()); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,84 @@ | |||||||
|  | package org.dromara.common.core.utils; | ||||||
|  |  | ||||||
|  | import cn.hutool.core.lang.PatternPool; | ||||||
|  | import cn.hutool.core.net.NetUtil; | ||||||
|  | import lombok.AccessLevel; | ||||||
|  | import lombok.NoArgsConstructor; | ||||||
|  | import lombok.extern.slf4j.Slf4j; | ||||||
|  | import org.dromara.common.core.utils.regex.RegexUtils; | ||||||
|  |  | ||||||
|  | import java.net.Inet6Address; | ||||||
|  | import java.net.InetAddress; | ||||||
|  | import java.net.UnknownHostException; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 增强网络相关工具类 | ||||||
|  |  * | ||||||
|  |  * @author 秋辞未寒 | ||||||
|  |  */ | ||||||
|  | @Slf4j | ||||||
|  | @NoArgsConstructor(access = AccessLevel.PRIVATE) | ||||||
|  | public class NetUtils extends NetUtil { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 判断是否为IPv6地址 | ||||||
|  |      * | ||||||
|  |      * @param ip IP地址 | ||||||
|  |      * @return 是否为IPv6地址 | ||||||
|  |      */ | ||||||
|  |     public static boolean isIPv6(String ip) { | ||||||
|  |         try { | ||||||
|  |             // 判断是否为IPv6地址 | ||||||
|  |             return InetAddress.getByName(ip) instanceof Inet6Address; | ||||||
|  |         } catch (UnknownHostException e) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 判断IPv6地址是否为内网地址 | ||||||
|  |      * <br><br> | ||||||
|  |      * 以下地址将归类为本地地址,如有业务场景有需要,请根据需求自行处理: | ||||||
|  |      * <pre> | ||||||
|  |      * 通配符地址 0:0:0:0:0:0:0:0 | ||||||
|  |      * 链路本地地址 fe80::/10 | ||||||
|  |      * 唯一本地地址 fec0::/10 | ||||||
|  |      * 环回地址 ::1 | ||||||
|  |      * </pre> | ||||||
|  |      * | ||||||
|  |      * @param ip IP地址 | ||||||
|  |      * @return 是否为内网地址 | ||||||
|  |      */ | ||||||
|  |     public static boolean isInnerIPv6(String ip) { | ||||||
|  |         try { | ||||||
|  |             // 判断是否为IPv6地址 | ||||||
|  |             if (InetAddress.getByName(ip) instanceof Inet6Address inet6Address) { | ||||||
|  |                 // isAnyLocalAddress 判断是否为通配符地址,通常不会将其视为内网地址,根据业务场景自行处理判断 | ||||||
|  |                 // isLinkLocalAddress 判断是否为链路本地地址,通常不算内网地址,是否划分归属于内网需要根据业务场景自行处理判断 | ||||||
|  |                 // isLoopbackAddress 判断是否为环回地址,与IPv4的 127.0.0.1 同理,用于表示本机 | ||||||
|  |                 // isSiteLocalAddress 判断是否为本地站点地址,IPv6唯一本地地址(Unique Local Addresses,简称ULA) | ||||||
|  |                 if (inet6Address.isAnyLocalAddress() | ||||||
|  |                     || inet6Address.isLinkLocalAddress() | ||||||
|  |                     || inet6Address.isLoopbackAddress() | ||||||
|  |                     || inet6Address.isSiteLocalAddress()) { | ||||||
|  |                     return true; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } catch (UnknownHostException e) { | ||||||
|  |             // 注意,isInnerIPv6方法和isIPv6方法的适用范围不同,所以此处不能忽略其异常信息。 | ||||||
|  |             throw new IllegalArgumentException("Invalid IPv6 address!", e); | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 判断是否为IPv4地址 | ||||||
|  |      * | ||||||
|  |      * @param ip IP地址 | ||||||
|  |      * @return 是否为IPv4地址 | ||||||
|  |      */ | ||||||
|  |     public static boolean isIPv4(String ip) { | ||||||
|  |         return RegexUtils.isMatch(PatternPool.IPV4, ip); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,60 @@ | |||||||
|  | package org.dromara.common.core.utils; | ||||||
|  |  | ||||||
|  | import cn.hutool.core.util.ObjectUtil; | ||||||
|  | import lombok.AccessLevel; | ||||||
|  | import lombok.NoArgsConstructor; | ||||||
|  |  | ||||||
|  | import java.util.function.Function; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 对象工具类 | ||||||
|  |  * | ||||||
|  |  * @author 秋辞未寒 | ||||||
|  |  */ | ||||||
|  | @NoArgsConstructor(access = AccessLevel.PRIVATE) | ||||||
|  | public class ObjectUtils extends ObjectUtil { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 如果对象不为空,则获取对象中的某个字段 ObjectUtils.notNullGetter(user, User::getName); | ||||||
|  |      * | ||||||
|  |      * @param obj 对象 | ||||||
|  |      * @param func 获取方法 | ||||||
|  |      * @return 对象字段 | ||||||
|  |      */ | ||||||
|  |     public static <T, E> E notNullGetter(T obj, Function<T, E> func) { | ||||||
|  |         if (isNotNull(obj) && isNotNull(func)) { | ||||||
|  |             return func.apply(obj); | ||||||
|  |         } | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 如果对象不为空,则获取对象中的某个字段,否则返回默认值 | ||||||
|  |      * | ||||||
|  |      * @param obj          对象 | ||||||
|  |      * @param func         获取方法 | ||||||
|  |      * @param defaultValue 默认值 | ||||||
|  |      * @return 对象字段 | ||||||
|  |      */ | ||||||
|  |     public static <T, E> E notNullGetter(T obj, Function<T, E> func, E defaultValue) { | ||||||
|  |         if (isNotNull(obj) && isNotNull(func)) { | ||||||
|  |             return func.apply(obj); | ||||||
|  |         } | ||||||
|  |         return defaultValue; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 如果值不为空,则返回值,否则返回默认值 | ||||||
|  |      * | ||||||
|  |      * @param obj          对象 | ||||||
|  |      * @param defaultValue 默认值 | ||||||
|  |      * @return 对象字段 | ||||||
|  |      */ | ||||||
|  |     public static <T> T notNull(T obj, T defaultValue) { | ||||||
|  |         if (isNotNull(obj)) { | ||||||
|  |             return obj; | ||||||
|  |         } | ||||||
|  |         return defaultValue; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -25,7 +25,7 @@ import java.util.HashMap; | |||||||
| import java.util.Map; | import java.util.Map; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * 客户端工具类 |  * 客户端工具类,提供获取请求参数、响应处理、头部信息等常用操作 | ||||||
|  * |  * | ||||||
|  * @author ruoyi |  * @author ruoyi | ||||||
|  */ |  */ | ||||||
| @@ -33,52 +33,73 @@ import java.util.Map; | |||||||
| public class ServletUtils extends JakartaServletUtil { | public class ServletUtils extends JakartaServletUtil { | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 获取String参数 |      * 获取指定名称的 String 类型的请求参数 | ||||||
|  |      * | ||||||
|  |      * @param name 参数名 | ||||||
|  |      * @return 参数值 | ||||||
|      */ |      */ | ||||||
|     public static String getParameter(String name) { |     public static String getParameter(String name) { | ||||||
|         return getRequest().getParameter(name); |         return getRequest().getParameter(name); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 获取String参数 |      * 获取指定名称的 String 类型的请求参数,若参数不存在,则返回默认值 | ||||||
|  |      * | ||||||
|  |      * @param name         参数名 | ||||||
|  |      * @param defaultValue 默认值 | ||||||
|  |      * @return 参数值或默认值 | ||||||
|      */ |      */ | ||||||
|     public static String getParameter(String name, String defaultValue) { |     public static String getParameter(String name, String defaultValue) { | ||||||
|         return Convert.toStr(getRequest().getParameter(name), defaultValue); |         return Convert.toStr(getRequest().getParameter(name), defaultValue); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 获取Integer参数 |      * 获取指定名称的 Integer 类型的请求参数 | ||||||
|  |      * | ||||||
|  |      * @param name 参数名 | ||||||
|  |      * @return 参数值 | ||||||
|      */ |      */ | ||||||
|     public static Integer getParameterToInt(String name) { |     public static Integer getParameterToInt(String name) { | ||||||
|         return Convert.toInt(getRequest().getParameter(name)); |         return Convert.toInt(getRequest().getParameter(name)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 获取Integer参数 |      * 获取指定名称的 Integer 类型的请求参数,若参数不存在,则返回默认值 | ||||||
|  |      * | ||||||
|  |      * @param name         参数名 | ||||||
|  |      * @param defaultValue 默认值 | ||||||
|  |      * @return 参数值或默认值 | ||||||
|      */ |      */ | ||||||
|     public static Integer getParameterToInt(String name, Integer defaultValue) { |     public static Integer getParameterToInt(String name, Integer defaultValue) { | ||||||
|         return Convert.toInt(getRequest().getParameter(name), defaultValue); |         return Convert.toInt(getRequest().getParameter(name), defaultValue); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 获取Boolean参数 |      * 获取指定名称的 Boolean 类型的请求参数 | ||||||
|  |      * | ||||||
|  |      * @param name 参数名 | ||||||
|  |      * @return 参数值 | ||||||
|      */ |      */ | ||||||
|     public static Boolean getParameterToBool(String name) { |     public static Boolean getParameterToBool(String name) { | ||||||
|         return Convert.toBool(getRequest().getParameter(name)); |         return Convert.toBool(getRequest().getParameter(name)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 获取Boolean参数 |      * 获取指定名称的 Boolean 类型的请求参数,若参数不存在,则返回默认值 | ||||||
|  |      * | ||||||
|  |      * @param name         参数名 | ||||||
|  |      * @param defaultValue 默认值 | ||||||
|  |      * @return 参数值或默认值 | ||||||
|      */ |      */ | ||||||
|     public static Boolean getParameterToBool(String name, Boolean defaultValue) { |     public static Boolean getParameterToBool(String name, Boolean defaultValue) { | ||||||
|         return Convert.toBool(getRequest().getParameter(name), defaultValue); |         return Convert.toBool(getRequest().getParameter(name), defaultValue); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 获得所有请求参数 |      * 获取所有请求参数(以 Map 的形式返回) | ||||||
|      * |      * | ||||||
|      * @param request 请求对象{@link ServletRequest} |      * @param request 请求对象{@link ServletRequest} | ||||||
|      * @return Map |      * @return 请求参数的 Map,键为参数名,值为参数值数组 | ||||||
|      */ |      */ | ||||||
|     public static Map<String, String[]> getParams(ServletRequest request) { |     public static Map<String, String[]> getParams(ServletRequest request) { | ||||||
|         final Map<String, String[]> map = request.getParameterMap(); |         final Map<String, String[]> map = request.getParameterMap(); | ||||||
| @@ -86,10 +107,10 @@ public class ServletUtils extends JakartaServletUtil { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 获得所有请求参数 |      * 获取所有请求参数(以 Map 的形式返回,值为字符串形式的拼接) | ||||||
|      * |      * | ||||||
|      * @param request 请求对象{@link ServletRequest} |      * @param request 请求对象{@link ServletRequest} | ||||||
|      * @return Map |      * @return 请求参数的 Map,键为参数名,值为拼接后的字符串 | ||||||
|      */ |      */ | ||||||
|     public static Map<String, String> getParamMap(ServletRequest request) { |     public static Map<String, String> getParamMap(ServletRequest request) { | ||||||
|         Map<String, String> params = new HashMap<>(); |         Map<String, String> params = new HashMap<>(); | ||||||
| @@ -100,7 +121,9 @@ public class ServletUtils extends JakartaServletUtil { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 获取request |      * 获取当前 HTTP 请求对象 | ||||||
|  |      * | ||||||
|  |      * @return 当前 HTTP 请求对象 | ||||||
|      */ |      */ | ||||||
|     public static HttpServletRequest getRequest() { |     public static HttpServletRequest getRequest() { | ||||||
|         try { |         try { | ||||||
| @@ -111,7 +134,9 @@ public class ServletUtils extends JakartaServletUtil { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 获取response |      * 获取当前 HTTP 响应对象 | ||||||
|  |      * | ||||||
|  |      * @return 当前 HTTP 响应对象 | ||||||
|      */ |      */ | ||||||
|     public static HttpServletResponse getResponse() { |     public static HttpServletResponse getResponse() { | ||||||
|         try { |         try { | ||||||
| @@ -122,12 +147,25 @@ public class ServletUtils extends JakartaServletUtil { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 获取session |      * 获取当前请求的 HttpSession 对象 | ||||||
|  |      * <p> | ||||||
|  |      * 如果当前请求已经关联了一个会话(即已经存在有效的 session ID), | ||||||
|  |      * 则返回该会话对象;如果没有关联会话,则会创建一个新的会话对象并返回。 | ||||||
|  |      * <p> | ||||||
|  |      * HttpSession 用于存储会话级别的数据,如用户登录信息、购物车内容等, | ||||||
|  |      * 可以在多个请求之间共享会话数据 | ||||||
|  |      * | ||||||
|  |      * @return 当前请求的 HttpSession 对象 | ||||||
|      */ |      */ | ||||||
|     public static HttpSession getSession() { |     public static HttpSession getSession() { | ||||||
|         return getRequest().getSession(); |         return getRequest().getSession(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 获取当前请求的请求属性 | ||||||
|  |      * | ||||||
|  |      * @return {@link ServletRequestAttributes} 请求属性对象 | ||||||
|  |      */ | ||||||
|     public static ServletRequestAttributes getRequestAttributes() { |     public static ServletRequestAttributes getRequestAttributes() { | ||||||
|         try { |         try { | ||||||
|             RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); |             RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); | ||||||
| @@ -137,6 +175,13 @@ public class ServletUtils extends JakartaServletUtil { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 获取指定请求头的值,如果头部为空则返回空字符串 | ||||||
|  |      * | ||||||
|  |      * @param request 请求对象 | ||||||
|  |      * @param name    头部名称 | ||||||
|  |      * @return 头部值 | ||||||
|  |      */ | ||||||
|     public static String getHeader(HttpServletRequest request, String name) { |     public static String getHeader(HttpServletRequest request, String name) { | ||||||
|         String value = request.getHeader(name); |         String value = request.getHeader(name); | ||||||
|         if (StringUtils.isEmpty(value)) { |         if (StringUtils.isEmpty(value)) { | ||||||
| @@ -145,6 +190,12 @@ public class ServletUtils extends JakartaServletUtil { | |||||||
|         return urlDecode(value); |         return urlDecode(value); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 获取所有请求头的 Map,键为头部名称,值为头部值 | ||||||
|  |      * | ||||||
|  |      * @param request 请求对象 | ||||||
|  |      * @return 请求头的 Map | ||||||
|  |      */ | ||||||
|     public static Map<String, String> getHeaders(HttpServletRequest request) { |     public static Map<String, String> getHeaders(HttpServletRequest request) { | ||||||
|         Map<String, String> map = new LinkedCaseInsensitiveMap<>(); |         Map<String, String> map = new LinkedCaseInsensitiveMap<>(); | ||||||
|         Enumeration<String> enumeration = request.getHeaderNames(); |         Enumeration<String> enumeration = request.getHeaderNames(); | ||||||
| @@ -159,7 +210,7 @@ public class ServletUtils extends JakartaServletUtil { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 将字符串渲染到客户端 |      * 将字符串渲染到客户端(以 JSON 格式返回) | ||||||
|      * |      * | ||||||
|      * @param response 渲染对象 |      * @param response 渲染对象 | ||||||
|      * @param string   待渲染的字符串 |      * @param string   待渲染的字符串 | ||||||
| @@ -176,37 +227,47 @@ public class ServletUtils extends JakartaServletUtil { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 是否是Ajax异步请求 |      * 判断当前请求是否为 Ajax 异步请求 | ||||||
|      * |      * | ||||||
|      * @param request |      * @param request 请求对象 | ||||||
|  |      * @return 是否为 Ajax 请求 | ||||||
|      */ |      */ | ||||||
|     public static boolean isAjaxRequest(HttpServletRequest request) { |     public static boolean isAjaxRequest(HttpServletRequest request) { | ||||||
|  |  | ||||||
|  |         // 判断 Accept 头部是否包含 application/json | ||||||
|         String accept = request.getHeader("accept"); |         String accept = request.getHeader("accept"); | ||||||
|         if (accept != null && accept.contains(MediaType.APPLICATION_JSON_VALUE)) { |         if (accept != null && accept.contains(MediaType.APPLICATION_JSON_VALUE)) { | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         // 判断 X-Requested-With 头部是否包含 XMLHttpRequest | ||||||
|         String xRequestedWith = request.getHeader("X-Requested-With"); |         String xRequestedWith = request.getHeader("X-Requested-With"); | ||||||
|         if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) { |         if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) { | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         // 判断 URI 后缀是否为 .json 或 .xml | ||||||
|         String uri = request.getRequestURI(); |         String uri = request.getRequestURI(); | ||||||
|         if (StringUtils.equalsAnyIgnoreCase(uri, ".json", ".xml")) { |         if (StringUtils.equalsAnyIgnoreCase(uri, ".json", ".xml")) { | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         // 判断请求参数 __ajax 是否为 json 或 xml | ||||||
|         String ajax = request.getParameter("__ajax"); |         String ajax = request.getParameter("__ajax"); | ||||||
|         return StringUtils.equalsAnyIgnoreCase(ajax, "json", "xml"); |         return StringUtils.equalsAnyIgnoreCase(ajax, "json", "xml"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 获取客户端 IP 地址 | ||||||
|  |      * | ||||||
|  |      * @return 客户端 IP 地址 | ||||||
|  |      */ | ||||||
|     public static String getClientIP() { |     public static String getClientIP() { | ||||||
|         return getClientIP(getRequest()); |         return getClientIP(getRequest()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 内容编码 |      * 对内容进行 URL 编码 | ||||||
|      * |      * | ||||||
|      * @param str 内容 |      * @param str 内容 | ||||||
|      * @return 编码后的内容 |      * @return 编码后的内容 | ||||||
| @@ -216,7 +277,7 @@ public class ServletUtils extends JakartaServletUtil { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 内容解码 |      * 对内容进行 URL 解码 | ||||||
|      * |      * | ||||||
|      * @param str 内容 |      * @param str 内容 | ||||||
|      * @return 解码后的内容 |      * @return 解码后的内容 | ||||||
|   | |||||||
| @@ -4,10 +4,9 @@ import cn.hutool.core.collection.CollUtil; | |||||||
| import cn.hutool.core.convert.Convert; | import cn.hutool.core.convert.Convert; | ||||||
| import cn.hutool.core.lang.Validator; | import cn.hutool.core.lang.Validator; | ||||||
| import cn.hutool.core.util.StrUtil; | import cn.hutool.core.util.StrUtil; | ||||||
| import lombok.AccessLevel; |  | ||||||
| import lombok.NoArgsConstructor; |  | ||||||
| import org.springframework.util.AntPathMatcher; | import org.springframework.util.AntPathMatcher; | ||||||
|  |  | ||||||
|  | import java.nio.charset.Charset; | ||||||
| import java.util.*; | import java.util.*; | ||||||
| import java.util.function.Function; | import java.util.function.Function; | ||||||
| import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||||
| @@ -17,13 +16,16 @@ import java.util.stream.Collectors; | |||||||
|  * |  * | ||||||
|  * @author Lion Li |  * @author Lion Li | ||||||
|  */ |  */ | ||||||
| @NoArgsConstructor(access = AccessLevel.PRIVATE) |  | ||||||
| public class StringUtils extends org.apache.commons.lang3.StringUtils { | public class StringUtils extends org.apache.commons.lang3.StringUtils { | ||||||
|  |  | ||||||
|     public static final String SEPARATOR = ","; |     public static final String SEPARATOR = ","; | ||||||
|  |  | ||||||
|     public static final String SLASH = "/"; |     public static final String SLASH = "/"; | ||||||
|  |  | ||||||
|  |     @Deprecated | ||||||
|  |     private StringUtils() { | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 获取参数不为空值 |      * 获取参数不为空值 | ||||||
|      * |      * | ||||||
| @@ -317,7 +319,47 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils { | |||||||
|             .stream() |             .stream() | ||||||
|             .filter(Objects::nonNull) |             .filter(Objects::nonNull) | ||||||
|             .map(mapper) |             .map(mapper) | ||||||
|  |             .filter(Objects::nonNull) | ||||||
|             .collect(Collectors.toList()); |             .collect(Collectors.toList()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 不区分大小写检查 CharSequence 是否以指定的前缀开头。 | ||||||
|  |      * | ||||||
|  |      * @param str     要检查的 CharSequence 可能为 null | ||||||
|  |      * @param prefixs 要查找的前缀可能为 null | ||||||
|  |      * @return 是否包含 | ||||||
|  |      */ | ||||||
|  |     public static boolean startWithAnyIgnoreCase(CharSequence str, CharSequence... prefixs) { | ||||||
|  |         // 判断是否是以指定字符串开头 | ||||||
|  |         for (CharSequence prefix : prefixs) { | ||||||
|  |             if (StringUtils.startsWithIgnoreCase(str, prefix)) { | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 将字符串从源字符集转换为目标字符集 | ||||||
|  |      * | ||||||
|  |      * @param input       原始字符串 | ||||||
|  |      * @param fromCharset 源字符集 | ||||||
|  |      * @param toCharset   目标字符集 | ||||||
|  |      * @return 转换后的字符串 | ||||||
|  |      */ | ||||||
|  |     public static String convert(String input, Charset fromCharset, Charset toCharset) { | ||||||
|  |         if (isBlank(input)) { | ||||||
|  |             return input; | ||||||
|  |         } | ||||||
|  |         try { | ||||||
|  |             // 从源字符集获取字节 | ||||||
|  |             byte[] bytes = input.getBytes(fromCharset); | ||||||
|  |             // 使用目标字符集解码 | ||||||
|  |             return new String(bytes, toCharset); | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             return input; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -14,18 +14,6 @@ import java.util.concurrent.*; | |||||||
| @Slf4j | @Slf4j | ||||||
| @NoArgsConstructor(access = AccessLevel.PRIVATE) | @NoArgsConstructor(access = AccessLevel.PRIVATE) | ||||||
| public class Threads { | public class Threads { | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * sleep等待,单位为毫秒 |  | ||||||
|      */ |  | ||||||
|     public static void sleep(long milliseconds) { |  | ||||||
|         try { |  | ||||||
|             Thread.sleep(milliseconds); |  | ||||||
|         } catch (InterruptedException e) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 停止线程池 |      * 停止线程池 | ||||||
|      * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务. |      * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务. | ||||||
|   | |||||||
| @@ -5,11 +5,13 @@ import cn.hutool.core.lang.tree.Tree; | |||||||
| import cn.hutool.core.lang.tree.TreeNodeConfig; | import cn.hutool.core.lang.tree.TreeNodeConfig; | ||||||
| import cn.hutool.core.lang.tree.TreeUtil; | import cn.hutool.core.lang.tree.TreeUtil; | ||||||
| import cn.hutool.core.lang.tree.parser.NodeParser; | import cn.hutool.core.lang.tree.parser.NodeParser; | ||||||
| import org.dromara.common.core.utils.reflect.ReflectUtils; |  | ||||||
| import lombok.AccessLevel; | import lombok.AccessLevel; | ||||||
| import lombok.NoArgsConstructor; | import lombok.NoArgsConstructor; | ||||||
|  | import org.dromara.common.core.utils.reflect.ReflectUtils; | ||||||
|  |  | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  | import java.util.stream.Collectors; | ||||||
|  | import java.util.stream.Stream; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * 扩展 hutool TreeUtil 封装系统树构建 |  * 扩展 hutool TreeUtil 封装系统树构建 | ||||||
| @@ -24,12 +26,71 @@ public class TreeBuildUtils extends TreeUtil { | |||||||
|      */ |      */ | ||||||
|     public static final TreeNodeConfig DEFAULT_CONFIG = TreeNodeConfig.DEFAULT_CONFIG.setNameKey("label"); |     public static final TreeNodeConfig DEFAULT_CONFIG = TreeNodeConfig.DEFAULT_CONFIG.setNameKey("label"); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 构建树形结构 | ||||||
|  |      * | ||||||
|  |      * @param <T>        输入节点的类型 | ||||||
|  |      * @param <K>        节点ID的类型 | ||||||
|  |      * @param list       节点列表,其中包含了要构建树形结构的所有节点 | ||||||
|  |      * @param nodeParser 解析器,用于将输入节点转换为树节点 | ||||||
|  |      * @return 构建好的树形结构列表 | ||||||
|  |      */ | ||||||
|     public static <T, K> List<Tree<K>> build(List<T> list, NodeParser<T, K> nodeParser) { |     public static <T, K> List<Tree<K>> build(List<T> list, NodeParser<T, K> nodeParser) { | ||||||
|         if (CollUtil.isEmpty(list)) { |         if (CollUtil.isEmpty(list)) { | ||||||
|             return null; |             return CollUtil.newArrayList(); | ||||||
|         } |         } | ||||||
|         K k = ReflectUtils.invokeGetter(list.get(0), "parentId"); |         K k = ReflectUtils.invokeGetter(list.get(0), "parentId"); | ||||||
|         return TreeUtil.build(list, k, DEFAULT_CONFIG, nodeParser); |         return TreeUtil.build(list, k, DEFAULT_CONFIG, nodeParser); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 构建树形结构 | ||||||
|  |      * | ||||||
|  |      * @param <T>        输入节点的类型 | ||||||
|  |      * @param <K>        节点ID的类型 | ||||||
|  |      * @param parentId   顶级节点 | ||||||
|  |      * @param list       节点列表,其中包含了要构建树形结构的所有节点 | ||||||
|  |      * @param nodeParser 解析器,用于将输入节点转换为树节点 | ||||||
|  |      * @return 构建好的树形结构列表 | ||||||
|  |      */ | ||||||
|  |     public static <T, K> List<Tree<K>> build(List<T> list, K parentId, NodeParser<T, K> nodeParser) { | ||||||
|  |         if (CollUtil.isEmpty(list)) { | ||||||
|  |             return CollUtil.newArrayList(); | ||||||
|  |         } | ||||||
|  |         return TreeUtil.build(list, parentId, DEFAULT_CONFIG, nodeParser); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 获取节点列表中所有节点的叶子节点 | ||||||
|  |      * | ||||||
|  |      * @param <K>   节点ID的类型 | ||||||
|  |      * @param nodes 节点列表 | ||||||
|  |      * @return 包含所有叶子节点的列表 | ||||||
|  |      */ | ||||||
|  |     public static <K> List<Tree<K>> getLeafNodes(List<Tree<K>> nodes) { | ||||||
|  |         if (CollUtil.isEmpty(nodes)) { | ||||||
|  |             return CollUtil.newArrayList(); | ||||||
|  |         } | ||||||
|  |         return nodes.stream() | ||||||
|  |             .flatMap(TreeBuildUtils::extractLeafNodes) | ||||||
|  |             .collect(Collectors.toList()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 获取指定节点下的所有叶子节点 | ||||||
|  |      * | ||||||
|  |      * @param <K>  节点ID的类型 | ||||||
|  |      * @param node 要查找叶子节点的根节点 | ||||||
|  |      * @return 包含所有叶子节点的列表 | ||||||
|  |      */ | ||||||
|  |     private static <K> Stream<Tree<K>> extractLeafNodes(Tree<K> node) { | ||||||
|  |         if (!node.hasChild()) { | ||||||
|  |             return Stream.of(node); | ||||||
|  |         } else { | ||||||
|  |             // 递归调用,获取所有子节点的叶子节点 | ||||||
|  |             return node.getChildren().stream() | ||||||
|  |                 .flatMap(TreeBuildUtils::extractLeafNodes); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,11 +1,11 @@ | |||||||
| package org.dromara.common.core.utils.ip; | package org.dromara.common.core.utils.ip; | ||||||
|  |  | ||||||
| import cn.hutool.core.net.NetUtil; |  | ||||||
| import cn.hutool.http.HtmlUtil; | import cn.hutool.http.HtmlUtil; | ||||||
| import org.dromara.common.core.utils.StringUtils; |  | ||||||
| import lombok.AccessLevel; | import lombok.AccessLevel; | ||||||
| import lombok.NoArgsConstructor; | import lombok.NoArgsConstructor; | ||||||
| import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||||
|  | import org.dromara.common.core.utils.NetUtils; | ||||||
|  | import org.dromara.common.core.utils.StringUtils; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * 获取地址类 |  * 获取地址类 | ||||||
| @@ -20,14 +20,24 @@ public class AddressUtils { | |||||||
|     public static final String UNKNOWN = "XX XX"; |     public static final String UNKNOWN = "XX XX"; | ||||||
|  |  | ||||||
|     public static String getRealAddressByIP(String ip) { |     public static String getRealAddressByIP(String ip) { | ||||||
|         if (StringUtils.isBlank(ip)) { |         // 处理空串并过滤HTML标签 | ||||||
|  |         ip = HtmlUtil.cleanHtmlTag(StringUtils.blankToDefault(ip,"")); | ||||||
|  |         boolean isIPv6 = NetUtils.isIPv6(ip); | ||||||
|  |         // 判断是否为IPv4或IPv6,如果不是则返回未知地址 | ||||||
|  |         if (!NetUtils.isIPv4(ip) && !isIPv6) { | ||||||
|             return UNKNOWN; |             return UNKNOWN; | ||||||
|         } |         } | ||||||
|         // 内网不查询 |         // 内网不查询 | ||||||
|         ip = StringUtils.contains(ip, "0:0:0:0:0:0:0:1") ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip); |         if (NetUtils.isInnerIPv6(ip) || NetUtils.isInnerIP(ip)) { | ||||||
|         if (NetUtil.isInnerIP(ip)) { |  | ||||||
|             return "内网IP"; |             return "内网IP"; | ||||||
|         } |         } | ||||||
|  |         // 不支持IPv6,不再进行没有必要的IP地址信息的解析,直接返回 | ||||||
|  |         if (isIPv6) { | ||||||
|  |             log.warn("ip2region不支持IPV6地址解析:{}", ip); | ||||||
|  |             // 如有需要,可自行实现IPv6地址信息解析逻辑,并在这里返回 | ||||||
|  |             return "未知"; | ||||||
|  |         } | ||||||
|         return RegionUtils.getCityInfo(ip); |         return RegionUtils.getCityInfo(ip); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -21,7 +21,8 @@ public final class RegexUtils extends ReUtil { | |||||||
|      */ |      */ | ||||||
|     public static String extractFromString(String input, String regex, String defaultInput) { |     public static String extractFromString(String input, String regex, String defaultInput) { | ||||||
|         try { |         try { | ||||||
|             return ReUtil.get(regex, input, 1); |             String str = ReUtil.get(regex, input, 1); | ||||||
|  |             return str == null ? defaultInput : str; | ||||||
|         } catch (Exception e) { |         } catch (Exception e) { | ||||||
|             return defaultInput; |             return defaultInput; | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -15,7 +15,7 @@ public class SqlUtil { | |||||||
|     /** |     /** | ||||||
|      * 定义常用的 sql关键字 |      * 定义常用的 sql关键字 | ||||||
|      */ |      */ | ||||||
|     public static final String SQL_REGEX = "select |insert |delete |update |drop |count |exec |chr |mid |master |truncate |char |and |declare "; |     public static String SQL_REGEX = "\u000B|and |extractvalue|updatexml|sleep|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()"; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) |      * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) | ||||||
|   | |||||||
| @@ -0,0 +1,48 @@ | |||||||
|  | package org.dromara.common.core.validate.enumd; | ||||||
|  |  | ||||||
|  | import jakarta.validation.Constraint; | ||||||
|  | import jakarta.validation.Payload; | ||||||
|  |  | ||||||
|  | import java.lang.annotation.*; | ||||||
|  |  | ||||||
|  | import static java.lang.annotation.ElementType.*; | ||||||
|  | import static java.lang.annotation.RetentionPolicy.RUNTIME; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 自定义枚举校验 | ||||||
|  |  * | ||||||
|  |  * @author 秋辞未寒 | ||||||
|  |  * @date 2024-12-09 | ||||||
|  |  */ | ||||||
|  | @Documented | ||||||
|  | @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) | ||||||
|  | @Retention(RUNTIME) | ||||||
|  | @Repeatable(EnumPattern.List.class) // 允许在同一元素上多次使用该注解 | ||||||
|  | @Constraint(validatedBy = {EnumPatternValidator.class}) | ||||||
|  | public @interface EnumPattern { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 需要校验的枚举类型 | ||||||
|  |      */ | ||||||
|  |     Class<? extends Enum<?>> type(); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 枚举类型校验值字段名称 | ||||||
|  |      * 需确保该字段实现了 getter 方法 | ||||||
|  |      */ | ||||||
|  |     String fieldName(); | ||||||
|  |  | ||||||
|  |     String message() default "输入值不在枚举范围内"; | ||||||
|  |  | ||||||
|  |     Class<?>[] groups() default {}; | ||||||
|  |  | ||||||
|  |     Class<? extends Payload>[] payload() default {}; | ||||||
|  |  | ||||||
|  |     @Documented | ||||||
|  |     @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) | ||||||
|  |     @Retention(RUNTIME) | ||||||
|  |     @interface List { | ||||||
|  |         EnumPattern[] value(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,37 @@ | |||||||
|  | package org.dromara.common.core.validate.enumd; | ||||||
|  |  | ||||||
|  | import jakarta.validation.ConstraintValidator; | ||||||
|  | import jakarta.validation.ConstraintValidatorContext; | ||||||
|  | import org.dromara.common.core.utils.StringUtils; | ||||||
|  | import org.dromara.common.core.utils.reflect.ReflectUtils; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 自定义枚举校验注解实现 | ||||||
|  |  * | ||||||
|  |  * @author 秋辞未寒 | ||||||
|  |  * @date 2024-12-09 | ||||||
|  |  */ | ||||||
|  | public class EnumPatternValidator implements ConstraintValidator<EnumPattern, String> { | ||||||
|  |  | ||||||
|  |     private EnumPattern annotation;; | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void initialize(EnumPattern annotation) { | ||||||
|  |         ConstraintValidator.super.initialize(annotation); | ||||||
|  |         this.annotation = annotation; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { | ||||||
|  |         if (StringUtils.isNotBlank(value)) { | ||||||
|  |             String fieldName = annotation.fieldName(); | ||||||
|  |             for (Object e : annotation.type().getEnumConstants()) { | ||||||
|  |                 if (value.equals(ReflectUtils.invokeGetter(e, fieldName))) { | ||||||
|  |                     return true; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -1,6 +1,5 @@ | |||||||
| org.dromara.common.core.config.ApplicationConfig | org.dromara.common.core.config.ApplicationConfig | ||||||
| org.dromara.common.core.config.AsyncConfig | org.dromara.common.core.config.AsyncConfig | ||||||
| org.dromara.common.core.config.RuoYiConfig |  | ||||||
| org.dromara.common.core.config.ThreadPoolConfig | org.dromara.common.core.config.ThreadPoolConfig | ||||||
| org.dromara.common.core.config.ValidatorConfig | org.dromara.common.core.config.ValidatorConfig | ||||||
| org.dromara.common.core.utils.SpringUtils | org.dromara.common.core.utils.SpringUtils | ||||||
|   | |||||||
| @@ -30,7 +30,7 @@ import java.util.Optional; | |||||||
| import java.util.Set; | import java.util.Set; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Swagger 文档配置 |  * 接口文档配置 | ||||||
|  * |  * | ||||||
|  * @author Lion Li |  * @author Lion Li | ||||||
|  */ |  */ | ||||||
|   | |||||||
| @@ -1,11 +1,11 @@ | |||||||
| package org.dromara.common.encrypt.core; | package org.dromara.common.encrypt.core; | ||||||
|  |  | ||||||
| import cn.hutool.core.collection.CollUtil; | import cn.hutool.core.collection.CollUtil; | ||||||
| import cn.hutool.core.util.ObjectUtil; |  | ||||||
| import cn.hutool.core.util.ReflectUtil; | import cn.hutool.core.util.ReflectUtil; | ||||||
| import lombok.NoArgsConstructor; | import lombok.NoArgsConstructor; | ||||||
| import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||||
| import org.apache.ibatis.io.Resources; | import org.apache.ibatis.io.Resources; | ||||||
|  | import org.dromara.common.core.utils.ObjectUtils; | ||||||
| import org.dromara.common.core.utils.StringUtils; | import org.dromara.common.core.utils.StringUtils; | ||||||
| import org.dromara.common.encrypt.annotation.EncryptField; | import org.dromara.common.encrypt.annotation.EncryptField; | ||||||
| import org.springframework.context.ConfigurableApplicationContext; | import org.springframework.context.ConfigurableApplicationContext; | ||||||
| @@ -17,7 +17,10 @@ import org.springframework.core.type.classreading.CachingMetadataReaderFactory; | |||||||
| import org.springframework.util.ClassUtils; | import org.springframework.util.ClassUtils; | ||||||
|  |  | ||||||
| import java.lang.reflect.Field; | import java.lang.reflect.Field; | ||||||
| import java.util.*; | import java.util.Arrays; | ||||||
|  | import java.util.HashSet; | ||||||
|  | import java.util.Map; | ||||||
|  | import java.util.Set; | ||||||
| import java.util.concurrent.ConcurrentHashMap; | import java.util.concurrent.ConcurrentHashMap; | ||||||
| import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||||
|  |  | ||||||
| @@ -34,7 +37,7 @@ public class EncryptorManager { | |||||||
|     /** |     /** | ||||||
|      * 缓存加密器 |      * 缓存加密器 | ||||||
|      */ |      */ | ||||||
|     Map<EncryptContext, IEncryptor> encryptorMap = new ConcurrentHashMap<>(); |     Map<Integer, IEncryptor> encryptorMap = new ConcurrentHashMap<>(); | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 类加密字段缓存 |      * 类加密字段缓存 | ||||||
| @@ -55,10 +58,7 @@ public class EncryptorManager { | |||||||
|      * 获取类加密字段缓存 |      * 获取类加密字段缓存 | ||||||
|      */ |      */ | ||||||
|     public Set<Field> getFieldCache(Class<?> sourceClazz) { |     public Set<Field> getFieldCache(Class<?> sourceClazz) { | ||||||
|         if (ObjectUtil.isNotNull(fieldCache)) { |         return ObjectUtils.notNullGetter(fieldCache, f -> f.get(sourceClazz)); | ||||||
|             return fieldCache.get(sourceClazz); |  | ||||||
|         } |  | ||||||
|         return null; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -67,11 +67,12 @@ public class EncryptorManager { | |||||||
|      * @param encryptContext 加密执行者需要的相关配置参数 |      * @param encryptContext 加密执行者需要的相关配置参数 | ||||||
|      */ |      */ | ||||||
|     public IEncryptor registAndGetEncryptor(EncryptContext encryptContext) { |     public IEncryptor registAndGetEncryptor(EncryptContext encryptContext) { | ||||||
|         if (encryptorMap.containsKey(encryptContext)) { |         int key = encryptContext.hashCode(); | ||||||
|             return encryptorMap.get(encryptContext); |         if (encryptorMap.containsKey(key)) { | ||||||
|  |             return encryptorMap.get(key); | ||||||
|         } |         } | ||||||
|         IEncryptor encryptor = ReflectUtil.newInstance(encryptContext.getAlgorithm().getClazz(), encryptContext); |         IEncryptor encryptor = ReflectUtil.newInstance(encryptContext.getAlgorithm().getClazz(), encryptContext); | ||||||
|         encryptorMap.put(encryptContext, encryptor); |         encryptorMap.put(key, encryptor); | ||||||
|         return encryptor; |         return encryptor; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -81,7 +82,7 @@ public class EncryptorManager { | |||||||
|      * @param encryptContext 加密执行者需要的相关配置参数 |      * @param encryptContext 加密执行者需要的相关配置参数 | ||||||
|      */ |      */ | ||||||
|     public void removeEncryptor(EncryptContext encryptContext) { |     public void removeEncryptor(EncryptContext encryptContext) { | ||||||
|         this.encryptorMap.remove(encryptContext); |         this.encryptorMap.remove(encryptContext.hashCode()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|   | |||||||
| @@ -99,7 +99,7 @@ public class CryptoFilter implements Filter { | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } catch (Exception e) { |         } catch (Exception e) { | ||||||
|             throw new RuntimeException(e); |             return null; | ||||||
|         } |         } | ||||||
|         return null; |         return null; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -76,12 +76,14 @@ public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper { | |||||||
|         String encryptPassword = EncryptUtils.encryptByRsa(encryptAes, publicKey); |         String encryptPassword = EncryptUtils.encryptByRsa(encryptAes, publicKey); | ||||||
|  |  | ||||||
|         // 设置响应头 |         // 设置响应头 | ||||||
|  |         // vue版本需要设置 | ||||||
|         servletResponse.addHeader("Access-Control-Expose-Headers", headerFlag); |         servletResponse.addHeader("Access-Control-Expose-Headers", headerFlag); | ||||||
|         servletResponse.setHeader(headerFlag, encryptPassword); |  | ||||||
|         servletResponse.setHeader("Access-Control-Allow-Origin", "*"); |         servletResponse.setHeader("Access-Control-Allow-Origin", "*"); | ||||||
|         servletResponse.setHeader("Access-Control-Allow-Methods", "*"); |         servletResponse.setHeader("Access-Control-Allow-Methods", "*"); | ||||||
|  |         servletResponse.setHeader(headerFlag, encryptPassword); | ||||||
|         servletResponse.setCharacterEncoding(StandardCharsets.UTF_8.toString()); |         servletResponse.setCharacterEncoding(StandardCharsets.UTF_8.toString()); | ||||||
|  |  | ||||||
|  |  | ||||||
|         // 获取原始内容 |         // 获取原始内容 | ||||||
|         String originalBody = this.getContent(); |         String originalBody = this.getContent(); | ||||||
|         // 对内容进行加密 |         // 对内容进行加密 | ||||||
|   | |||||||
| @@ -19,10 +19,12 @@ import java.util.Map; | |||||||
|  * @author 老马 |  * @author 老马 | ||||||
|  */ |  */ | ||||||
| public class EncryptUtils { | public class EncryptUtils { | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 公钥 |      * 公钥 | ||||||
|      */ |      */ | ||||||
|     public static final String PUBLIC_KEY = "publicKey"; |     public static final String PUBLIC_KEY = "publicKey"; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 私钥 |      * 私钥 | ||||||
|      */ |      */ | ||||||
| @@ -51,7 +53,7 @@ public class EncryptUtils { | |||||||
|     /** |     /** | ||||||
|      * AES加密 |      * AES加密 | ||||||
|      * |      * | ||||||
|      * @param data     待解密数据 |      * @param data     待加密数据 | ||||||
|      * @param password 秘钥字符串 |      * @param password 秘钥字符串 | ||||||
|      * @return 加密后字符串, 采用Base64编码 |      * @return 加密后字符串, 采用Base64编码 | ||||||
|      */ |      */ | ||||||
| @@ -70,7 +72,7 @@ public class EncryptUtils { | |||||||
|     /** |     /** | ||||||
|      * AES加密 |      * AES加密 | ||||||
|      * |      * | ||||||
|      * @param data     待解密数据 |      * @param data     待加密数据 | ||||||
|      * @param password 秘钥字符串 |      * @param password 秘钥字符串 | ||||||
|      * @return 加密后字符串, 采用Hex编码 |      * @return 加密后字符串, 采用Hex编码 | ||||||
|      */ |      */ | ||||||
| @@ -208,7 +210,7 @@ public class EncryptUtils { | |||||||
|     /** |     /** | ||||||
|      * sm2私钥解密 |      * sm2私钥解密 | ||||||
|      * |      * | ||||||
|      * @param data       待加密数据 |      * @param data       待解密数据 | ||||||
|      * @param privateKey 私钥 |      * @param privateKey 私钥 | ||||||
|      * @return 解密后字符串 |      * @return 解密后字符串 | ||||||
|      */ |      */ | ||||||
| @@ -266,7 +268,7 @@ public class EncryptUtils { | |||||||
|     /** |     /** | ||||||
|      * rsa私钥解密 |      * rsa私钥解密 | ||||||
|      * |      * | ||||||
|      * @param data       待加密数据 |      * @param data       待解密数据 | ||||||
|      * @param privateKey 私钥 |      * @param privateKey 私钥 | ||||||
|      * @return 解密后字符串 |      * @return 解密后字符串 | ||||||
|      */ |      */ | ||||||
|   | |||||||
| @@ -22,13 +22,8 @@ | |||||||
|         </dependency> |         </dependency> | ||||||
|  |  | ||||||
|         <dependency> |         <dependency> | ||||||
|             <groupId>com.alibaba</groupId> |             <groupId>cn.idev.excel</groupId> | ||||||
|             <artifactId>easyexcel</artifactId> |             <artifactId>fastexcel</artifactId> | ||||||
|         </dependency> |  | ||||||
|         <dependency> |  | ||||||
|             <artifactId>commons-compress</artifactId> |  | ||||||
|             <groupId>org.apache.commons</groupId> |  | ||||||
|             <version>1.26.2</version> |  | ||||||
|         </dependency> |         </dependency> | ||||||
|     </dependencies> |     </dependencies> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -0,0 +1,24 @@ | |||||||
|  | package org.dromara.common.excel.annotation; | ||||||
|  |  | ||||||
|  | import java.lang.annotation.ElementType; | ||||||
|  | import java.lang.annotation.Retention; | ||||||
|  | import java.lang.annotation.RetentionPolicy; | ||||||
|  | import java.lang.annotation.Target; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 批注 | ||||||
|  |  * @author guzhouyanyu | ||||||
|  |  */ | ||||||
|  | @Target({ElementType.FIELD}) | ||||||
|  | @Retention(RetentionPolicy.RUNTIME) | ||||||
|  | public @interface ExcelNotation { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * col index | ||||||
|  |      */ | ||||||
|  |     int index() default -1; | ||||||
|  |     /** | ||||||
|  |      * 批注内容 | ||||||
|  |      */ | ||||||
|  |     String value() default ""; | ||||||
|  | } | ||||||
| @@ -0,0 +1,26 @@ | |||||||
|  | package org.dromara.common.excel.annotation; | ||||||
|  |  | ||||||
|  | import org.apache.poi.ss.usermodel.IndexedColors; | ||||||
|  |  | ||||||
|  | import java.lang.annotation.ElementType; | ||||||
|  | import java.lang.annotation.Retention; | ||||||
|  | import java.lang.annotation.RetentionPolicy; | ||||||
|  | import java.lang.annotation.Target; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 是否必填 | ||||||
|  |  * @author guzhouyanyu | ||||||
|  |  */ | ||||||
|  | @Target({ElementType.FIELD}) | ||||||
|  | @Retention(RetentionPolicy.RUNTIME) | ||||||
|  | public @interface ExcelRequired { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * col index | ||||||
|  |      */ | ||||||
|  |     int index() default -1; | ||||||
|  |     /** | ||||||
|  |      * 字体颜色 | ||||||
|  |      */ | ||||||
|  |     IndexedColors fontColor() default IndexedColors.RED; | ||||||
|  | } | ||||||
| @@ -2,12 +2,12 @@ package org.dromara.common.excel.convert; | |||||||
|  |  | ||||||
| import cn.hutool.core.convert.Convert; | import cn.hutool.core.convert.Convert; | ||||||
| import cn.hutool.core.util.ObjectUtil; | import cn.hutool.core.util.ObjectUtil; | ||||||
| import com.alibaba.excel.converters.Converter; | import cn.idev.excel.converters.Converter; | ||||||
| import com.alibaba.excel.enums.CellDataTypeEnum; | import cn.idev.excel.enums.CellDataTypeEnum; | ||||||
| import com.alibaba.excel.metadata.GlobalConfiguration; | import cn.idev.excel.metadata.GlobalConfiguration; | ||||||
| import com.alibaba.excel.metadata.data.ReadCellData; | import cn.idev.excel.metadata.data.ReadCellData; | ||||||
| import com.alibaba.excel.metadata.data.WriteCellData; | import cn.idev.excel.metadata.data.WriteCellData; | ||||||
| import com.alibaba.excel.metadata.property.ExcelContentProperty; | import cn.idev.excel.metadata.property.ExcelContentProperty; | ||||||
| import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||||
|  |  | ||||||
| import java.math.BigDecimal; | import java.math.BigDecimal; | ||||||
|   | |||||||
| @@ -3,12 +3,12 @@ package org.dromara.common.excel.convert; | |||||||
| import cn.hutool.core.annotation.AnnotationUtil; | import cn.hutool.core.annotation.AnnotationUtil; | ||||||
| import cn.hutool.core.convert.Convert; | import cn.hutool.core.convert.Convert; | ||||||
| import cn.hutool.core.util.ObjectUtil; | import cn.hutool.core.util.ObjectUtil; | ||||||
| import com.alibaba.excel.converters.Converter; | import cn.idev.excel.converters.Converter; | ||||||
| import com.alibaba.excel.enums.CellDataTypeEnum; | import cn.idev.excel.enums.CellDataTypeEnum; | ||||||
| import com.alibaba.excel.metadata.GlobalConfiguration; | import cn.idev.excel.metadata.GlobalConfiguration; | ||||||
| import com.alibaba.excel.metadata.data.ReadCellData; | import cn.idev.excel.metadata.data.ReadCellData; | ||||||
| import com.alibaba.excel.metadata.data.WriteCellData; | import cn.idev.excel.metadata.data.WriteCellData; | ||||||
| import com.alibaba.excel.metadata.property.ExcelContentProperty; | import cn.idev.excel.metadata.property.ExcelContentProperty; | ||||||
| import org.dromara.common.excel.annotation.ExcelDictFormat; | import org.dromara.common.excel.annotation.ExcelDictFormat; | ||||||
| import org.dromara.common.core.service.DictService; | import org.dromara.common.core.service.DictService; | ||||||
| import org.dromara.common.core.utils.SpringUtils; | import org.dromara.common.core.utils.SpringUtils; | ||||||
|   | |||||||
| @@ -3,12 +3,12 @@ package org.dromara.common.excel.convert; | |||||||
| import cn.hutool.core.annotation.AnnotationUtil; | import cn.hutool.core.annotation.AnnotationUtil; | ||||||
| import cn.hutool.core.convert.Convert; | import cn.hutool.core.convert.Convert; | ||||||
| import cn.hutool.core.util.ObjectUtil; | import cn.hutool.core.util.ObjectUtil; | ||||||
| import com.alibaba.excel.converters.Converter; | import cn.idev.excel.converters.Converter; | ||||||
| import com.alibaba.excel.enums.CellDataTypeEnum; | import cn.idev.excel.enums.CellDataTypeEnum; | ||||||
| import com.alibaba.excel.metadata.GlobalConfiguration; | import cn.idev.excel.metadata.GlobalConfiguration; | ||||||
| import com.alibaba.excel.metadata.data.ReadCellData; | import cn.idev.excel.metadata.data.ReadCellData; | ||||||
| import com.alibaba.excel.metadata.data.WriteCellData; | import cn.idev.excel.metadata.data.WriteCellData; | ||||||
| import com.alibaba.excel.metadata.property.ExcelContentProperty; | import cn.idev.excel.metadata.property.ExcelContentProperty; | ||||||
| import org.dromara.common.core.utils.reflect.ReflectUtils; | import org.dromara.common.core.utils.reflect.ReflectUtils; | ||||||
| import org.dromara.common.excel.annotation.ExcelEnumFormat; | import org.dromara.common.excel.annotation.ExcelEnumFormat; | ||||||
| import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||||
|   | |||||||
| @@ -3,11 +3,11 @@ package org.dromara.common.excel.core; | |||||||
| import cn.hutool.core.collection.CollUtil; | import cn.hutool.core.collection.CollUtil; | ||||||
| import cn.hutool.core.util.ReflectUtil; | import cn.hutool.core.util.ReflectUtil; | ||||||
| import cn.hutool.core.util.StrUtil; | import cn.hutool.core.util.StrUtil; | ||||||
| import com.alibaba.excel.annotation.ExcelProperty; | import cn.idev.excel.annotation.ExcelProperty; | ||||||
| import com.alibaba.excel.metadata.Head; | import cn.idev.excel.metadata.Head; | ||||||
| import com.alibaba.excel.write.handler.WorkbookWriteHandler; | import cn.idev.excel.write.handler.WorkbookWriteHandler; | ||||||
| import com.alibaba.excel.write.handler.context.WorkbookWriteHandlerContext; | import cn.idev.excel.write.handler.context.WorkbookWriteHandlerContext; | ||||||
| import com.alibaba.excel.write.merge.AbstractMergeStrategy; | import cn.idev.excel.write.merge.AbstractMergeStrategy; | ||||||
| import lombok.AllArgsConstructor; | import lombok.AllArgsConstructor; | ||||||
| import lombok.Data; | import lombok.Data; | ||||||
| import lombok.SneakyThrows; | import lombok.SneakyThrows; | ||||||
| @@ -112,7 +112,13 @@ public class CellMergeStrategy extends AbstractMergeStrategy implements Workbook | |||||||
|                         } |                         } | ||||||
|                         map.put(field, new RepeatCell(val, i)); |                         map.put(field, new RepeatCell(val, i)); | ||||||
|                     } else if (i == list.size() - 1) { |                     } else if (i == list.size() - 1) { | ||||||
|                         if (i > repeatCell.getCurrent() && isMerge(list, i, field)) { |                         if (!isMerge(list, i, field)) { | ||||||
|  |                             // 如果最后一行不能合并,检查之前的数据是否需要合并 | ||||||
|  |                             if (i - repeatCell.getCurrent() > 1) { | ||||||
|  |                                 cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum)); | ||||||
|  |                             } | ||||||
|  |                         } else if (i > repeatCell.getCurrent()) { | ||||||
|  |                             // 如果最后一行可以合并,则直接合并到最后 | ||||||
|                             cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum)); |                             cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum)); | ||||||
|                         } |                         } | ||||||
|                     } else if (!isMerge(list, i, field)) { |                     } else if (!isMerge(list, i, field)) { | ||||||
|   | |||||||
| @@ -1,10 +1,10 @@ | |||||||
| package org.dromara.common.excel.core; | package org.dromara.common.excel.core; | ||||||
|  |  | ||||||
| import cn.hutool.core.util.StrUtil; | import cn.hutool.core.util.StrUtil; | ||||||
| import com.alibaba.excel.context.AnalysisContext; | import cn.idev.excel.context.AnalysisContext; | ||||||
| import com.alibaba.excel.event.AnalysisEventListener; | import cn.idev.excel.event.AnalysisEventListener; | ||||||
| import com.alibaba.excel.exception.ExcelAnalysisException; | import cn.idev.excel.exception.ExcelAnalysisException; | ||||||
| import com.alibaba.excel.exception.ExcelDataConvertException; | import cn.idev.excel.exception.ExcelDataConvertException; | ||||||
| import org.dromara.common.core.utils.StreamUtils; | import org.dromara.common.core.utils.StreamUtils; | ||||||
| import org.dromara.common.core.utils.ValidatorUtils; | import org.dromara.common.core.utils.ValidatorUtils; | ||||||
| import org.dromara.common.json.utils.JsonUtils; | import org.dromara.common.json.utils.JsonUtils; | ||||||
|   | |||||||
| @@ -5,12 +5,12 @@ import cn.hutool.core.util.ArrayUtil; | |||||||
| import cn.hutool.core.util.EnumUtil; | import cn.hutool.core.util.EnumUtil; | ||||||
| import cn.hutool.core.util.ObjectUtil; | import cn.hutool.core.util.ObjectUtil; | ||||||
| import cn.hutool.core.util.StrUtil; | import cn.hutool.core.util.StrUtil; | ||||||
| import com.alibaba.excel.metadata.FieldCache; | import cn.idev.excel.metadata.FieldCache; | ||||||
| import com.alibaba.excel.metadata.FieldWrapper; | import cn.idev.excel.metadata.FieldWrapper; | ||||||
| import com.alibaba.excel.util.ClassUtils; | import cn.idev.excel.util.ClassUtils; | ||||||
| import com.alibaba.excel.write.handler.SheetWriteHandler; | import cn.idev.excel.write.handler.SheetWriteHandler; | ||||||
| import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; | import cn.idev.excel.write.metadata.holder.WriteSheetHolder; | ||||||
| import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; | import cn.idev.excel.write.metadata.holder.WriteWorkbookHolder; | ||||||
| import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||||
| import org.apache.poi.ss.usermodel.*; | import org.apache.poi.ss.usermodel.*; | ||||||
| import org.apache.poi.ss.util.CellRangeAddressList; | import org.apache.poi.ss.util.CellRangeAddressList; | ||||||
| @@ -55,6 +55,7 @@ public class ExcelDownHandler implements SheetWriteHandler { | |||||||
|      * 下拉可选项 |      * 下拉可选项 | ||||||
|      */ |      */ | ||||||
|     private final List<DropDownOptions> dropDownOptions; |     private final List<DropDownOptions> dropDownOptions; | ||||||
|  |     private final DictService dictService; | ||||||
|     /** |     /** | ||||||
|      * 当前单选进度 |      * 当前单选进度 | ||||||
|      */ |      */ | ||||||
| @@ -63,7 +64,6 @@ public class ExcelDownHandler implements SheetWriteHandler { | |||||||
|      * 当前联动选择进度 |      * 当前联动选择进度 | ||||||
|      */ |      */ | ||||||
|     private int currentLinkedOptionsSheetIndex; |     private int currentLinkedOptionsSheetIndex; | ||||||
|     private final DictService dictService; |  | ||||||
|  |  | ||||||
|     public ExcelDownHandler(List<DropDownOptions> options) { |     public ExcelDownHandler(List<DropDownOptions> options) { | ||||||
|         this.dropDownOptions = options; |         this.dropDownOptions = options; | ||||||
| @@ -139,8 +139,8 @@ public class ExcelDownHandler implements SheetWriteHandler { | |||||||
|             } else if (everyOptions.getOptions().size() > 10) { |             } else if (everyOptions.getOptions().size() > 10) { | ||||||
|                 // 当一级选项参数个数大于10,使用额外表的形式 |                 // 当一级选项参数个数大于10,使用额外表的形式 | ||||||
|                 dropDownWithSheet(helper, workbook, sheet, everyOptions.getIndex(), everyOptions.getOptions()); |                 dropDownWithSheet(helper, workbook, sheet, everyOptions.getIndex(), everyOptions.getOptions()); | ||||||
|             } else if (everyOptions.getOptions().size() != 0) { |             } else { | ||||||
|                 // 当一级选项个数不为空,使用默认形式 |                 // 否则使用默认形式 | ||||||
|                 dropDownWithSimple(helper, sheet, everyOptions.getIndex(), everyOptions.getOptions()); |                 dropDownWithSimple(helper, sheet, everyOptions.getIndex(), everyOptions.getOptions()); | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
| @@ -171,10 +171,24 @@ public class ExcelDownHandler implements SheetWriteHandler { | |||||||
|         Sheet linkedOptionsDataSheet = workbook.createSheet(WorkbookUtil.createSafeSheetName(linkedOptionsSheetName)); |         Sheet linkedOptionsDataSheet = workbook.createSheet(WorkbookUtil.createSafeSheetName(linkedOptionsSheetName)); | ||||||
|         // 将下拉表隐藏 |         // 将下拉表隐藏 | ||||||
|         workbook.setSheetHidden(workbook.getSheetIndex(linkedOptionsDataSheet), true); |         workbook.setSheetHidden(workbook.getSheetIndex(linkedOptionsDataSheet), true); | ||||||
|         // 完善横向的一级选项数据表 |         // 选项数据 | ||||||
|         List<String> firstOptions = options.getOptions(); |         List<String> firstOptions = options.getOptions(); | ||||||
|         Map<String, List<String>> secoundOptionsMap = options.getNextOptions(); |         Map<String, List<String>> secoundOptionsMap = options.getNextOptions(); | ||||||
|  |  | ||||||
|  |         // 采用按行填充数据的方式,避免出现数据无法写入的问题 | ||||||
|  |         // Attempting to write a row in the range that is already written to disk | ||||||
|  |  | ||||||
|  |         // 使用ArrayList记载数据,防止乱序 | ||||||
|  |         List<String> columnNames = new ArrayList<>(); | ||||||
|  |         // 写入第一行,即第一级的数据 | ||||||
|  |         Row firstRow = linkedOptionsDataSheet.createRow(0); | ||||||
|  |         for (int columnIndex = 0; columnIndex < firstOptions.size(); columnIndex++) { | ||||||
|  |             String columnName = firstOptions.get(columnIndex); | ||||||
|  |             firstRow.createCell(columnIndex) | ||||||
|  |                 .setCellValue(columnName); | ||||||
|  |             columnNames.add(columnName); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         // 创建名称管理器 |         // 创建名称管理器 | ||||||
|         Name name = workbook.createName(); |         Name name = workbook.createName(); | ||||||
|         // 设置名称管理器的别名 |         // 设置名称管理器的别名 | ||||||
| @@ -190,28 +204,12 @@ public class ExcelDownHandler implements SheetWriteHandler { | |||||||
|         // 设置数据校验为序列模式,引用的是名称管理器中的别名 |         // 设置数据校验为序列模式,引用的是名称管理器中的别名 | ||||||
|         this.markOptionsToSheet(helper, sheet, options.getIndex(), helper.createFormulaListConstraint(linkedOptionsSheetName)); |         this.markOptionsToSheet(helper, sheet, options.getIndex(), helper.createFormulaListConstraint(linkedOptionsSheetName)); | ||||||
|  |  | ||||||
|         for (int columIndex = 0; columIndex < firstOptions.size(); columIndex++) { |         // 创建二级选项的名称管理器 | ||||||
|             // 先提取主表中一级下拉的列名 |         for (int columIndex = 0; columIndex < columnNames.size(); columIndex++) { | ||||||
|  |             // 列名 | ||||||
|             String firstOptionsColumnName = getExcelColumnName(columIndex); |             String firstOptionsColumnName = getExcelColumnName(columIndex); | ||||||
|             // 一次循环是每一个一级选项 |             // 对应的一级值 | ||||||
|             int finalI = columIndex; |             String thisFirstOptionsValue = columnNames.get(columIndex); | ||||||
|             // 本次循环的一级选项值 |  | ||||||
|             String thisFirstOptionsValue = firstOptions.get(columIndex); |  | ||||||
|             // 创建第一行的数据 |  | ||||||
|             Optional.ofNullable(linkedOptionsDataSheet.getRow(0)) |  | ||||||
|                 // 如果不存在则创建第一行 |  | ||||||
|                 .orElseGet(() -> linkedOptionsDataSheet.createRow(finalI)) |  | ||||||
|                 // 第一行当前列 |  | ||||||
|                 .createCell(columIndex) |  | ||||||
|                 // 设置值为当前一级选项值 |  | ||||||
|                 .setCellValue(thisFirstOptionsValue); |  | ||||||
|  |  | ||||||
|             // 第二行开始,设置第二级别选项参数 |  | ||||||
|             List<String> secondOptions = secoundOptionsMap.get(thisFirstOptionsValue); |  | ||||||
|             if (CollUtil.isEmpty(secondOptions)) { |  | ||||||
|                 // 必须保证至少有一个关联选项,否则将导致Excel解析错误 |  | ||||||
|                 secondOptions = Collections.singletonList("暂无_0"); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             // 以该一级选项值创建子名称管理器 |             // 以该一级选项值创建子名称管理器 | ||||||
|             Name sonName = workbook.createName(); |             Name sonName = workbook.createName(); | ||||||
| @@ -222,7 +220,9 @@ public class ExcelDownHandler implements SheetWriteHandler { | |||||||
|                 linkedOptionsSheetName, |                 linkedOptionsSheetName, | ||||||
|                 firstOptionsColumnName, |                 firstOptionsColumnName, | ||||||
|                 firstOptionsColumnName, |                 firstOptionsColumnName, | ||||||
|                 secondOptions.size() + 1 |                 // 二级选项存在则设置为(选项个数+1)行,否则设置为2行 | ||||||
|  |                 Math.max(Optional.ofNullable(secoundOptionsMap.get(thisFirstOptionsValue)) | ||||||
|  |                     .orElseGet(ArrayList::new).size(), 1) + 1 | ||||||
|             ); |             ); | ||||||
|             // 设置名称管理器的引用位置 |             // 设置名称管理器的引用位置 | ||||||
|             sonName.setRefersToFormula(sonFunction); |             sonName.setRefersToFormula(sonFunction); | ||||||
| @@ -235,25 +235,51 @@ public class ExcelDownHandler implements SheetWriteHandler { | |||||||
|                 // 二级只能主表每一行的每一列添加二级校验 |                 // 二级只能主表每一行的每一列添加二级校验 | ||||||
|                 markLinkedOptionsToSheet(helper, sheet, i, options.getNextIndex(), helper.createFormulaListConstraint(secondOptionsFunction)); |                 markLinkedOptionsToSheet(helper, sheet, i, options.getNextIndex(), helper.createFormulaListConstraint(secondOptionsFunction)); | ||||||
|             } |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|             for (int rowIndex = 0; rowIndex < secondOptions.size(); rowIndex++) { |         // 将二级数据处理为按行区分 | ||||||
|                 // 从第二行开始填充二级选项 |         Map<Integer, List<String>> columnValueMap = new HashMap<>(); | ||||||
|                 int finalRowIndex = rowIndex + 1; |         int currentRow = 1; | ||||||
|                 int finalColumIndex = columIndex; |         while (currentRow >= 0) { | ||||||
|  |             boolean flag = false; | ||||||
|                 Row row = Optional.ofNullable(linkedOptionsDataSheet.getRow(finalRowIndex)) |             List<String> rowData = new ArrayList<>(); | ||||||
|                     // 没有则创建 |             for (String columnName : columnNames) { | ||||||
|                     .orElseGet(() -> linkedOptionsDataSheet.createRow(finalRowIndex)); |                 List<String> data = secoundOptionsMap.get(columnName); | ||||||
|                 Optional |                 if (CollUtil.isEmpty(data)) { | ||||||
|                     // 在本级一级选项所在的列 |                     // 添加空字符串填充位置 | ||||||
|                     .ofNullable(row.getCell(finalColumIndex)) |                     rowData.add(" "); | ||||||
|                     // 不存在则创建 |                     continue; | ||||||
|                     .orElseGet(() -> row.createCell(finalColumIndex)) |                 } | ||||||
|                     // 设置二级选项值 |                 // 取第一个 | ||||||
|                     .setCellValue(secondOptions.get(rowIndex)); |                 String str = data.get(0); | ||||||
|  |                 rowData.add(str); | ||||||
|  |                 // 通过移除的方式避免重复 | ||||||
|  |                 data.remove(0); | ||||||
|  |                 // 设置可以继续 | ||||||
|  |                 flag = true; | ||||||
|  |             } | ||||||
|  |             columnValueMap.put(currentRow, rowData); | ||||||
|  |             // 可以继续,则增加行数,否则置为负数跳出循环 | ||||||
|  |             if (flag) { | ||||||
|  |                 currentRow++; | ||||||
|  |             } else { | ||||||
|  |                 currentRow = -1; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         // 填充第二级选项数据 | ||||||
|  |         columnValueMap.forEach((rowIndex, rowValues) -> { | ||||||
|  |             Row row = linkedOptionsDataSheet.createRow(rowIndex); | ||||||
|  |             for (int columnIndex = 0; columnIndex < rowValues.size(); columnIndex++) { | ||||||
|  |                 String rowValue = rowValues.get(columnIndex); | ||||||
|  |                 // 填充位置的部分不渲染 | ||||||
|  |                 if (StrUtil.isNotBlank(rowValue)) { | ||||||
|  |                     row.createCell(columnIndex) | ||||||
|  |                         .setCellValue(rowValue); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |  | ||||||
|         currentLinkedOptionsSheetIndex++; |         currentLinkedOptionsSheetIndex++; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -265,9 +291,11 @@ public class ExcelDownHandler implements SheetWriteHandler { | |||||||
|      * @param value    下拉选可选值 |      * @param value    下拉选可选值 | ||||||
|      */ |      */ | ||||||
|     private void dropDownWithSheet(DataValidationHelper helper, Workbook workbook, Sheet sheet, Integer celIndex, List<String> value) { |     private void dropDownWithSheet(DataValidationHelper helper, Workbook workbook, Sheet sheet, Integer celIndex, List<String> value) { | ||||||
|  |         //由于poi的写出相关问题,超过100个会被临时写进硬盘,导致后续内存合并会出Attempting to write a row[] in the range [] that is already written to disk | ||||||
|  |         String tmpOptionsSheetName = OPTIONS_SHEET_NAME + "_" + currentOptionsColumnIndex; | ||||||
|         // 创建下拉数据表 |         // 创建下拉数据表 | ||||||
|         Sheet simpleDataSheet = Optional.ofNullable(workbook.getSheet(WorkbookUtil.createSafeSheetName(OPTIONS_SHEET_NAME))) |         Sheet simpleDataSheet = Optional.ofNullable(workbook.getSheet(WorkbookUtil.createSafeSheetName(tmpOptionsSheetName))) | ||||||
|             .orElseGet(() -> workbook.createSheet(WorkbookUtil.createSafeSheetName(OPTIONS_SHEET_NAME))); |             .orElseGet(() -> workbook.createSheet(WorkbookUtil.createSafeSheetName(tmpOptionsSheetName))); | ||||||
|         // 将下拉表隐藏 |         // 将下拉表隐藏 | ||||||
|         workbook.setSheetHidden(workbook.getSheetIndex(simpleDataSheet), true); |         workbook.setSheetHidden(workbook.getSheetIndex(simpleDataSheet), true); | ||||||
|         // 完善纵向的一级选项数据表 |         // 完善纵向的一级选项数据表 | ||||||
| @@ -276,9 +304,9 @@ public class ExcelDownHandler implements SheetWriteHandler { | |||||||
|             // 获取每一选项行,如果没有则创建 |             // 获取每一选项行,如果没有则创建 | ||||||
|             Row row = Optional.ofNullable(simpleDataSheet.getRow(i)) |             Row row = Optional.ofNullable(simpleDataSheet.getRow(i)) | ||||||
|                 .orElseGet(() -> simpleDataSheet.createRow(finalI)); |                 .orElseGet(() -> simpleDataSheet.createRow(finalI)); | ||||||
|             // 获取本级选项对应的选项列,如果没有则创建 |             // 获取本级选项对应的选项列,如果没有则创建。上述采用多个sheet,默认索引为1列 | ||||||
|             Cell cell = Optional.ofNullable(row.getCell(currentOptionsColumnIndex)) |             Cell cell = Optional.ofNullable(row.getCell(0)) | ||||||
|                 .orElseGet(() -> row.createCell(currentOptionsColumnIndex)); |                 .orElseGet(() -> row.createCell(0)); | ||||||
|             // 设置值 |             // 设置值 | ||||||
|             cell.setCellValue(value.get(i)); |             cell.setCellValue(value.get(i)); | ||||||
|         } |         } | ||||||
| @@ -286,13 +314,13 @@ public class ExcelDownHandler implements SheetWriteHandler { | |||||||
|         // 创建名称管理器 |         // 创建名称管理器 | ||||||
|         Name name = workbook.createName(); |         Name name = workbook.createName(); | ||||||
|         // 设置名称管理器的别名 |         // 设置名称管理器的别名 | ||||||
|         String nameName = String.format("%s_%d", OPTIONS_SHEET_NAME, celIndex); |         String nameName = String.format("%s_%d", tmpOptionsSheetName, celIndex); | ||||||
|         name.setNameName(nameName); |         name.setNameName(nameName); | ||||||
|         // 以纵向第一列创建一级下拉拼接引用位置 |         // 以纵向第一列创建一级下拉拼接引用位置 | ||||||
|         String function = String.format("%s!$%s$1:$%s$%d", |         String function = String.format("%s!$%s$1:$%s$%d", | ||||||
|             OPTIONS_SHEET_NAME, |             tmpOptionsSheetName, | ||||||
|             getExcelColumnName(currentOptionsColumnIndex), |             getExcelColumnName(0), | ||||||
|             getExcelColumnName(currentOptionsColumnIndex), |             getExcelColumnName(0), | ||||||
|             value.size()); |             value.size()); | ||||||
|         // 设置名称管理器的引用位置 |         // 设置名称管理器的引用位置 | ||||||
|         name.setRefersToFormula(function); |         name.setRefersToFormula(function); | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| package org.dromara.common.excel.core; | package org.dromara.common.excel.core; | ||||||
|  |  | ||||||
| import com.alibaba.excel.read.listener.ReadListener; | import cn.idev.excel.read.listener.ReadListener; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Excel 导入监听 |  * Excel 导入监听 | ||||||
|   | |||||||
| @@ -0,0 +1,135 @@ | |||||||
|  | package org.dromara.common.excel.handler; | ||||||
|  |  | ||||||
|  | import cn.hutool.core.collection.CollUtil; | ||||||
|  | import cn.idev.excel.metadata.data.DataFormatData; | ||||||
|  | import cn.idev.excel.metadata.data.WriteCellData; | ||||||
|  | import cn.idev.excel.util.StyleUtil; | ||||||
|  | import cn.idev.excel.write.handler.CellWriteHandler; | ||||||
|  | import cn.idev.excel.write.handler.SheetWriteHandler; | ||||||
|  | import cn.idev.excel.write.handler.context.CellWriteHandlerContext; | ||||||
|  | import cn.idev.excel.write.metadata.holder.WriteSheetHolder; | ||||||
|  | import cn.idev.excel.write.metadata.style.WriteCellStyle; | ||||||
|  | import cn.idev.excel.write.metadata.style.WriteFont; | ||||||
|  | import org.apache.poi.ss.usermodel.*; | ||||||
|  | import org.apache.poi.xssf.usermodel.XSSFClientAnchor; | ||||||
|  | import org.apache.poi.xssf.usermodel.XSSFRichTextString; | ||||||
|  | import org.dromara.common.core.utils.reflect.ReflectUtils; | ||||||
|  | import org.dromara.common.excel.annotation.ExcelNotation; | ||||||
|  | import org.dromara.common.excel.annotation.ExcelRequired; | ||||||
|  |  | ||||||
|  | import java.lang.reflect.Field; | ||||||
|  | import java.util.HashMap; | ||||||
|  | import java.util.Map; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 批注、必填 | ||||||
|  |  * | ||||||
|  |  * @author guzhouyanyu | ||||||
|  |  */ | ||||||
|  | public class DataWriteHandler implements SheetWriteHandler, CellWriteHandler { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 批注 | ||||||
|  |      */ | ||||||
|  |     private final Map<Integer, String> notationMap; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 头列字体颜色 | ||||||
|  |      */ | ||||||
|  |     private final Map<Integer, Short> headColumnMap; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     public DataWriteHandler(Class<?> clazz) { | ||||||
|  |         notationMap = getNotationMap(clazz); | ||||||
|  |         headColumnMap = getRequiredMap(clazz); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void afterCellDispose(CellWriteHandlerContext context) { | ||||||
|  |         if (CollUtil.isEmpty(notationMap) && CollUtil.isEmpty(headColumnMap)) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         WriteCellData<?> cellData = context.getFirstCellData(); | ||||||
|  |         WriteCellStyle writeCellStyle = cellData.getOrCreateStyle(); | ||||||
|  |  | ||||||
|  |         DataFormatData dataFormatData = new DataFormatData(); | ||||||
|  |         // 单元格设置为文本格式 | ||||||
|  |         dataFormatData.setIndex((short) 49); | ||||||
|  |         writeCellStyle.setDataFormatData(dataFormatData); | ||||||
|  |  | ||||||
|  |         if (context.getHead()) { | ||||||
|  |             Cell cell = context.getCell(); | ||||||
|  |             WriteSheetHolder writeSheetHolder = context.getWriteSheetHolder(); | ||||||
|  |             Sheet sheet = writeSheetHolder.getSheet(); | ||||||
|  |             Workbook workbook = writeSheetHolder.getSheet().getWorkbook(); | ||||||
|  |             Drawing<?> drawing = sheet.createDrawingPatriarch(); | ||||||
|  |             // 设置标题字体样式 | ||||||
|  |             WriteFont headWriteFont = new WriteFont(); | ||||||
|  |             // 加粗 | ||||||
|  |             headWriteFont.setBold(true); | ||||||
|  |             if (CollUtil.isNotEmpty(headColumnMap) && headColumnMap.containsKey(cell.getColumnIndex())) { | ||||||
|  |                 // 设置字体颜色 | ||||||
|  |                 headWriteFont.setColor(headColumnMap.get(cell.getColumnIndex())); | ||||||
|  |             } | ||||||
|  |             writeCellStyle.setWriteFont(headWriteFont); | ||||||
|  |             CellStyle cellStyle = StyleUtil.buildCellStyle(workbook, null, writeCellStyle); | ||||||
|  |             cell.setCellStyle(cellStyle); | ||||||
|  |  | ||||||
|  |             if (CollUtil.isNotEmpty(notationMap) && notationMap.containsKey(cell.getColumnIndex())) { | ||||||
|  |                 // 批注内容 | ||||||
|  |                 String notationContext = notationMap.get(cell.getColumnIndex()); | ||||||
|  |                 // 创建绘图对象 | ||||||
|  |                 Comment comment = drawing.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), 0, (short) 5, 5)); | ||||||
|  |                 comment.setString(new XSSFRichTextString(notationContext)); | ||||||
|  |                 cell.setCellComment(comment); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 获取必填列 | ||||||
|  |      */ | ||||||
|  |     private static Map<Integer, Short> getRequiredMap(Class<?> clazz) { | ||||||
|  |         Map<Integer, Short> requiredMap = new HashMap<>(); | ||||||
|  |         Field[] fields = clazz.getDeclaredFields(); | ||||||
|  |         // 检查 fields 数组是否为空 | ||||||
|  |         if (fields.length == 0) { | ||||||
|  |             return requiredMap; | ||||||
|  |         } | ||||||
|  |         Field[] filteredFields = ReflectUtils.getFields(clazz, field -> !"serialVersionUID".equals(field.getName())); | ||||||
|  |  | ||||||
|  |         for (int i = 0; i < filteredFields.length; i++) { | ||||||
|  |             Field field = filteredFields[i]; | ||||||
|  |             if (!field.isAnnotationPresent(ExcelRequired.class)) { | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |             ExcelRequired excelRequired = field.getAnnotation(ExcelRequired.class); | ||||||
|  |             int columnIndex =  excelRequired.index() == -1 ? i : excelRequired.index(); | ||||||
|  |             requiredMap.put(columnIndex, excelRequired.fontColor().getIndex()); | ||||||
|  |         } | ||||||
|  |         return requiredMap; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 获取批注 | ||||||
|  |      */ | ||||||
|  |     private static Map<Integer, String> getNotationMap(Class<?> clazz) { | ||||||
|  |         Map<Integer, String> notationMap = new HashMap<>(); | ||||||
|  |         Field[] fields = clazz.getDeclaredFields(); | ||||||
|  |         // 检查 fields 数组是否为空 | ||||||
|  |         if (fields.length == 0) { | ||||||
|  |             return notationMap; | ||||||
|  |         } | ||||||
|  |         Field[] filteredFields = ReflectUtils.getFields(clazz, field -> !"serialVersionUID".equals(field.getName())); | ||||||
|  |         for (int i = 0; i < filteredFields.length; i++) { | ||||||
|  |             Field field = filteredFields[i]; | ||||||
|  |             if (!field.isAnnotationPresent(ExcelNotation.class)) { | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |             ExcelNotation excelNotation = field.getAnnotation(ExcelNotation.class); | ||||||
|  |             int columnIndex =  excelNotation.index() == -1 ? i : excelNotation.index(); | ||||||
|  |             notationMap.put(columnIndex, excelNotation.value()); | ||||||
|  |         } | ||||||
|  |         return notationMap; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -3,13 +3,13 @@ package org.dromara.common.excel.utils; | |||||||
| import cn.hutool.core.collection.CollUtil; | import cn.hutool.core.collection.CollUtil; | ||||||
| import cn.hutool.core.io.resource.ClassPathResource; | import cn.hutool.core.io.resource.ClassPathResource; | ||||||
| import cn.hutool.core.util.IdUtil; | import cn.hutool.core.util.IdUtil; | ||||||
| import com.alibaba.excel.EasyExcel; | import cn.idev.excel.FastExcel; | ||||||
| import com.alibaba.excel.ExcelWriter; | import cn.idev.excel.ExcelWriter; | ||||||
| import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder; | import cn.idev.excel.write.builder.ExcelWriterSheetBuilder; | ||||||
| import com.alibaba.excel.write.metadata.WriteSheet; | import cn.idev.excel.write.metadata.WriteSheet; | ||||||
| import com.alibaba.excel.write.metadata.fill.FillConfig; | import cn.idev.excel.write.metadata.fill.FillConfig; | ||||||
| import com.alibaba.excel.write.metadata.fill.FillWrapper; | import cn.idev.excel.write.metadata.fill.FillWrapper; | ||||||
| import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; | import cn.idev.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; | ||||||
| import jakarta.servlet.ServletOutputStream; | import jakarta.servlet.ServletOutputStream; | ||||||
| import jakarta.servlet.http.HttpServletResponse; | import jakarta.servlet.http.HttpServletResponse; | ||||||
| import lombok.AccessLevel; | import lombok.AccessLevel; | ||||||
| @@ -18,6 +18,7 @@ import org.dromara.common.core.utils.StringUtils; | |||||||
| import org.dromara.common.core.utils.file.FileUtils; | import org.dromara.common.core.utils.file.FileUtils; | ||||||
| import org.dromara.common.excel.convert.ExcelBigNumberConvert; | import org.dromara.common.excel.convert.ExcelBigNumberConvert; | ||||||
| import org.dromara.common.excel.core.*; | import org.dromara.common.excel.core.*; | ||||||
|  | import org.dromara.common.excel.handler.DataWriteHandler; | ||||||
|  |  | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.io.InputStream; | import java.io.InputStream; | ||||||
| @@ -42,7 +43,7 @@ public class ExcelUtil { | |||||||
|      * @return 转换后集合 |      * @return 转换后集合 | ||||||
|      */ |      */ | ||||||
|     public static <T> List<T> importExcel(InputStream is, Class<T> clazz) { |     public static <T> List<T> importExcel(InputStream is, Class<T> clazz) { | ||||||
|         return EasyExcel.read(is).head(clazz).autoCloseStream(false).sheet().doReadSync(); |         return FastExcel.read(is).head(clazz).autoCloseStream(false).sheet().doReadSync(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -56,7 +57,7 @@ public class ExcelUtil { | |||||||
|      */ |      */ | ||||||
|     public static <T> ExcelResult<T> importExcel(InputStream is, Class<T> clazz, boolean isValidate) { |     public static <T> ExcelResult<T> importExcel(InputStream is, Class<T> clazz, boolean isValidate) { | ||||||
|         DefaultExcelListener<T> listener = new DefaultExcelListener<>(isValidate); |         DefaultExcelListener<T> listener = new DefaultExcelListener<>(isValidate); | ||||||
|         EasyExcel.read(is, clazz, listener).sheet().doRead(); |         FastExcel.read(is, clazz, listener).sheet().doRead(); | ||||||
|         return listener.getExcelResult(); |         return listener.getExcelResult(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -69,7 +70,7 @@ public class ExcelUtil { | |||||||
|      * @return 转换后集合 |      * @return 转换后集合 | ||||||
|      */ |      */ | ||||||
|     public static <T> ExcelResult<T> importExcel(InputStream is, Class<T> clazz, ExcelListener<T> listener) { |     public static <T> ExcelResult<T> importExcel(InputStream is, Class<T> clazz, ExcelListener<T> listener) { | ||||||
|         EasyExcel.read(is, clazz, listener).sheet().doRead(); |         FastExcel.read(is, clazz, listener).sheet().doRead(); | ||||||
|         return listener.getExcelResult(); |         return listener.getExcelResult(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -185,12 +186,13 @@ public class ExcelUtil { | |||||||
|      */ |      */ | ||||||
|     public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, boolean merge, |     public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, boolean merge, | ||||||
|                                        OutputStream os, List<DropDownOptions> options) { |                                        OutputStream os, List<DropDownOptions> options) { | ||||||
|         ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz) |         ExcelWriterSheetBuilder builder = FastExcel.write(os, clazz) | ||||||
|             .autoCloseStream(false) |             .autoCloseStream(false) | ||||||
|             // 自动适配 |             // 自动适配 | ||||||
|             .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) |             .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) | ||||||
|             // 大数值自动转换 防止失真 |             // 大数值自动转换 防止失真 | ||||||
|             .registerConverter(new ExcelBigNumberConvert()) |             .registerConverter(new ExcelBigNumberConvert()) | ||||||
|  |             .registerWriteHandler(new DataWriteHandler(clazz)) | ||||||
|             .sheet(sheetName); |             .sheet(sheetName); | ||||||
|         if (merge) { |         if (merge) { | ||||||
|             // 合并处理器 |             // 合并处理器 | ||||||
| @@ -211,8 +213,11 @@ public class ExcelUtil { | |||||||
|      * @param data         模板需要的数据 |      * @param data         模板需要的数据 | ||||||
|      * @param response     响应体 |      * @param response     响应体 | ||||||
|      */ |      */ | ||||||
|     public static void exportTemplate(List<Object> data, String filename, String templatePath, HttpServletResponse response) { |     public static <T> void exportTemplate(List<T> data, String filename, String templatePath, HttpServletResponse response) { | ||||||
|         try { |         try { | ||||||
|  |             if (CollUtil.isEmpty(data)) { | ||||||
|  |                 throw new IllegalArgumentException("数据为空"); | ||||||
|  |             } | ||||||
|             resetResponse(filename, response); |             resetResponse(filename, response); | ||||||
|             ServletOutputStream os = response.getOutputStream(); |             ServletOutputStream os = response.getOutputStream(); | ||||||
|             exportTemplate(data, templatePath, os); |             exportTemplate(data, templatePath, os); | ||||||
| @@ -230,21 +235,20 @@ public class ExcelUtil { | |||||||
|      * @param data         模板需要的数据 |      * @param data         模板需要的数据 | ||||||
|      * @param os           输出流 |      * @param os           输出流 | ||||||
|      */ |      */ | ||||||
|     public static void exportTemplate(List<Object> data, String templatePath, OutputStream os) { |     public static <T> void exportTemplate(List<T> data, String templatePath, OutputStream os) { | ||||||
|         ClassPathResource templateResource = new ClassPathResource(templatePath); |         ClassPathResource templateResource = new ClassPathResource(templatePath); | ||||||
|         ExcelWriter excelWriter = EasyExcel.write(os) |         ExcelWriter excelWriter = FastExcel.write(os) | ||||||
|             .withTemplate(templateResource.getStream()) |             .withTemplate(templateResource.getStream()) | ||||||
|             .autoCloseStream(false) |             .autoCloseStream(false) | ||||||
|             // 大数值自动转换 防止失真 |             // 大数值自动转换 防止失真 | ||||||
|             .registerConverter(new ExcelBigNumberConvert()) |             .registerConverter(new ExcelBigNumberConvert()) | ||||||
|  |             .registerWriteHandler(new DataWriteHandler(data.get(0).getClass())) | ||||||
|             .build(); |             .build(); | ||||||
|         WriteSheet writeSheet = EasyExcel.writerSheet().build(); |         WriteSheet writeSheet = FastExcel.writerSheet().build(); | ||||||
|         if (CollUtil.isEmpty(data)) { |         FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); | ||||||
|             throw new IllegalArgumentException("数据为空"); |  | ||||||
|         } |  | ||||||
|         // 单表多数据导出 模板格式为 {.属性} |         // 单表多数据导出 模板格式为 {.属性} | ||||||
|         for (Object d : data) { |         for (T d : data) { | ||||||
|             excelWriter.fill(d, writeSheet); |             excelWriter.fill(d, fillConfig, writeSheet); | ||||||
|         } |         } | ||||||
|         excelWriter.finish(); |         excelWriter.finish(); | ||||||
|     } |     } | ||||||
| @@ -261,6 +265,9 @@ public class ExcelUtil { | |||||||
|      */ |      */ | ||||||
|     public static void exportTemplateMultiList(Map<String, Object> data, String filename, String templatePath, HttpServletResponse response) { |     public static void exportTemplateMultiList(Map<String, Object> data, String filename, String templatePath, HttpServletResponse response) { | ||||||
|         try { |         try { | ||||||
|  |             if (CollUtil.isEmpty(data)) { | ||||||
|  |                 throw new IllegalArgumentException("数据为空"); | ||||||
|  |             } | ||||||
|             resetResponse(filename, response); |             resetResponse(filename, response); | ||||||
|             ServletOutputStream os = response.getOutputStream(); |             ServletOutputStream os = response.getOutputStream(); | ||||||
|             exportTemplateMultiList(data, templatePath, os); |             exportTemplateMultiList(data, templatePath, os); | ||||||
| @@ -281,6 +288,9 @@ public class ExcelUtil { | |||||||
|      */ |      */ | ||||||
|     public static void exportTemplateMultiSheet(List<Map<String, Object>> data, String filename, String templatePath, HttpServletResponse response) { |     public static void exportTemplateMultiSheet(List<Map<String, Object>> data, String filename, String templatePath, HttpServletResponse response) { | ||||||
|         try { |         try { | ||||||
|  |             if (CollUtil.isEmpty(data)) { | ||||||
|  |                 throw new IllegalArgumentException("数据为空"); | ||||||
|  |             } | ||||||
|             resetResponse(filename, response); |             resetResponse(filename, response); | ||||||
|             ServletOutputStream os = response.getOutputStream(); |             ServletOutputStream os = response.getOutputStream(); | ||||||
|             exportTemplateMultiSheet(data, templatePath, os); |             exportTemplateMultiSheet(data, templatePath, os); | ||||||
| @@ -300,16 +310,13 @@ public class ExcelUtil { | |||||||
|      */ |      */ | ||||||
|     public static void exportTemplateMultiList(Map<String, Object> data, String templatePath, OutputStream os) { |     public static void exportTemplateMultiList(Map<String, Object> data, String templatePath, OutputStream os) { | ||||||
|         ClassPathResource templateResource = new ClassPathResource(templatePath); |         ClassPathResource templateResource = new ClassPathResource(templatePath); | ||||||
|         ExcelWriter excelWriter = EasyExcel.write(os) |         ExcelWriter excelWriter = FastExcel.write(os) | ||||||
|             .withTemplate(templateResource.getStream()) |             .withTemplate(templateResource.getStream()) | ||||||
|             .autoCloseStream(false) |             .autoCloseStream(false) | ||||||
|             // 大数值自动转换 防止失真 |             // 大数值自动转换 防止失真 | ||||||
|             .registerConverter(new ExcelBigNumberConvert()) |             .registerConverter(new ExcelBigNumberConvert()) | ||||||
|             .build(); |             .build(); | ||||||
|         WriteSheet writeSheet = EasyExcel.writerSheet().build(); |         WriteSheet writeSheet = FastExcel.writerSheet().build(); | ||||||
|         if (CollUtil.isEmpty(data)) { |  | ||||||
|             throw new IllegalArgumentException("数据为空"); |  | ||||||
|         } |  | ||||||
|         for (Map.Entry<String, Object> map : data.entrySet()) { |         for (Map.Entry<String, Object> map : data.entrySet()) { | ||||||
|             // 设置列表后续还有数据 |             // 设置列表后续还有数据 | ||||||
|             FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); |             FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); | ||||||
| @@ -317,7 +324,7 @@ public class ExcelUtil { | |||||||
|                 // 多表导出必须使用 FillWrapper |                 // 多表导出必须使用 FillWrapper | ||||||
|                 excelWriter.fill(new FillWrapper(map.getKey(), (Collection<?>) map.getValue()), fillConfig, writeSheet); |                 excelWriter.fill(new FillWrapper(map.getKey(), (Collection<?>) map.getValue()), fillConfig, writeSheet); | ||||||
|             } else { |             } else { | ||||||
|                 excelWriter.fill(map.getValue(), writeSheet); |                 excelWriter.fill(map.getValue(), fillConfig, writeSheet); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         excelWriter.finish(); |         excelWriter.finish(); | ||||||
| @@ -334,17 +341,14 @@ public class ExcelUtil { | |||||||
|      */ |      */ | ||||||
|     public static void exportTemplateMultiSheet(List<Map<String, Object>> data, String templatePath, OutputStream os) { |     public static void exportTemplateMultiSheet(List<Map<String, Object>> data, String templatePath, OutputStream os) { | ||||||
|         ClassPathResource templateResource = new ClassPathResource(templatePath); |         ClassPathResource templateResource = new ClassPathResource(templatePath); | ||||||
|         ExcelWriter excelWriter = EasyExcel.write(os) |         ExcelWriter excelWriter = FastExcel.write(os) | ||||||
|             .withTemplate(templateResource.getStream()) |             .withTemplate(templateResource.getStream()) | ||||||
|             .autoCloseStream(false) |             .autoCloseStream(false) | ||||||
|             // 大数值自动转换 防止失真 |             // 大数值自动转换 防止失真 | ||||||
|             .registerConverter(new ExcelBigNumberConvert()) |             .registerConverter(new ExcelBigNumberConvert()) | ||||||
|             .build(); |             .build(); | ||||||
|         if (CollUtil.isEmpty(data)) { |  | ||||||
|             throw new IllegalArgumentException("数据为空"); |  | ||||||
|         } |  | ||||||
|         for (int i = 0; i < data.size(); i++) { |         for (int i = 0; i < data.size(); i++) { | ||||||
|             WriteSheet writeSheet = EasyExcel.writerSheet(i).build(); |             WriteSheet writeSheet = FastExcel.writerSheet(i).build(); | ||||||
|             for (Map.Entry<String, Object> map : data.get(i).entrySet()) { |             for (Map.Entry<String, Object> map : data.get(i).entrySet()) { | ||||||
|                 // 设置列表后续还有数据 |                 // 设置列表后续还有数据 | ||||||
|                 FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); |                 FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); | ||||||
|   | |||||||
| @@ -32,7 +32,7 @@ public class BigNumberSerializer extends NumberSerializer { | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void serialize(Number value, JsonGenerator gen, SerializerProvider provider) throws IOException { |     public void serialize(Number value, JsonGenerator gen, SerializerProvider provider) throws IOException { | ||||||
|         // 超出范围 序列化位字符串 |         // 超出范围 序列化为字符串 | ||||||
|         if (value.longValue() > MIN_SAFE_INTEGER && value.longValue() < MAX_SAFE_INTEGER) { |         if (value.longValue() > MIN_SAFE_INTEGER && value.longValue() < MAX_SAFE_INTEGER) { | ||||||
|             super.serialize(value, gen, provider); |             super.serialize(value, gen, provider); | ||||||
|         } else { |         } else { | ||||||
|   | |||||||
| @@ -100,7 +100,7 @@ public class LogAspect { | |||||||
|  |  | ||||||
|             if (e != null) { |             if (e != null) { | ||||||
|                 operLog.setStatus(BusinessStatus.FAIL.ordinal()); |                 operLog.setStatus(BusinessStatus.FAIL.ordinal()); | ||||||
|                 operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000)); |                 operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 3800)); | ||||||
|             } |             } | ||||||
|             // 设置方法名称 |             // 设置方法名称 | ||||||
|             String className = joinPoint.getTarget().getClass().getName(); |             String className = joinPoint.getTarget().getClass().getName(); | ||||||
| @@ -113,13 +113,12 @@ public class LogAspect { | |||||||
|             // 设置消耗时间 |             // 设置消耗时间 | ||||||
|             StopWatch stopWatch = KEY_CACHE.get(); |             StopWatch stopWatch = KEY_CACHE.get(); | ||||||
|             stopWatch.stop(); |             stopWatch.stop(); | ||||||
|             operLog.setCostTime(stopWatch.getTime()); |             operLog.setCostTime(stopWatch.getDuration().toMillis()); | ||||||
|             // 发布事件保存数据库 |             // 发布事件保存数据库 | ||||||
|             SpringUtils.context().publishEvent(operLog); |             SpringUtils.context().publishEvent(operLog); | ||||||
|         } catch (Exception exp) { |         } catch (Exception exp) { | ||||||
|             // 记录本地异常日志 |             // 记录本地异常日志 | ||||||
|             log.error("异常信息:{}", exp.getMessage()); |             log.error("异常信息:{}", exp.getMessage()); | ||||||
|             exp.printStackTrace(); |  | ||||||
|         } finally { |         } finally { | ||||||
|             KEY_CACHE.remove(); |             KEY_CACHE.remove(); | ||||||
|         } |         } | ||||||
| @@ -146,7 +145,7 @@ public class LogAspect { | |||||||
|         } |         } | ||||||
|         // 是否需要保存response,参数和值 |         // 是否需要保存response,参数和值 | ||||||
|         if (log.isSaveResponseData() && ObjectUtil.isNotNull(jsonResult)) { |         if (log.isSaveResponseData() && ObjectUtil.isNotNull(jsonResult)) { | ||||||
|             operLog.setJsonResult(StringUtils.substring(JsonUtils.toJsonString(jsonResult), 0, 2000)); |             operLog.setJsonResult(StringUtils.substring(JsonUtils.toJsonString(jsonResult), 0, 3800)); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -159,14 +158,13 @@ public class LogAspect { | |||||||
|     private void setRequestValue(JoinPoint joinPoint, OperLogEvent operLog, String[] excludeParamNames) throws Exception { |     private void setRequestValue(JoinPoint joinPoint, OperLogEvent operLog, String[] excludeParamNames) throws Exception { | ||||||
|         Map<String, String> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest()); |         Map<String, String> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest()); | ||||||
|         String requestMethod = operLog.getRequestMethod(); |         String requestMethod = operLog.getRequestMethod(); | ||||||
|         if (MapUtil.isEmpty(paramsMap) |         if (MapUtil.isEmpty(paramsMap) && StringUtils.equalsAny(requestMethod, HttpMethod.PUT.name(), HttpMethod.POST.name(), HttpMethod.DELETE.name())) { | ||||||
|                 && HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) { |  | ||||||
|             String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames); |             String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames); | ||||||
|             operLog.setOperParam(StringUtils.substring(params, 0, 2000)); |             operLog.setOperParam(StringUtils.substring(params, 0, 3800)); | ||||||
|         } else { |         } else { | ||||||
|             MapUtil.removeAny(paramsMap, EXCLUDE_PROPERTIES); |             MapUtil.removeAny(paramsMap, EXCLUDE_PROPERTIES); | ||||||
|             MapUtil.removeAny(paramsMap, excludeParamNames); |             MapUtil.removeAny(paramsMap, excludeParamNames); | ||||||
|             operLog.setOperParam(StringUtils.substring(JsonUtils.toJsonString(paramsMap), 0, 2000)); |             operLog.setOperParam(StringUtils.substring(JsonUtils.toJsonString(paramsMap), 0, 3800)); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -43,7 +43,13 @@ public class MailProperties { | |||||||
|     private String pass; |     private String pass; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 发送方,遵循RFC-822标准 |      * 发送方,遵循RFC-822标准<br> | ||||||
|  |      * 发件人可以是以下形式: | ||||||
|  |      * | ||||||
|  |      * <pre> | ||||||
|  |      * 1. user@xxx.xx | ||||||
|  |      * 2.  name <user@xxx.xx> | ||||||
|  |      * </pre> | ||||||
|      */ |      */ | ||||||
|     private String from; |     private String from; | ||||||
|  |  | ||||||
|   | |||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user