commit 2b8d36284aaf9dd36f9c194a3e9dcec4998dccf1 Author: hhb <839062268@qq.com> Date: Tue Oct 31 23:40:08 2023 +0800 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f9d49d2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +### IDEA ### +.idea/* +*.iml +*/target/* +*/*.iml +/.gradle/ +/application.pid +/.jpb/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9f53f73 --- /dev/null +++ b/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "{}" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright 2019-2023 Zheng Jie + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..4b58280 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +先运行sql/finance.sql diff --git a/eladmin-common/pom.xml b/eladmin-common/pom.xml new file mode 100644 index 0000000..23d1024 --- /dev/null +++ b/eladmin-common/pom.xml @@ -0,0 +1,26 @@ + + + + eladmin + me.zhengjie + 2.7 + + 4.0.0 + + 5.8.20 + + + eladmin-common + 公共模块 + + + + + cn.hutool + hutool-all + ${hutool.version} + + + \ No newline at end of file diff --git a/eladmin-common/src/main/java/me/zhengjie/annotation/AnonymousAccess.java b/eladmin-common/src/main/java/me/zhengjie/annotation/AnonymousAccess.java new file mode 100644 index 0000000..b2c168f --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/annotation/AnonymousAccess.java @@ -0,0 +1,30 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.annotation; + +import java.lang.annotation.*; + +/** + * @author jacky + * 用于标记匿名访问方法 + */ +@Inherited +@Documented +@Target({ElementType.METHOD,ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface AnonymousAccess { + +} diff --git a/eladmin-common/src/main/java/me/zhengjie/annotation/DataPermission.java b/eladmin-common/src/main/java/me/zhengjie/annotation/DataPermission.java new file mode 100644 index 0000000..044d551 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/annotation/DataPermission.java @@ -0,0 +1,47 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + *

+ * 用于判断是否过滤数据权限 + * 1、如果没有用到 @OneToOne 这种关联关系,只需要填写 fieldName [参考:DeptQueryCriteria.class] + * 2、如果用到了 @OneToOne ,fieldName 和 joinName 都需要填写,拿UserQueryCriteria.class举例: + * 应该是 @DataPermission(joinName = "dept", fieldName = "id") + *

+ * @author Zheng Jie + * @website https://eladmin.vip + * @date 2020-05-07 + **/ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface DataPermission { + + /** + * Entity 中的字段名称 + */ + String fieldName() default ""; + + /** + * Entity 中与部门关联的字段名称 + */ + String joinName() default ""; +} diff --git a/eladmin-common/src/main/java/me/zhengjie/annotation/Limit.java b/eladmin-common/src/main/java/me/zhengjie/annotation/Limit.java new file mode 100644 index 0000000..58b41cf --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/annotation/Limit.java @@ -0,0 +1,49 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.annotation; + +import me.zhengjie.aspect.LimitType; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author jacky + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Limit { + + // 资源名称,用于描述接口功能 + String name() default ""; + + // 资源 key + String key() default ""; + + // key prefix + String prefix() default ""; + + // 时间的,单位秒 + int period(); + + // 限制访问次数 + int count(); + + // 限制类型 + LimitType limitType() default LimitType.CUSTOMER; + +} diff --git a/eladmin-common/src/main/java/me/zhengjie/annotation/Query.java b/eladmin-common/src/main/java/me/zhengjie/annotation/Query.java new file mode 100644 index 0000000..fe616f1 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/annotation/Query.java @@ -0,0 +1,92 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Zheng Jie + * @date 2019-6-4 13:52:30 + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Query { + + // Dong ZhaoYang 2017/8/7 基本对象的属性名 + String propName() default ""; + // Dong ZhaoYang 2017/8/7 查询方式 + Type type() default Type.EQUAL; + + /** + * 连接查询的属性名,如User类中的dept + */ + String joinName() default ""; + + /** + * 默认左连接 + */ + Join join() default Join.LEFT; + + /** + * 多字段模糊搜索,仅支持String类型字段,多个用逗号隔开, 如@Query(blurry = "email,username") + */ + String blurry() default ""; + + enum Type { + // jie 2019/6/4 相等 + EQUAL + // Dong ZhaoYang 2017/8/7 大于等于 + , GREATER_THAN + // Dong ZhaoYang 2017/8/7 小于等于 + , LESS_THAN + // Dong ZhaoYang 2017/8/7 中模糊查询 + , INNER_LIKE + // Dong ZhaoYang 2017/8/7 左模糊查询 + , LEFT_LIKE + // Dong ZhaoYang 2017/8/7 右模糊查询 + , RIGHT_LIKE + // Dong ZhaoYang 2017/8/7 小于 + , LESS_THAN_NQ + // jie 2019/6/4 包含 + , IN + // 不包含 + , NOT_IN + // 不等于 + ,NOT_EQUAL + // between + ,BETWEEN + // 不为空 + ,NOT_NULL + // 为空 + ,IS_NULL, + // Aborn Jiang 2022/06/01, 对应SQL: SELECT * FROM table WHERE FIND_IN_SET('querytag', table.tags); + FIND_IN_SET + } + + /** + * @author Zheng Jie + * 适用于简单连接查询,复杂的请自定义该注解,或者使用sql查询 + */ + enum Join { + /** jie 2019-6-4 13:18:30 */ + LEFT, RIGHT, INNER + } + +} + diff --git a/eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousDeleteMapping.java b/eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousDeleteMapping.java new file mode 100644 index 0000000..6a81c2e --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousDeleteMapping.java @@ -0,0 +1,91 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.zhengjie.annotation.rest; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import me.zhengjie.annotation.AnonymousAccess; +import org.springframework.core.annotation.AliasFor; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +/** + * Annotation for mapping HTTP {@code DELETE} requests onto specific handler + * methods. + * 支持匿名访问 DeleteMapping + * + * @author liaojinlong + * @see AnonymousGetMapping + * @see AnonymousPostMapping + * @see AnonymousPutMapping + * @see AnonymousPatchMapping + * @see RequestMapping + */ +@AnonymousAccess +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@RequestMapping(method = RequestMethod.DELETE) +public @interface AnonymousDeleteMapping { + + /** + * Alias for {@link RequestMapping#name}. + */ + @AliasFor(annotation = RequestMapping.class) + String name() default ""; + + /** + * Alias for {@link RequestMapping#value}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] value() default {}; + + /** + * Alias for {@link RequestMapping#path}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] path() default {}; + + /** + * Alias for {@link RequestMapping#params}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] params() default {}; + + /** + * Alias for {@link RequestMapping#headers}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] headers() default {}; + + /** + * Alias for {@link RequestMapping#consumes}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] consumes() default {}; + + /** + * Alias for {@link RequestMapping#produces}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] produces() default {}; + +} diff --git a/eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousGetMapping.java b/eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousGetMapping.java new file mode 100644 index 0000000..c260a71 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousGetMapping.java @@ -0,0 +1,90 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.zhengjie.annotation.rest; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import me.zhengjie.annotation.AnonymousAccess; +import org.springframework.core.annotation.AliasFor; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +/** + * Annotation for mapping HTTP {@code GET} requests onto specific handler + * methods. + *

+ * 支持匿名访问 GetMapping + * + * @author liaojinlong + * @see RequestMapping + */ +@AnonymousAccess +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@RequestMapping(method = RequestMethod.GET) +public @interface AnonymousGetMapping { + + /** + * Alias for {@link RequestMapping#name}. + */ + @AliasFor(annotation = RequestMapping.class) + String name() default ""; + + /** + * Alias for {@link RequestMapping#value}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] value() default {}; + + /** + * Alias for {@link RequestMapping#path}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] path() default {}; + + /** + * Alias for {@link RequestMapping#params}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] params() default {}; + + /** + * Alias for {@link RequestMapping#headers}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] headers() default {}; + + /** + * Alias for {@link RequestMapping#consumes}. + * + * @since 4.3.5 + */ + @AliasFor(annotation = RequestMapping.class) + String[] consumes() default {}; + + /** + * Alias for {@link RequestMapping#produces}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] produces() default {}; + +} diff --git a/eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousPatchMapping.java b/eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousPatchMapping.java new file mode 100644 index 0000000..6686617 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousPatchMapping.java @@ -0,0 +1,91 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.zhengjie.annotation.rest; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import me.zhengjie.annotation.AnonymousAccess; +import org.springframework.core.annotation.AliasFor; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +/** + * Annotation for mapping HTTP {@code PATCH} requests onto specific handler + * methods. + * * 支持匿名访问 PatchMapping + * + * @author liaojinlong + * @see AnonymousGetMapping + * @see AnonymousPostMapping + * @see AnonymousPutMapping + * @see AnonymousDeleteMapping + * @see RequestMapping + */ +@AnonymousAccess +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@RequestMapping(method = RequestMethod.PATCH) +public @interface AnonymousPatchMapping { + + /** + * Alias for {@link RequestMapping#name}. + */ + @AliasFor(annotation = RequestMapping.class) + String name() default ""; + + /** + * Alias for {@link RequestMapping#value}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] value() default {}; + + /** + * Alias for {@link RequestMapping#path}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] path() default {}; + + /** + * Alias for {@link RequestMapping#params}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] params() default {}; + + /** + * Alias for {@link RequestMapping#headers}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] headers() default {}; + + /** + * Alias for {@link RequestMapping#consumes}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] consumes() default {}; + + /** + * Alias for {@link RequestMapping#produces}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] produces() default {}; + +} diff --git a/eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousPostMapping.java b/eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousPostMapping.java new file mode 100644 index 0000000..8f1cdcd --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousPostMapping.java @@ -0,0 +1,91 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.zhengjie.annotation.rest; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import me.zhengjie.annotation.AnonymousAccess; +import org.springframework.core.annotation.AliasFor; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +/** + * Annotation for mapping HTTP {@code POST} requests onto specific handler + * methods. + * 支持匿名访问 PostMapping + * + * @author liaojinlong + * @see AnonymousGetMapping + * @see AnonymousPostMapping + * @see AnonymousPutMapping + * @see AnonymousDeleteMapping + * @see RequestMapping + */ +@AnonymousAccess +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@RequestMapping(method = RequestMethod.POST) +public @interface AnonymousPostMapping { + + /** + * Alias for {@link RequestMapping#name}. + */ + @AliasFor(annotation = RequestMapping.class) + String name() default ""; + + /** + * Alias for {@link RequestMapping#value}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] value() default {}; + + /** + * Alias for {@link RequestMapping#path}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] path() default {}; + + /** + * Alias for {@link RequestMapping#params}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] params() default {}; + + /** + * Alias for {@link RequestMapping#headers}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] headers() default {}; + + /** + * Alias for {@link RequestMapping#consumes}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] consumes() default {}; + + /** + * Alias for {@link RequestMapping#produces}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] produces() default {}; + +} diff --git a/eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousPutMapping.java b/eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousPutMapping.java new file mode 100644 index 0000000..7c417da --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/annotation/rest/AnonymousPutMapping.java @@ -0,0 +1,91 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.zhengjie.annotation.rest; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import me.zhengjie.annotation.AnonymousAccess; +import org.springframework.core.annotation.AliasFor; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +/** + * Annotation for mapping HTTP {@code PUT} requests onto specific handler + * methods. + * * 支持匿名访问 PutMapping + * + * @author liaojinlong + * @see AnonymousGetMapping + * @see AnonymousPostMapping + * @see AnonymousPutMapping + * @see AnonymousDeleteMapping + * @see RequestMapping + */ +@AnonymousAccess +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@RequestMapping(method = RequestMethod.PUT) +public @interface AnonymousPutMapping { + + /** + * Alias for {@link RequestMapping#name}. + */ + @AliasFor(annotation = RequestMapping.class) + String name() default ""; + + /** + * Alias for {@link RequestMapping#value}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] value() default {}; + + /** + * Alias for {@link RequestMapping#path}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] path() default {}; + + /** + * Alias for {@link RequestMapping#params}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] params() default {}; + + /** + * Alias for {@link RequestMapping#headers}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] headers() default {}; + + /** + * Alias for {@link RequestMapping#consumes}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] consumes() default {}; + + /** + * Alias for {@link RequestMapping#produces}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] produces() default {}; + +} diff --git a/eladmin-common/src/main/java/me/zhengjie/aspect/LimitAspect.java b/eladmin-common/src/main/java/me/zhengjie/aspect/LimitAspect.java new file mode 100644 index 0000000..31f4040 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/aspect/LimitAspect.java @@ -0,0 +1,99 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.aspect; + +import com.google.common.collect.ImmutableList; +import me.zhengjie.annotation.Limit; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.utils.RequestHolder; +import me.zhengjie.utils.StringUtils; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.data.redis.core.script.RedisScript; +import org.springframework.stereotype.Component; +import javax.servlet.http.HttpServletRequest; +import java.lang.reflect.Method; + +/** + * @author / + */ +@Aspect +@Component +public class LimitAspect { + + private final RedisTemplate redisTemplate; + private static final Logger logger = LoggerFactory.getLogger(LimitAspect.class); + + public LimitAspect(RedisTemplate redisTemplate) { + this.redisTemplate = redisTemplate; + } + + @Pointcut("@annotation(me.zhengjie.annotation.Limit)") + public void pointcut() { + } + + @Around("pointcut()") + public Object around(ProceedingJoinPoint joinPoint) throws Throwable { + HttpServletRequest request = RequestHolder.getHttpServletRequest(); + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method signatureMethod = signature.getMethod(); + Limit limit = signatureMethod.getAnnotation(Limit.class); + LimitType limitType = limit.limitType(); + String key = limit.key(); + if (StringUtils.isEmpty(key)) { + if (limitType == LimitType.IP) { + key = StringUtils.getIp(request); + } else { + key = signatureMethod.getName(); + } + } + + ImmutableList keys = ImmutableList.of(StringUtils.join(limit.prefix(), "_", key, "_", request.getRequestURI().replace("/","_"))); + + String luaScript = buildLuaScript(); + RedisScript redisScript = new DefaultRedisScript<>(luaScript, Number.class); + Number count = redisTemplate.execute(redisScript, keys, limit.count(), limit.period()); + if (null != count && count.intValue() <= limit.count()) { + logger.info("第{}次访问key为 {},描述为 [{}] 的接口", count, keys, limit.name()); + return joinPoint.proceed(); + } else { + throw new BadRequestException("访问次数受限制"); + } + } + + /** + * 限流脚本 + */ + private String buildLuaScript() { + return "local c" + + "\nc = redis.call('get',KEYS[1])" + + "\nif c and tonumber(c) > tonumber(ARGV[1]) then" + + "\nreturn c;" + + "\nend" + + "\nc = redis.call('incr',KEYS[1])" + + "\nif tonumber(c) == 1 then" + + "\nredis.call('expire',KEYS[1],ARGV[2])" + + "\nend" + + "\nreturn c;"; + } +} diff --git a/eladmin-common/src/main/java/me/zhengjie/aspect/LimitType.java b/eladmin-common/src/main/java/me/zhengjie/aspect/LimitType.java new file mode 100644 index 0000000..bf3f09e --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/aspect/LimitType.java @@ -0,0 +1,27 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.aspect; + +/** + * 限流枚举 + * @author / + */ +public enum LimitType { + // 默认 + CUSTOMER, + // by ip addr + IP +} diff --git a/eladmin-common/src/main/java/me/zhengjie/base/BaseDTO.java b/eladmin-common/src/main/java/me/zhengjie/base/BaseDTO.java new file mode 100644 index 0000000..e9e7cc2 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/base/BaseDTO.java @@ -0,0 +1,40 @@ +package me.zhengjie.base; + +import lombok.Getter; +import lombok.Setter; +import org.apache.commons.lang3.builder.ToStringBuilder; +import java.io.Serializable; +import java.lang.reflect.Field; +import java.sql.Timestamp; + +/** + * @author Zheng Jie + * @date 2019年10月24日20:48:53 + */ +@Getter +@Setter +public class BaseDTO implements Serializable { + + private String createBy; + + private String updateBy; + + private Timestamp createTime; + + private Timestamp updateTime; + + @Override + public String toString() { + ToStringBuilder builder = new ToStringBuilder(this); + Field[] fields = this.getClass().getDeclaredFields(); + try { + for (Field f : fields) { + f.setAccessible(true); + builder.append(f.getName(), f.get(this)).append("\n"); + } + } catch (Exception e) { + builder.append("toString builder encounter an error"); + } + return builder.toString(); + } +} diff --git a/eladmin-common/src/main/java/me/zhengjie/base/BaseEntity.java b/eladmin-common/src/main/java/me/zhengjie/base/BaseEntity.java new file mode 100644 index 0000000..618e100 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/base/BaseEntity.java @@ -0,0 +1,85 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.base; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.hibernate.annotations.CreationTimestamp; +import org.hibernate.annotations.UpdateTimestamp; +import org.springframework.data.annotation.CreatedBy; +import org.springframework.data.annotation.LastModifiedBy; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; +import javax.persistence.Column; +import javax.persistence.EntityListeners; +import javax.persistence.MappedSuperclass; +import java.io.Serializable; +import java.lang.reflect.Field; +import java.sql.Timestamp; + +/** + * 通用字段, is_del 根据需求自行添加 + * @author Zheng Jie + * @Date 2019年10月24日20:46:32 + */ +@Getter +@Setter +@MappedSuperclass +@EntityListeners(AuditingEntityListener.class) +public class BaseEntity implements Serializable { + + @CreatedBy + @Column(name = "create_by", updatable = false) + @ApiModelProperty(value = "创建人", hidden = true) + private String createBy; + + @LastModifiedBy + @Column(name = "update_by") + @ApiModelProperty(value = "更新人", hidden = true) + private String updateBy; + + @CreationTimestamp + @Column(name = "create_time", updatable = false) + @ApiModelProperty(value = "创建时间", hidden = true) + private Timestamp createTime; + + @UpdateTimestamp + @Column(name = "update_time") + @ApiModelProperty(value = "更新时间", hidden = true) + private Timestamp updateTime; + + /* 分组校验 */ + public @interface Create {} + + /* 分组校验 */ + public @interface Update {} + + @Override + public String toString() { + ToStringBuilder builder = new ToStringBuilder(this); + Field[] fields = this.getClass().getDeclaredFields(); + try { + for (Field f : fields) { + f.setAccessible(true); + builder.append(f.getName(), f.get(this)).append("\n"); + } + } catch (Exception e) { + builder.append("toString builder encounter an error"); + } + return builder.toString(); + } +} diff --git a/eladmin-common/src/main/java/me/zhengjie/base/BaseMapper.java b/eladmin-common/src/main/java/me/zhengjie/base/BaseMapper.java new file mode 100644 index 0000000..e8bb825 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/base/BaseMapper.java @@ -0,0 +1,53 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.base; + +import java.util.List; + +/** + * @author Zheng Jie + * @date 2018-11-23 + */ +public interface BaseMapper { + + /** + * DTO转Entity + * @param dto / + * @return / + */ + E toEntity(D dto); + + /** + * Entity转DTO + * @param entity / + * @return / + */ + D toDto(E entity); + + /** + * DTO集合转Entity集合 + * @param dtoList / + * @return / + */ + List toEntity(List dtoList); + + /** + * Entity集合转DTO集合 + * @param entityList / + * @return / + */ + List toDto(List entityList); +} diff --git a/eladmin-common/src/main/java/me/zhengjie/config/AuditorConfig.java b/eladmin-common/src/main/java/me/zhengjie/config/AuditorConfig.java new file mode 100644 index 0000000..006ef6b --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/config/AuditorConfig.java @@ -0,0 +1,45 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.config; + +import me.zhengjie.utils.SecurityUtils; +import org.springframework.data.domain.AuditorAware; +import org.springframework.stereotype.Component; +import java.util.Optional; + +/** + * @description : 设置审计 + * @author : Dong ZhaoYang + * @date : 2019/10/28 + */ +@Component("auditorAware") +public class AuditorConfig implements AuditorAware { + + /** + * 返回操作员标志信息 + * + * @return / + */ + @Override + public Optional getCurrentAuditor() { + try { + // 这里应根据实际业务情况获取具体信息 + return Optional.of(SecurityUtils.getCurrentUsername()); + }catch (Exception ignored){} + // 用户定时任务,或者无Token调用的情况 + return Optional.of("System"); + } +} diff --git a/eladmin-common/src/main/java/me/zhengjie/config/AuthorityConfig.java b/eladmin-common/src/main/java/me/zhengjie/config/AuthorityConfig.java new file mode 100644 index 0000000..658e621 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/config/AuthorityConfig.java @@ -0,0 +1,37 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.config; + +import me.zhengjie.utils.SecurityUtils; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.stereotype.Service; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author Zheng Jie + */ +@Service(value = "el") +public class AuthorityConfig { + + public Boolean check(String ...permissions){ + // 获取当前用户的所有权限 + List elPermissions = SecurityUtils.getCurrentUser().getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()); + // 判断当前用户的所有权限是否包含接口上定义的权限 + return elPermissions.contains("admin") || Arrays.stream(permissions).anyMatch(elPermissions::contains); + } +} diff --git a/eladmin-common/src/main/java/me/zhengjie/config/FileProperties.java b/eladmin-common/src/main/java/me/zhengjie/config/FileProperties.java new file mode 100644 index 0000000..3d0a5c6 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/config/FileProperties.java @@ -0,0 +1,60 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.config; + +import lombok.Data; +import me.zhengjie.utils.ElConstant; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + * @author Zheng Jie + */ +@Data +@Configuration +@ConfigurationProperties(prefix = "file") +public class FileProperties { + + /** 文件大小限制 */ + private Long maxSize; + + /** 头像大小限制 */ + private Long avatarMaxSize; + + private ElPath mac; + + private ElPath linux; + + private ElPath windows; + + public ElPath getPath(){ + String os = System.getProperty("os.name"); + if(os.toLowerCase().startsWith(ElConstant.WIN)) { + return windows; + } else if(os.toLowerCase().startsWith(ElConstant.MAC)){ + return mac; + } + return linux; + } + + @Data + public static class ElPath{ + + private String path; + + private String avatar; + } +} diff --git a/eladmin-common/src/main/java/me/zhengjie/config/RedisConfig.java b/eladmin-common/src/main/java/me/zhengjie/config/RedisConfig.java new file mode 100644 index 0000000..d36a0be --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/config/RedisConfig.java @@ -0,0 +1,225 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.config; + +import cn.hutool.core.lang.Assert; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.parser.ParserConfig; +import com.alibaba.fastjson.serializer.SerializerFeature; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.digest.DigestUtils; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.data.redis.RedisProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cache.Cache; +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.interceptor.CacheErrorHandler; +import org.springframework.cache.interceptor.KeyGenerator; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.RedisSerializationContext; +import org.springframework.data.redis.serializer.RedisSerializer; +import reactor.util.annotation.Nullable; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.HashMap; +import java.util.Map; + + +/** + * @author Zheng Jie + * @date 2018-11-24 + */ +@Slf4j +@Configuration +@EnableCaching +@ConditionalOnClass(RedisOperations.class) +@EnableConfigurationProperties(RedisProperties.class) +public class RedisConfig extends CachingConfigurerSupport { + + /** + * 设置 redis 数据默认过期时间,默认2小时 + * 设置@cacheable 序列化方式 + */ + @Bean + public RedisCacheConfiguration redisCacheConfiguration(){ + FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class); + RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig(); + configuration = configuration.serializeValuesWith(RedisSerializationContext. + SerializationPair.fromSerializer(fastJsonRedisSerializer)).entryTtl(Duration.ofHours(2)); + return configuration; + } + + @SuppressWarnings("all") + @Bean(name = "redisTemplate") + @ConditionalOnMissingBean(name = "redisTemplate") + public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { + RedisTemplate template = new RedisTemplate<>(); + //序列化 + FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class); + // value值的序列化采用fastJsonRedisSerializer + template.setValueSerializer(fastJsonRedisSerializer); + template.setHashValueSerializer(fastJsonRedisSerializer); + // fastjson 升级到 1.2.83 后需要指定序列化白名单 + ParserConfig.getGlobalInstance().addAccept("me.zhengjie.domain"); + ParserConfig.getGlobalInstance().addAccept("me.zhengjie.service.dto"); + // 模块内的实体类 + ParserConfig.getGlobalInstance().addAccept("me.zhengjie.modules.mnt.domain"); + ParserConfig.getGlobalInstance().addAccept("me.zhengjie.modules.quartz.domain"); + ParserConfig.getGlobalInstance().addAccept("me.zhengjie.modules.system.domain"); + // 模块内的 Dto + ParserConfig.getGlobalInstance().addAccept("me.zhengjie.modules.mnt.service.dto"); + ParserConfig.getGlobalInstance().addAccept("me.zhengjie.modules.quartz.service.dto"); + ParserConfig.getGlobalInstance().addAccept("me.zhengjie.modules.security.service.dto"); + ParserConfig.getGlobalInstance().addAccept("me.zhengjie.modules.system.service.dto"); + // key的序列化采用StringRedisSerializer + template.setKeySerializer(new StringRedisSerializer()); + template.setHashKeySerializer(new StringRedisSerializer()); + template.setConnectionFactory(redisConnectionFactory); + return template; + } + + /** + * 自定义缓存key生成策略,默认将使用该策略 + */ + @Bean + @Override + public KeyGenerator keyGenerator() { + return (target, method, params) -> { + Map container = new HashMap<>(8); + Class targetClassClass = target.getClass(); + // 类地址 + container.put("class",targetClassClass.toGenericString()); + // 方法名称 + container.put("methodName",method.getName()); + // 包名称 + container.put("package",targetClassClass.getPackage()); + // 参数列表 + for (int i = 0; i < params.length; i++) { + container.put(String.valueOf(i),params[i]); + } + // 转为JSON字符串 + String jsonString = JSON.toJSONString(container); + // 做SHA256 Hash计算,得到一个SHA256摘要作为Key + return DigestUtils.sha256Hex(jsonString); + }; + } + + @Bean + @Override + @SuppressWarnings({"all"}) + public CacheErrorHandler errorHandler() { + // 异常处理,当Redis发生异常时,打印日志,但是程序正常走 + log.info("初始化 -> [{}]", "Redis CacheErrorHandler"); + return new CacheErrorHandler() { + @Override + public void handleCacheGetError(RuntimeException e, Cache cache, Object key) { + log.error("Redis occur handleCacheGetError:key -> [{}]", key, e); + } + + @Override + public void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) { + log.error("Redis occur handleCachePutError:key -> [{}];value -> [{}]", key, value, e); + } + + @Override + public void handleCacheEvictError(RuntimeException e, Cache cache, Object key) { + log.error("Redis occur handleCacheEvictError:key -> [{}]", key, e); + } + + @Override + public void handleCacheClearError(RuntimeException e, Cache cache) { + log.error("Redis occur handleCacheClearError:", e); + } + }; + } +} + +/** + * Value 序列化 + * + * @author / + * @param + */ +class FastJsonRedisSerializer implements RedisSerializer { + + private final Class clazz; + + FastJsonRedisSerializer(Class clazz) { + super(); + this.clazz = clazz; + } + + @Override + public byte[] serialize(T t) { + if (t == null) { + return new byte[0]; + } + return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(StandardCharsets.UTF_8); + } + + @Override + public T deserialize(byte[] bytes) { + if (bytes == null || bytes.length == 0) { + return null; + } + String str = new String(bytes, StandardCharsets.UTF_8); + return JSON.parseObject(str, clazz); + } + +} + +/** + * 重写序列化器 + * + * @author / + */ +class StringRedisSerializer implements RedisSerializer { + + private final Charset charset; + + StringRedisSerializer() { + this(StandardCharsets.UTF_8); + } + + private StringRedisSerializer(Charset charset) { + Assert.notNull(charset, "Charset must not be null!"); + this.charset = charset; + } + + @Override + public String deserialize(byte[] bytes) { + return (bytes == null ? null : new String(bytes, charset)); + } + + @Override + public @Nullable byte[] serialize(Object object) { + String string = JSON.toJSONString(object); + + if (org.apache.commons.lang3.StringUtils.isBlank(string)) { + return null; + } + string = string.replace("\"", ""); + return string.getBytes(charset); + } +} diff --git a/eladmin-common/src/main/java/me/zhengjie/config/RsaProperties.java b/eladmin-common/src/main/java/me/zhengjie/config/RsaProperties.java new file mode 100644 index 0000000..0a4a537 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/config/RsaProperties.java @@ -0,0 +1,38 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.config; + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * @author Zheng Jie + * @website https://eladmin.vip + * @description + * @date 2020-05-18 + **/ +@Data +@Component +public class RsaProperties { + + public static String privateKey; + + @Value("${rsa.private_key}") + public void setPrivateKey(String privateKey) { + RsaProperties.privateKey = privateKey; + } +} \ No newline at end of file diff --git a/eladmin-common/src/main/java/me/zhengjie/config/SwaggerConfig.java b/eladmin-common/src/main/java/me/zhengjie/config/SwaggerConfig.java new file mode 100644 index 0000000..1edbcc4 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/config/SwaggerConfig.java @@ -0,0 +1,151 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.config; + +import cn.hutool.core.collection.CollUtil; +import com.fasterxml.classmate.TypeResolver; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; +import org.springframework.data.domain.Pageable; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.schema.AlternateTypeRule; +import springfox.documentation.schema.AlternateTypeRuleConvention; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.ApiKey; +import springfox.documentation.service.AuthorizationScope; +import springfox.documentation.service.SecurityReference; +import springfox.documentation.service.SecurityScheme; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spi.service.contexts.SecurityContext; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +import java.util.ArrayList; +import java.util.List; +import static springfox.documentation.schema.AlternateTypeRules.newRule; + +/** + * api页面 /doc.html + * @author Zheng Jie + * @date 2018-11-23 + */ +@Configuration +@EnableSwagger2 +public class SwaggerConfig { + + @Value("${jwt.header}") + private String tokenHeader; + + @Value("${swagger.enabled}") + private Boolean enabled; + + @Bean + @SuppressWarnings("all") + public Docket createRestApi() { + return new Docket(DocumentationType.SWAGGER_2) + .enable(enabled) + .pathMapping("/") + .apiInfo(apiInfo()) + .select() + .paths(PathSelectors.regex("^(?!/error).*")) + .paths(PathSelectors.any()) + .build() + //添加登陆认证 + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()); + } + + private ApiInfo apiInfo() { + return new ApiInfoBuilder() + .description("一个简单且易上手的 Spring boot 后台管理框架") + .title("ELADMIN 接口文档") + .version("2.7") + .build(); + } + + private List securitySchemes() { + //设置请求头信息 + List securitySchemes = new ArrayList<>(); + ApiKey apiKey = new ApiKey(tokenHeader, tokenHeader, "header"); + securitySchemes.add(apiKey); + return securitySchemes; + } + + private List securityContexts() { + //设置需要登录认证的路径 + List securityContexts = new ArrayList<>(); + securityContexts.add(getContextByPath()); + return securityContexts; + } + + private SecurityContext getContextByPath() { + return SecurityContext.builder() + .securityReferences(defaultAuth()) + // 表示 /auth/code、/auth/login 接口不需要使用securitySchemes即不需要带token + .operationSelector(o->o.requestMappingPattern().matches("^(?!/auth/code|/auth/login).*$")) + .build(); + } + + private List defaultAuth() { + List securityReferences = new ArrayList<>(); + AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); + AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; + authorizationScopes[0] = authorizationScope; + securityReferences.add(new SecurityReference(tokenHeader, authorizationScopes)); + return securityReferences; + } +} + +/** + * 将Pageable转换展示在swagger中 + */ +@Configuration +class SwaggerDataConfig { + + @Bean + public AlternateTypeRuleConvention pageableConvention(final TypeResolver resolver) { + return new AlternateTypeRuleConvention() { + @Override + public int getOrder() { + return Ordered.HIGHEST_PRECEDENCE; + } + + @Override + public List rules() { + return CollUtil.newArrayList(newRule(resolver.resolve(Pageable.class), resolver.resolve(Page.class))); + } + }; + } + + @ApiModel + @Data + private static class Page { + @ApiModelProperty("页码 (0..N)") + private Integer page; + + @ApiModelProperty("每页显示的数目") + private Integer size; + + @ApiModelProperty("以下列格式排序标准:property[,asc | desc]。 默认排序顺序为升序。 支持多种排序条件:如:id,asc") + private List sort; + } +} diff --git a/eladmin-common/src/main/java/me/zhengjie/exception/BadConfigurationException.java b/eladmin-common/src/main/java/me/zhengjie/exception/BadConfigurationException.java new file mode 100644 index 0000000..ede3691 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/exception/BadConfigurationException.java @@ -0,0 +1,98 @@ +/* + * Copyright 2019-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.exception; + +/** + * 统一关于错误配置信息 异常 + * + * @author: liaojinlong + * @date: 2020/6/10 18:06 + */ +public class BadConfigurationException extends RuntimeException { + /** + * Constructs a new runtime exception with {@code null} as its + * detail message. The cause is not initialized, and may subsequently be + * initialized by a call to {@link #initCause}. + */ + public BadConfigurationException() { + super(); + } + + /** + * Constructs a new runtime exception with the specified detail message. + * The cause is not initialized, and may subsequently be initialized by a + * call to {@link #initCause}. + * + * @param message the detail message. The detail message is saved for + * later retrieval by the {@link #getMessage()} method. + */ + public BadConfigurationException(String message) { + super(message); + } + + /** + * Constructs a new runtime exception with the specified detail message and + * cause.

Note that the detail message associated with + * {@code cause} is not automatically incorporated in + * this runtime exception's detail message. + * + * @param message the detail message (which is saved for later retrieval + * by the {@link #getMessage()} method). + * @param cause the cause (which is saved for later retrieval by the + * {@link #getCause()} method). (A {@code null} value is + * permitted, and indicates that the cause is nonexistent or + * unknown.) + * @since 1.4 + */ + public BadConfigurationException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new runtime exception with the specified cause and a + * detail message of {@code (cause==null ? null : cause.toString())} + * (which typically contains the class and detail message of + * {@code cause}). This constructor is useful for runtime exceptions + * that are little more than wrappers for other throwables. + * + * @param cause the cause (which is saved for later retrieval by the + * {@link #getCause()} method). (A {@code null} value is + * permitted, and indicates that the cause is nonexistent or + * unknown.) + * @since 1.4 + */ + public BadConfigurationException(Throwable cause) { + super(cause); + } + + /** + * Constructs a new runtime exception with the specified detail + * message, cause, suppression enabled or disabled, and writable + * stack trace enabled or disabled. + * + * @param message the detail message. + * @param cause the cause. (A {@code null} value is permitted, + * and indicates that the cause is nonexistent or unknown.) + * @param enableSuppression whether or not suppression is enabled + * or disabled + * @param writableStackTrace whether or not the stack trace should + * be writable + * @since 1.7 + */ + protected BadConfigurationException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/eladmin-common/src/main/java/me/zhengjie/exception/BadRequestException.java b/eladmin-common/src/main/java/me/zhengjie/exception/BadRequestException.java new file mode 100644 index 0000000..900453b --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/exception/BadRequestException.java @@ -0,0 +1,40 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; +import static org.springframework.http.HttpStatus.BAD_REQUEST; + +/** + * @author Zheng Jie + * @date 2018-11-23 + * 统一异常处理 + */ +@Getter +public class BadRequestException extends RuntimeException{ + + private Integer status = BAD_REQUEST.value(); + + public BadRequestException(String msg){ + super(msg); + } + + public BadRequestException(HttpStatus status,String msg){ + super(msg); + this.status = status.value(); + } +} diff --git a/eladmin-common/src/main/java/me/zhengjie/exception/EntityExistException.java b/eladmin-common/src/main/java/me/zhengjie/exception/EntityExistException.java new file mode 100644 index 0000000..03f9bf2 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/exception/EntityExistException.java @@ -0,0 +1,34 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.exception; + +import org.springframework.util.StringUtils; + +/** + * @author Zheng Jie + * @date 2018-11-23 + */ +public class EntityExistException extends RuntimeException { + + public EntityExistException(Class clazz, String field, String val) { + super(EntityExistException.generateMessage(clazz.getSimpleName(), field, val)); + } + + private static String generateMessage(String entity, String field, String val) { + return StringUtils.capitalize(entity) + + " with " + field + " "+ val + " existed"; + } +} \ No newline at end of file diff --git a/eladmin-common/src/main/java/me/zhengjie/exception/EntityNotFoundException.java b/eladmin-common/src/main/java/me/zhengjie/exception/EntityNotFoundException.java new file mode 100644 index 0000000..bcdc956 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/exception/EntityNotFoundException.java @@ -0,0 +1,34 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.exception; + +import org.springframework.util.StringUtils; + +/** + * @author Zheng Jie + * @date 2018-11-23 + */ +public class EntityNotFoundException extends RuntimeException { + + public EntityNotFoundException(Class clazz, String field, String val) { + super(EntityNotFoundException.generateMessage(clazz.getSimpleName(), field, val)); + } + + private static String generateMessage(String entity, String field, String val) { + return StringUtils.capitalize(entity) + + " with " + field + " "+ val + " does not exist"; + } +} \ No newline at end of file diff --git a/eladmin-common/src/main/java/me/zhengjie/exception/handler/ApiError.java b/eladmin-common/src/main/java/me/zhengjie/exception/handler/ApiError.java new file mode 100644 index 0000000..977788c --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/exception/handler/ApiError.java @@ -0,0 +1,49 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.exception.handler; + +import lombok.Data; + +/** + * @author Zheng Jie + * @date 2018-11-23 + */ +@Data +class ApiError { + + private Integer status = 400; + private Long timestamp; + private String message; + + private ApiError() { + timestamp = System.currentTimeMillis(); + } + + public static ApiError error(String message){ + ApiError apiError = new ApiError(); + apiError.setMessage(message); + return apiError; + } + + public static ApiError error(Integer status, String message){ + ApiError apiError = new ApiError(); + apiError.setStatus(status); + apiError.setMessage(message); + return apiError; + } +} + + diff --git a/eladmin-common/src/main/java/me/zhengjie/exception/handler/GlobalExceptionHandler.java b/eladmin-common/src/main/java/me/zhengjie/exception/handler/GlobalExceptionHandler.java new file mode 100644 index 0000000..23cbb38 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/exception/handler/GlobalExceptionHandler.java @@ -0,0 +1,113 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.exception.handler; + +import lombok.extern.slf4j.Slf4j; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.exception.EntityExistException; +import me.zhengjie.exception.EntityNotFoundException; +import me.zhengjie.utils.ThrowableUtil; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.validation.FieldError; +import org.springframework.validation.ObjectError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import static org.springframework.http.HttpStatus.*; + +/** + * @author Zheng Jie + * @date 2018-11-23 + */ +@Slf4j +@RestControllerAdvice +public class GlobalExceptionHandler { + + /** + * 处理所有不可知的异常 + */ + @ExceptionHandler(Throwable.class) + public ResponseEntity handleException(Throwable e){ + // 打印堆栈信息 + log.error(ThrowableUtil.getStackTrace(e)); + return buildResponseEntity(ApiError.error(e.getMessage())); + } + + /** + * BadCredentialsException + */ + @ExceptionHandler(BadCredentialsException.class) + public ResponseEntity badCredentialsException(BadCredentialsException e){ + // 打印堆栈信息 + String message = "坏的凭证".equals(e.getMessage()) ? "用户名或密码不正确" : e.getMessage(); + log.error(message); + return buildResponseEntity(ApiError.error(message)); + } + + /** + * 处理自定义异常 + */ + @ExceptionHandler(value = BadRequestException.class) + public ResponseEntity badRequestException(BadRequestException e) { + // 打印堆栈信息 + log.error(ThrowableUtil.getStackTrace(e)); + return buildResponseEntity(ApiError.error(e.getStatus(),e.getMessage())); + } + + /** + * 处理 EntityExist + */ + @ExceptionHandler(value = EntityExistException.class) + public ResponseEntity entityExistException(EntityExistException e) { + // 打印堆栈信息 + log.error(ThrowableUtil.getStackTrace(e)); + return buildResponseEntity(ApiError.error(e.getMessage())); + } + + /** + * 处理 EntityNotFound + */ + @ExceptionHandler(value = EntityNotFoundException.class) + public ResponseEntity entityNotFoundException(EntityNotFoundException e) { + // 打印堆栈信息 + log.error(ThrowableUtil.getStackTrace(e)); + return buildResponseEntity(ApiError.error(NOT_FOUND.value(),e.getMessage())); + } + + /** + * 处理所有接口数据验证异常 + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity handleMethodArgumentNotValidException(MethodArgumentNotValidException e){ + // 打印堆栈信息 + log.error(ThrowableUtil.getStackTrace(e)); + ObjectError objectError = e.getBindingResult().getAllErrors().get(0); + String message = objectError.getDefaultMessage(); + if (objectError instanceof FieldError) { + message = ((FieldError) objectError).getField() + ": " + message; + } + return buildResponseEntity(ApiError.error(message)); + } + + /** + * 统一返回 + */ + private ResponseEntity buildResponseEntity(ApiError apiError) { + return new ResponseEntity<>(apiError, HttpStatus.valueOf(apiError.getStatus())); + } +} diff --git a/eladmin-common/src/main/java/me/zhengjie/utils/CacheKey.java b/eladmin-common/src/main/java/me/zhengjie/utils/CacheKey.java new file mode 100644 index 0000000..7485713 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/utils/CacheKey.java @@ -0,0 +1,58 @@ +/* + * Copyright 2019-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.utils; + +/** + * @author: liaojinlong + * @date: 2020/6/11 15:49 + * @apiNote: 关于缓存的Key集合 + */ +public interface CacheKey { + + /** + * 用户 + */ + String USER_ID = "user::id:"; + /** + * 数据 + */ + String DATA_USER = "data::user:"; + /** + * 菜单 + */ + String MENU_ID = "menu::id:"; + String MENU_USER = "menu::user:"; + /** + * 角色授权 + */ + String ROLE_AUTH = "role::auth:"; + /** + * 角色信息 + */ + String ROLE_ID = "role::id:"; + /** + * 部门 + */ + String DEPT_ID = "dept::id:"; + /** + * 岗位 + */ + String JOB_ID = "job::id:"; + /** + * 数据字典 + */ + String DICT_NAME = "dict::name:"; +} diff --git a/eladmin-common/src/main/java/me/zhengjie/utils/CallBack.java b/eladmin-common/src/main/java/me/zhengjie/utils/CallBack.java new file mode 100644 index 0000000..9b10812 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/utils/CallBack.java @@ -0,0 +1,43 @@ +/* + * Copyright 2019-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.zhengjie.utils; + +/** + * @author: liaojinlong + * @date: 2020/6/9 17:02 + * @since: 1.0 + * @see {@link SpringContextHolder} + * 针对某些初始化方法,在SpringContextHolder 初始化前时,
+ * 可提交一个 提交回调任务。
+ * 在SpringContextHolder 初始化后,进行回调使用 + */ + +public interface CallBack { + /** + * 回调执行方法 + */ + void executor(); + + /** + * 本回调任务名称 + * @return / + */ + default String getCallBackName() { + return Thread.currentThread().getId() + ":" + this.getClass().getName(); + } +} + diff --git a/eladmin-common/src/main/java/me/zhengjie/utils/CloseUtil.java b/eladmin-common/src/main/java/me/zhengjie/utils/CloseUtil.java new file mode 100644 index 0000000..f39919f --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/utils/CloseUtil.java @@ -0,0 +1,47 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.utils; + +import java.io.Closeable; + +/** + * @author Zheng Jie + * @website https://eladmin.vip + * @description 用于关闭各种连接,缺啥补啥 + * @date 2021-03-05 + **/ +public class CloseUtil { + + public static void close(Closeable closeable) { + if (null != closeable) { + try { + closeable.close(); + } catch (Exception e) { + // 静默关闭 + } + } + } + + public static void close(AutoCloseable closeable) { + if (null != closeable) { + try { + closeable.close(); + } catch (Exception e) { + // 静默关闭 + } + } + } +} diff --git a/eladmin-common/src/main/java/me/zhengjie/utils/DateUtil.java b/eladmin-common/src/main/java/me/zhengjie/utils/DateUtil.java new file mode 100644 index 0000000..0b0bf63 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/utils/DateUtil.java @@ -0,0 +1,160 @@ +/* + * Copyright 2019-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.zhengjie.utils; + +import java.time.*; +import java.time.format.DateTimeFormatter; +import java.util.Date; + +/** + * @author: liaojinlong + * @date: 2020/6/11 16:28 + * @apiNote: JDK 8 新日期类 格式化与字符串转换 工具类 + */ +public class DateUtil { + + public static final DateTimeFormatter DFY_MD_HMS = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + public static final DateTimeFormatter DFY_MD = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + /** + * LocalDateTime 转时间戳 + * + * @param localDateTime / + * @return / + */ + public static Long getTimeStamp(LocalDateTime localDateTime) { + return localDateTime.atZone(ZoneId.systemDefault()).toEpochSecond(); + } + + /** + * 时间戳转LocalDateTime + * + * @param timeStamp / + * @return / + */ + public static LocalDateTime fromTimeStamp(Long timeStamp) { + return LocalDateTime.ofEpochSecond(timeStamp, 0, OffsetDateTime.now().getOffset()); + } + + /** + * LocalDateTime 转 Date + * Jdk8 后 不推荐使用 {@link Date} Date + * + * @param localDateTime / + * @return / + */ + public static Date toDate(LocalDateTime localDateTime) { + return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()); + } + + /** + * LocalDate 转 Date + * Jdk8 后 不推荐使用 {@link Date} Date + * + * @param localDate / + * @return / + */ + public static Date toDate(LocalDate localDate) { + return toDate(localDate.atTime(LocalTime.now(ZoneId.systemDefault()))); + } + + + /** + * Date转 LocalDateTime + * Jdk8 后 不推荐使用 {@link Date} Date + * + * @param date / + * @return / + */ + public static LocalDateTime toLocalDateTime(Date date) { + return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()); + } + + /** + * 日期 格式化 + * + * @param localDateTime / + * @param patten / + * @return / + */ + public static String localDateTimeFormat(LocalDateTime localDateTime, String patten) { + DateTimeFormatter df = DateTimeFormatter.ofPattern(patten); + return df.format(localDateTime); + } + + /** + * 日期 格式化 + * + * @param localDateTime / + * @param df / + * @return / + */ + public static String localDateTimeFormat(LocalDateTime localDateTime, DateTimeFormatter df) { + return df.format(localDateTime); + } + + /** + * 日期格式化 yyyy-MM-dd HH:mm:ss + * + * @param localDateTime / + * @return / + */ + public static String localDateTimeFormatyMdHms(LocalDateTime localDateTime) { + return DFY_MD_HMS.format(localDateTime); + } + + /** + * 日期格式化 yyyy-MM-dd + * + * @param localDateTime / + * @return / + */ + public String localDateTimeFormatyMd(LocalDateTime localDateTime) { + return DFY_MD.format(localDateTime); + } + + /** + * 字符串转 LocalDateTime ,字符串格式 yyyy-MM-dd + * + * @param localDateTime / + * @return / + */ + public static LocalDateTime parseLocalDateTimeFormat(String localDateTime, String pattern) { + DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern); + return LocalDateTime.from(dateTimeFormatter.parse(localDateTime)); + } + + /** + * 字符串转 LocalDateTime ,字符串格式 yyyy-MM-dd + * + * @param localDateTime / + * @return / + */ + public static LocalDateTime parseLocalDateTimeFormat(String localDateTime, DateTimeFormatter dateTimeFormatter) { + return LocalDateTime.from(dateTimeFormatter.parse(localDateTime)); + } + + /** + * 字符串转 LocalDateTime ,字符串格式 yyyy-MM-dd HH:mm:ss + * + * @param localDateTime / + * @return / + */ + public static LocalDateTime parseLocalDateTimeFormatyMdHms(String localDateTime) { + return LocalDateTime.from(DFY_MD_HMS.parse(localDateTime)); + } +} diff --git a/eladmin-common/src/main/java/me/zhengjie/utils/ElConstant.java b/eladmin-common/src/main/java/me/zhengjie/utils/ElConstant.java new file mode 100644 index 0000000..479b1e6 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/utils/ElConstant.java @@ -0,0 +1,34 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.utils; + +/** + * 常用静态常量 + * + * @author Zheng Jie + * @date 2018-12-26 + */ +public class ElConstant { + /** + * win 系统 + */ + public static final String WIN = "win"; + + /** + * mac 系统 + */ + public static final String MAC = "mac"; +} diff --git a/eladmin-common/src/main/java/me/zhengjie/utils/EncryptUtils.java b/eladmin-common/src/main/java/me/zhengjie/utils/EncryptUtils.java new file mode 100644 index 0000000..4f334aa --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/utils/EncryptUtils.java @@ -0,0 +1,100 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.utils; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESKeySpec; +import javax.crypto.spec.IvParameterSpec; +import java.nio.charset.StandardCharsets; + +/** + * 加密 + * @author Zheng Jie + * @date 2018-11-23 + */ + +public class EncryptUtils { + + private static final String STR_PARAM = "Passw0rd"; + + private static Cipher cipher; + + private static final IvParameterSpec IV = new IvParameterSpec(STR_PARAM.getBytes(StandardCharsets.UTF_8)); + + private static DESKeySpec getDesKeySpec(String source) throws Exception { + if (source == null || source.length() == 0){ + return null; + } + cipher = Cipher.getInstance("DES/CBC/PKCS5Padding"); + String strKey = "Passw0rd"; + return new DESKeySpec(strKey.getBytes(StandardCharsets.UTF_8)); + } + + /** + * 对称加密 + */ + public static String desEncrypt(String source) throws Exception { + DESKeySpec desKeySpec = getDesKeySpec(source); + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); + SecretKey secretKey = keyFactory.generateSecret(desKeySpec); + cipher.init(Cipher.ENCRYPT_MODE, secretKey, IV); + return byte2hex( + cipher.doFinal(source.getBytes(StandardCharsets.UTF_8))).toUpperCase(); + } + + /** + * 对称解密 + */ + public static String desDecrypt(String source) throws Exception { + byte[] src = hex2byte(source.getBytes(StandardCharsets.UTF_8)); + DESKeySpec desKeySpec = getDesKeySpec(source); + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); + SecretKey secretKey = keyFactory.generateSecret(desKeySpec); + cipher.init(Cipher.DECRYPT_MODE, secretKey, IV); + byte[] retByte = cipher.doFinal(src); + return new String(retByte); + } + + private static String byte2hex(byte[] inStr) { + String stmp; + StringBuilder out = new StringBuilder(inStr.length * 2); + for (byte b : inStr) { + stmp = Integer.toHexString(b & 0xFF); + if (stmp.length() == 1) { + // 如果是0至F的单位字符串,则添加0 + out.append("0").append(stmp); + } else { + out.append(stmp); + } + } + return out.toString(); + } + + private static byte[] hex2byte(byte[] b) { + int size = 2; + if ((b.length % size) != 0){ + throw new IllegalArgumentException("长度不是偶数"); + } + byte[] b2 = new byte[b.length / 2]; + for (int n = 0; n < b.length; n += size) { + String item = new String(b, n, 2); + b2[n / 2] = (byte) Integer.parseInt(item, 16); + } + return b2; + } +} diff --git a/eladmin-common/src/main/java/me/zhengjie/utils/FileUtil.java b/eladmin-common/src/main/java/me/zhengjie/utils/FileUtil.java new file mode 100644 index 0000000..ca2b674 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/utils/FileUtil.java @@ -0,0 +1,395 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.utils; + +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.poi.excel.BigExcelWriter; +import cn.hutool.poi.excel.ExcelUtil; +import me.zhengjie.exception.BadRequestException; +import org.apache.poi.util.IOUtils; +import org.apache.poi.xssf.streaming.SXSSFSheet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.multipart.MultipartFile; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.security.MessageDigest; +import java.text.DecimalFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * File工具类,扩展 hutool 工具包 + * + * @author Zheng Jie + * @date 2018-12-27 + */ +public class FileUtil extends cn.hutool.core.io.FileUtil { + + private static final Logger log = LoggerFactory.getLogger(FileUtil.class); + + /** + * 系统临时目录 + *
+ * windows 包含路径分割符,但Linux 不包含, + * 在windows \\==\ 前提下, + * 为安全起见 同意拼装 路径分割符, + *

+     *       java.io.tmpdir
+     *       windows : C:\Users/xxx\AppData\Local\Temp\
+     *       linux: /temp
+     * 
+ */ + public static final String SYS_TEM_DIR = System.getProperty("java.io.tmpdir") + File.separator; + /** + * 定义GB的计算常量 + */ + private static final int GB = 1024 * 1024 * 1024; + /** + * 定义MB的计算常量 + */ + private static final int MB = 1024 * 1024; + /** + * 定义KB的计算常量 + */ + private static final int KB = 1024; + + /** + * 格式化小数 + */ + private static final DecimalFormat DF = new DecimalFormat("0.00"); + + public static final String IMAGE = "图片"; + public static final String TXT = "文档"; + public static final String MUSIC = "音乐"; + public static final String VIDEO = "视频"; + public static final String OTHER = "其他"; + + + /** + * MultipartFile转File + */ + public static File toFile(MultipartFile multipartFile) { + // 获取文件名 + String fileName = multipartFile.getOriginalFilename(); + // 获取文件后缀 + String prefix = "." + getExtensionName(fileName); + File file = null; + try { + // 用uuid作为文件名,防止生成的临时文件重复 + file = new File(SYS_TEM_DIR + IdUtil.simpleUUID() + prefix); + // MultipartFile to File + multipartFile.transferTo(file); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + return file; + } + + /** + * 获取文件扩展名,不带 . + */ + public static String getExtensionName(String filename) { + if ((filename != null) && (filename.length() > 0)) { + int dot = filename.lastIndexOf('.'); + if ((dot > -1) && (dot < (filename.length() - 1))) { + return filename.substring(dot + 1); + } + } + return filename; + } + + /** + * Java文件操作 获取不带扩展名的文件名 + */ + public static String getFileNameNoEx(String filename) { + if ((filename != null) && (filename.length() > 0)) { + int dot = filename.lastIndexOf('.'); + if ((dot > -1) && (dot < (filename.length()))) { + return filename.substring(0, dot); + } + } + return filename; + } + + /** + * 文件大小转换 + */ + public static String getSize(long size) { + String resultSize; + if (size / GB >= 1) { + //如果当前Byte的值大于等于1GB + resultSize = DF.format(size / (float) GB) + "GB "; + } else if (size / MB >= 1) { + //如果当前Byte的值大于等于1MB + resultSize = DF.format(size / (float) MB) + "MB "; + } else if (size / KB >= 1) { + //如果当前Byte的值大于等于1KB + resultSize = DF.format(size / (float) KB) + "KB "; + } else { + resultSize = size + "B "; + } + return resultSize; + } + + /** + * inputStream 转 File + */ + static File inputStreamToFile(InputStream ins, String name){ + File file = new File(SYS_TEM_DIR + name); + if (file.exists()) { + return file; + } + OutputStream os = null; + try { + os = new FileOutputStream(file); + int bytesRead; + int len = 8192; + byte[] buffer = new byte[len]; + while ((bytesRead = ins.read(buffer, 0, len)) != -1) { + os.write(buffer, 0, bytesRead); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + CloseUtil.close(os); + CloseUtil.close(ins); + } + return file; + } + + /** + * 将文件名解析成文件的上传路径 + */ + public static File upload(MultipartFile file, String filePath) { + Date date = new Date(); + SimpleDateFormat format = new SimpleDateFormat("yyyyMMddhhmmssS"); + // 过滤非法文件名 + String name = getFileNameNoEx(verifyFilename(file.getOriginalFilename())); + String suffix = getExtensionName(file.getOriginalFilename()); + String nowStr = "-" + format.format(date); + try { + String fileName = name + nowStr + "." + suffix; + String path = filePath + fileName; + // getCanonicalFile 可解析正确各种路径 + File dest = new File(path).getCanonicalFile(); + // 检测是否存在目录 + if (!dest.getParentFile().exists()) { + if (!dest.getParentFile().mkdirs()) { + System.out.println("was not successful."); + } + } + // 文件写入 + file.transferTo(dest); + return dest; + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return null; + } + + /** + * 导出excel + */ + public static void downloadExcel(List> list, HttpServletResponse response) throws IOException { + String tempPath = SYS_TEM_DIR + IdUtil.fastSimpleUUID() + ".xlsx"; + File file = new File(tempPath); + BigExcelWriter writer = ExcelUtil.getBigWriter(file); + // 一次性写出内容,使用默认样式,强制输出标题 + writer.write(list, true); + SXSSFSheet sheet = (SXSSFSheet)writer.getSheet(); + //上面需要强转SXSSFSheet 不然没有trackAllColumnsForAutoSizing方法 + sheet.trackAllColumnsForAutoSizing(); + //列宽自适应 + writer.autoSizeColumnAll(); + //response为HttpServletResponse对象 + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8"); + //test.xls是弹出下载对话框的文件名,不能为中文,中文请自行编码 + response.setHeader("Content-Disposition", "attachment;filename=file.xlsx"); + ServletOutputStream out = response.getOutputStream(); + // 终止后删除临时文件 + file.deleteOnExit(); + writer.flush(out, true); + //此处记得关闭输出Servlet流 + IoUtil.close(out); + } + + public static String getFileType(String type) { + String documents = "txt doc pdf ppt pps xlsx xls docx"; + String music = "mp3 wav wma mpa ram ra aac aif m4a"; + String video = "avi mpg mpe mpeg asf wmv mov qt rm mp4 flv m4v webm ogv ogg"; + String image = "bmp dib pcp dif wmf gif jpg tif eps psd cdr iff tga pcd mpt png jpeg"; + if (image.contains(type)) { + return IMAGE; + } else if (documents.contains(type)) { + return TXT; + } else if (music.contains(type)) { + return MUSIC; + } else if (video.contains(type)) { + return VIDEO; + } else { + return OTHER; + } + } + + public static void checkSize(long maxSize, long size) { + // 1M + int len = 1024 * 1024; + if (size > (maxSize * len)) { + throw new BadRequestException("文件超出规定大小:" + maxSize + "MB"); + } + } + + /** + * 判断两个文件是否相同 + */ + public static boolean check(File file1, File file2) { + String img1Md5 = getMd5(file1); + String img2Md5 = getMd5(file2); + if(img1Md5 != null){ + return img1Md5.equals(img2Md5); + } + return false; + } + + /** + * 判断两个文件是否相同 + */ + public static boolean check(String file1Md5, String file2Md5) { + return file1Md5.equals(file2Md5); + } + + private static byte[] getByte(File file) { + // 得到文件长度 + byte[] b = new byte[(int) file.length()]; + InputStream in = null; + try { + in = new FileInputStream(file); + try { + System.out.println(in.read(b)); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + return null; + } finally { + CloseUtil.close(in); + } + return b; + } + + private static String getMd5(byte[] bytes) { + // 16进制字符 + char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + try { + MessageDigest mdTemp = MessageDigest.getInstance("MD5"); + mdTemp.update(bytes); + byte[] md = mdTemp.digest(); + int j = md.length; + char[] str = new char[j * 2]; + int k = 0; + // 移位 输出字符串 + for (byte byte0 : md) { + str[k++] = hexDigits[byte0 >>> 4 & 0xf]; + str[k++] = hexDigits[byte0 & 0xf]; + } + return new String(str); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return null; + } + + /** + * 下载文件 + * + * @param request / + * @param response / + * @param file / + */ + public static void downloadFile(HttpServletRequest request, HttpServletResponse response, File file, boolean deleteOnExit) { + response.setCharacterEncoding(request.getCharacterEncoding()); + response.setContentType("application/octet-stream"); + FileInputStream fis = null; + try { + fis = new FileInputStream(file); + response.setHeader("Content-Disposition", "attachment; filename=" + file.getName()); + IOUtils.copy(fis, response.getOutputStream()); + response.flushBuffer(); + } catch (Exception e) { + log.error(e.getMessage(), e); + } finally { + if (fis != null) { + try { + fis.close(); + if (deleteOnExit) { + file.deleteOnExit(); + } + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } + } + } + + /** + * 验证并过滤非法的文件名 + * @param fileName 文件名 + * @return 文件名 + */ + public static String verifyFilename(String fileName) { + // 过滤掉特殊字符 + fileName = fileName.replaceAll("[\\\\/:*?\"<>|~\\s]", ""); + + // 去掉文件名开头和结尾的空格和点 + fileName = fileName.trim().replaceAll("^[. ]+|[. ]+$", ""); + + // 不允许文件名超过255(在Mac和Linux中)或260(在Windows中)个字符 + int maxFileNameLength = 255; + if (System.getProperty("os.name").startsWith("Windows")) { + maxFileNameLength = 260; + } + if (fileName.length() > maxFileNameLength) { + fileName = fileName.substring(0, maxFileNameLength); + } + + // 过滤掉控制字符 + fileName = fileName.replaceAll("[\\p{Cntrl}]", ""); + + // 过滤掉 ".." 路径 + fileName = fileName.replaceAll("\\.{2,}", ""); + + // 去掉文件名开头的 ".." + fileName = fileName.replaceAll("^\\.+/", ""); + + // 保留文件名中最后一个 "." 字符,过滤掉其他 "." + fileName = fileName.replaceAll("^(.*)(\\.[^.]*)$", "$1").replaceAll("\\.", "") + + fileName.replaceAll("^(.*)(\\.[^.]*)$", "$2"); + + return fileName; + } + + + public static String getMd5(File file) { + return getMd5(getByte(file)); + } +} diff --git a/eladmin-common/src/main/java/me/zhengjie/utils/PageResult.java b/eladmin-common/src/main/java/me/zhengjie/utils/PageResult.java new file mode 100644 index 0000000..0f1fdfe --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/utils/PageResult.java @@ -0,0 +1,16 @@ +package me.zhengjie.utils; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.List; + +@Getter +@RequiredArgsConstructor(access = AccessLevel.PACKAGE) +public class PageResult { + + private final List content; + + private final long totalElements; +} diff --git a/eladmin-common/src/main/java/me/zhengjie/utils/PageUtil.java b/eladmin-common/src/main/java/me/zhengjie/utils/PageUtil.java new file mode 100644 index 0000000..56020d9 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/utils/PageUtil.java @@ -0,0 +1,63 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.utils; + +import org.springframework.data.domain.Page; +import java.util.*; + +/** + * 分页工具 + * @author Zheng Jie + * @date 2018-12-10 + */ +public class PageUtil extends cn.hutool.core.util.PageUtil { + + /** + * List 分页 + */ + public static List paging(int page, int size , List list) { + int fromIndex = page * size; + int toIndex = page * size + size; + if(fromIndex > list.size()){ + return Collections.emptyList(); + } else if(toIndex >= list.size()) { + return list.subList(fromIndex,list.size()); + } else { + return list.subList(fromIndex,toIndex); + } + } + + /** + * Page 数据处理,预防redis反序列化报错 + */ + public static PageResult toPage(Page page) { + return new PageResult<>(page.getContent(), page.getTotalElements()); + } + + /** + * 自定义分页 + */ + public static PageResult toPage(List list, long totalElements) { + return new PageResult<>(list, totalElements); + } + + /** + * 返回空数据 + */ + public static PageResult noData () { + return new PageResult<>(null, 0); + } +} diff --git a/eladmin-common/src/main/java/me/zhengjie/utils/QueryHelp.java b/eladmin-common/src/main/java/me/zhengjie/utils/QueryHelp.java new file mode 100644 index 0000000..587ece9 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/utils/QueryHelp.java @@ -0,0 +1,218 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import lombok.extern.slf4j.Slf4j; +import me.zhengjie.annotation.DataPermission; +import me.zhengjie.annotation.Query; +import javax.persistence.criteria.*; +import java.lang.reflect.Field; +import java.util.*; + +/** + * @author Zheng Jie + * @date 2019-6-4 14:59:48 + */ +@Slf4j +@SuppressWarnings({"unchecked","all"}) +public class QueryHelp { + + public static Predicate getPredicate(Root root, Q query, CriteriaBuilder cb) { + List list = new ArrayList<>(); + if(query == null){ + return cb.and(list.toArray(new Predicate[0])); + } + // 数据权限验证 + DataPermission permission = query.getClass().getAnnotation(DataPermission.class); + if(permission != null){ + // 获取数据权限 + List dataScopes = SecurityUtils.getCurrentUserDataScope(); + if(CollectionUtil.isNotEmpty(dataScopes)){ + if(StringUtils.isNotBlank(permission.joinName()) && StringUtils.isNotBlank(permission.fieldName())) { + Join join = root.join(permission.joinName(), JoinType.LEFT); + list.add(getExpression(permission.fieldName(),join, root).in(dataScopes)); + } else if (StringUtils.isBlank(permission.joinName()) && StringUtils.isNotBlank(permission.fieldName())) { + list.add(getExpression(permission.fieldName(),null, root).in(dataScopes)); + } + } + } + try { + Map joinKey = new HashMap<>(); + List fields = getAllFields(query.getClass(), new ArrayList<>()); + for (Field field : fields) { + boolean accessible = field.isAccessible(); + // 设置对象的访问权限,保证对private的属性的访 + field.setAccessible(true); + Query q = field.getAnnotation(Query.class); + if (q != null) { + String propName = q.propName(); + String joinName = q.joinName(); + String blurry = q.blurry(); + String attributeName = isBlank(propName) ? field.getName() : propName; + Class fieldType = field.getType(); + Object val = field.get(query); + if (ObjectUtil.isNull(val) || "".equals(val)) { + continue; + } + Join join = null; + // 模糊多字段 + if (ObjectUtil.isNotEmpty(blurry)) { + String[] blurrys = blurry.split(","); + List orPredicate = new ArrayList<>(); + for (String s : blurrys) { + orPredicate.add(cb.like(root.get(s).as(String.class), "%" + val.toString() + "%")); + } + Predicate[] p = new Predicate[orPredicate.size()]; + list.add(cb.or(orPredicate.toArray(p))); + continue; + } + if (ObjectUtil.isNotEmpty(joinName)) { + join = joinKey.get(joinName); + if(join == null){ + String[] joinNames = joinName.split(">"); + for (String name : joinNames) { + switch (q.join()) { + case LEFT: + if(ObjectUtil.isNotNull(join) && ObjectUtil.isNotNull(val)){ + join = join.join(name, JoinType.LEFT); + } else { + join = root.join(name, JoinType.LEFT); + } + break; + case RIGHT: + if(ObjectUtil.isNotNull(join) && ObjectUtil.isNotNull(val)){ + join = join.join(name, JoinType.RIGHT); + } else { + join = root.join(name, JoinType.RIGHT); + } + break; + case INNER: + if(ObjectUtil.isNotNull(join) && ObjectUtil.isNotNull(val)){ + join = join.join(name, JoinType.INNER); + } else { + join = root.join(name, JoinType.INNER); + } + break; + default: break; + } + } + joinKey.put(joinName, join); + } + } + switch (q.type()) { + case EQUAL: + list.add(cb.equal(getExpression(attributeName,join,root) + .as((Class) fieldType),val)); + break; + case GREATER_THAN: + list.add(cb.greaterThanOrEqualTo(getExpression(attributeName,join,root) + .as((Class) fieldType), (Comparable) val)); + break; + case LESS_THAN: + list.add(cb.lessThanOrEqualTo(getExpression(attributeName,join,root) + .as((Class) fieldType), (Comparable) val)); + break; + case LESS_THAN_NQ: + list.add(cb.lessThan(getExpression(attributeName,join,root) + .as((Class) fieldType), (Comparable) val)); + break; + case INNER_LIKE: + list.add(cb.like(getExpression(attributeName,join,root) + .as(String.class), "%" + val.toString() + "%")); + break; + case LEFT_LIKE: + list.add(cb.like(getExpression(attributeName,join,root) + .as(String.class), "%" + val.toString())); + break; + case RIGHT_LIKE: + list.add(cb.like(getExpression(attributeName,join,root) + .as(String.class), val.toString() + "%")); + break; + case IN: + if (CollUtil.isNotEmpty((Collection)val)) { + list.add(getExpression(attributeName,join,root).in((Collection) val)); + } + break; + case NOT_IN: + if (CollUtil.isNotEmpty((Collection)val)) { + list.add(getExpression(attributeName,join,root).in((Collection) val).not()); + } + break; + case NOT_EQUAL: + list.add(cb.notEqual(getExpression(attributeName,join,root), val)); + break; + case NOT_NULL: + list.add(cb.isNotNull(getExpression(attributeName,join,root))); + break; + case IS_NULL: + list.add(cb.isNull(getExpression(attributeName,join,root))); + break; + case BETWEEN: + List between = new ArrayList<>((List)val); + if(between.size() == 2){ + list.add(cb.between(getExpression(attributeName, join, root).as((Class) between.get(0).getClass()), + (Comparable) between.get(0), (Comparable) between.get(1))); + } + break; + case FIND_IN_SET: + list.add(cb.greaterThan(cb.function("FIND_IN_SET", Integer.class, + cb.literal(val.toString()), root.get(attributeName)), 0)); + break; + default: break; + } + } + field.setAccessible(accessible); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + } + int size = list.size(); + return cb.and(list.toArray(new Predicate[size])); + } + + @SuppressWarnings("unchecked") + private static Expression getExpression(String attributeName, Join join, Root root) { + if (ObjectUtil.isNotEmpty(join)) { + return join.get(attributeName); + } else { + return root.get(attributeName); + } + } + + private static boolean isBlank(final CharSequence cs) { + int strLen; + if (cs == null || (strLen = cs.length()) == 0) { + return true; + } + for (int i = 0; i < strLen; i++) { + if (!Character.isWhitespace(cs.charAt(i))) { + return false; + } + } + return true; + } + + public static List getAllFields(Class clazz, List fields) { + if (clazz != null) { + fields.addAll(Arrays.asList(clazz.getDeclaredFields())); + getAllFields(clazz.getSuperclass(), fields); + } + return fields; + } +} diff --git a/eladmin-common/src/main/java/me/zhengjie/utils/RedisUtils.java b/eladmin-common/src/main/java/me/zhengjie/utils/RedisUtils.java new file mode 100644 index 0000000..65d0278 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/utils/RedisUtils.java @@ -0,0 +1,725 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.utils; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.*; +import org.springframework.data.redis.serializer.StringRedisSerializer; +import org.springframework.stereotype.Component; +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * @author / + */ +@Component +@SuppressWarnings({"unchecked", "all"}) +public class RedisUtils { + private static final Logger log = LoggerFactory.getLogger(RedisUtils.class); + + private RedisTemplate redisTemplate; + + public RedisUtils(RedisTemplate redisTemplate) { + this.redisTemplate = redisTemplate; + this.redisTemplate.setHashKeySerializer(new StringRedisSerializer()); + this.redisTemplate.setKeySerializer(new StringRedisSerializer()); + this.redisTemplate.setStringSerializer(new StringRedisSerializer()); + } + + /** + * 指定缓存失效时间 + * + * @param key 键 + * @param time 时间(秒) 注意:这里将会替换原有的时间 + */ + public boolean expire(String key, long time) { + try { + if (time > 0) { + redisTemplate.expire(key, time, TimeUnit.SECONDS); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + return false; + } + return true; + } + + /** + * 指定缓存失效时间 + * + * @param key 键 + * @param time 时间(秒) 注意:这里将会替换原有的时间 + * @param timeUnit 单位 + */ + public boolean expire(String key, long time, TimeUnit timeUnit) { + try { + if (time > 0) { + redisTemplate.expire(key, time, timeUnit); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + return false; + } + return true; + } + + /** + * 根据 key 获取过期时间 + * + * @param key 键 不能为null + * @return 时间(秒) 返回0代表为永久有效 + */ + public long getExpire(Object key) { + return redisTemplate.getExpire(key, TimeUnit.SECONDS); + } + + /** + * 查找匹配key + * + * @param pattern key + * @return / + */ + public List scan(String pattern) { + ScanOptions options = ScanOptions.scanOptions().match(pattern).build(); + RedisConnectionFactory factory = redisTemplate.getConnectionFactory(); + RedisConnection rc = Objects.requireNonNull(factory).getConnection(); + Cursor cursor = rc.scan(options); + List result = new ArrayList<>(); + while (cursor.hasNext()) { + result.add(new String(cursor.next())); + } + try { + RedisConnectionUtils.releaseConnection(rc, factory); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return result; + } + + /** + * 分页查询 key + * + * @param patternKey key + * @param page 页码 + * @param size 每页数目 + * @return / + */ + public List findKeysForPage(String patternKey, int page, int size) { + ScanOptions options = ScanOptions.scanOptions().match(patternKey).build(); + RedisConnectionFactory factory = redisTemplate.getConnectionFactory(); + RedisConnection rc = Objects.requireNonNull(factory).getConnection(); + Cursor cursor = rc.scan(options); + List result = new ArrayList<>(size); + int tmpIndex = 0; + int fromIndex = page * size; + int toIndex = page * size + size; + while (cursor.hasNext()) { + if (tmpIndex >= fromIndex && tmpIndex < toIndex) { + result.add(new String(cursor.next())); + tmpIndex++; + continue; + } + // 获取到满足条件的数据后,就可以退出了 + if (tmpIndex >= toIndex) { + break; + } + tmpIndex++; + cursor.next(); + } + try { + RedisConnectionUtils.releaseConnection(rc, factory); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return result; + } + + /** + * 判断key是否存在 + * + * @param key 键 + * @return true 存在 false不存在 + */ + public boolean hasKey(String key) { + try { + return redisTemplate.hasKey(key); + } catch (Exception e) { + log.error(e.getMessage(), e); + return false; + } + } + + /** + * 删除缓存 + * + * @param key 可以传一个值 或多个 + */ + public void del(String... keys) { + if (keys != null && keys.length > 0) { + if (keys.length == 1) { + boolean result = redisTemplate.delete(keys[0]); + log.debug("--------------------------------------------"); + log.debug(new StringBuilder("删除缓存:").append(keys[0]).append(",结果:").append(result).toString()); + log.debug("--------------------------------------------"); + } else { + Set keySet = new HashSet<>(); + for (String key : keys) { + if (redisTemplate.hasKey(key)) + keySet.add(key); + } + long count = redisTemplate.delete(keySet); + log.debug("--------------------------------------------"); + log.debug("成功删除缓存:" + keySet.toString()); + log.debug("缓存删除数量:" + count + "个"); + log.debug("--------------------------------------------"); + } + } + } + + /** + * 批量模糊删除key + * @param pattern + */ + public void scanDel(String pattern){ + ScanOptions options = ScanOptions.scanOptions().match(pattern).build(); + try (Cursor cursor = redisTemplate.executeWithStickyConnection( + (RedisCallback>) connection -> (Cursor) new ConvertingCursor<>( + connection.scan(options), redisTemplate.getKeySerializer()::deserialize))) { + while (cursor.hasNext()) { + redisTemplate.delete(cursor.next()); + } + } + } + + // ============================String============================= + + /** + * 普通缓存获取 + * + * @param key 键 + * @return 值 + */ + public Object get(String key) { + return key == null ? null : redisTemplate.opsForValue().get(key); + } + + /** + * 批量获取 + * + * @param keys + * @return + */ + public List multiGet(List keys) { + List list = redisTemplate.opsForValue().multiGet(Sets.newHashSet(keys)); + List resultList = Lists.newArrayList(); + Optional.ofNullable(list).ifPresent(e-> list.forEach(ele-> Optional.ofNullable(ele).ifPresent(resultList::add))); + return resultList; + } + + /** + * 普通缓存放入 + * + * @param key 键 + * @param value 值 + * @return true成功 false失败 + */ + public boolean set(String key, Object value) { + try { + redisTemplate.opsForValue().set(key, value); + return true; + } catch (Exception e) { + log.error(e.getMessage(), e); + return false; + } + } + + /** + * 普通缓存放入并设置时间 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期,注意:这里将会替换原有的时间 + * @return true成功 false 失败 + */ + public boolean set(String key, Object value, long time) { + try { + if (time > 0) { + redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); + } else { + set(key, value); + } + return true; + } catch (Exception e) { + log.error(e.getMessage(), e); + return false; + } + } + + /** + * 普通缓存放入并设置时间 + * + * @param key 键 + * @param value 值 + * @param time 时间,注意:这里将会替换原有的时间 + * @param timeUnit 类型 + * @return true成功 false 失败 + */ + public boolean set(String key, Object value, long time, TimeUnit timeUnit) { + try { + if (time > 0) { + redisTemplate.opsForValue().set(key, value, time, timeUnit); + } else { + set(key, value); + } + return true; + } catch (Exception e) { + log.error(e.getMessage(), e); + return false; + } + } + + // ================================Map================================= + + /** + * HashGet + * + * @param key 键 不能为null + * @param item 项 不能为null + * @return 值 + */ + public Object hget(String key, String item) { + return redisTemplate.opsForHash().get(key, item); + } + + /** + * 获取hashKey对应的所有键值 + * + * @param key 键 + * @return 对应的多个键值 + */ + public Map hmget(String key) { + return redisTemplate.opsForHash().entries(key); + + } + + /** + * HashSet + * + * @param key 键 + * @param map 对应多个键值 + * @return true 成功 false 失败 + */ + public boolean hmset(String key, Map map) { + try { + redisTemplate.opsForHash().putAll(key, map); + return true; + } catch (Exception e) { + log.error(e.getMessage(), e); + return false; + } + } + + /** + * HashSet + * + * @param key 键 + * @param map 对应多个键值 + * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间 + * @return true成功 false失败 + */ + public boolean hmset(String key, Map map, long time) { + try { + redisTemplate.opsForHash().putAll(key, map); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + log.error(e.getMessage(), e); + return false; + } + } + + /** + * 向一张hash表中放入数据,如果不存在将创建 + * + * @param key 键 + * @param item 项 + * @param value 值 + * @return true 成功 false失败 + */ + public boolean hset(String key, String item, Object value) { + try { + redisTemplate.opsForHash().put(key, item, value); + return true; + } catch (Exception e) { + log.error(e.getMessage(), e); + return false; + } + } + + /** + * 向一张hash表中放入数据,如果不存在将创建 + * + * @param key 键 + * @param item 项 + * @param value 值 + * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间 + * @return true 成功 false失败 + */ + public boolean hset(String key, String item, Object value, long time) { + try { + redisTemplate.opsForHash().put(key, item, value); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + log.error(e.getMessage(), e); + return false; + } + } + + /** + * 删除hash表中的值 + * + * @param key 键 不能为null + * @param item 项 可以使多个 不能为null + */ + public void hdel(String key, Object... item) { + redisTemplate.opsForHash().delete(key, item); + } + + /** + * 判断hash表中是否有该项的值 + * + * @param key 键 不能为null + * @param item 项 不能为null + * @return true 存在 false不存在 + */ + public boolean hHasKey(String key, String item) { + return redisTemplate.opsForHash().hasKey(key, item); + } + + /** + * hash递增 如果不存在,就会创建一个 并把新增后的值返回 + * + * @param key 键 + * @param item 项 + * @param by 要增加几(大于0) + * @return + */ + public double hincr(String key, String item, double by) { + return redisTemplate.opsForHash().increment(key, item, by); + } + + /** + * hash递减 + * + * @param key 键 + * @param item 项 + * @param by 要减少记(小于0) + * @return + */ + public double hdecr(String key, String item, double by) { + return redisTemplate.opsForHash().increment(key, item, -by); + } + + // ============================set============================= + + /** + * 根据key获取Set中的所有值 + * + * @param key 键 + * @return + */ + public Set sGet(String key) { + try { + return redisTemplate.opsForSet().members(key); + } catch (Exception e) { + log.error(e.getMessage(), e); + return null; + } + } + + /** + * 根据value从一个set中查询,是否存在 + * + * @param key 键 + * @param value 值 + * @return true 存在 false不存在 + */ + public boolean sHasKey(String key, Object value) { + try { + return redisTemplate.opsForSet().isMember(key, value); + } catch (Exception e) { + log.error(e.getMessage(), e); + return false; + } + } + + /** + * 将数据放入set缓存 + * + * @param key 键 + * @param values 值 可以是多个 + * @return 成功个数 + */ + public long sSet(String key, Object... values) { + try { + return redisTemplate.opsForSet().add(key, values); + } catch (Exception e) { + log.error(e.getMessage(), e); + return 0; + } + } + + /** + * 将set数据放入缓存 + * + * @param key 键 + * @param time 时间(秒) 注意:这里将会替换原有的时间 + * @param values 值 可以是多个 + * @return 成功个数 + */ + public long sSetAndTime(String key, long time, Object... values) { + try { + Long count = redisTemplate.opsForSet().add(key, values); + if (time > 0) { + expire(key, time); + } + return count; + } catch (Exception e) { + log.error(e.getMessage(), e); + return 0; + } + } + + /** + * 获取set缓存的长度 + * + * @param key 键 + * @return + */ + public long sGetSetSize(String key) { + try { + return redisTemplate.opsForSet().size(key); + } catch (Exception e) { + log.error(e.getMessage(), e); + return 0; + } + } + + /** + * 移除值为value的 + * + * @param key 键 + * @param values 值 可以是多个 + * @return 移除的个数 + */ + public long setRemove(String key, Object... values) { + try { + Long count = redisTemplate.opsForSet().remove(key, values); + return count; + } catch (Exception e) { + log.error(e.getMessage(), e); + return 0; + } + } + + // ===============================list================================= + + /** + * 获取list缓存的内容 + * + * @param key 键 + * @param start 开始 + * @param end 结束 0 到 -1代表所有值 + * @return + */ + public List lGet(String key, long start, long end) { + try { + return redisTemplate.opsForList().range(key, start, end); + } catch (Exception e) { + log.error(e.getMessage(), e); + return null; + } + } + + /** + * 获取list缓存的长度 + * + * @param key 键 + * @return + */ + public long lGetListSize(String key) { + try { + return redisTemplate.opsForList().size(key); + } catch (Exception e) { + log.error(e.getMessage(), e); + return 0; + } + } + + /** + * 通过索引 获取list中的值 + * + * @param key 键 + * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推 + * @return + */ + public Object lGetIndex(String key, long index) { + try { + return redisTemplate.opsForList().index(key, index); + } catch (Exception e) { + log.error(e.getMessage(), e); + return null; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @return + */ + public boolean lSet(String key, Object value) { + try { + redisTemplate.opsForList().rightPush(key, value); + return true; + } catch (Exception e) { + log.error(e.getMessage(), e); + return false; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒) 注意:这里将会替换原有的时间 + * @return + */ + public boolean lSet(String key, Object value, long time) { + try { + redisTemplate.opsForList().rightPush(key, value); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + log.error(e.getMessage(), e); + return false; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @return + */ + public boolean lSet(String key, List value) { + try { + redisTemplate.opsForList().rightPushAll(key, value); + return true; + } catch (Exception e) { + log.error(e.getMessage(), e); + return false; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒) 注意:这里将会替换原有的时间 + * @return + */ + public boolean lSet(String key, List value, long time) { + try { + redisTemplate.opsForList().rightPushAll(key, value); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + log.error(e.getMessage(), e); + return false; + } + } + + /** + * 根据索引修改list中的某条数据 + * + * @param key 键 + * @param index 索引 + * @param value 值 + * @return / + */ + public boolean lUpdateIndex(String key, long index, Object value) { + try { + redisTemplate.opsForList().set(key, index, value); + return true; + } catch (Exception e) { + log.error(e.getMessage(), e); + return false; + } + } + + /** + * 移除N个值为value + * + * @param key 键 + * @param count 移除多少个 + * @param value 值 + * @return 移除的个数 + */ + public long lRemove(String key, long count, Object value) { + try { + return redisTemplate.opsForList().remove(key, count, value); + } catch (Exception e) { + log.error(e.getMessage(), e); + return 0; + } + } + + /** + * @param prefix 前缀 + * @param ids id + */ + public void delByKeys(String prefix, Set ids) { + Set keys = new HashSet<>(); + for (Long id : ids) { + keys.addAll(redisTemplate.keys(new StringBuffer(prefix).append(id).toString())); + } + long count = redisTemplate.delete(keys); + // 此处提示可自行删除 + log.debug("--------------------------------------------"); + log.debug("成功删除缓存:" + keys.toString()); + log.debug("缓存删除数量:" + count + "个"); + log.debug("--------------------------------------------"); + } +} diff --git a/eladmin-common/src/main/java/me/zhengjie/utils/RequestHolder.java b/eladmin-common/src/main/java/me/zhengjie/utils/RequestHolder.java new file mode 100644 index 0000000..71a4b9e --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/utils/RequestHolder.java @@ -0,0 +1,33 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.utils; + +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import javax.servlet.http.HttpServletRequest; +import java.util.Objects; + +/** + * 获取 HttpServletRequest + * @author Zheng Jie + * @date 2018-11-24 + */ +public class RequestHolder { + + public static HttpServletRequest getHttpServletRequest() { + return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); + } +} diff --git a/eladmin-common/src/main/java/me/zhengjie/utils/RsaUtils.java b/eladmin-common/src/main/java/me/zhengjie/utils/RsaUtils.java new file mode 100644 index 0000000..8123ff3 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/utils/RsaUtils.java @@ -0,0 +1,198 @@ +package me.zhengjie.utils; + +import org.apache.commons.codec.binary.Base64; +import javax.crypto.Cipher; +import java.io.ByteArrayOutputStream; +import java.security.*; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +/** + * @author https://www.cnblogs.com/nihaorz/p/10690643.html + * @description Rsa 工具类,公钥私钥生成,加解密 + * @date 2020-05-18 + **/ +public class RsaUtils { + + private static final String SRC = "123456"; + + public static void main(String[] args) throws Exception { + System.out.println("\n"); + RsaKeyPair keyPair = generateKeyPair(); + System.out.println("公钥:" + keyPair.getPublicKey()); + System.out.println("私钥:" + keyPair.getPrivateKey()); + System.out.println("\n"); + test1(keyPair); + System.out.println("\n"); + test2(keyPair); + System.out.println("\n"); + } + + /** + * 公钥加密私钥解密 + */ + private static void test1(RsaKeyPair keyPair) throws Exception { + System.out.println("***************** 公钥加密私钥解密开始 *****************"); + String text1 = encryptByPublicKey(keyPair.getPublicKey(), RsaUtils.SRC); + String text2 = decryptByPrivateKey(keyPair.getPrivateKey(), text1); + System.out.println("加密前:" + RsaUtils.SRC); + System.out.println("加密后:" + text1); + System.out.println("解密后:" + text2); + if (RsaUtils.SRC.equals(text2)) { + System.out.println("解密字符串和原始字符串一致,解密成功"); + } else { + System.out.println("解密字符串和原始字符串不一致,解密失败"); + } + System.out.println("***************** 公钥加密私钥解密结束 *****************"); + } + + /** + * 私钥加密公钥解密 + * @throws Exception / + */ + private static void test2(RsaKeyPair keyPair) throws Exception { + System.out.println("***************** 私钥加密公钥解密开始 *****************"); + String text1 = encryptByPrivateKey(keyPair.getPrivateKey(), RsaUtils.SRC); + String text2 = decryptByPublicKey(keyPair.getPublicKey(), text1); + System.out.println("加密前:" + RsaUtils.SRC); + System.out.println("加密后:" + text1); + System.out.println("解密后:" + text2); + if (RsaUtils.SRC.equals(text2)) { + System.out.println("解密字符串和原始字符串一致,解密成功"); + } else { + System.out.println("解密字符串和原始字符串不一致,解密失败"); + } + System.out.println("***************** 私钥加密公钥解密结束 *****************"); + } + + /** + * 公钥解密 + * + * @param publicKeyText 公钥 + * @param text 待解密的信息 + * @return / + * @throws Exception / + */ + public static String decryptByPublicKey(String publicKeyText, String text) throws Exception { + X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText)); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec); + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, publicKey); + byte[] result = doLongerCipherFinal(Cipher.DECRYPT_MODE, cipher, Base64.decodeBase64(text)); + return new String(result); + } + + /** + * 私钥加密 + * + * @param privateKeyText 私钥 + * @param text 待加密的信息 + * @return / + * @throws Exception / + */ + public static String encryptByPrivateKey(String privateKeyText, String text) throws Exception { + PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText)); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.ENCRYPT_MODE, privateKey); + byte[] result = doLongerCipherFinal(Cipher.ENCRYPT_MODE, cipher, text.getBytes()); + return Base64.encodeBase64String(result); + } + + /** + * 私钥解密 + * + * @param privateKeyText 私钥 + * @param text 待解密的文本 + * @return / + * @throws Exception / + */ + public static String decryptByPrivateKey(String privateKeyText, String text) throws Exception { + PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText)); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec5); + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + byte[] result = doLongerCipherFinal(Cipher.DECRYPT_MODE, cipher, Base64.decodeBase64(text)); + return new String(result); + } + + /** + * 公钥加密 + * + * @param publicKeyText 公钥 + * @param text 待加密的文本 + * @return / + */ + public static String encryptByPublicKey(String publicKeyText, String text) throws Exception { + X509EncodedKeySpec x509EncodedKeySpec2 = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText)); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec2); + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + byte[] result = doLongerCipherFinal(Cipher.ENCRYPT_MODE, cipher, text.getBytes()); + return Base64.encodeBase64String(result); + } + + private static byte[] doLongerCipherFinal(int opMode,Cipher cipher, byte[] source) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + if (opMode == Cipher.DECRYPT_MODE) { + out.write(cipher.doFinal(source)); + } else { + int offset = 0; + int totalSize = source.length; + while (totalSize - offset > 0) { + int size = Math.min(cipher.getOutputSize(0) - 11, totalSize - offset); + out.write(cipher.doFinal(source, offset, size)); + offset += size; + } + } + out.close(); + return out.toByteArray(); + } + + /** + * 构建RSA密钥对 + * + * @return / + * @throws NoSuchAlgorithmException / + */ + public static RsaKeyPair generateKeyPair() throws NoSuchAlgorithmException { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(1024); + KeyPair keyPair = keyPairGenerator.generateKeyPair(); + RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic(); + RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate(); + String publicKeyString = Base64.encodeBase64String(rsaPublicKey.getEncoded()); + String privateKeyString = Base64.encodeBase64String(rsaPrivateKey.getEncoded()); + return new RsaKeyPair(publicKeyString, privateKeyString); + } + + + /** + * RSA密钥对对象 + */ + public static class RsaKeyPair { + + private final String publicKey; + private final String privateKey; + + public RsaKeyPair(String publicKey, String privateKey) { + this.publicKey = publicKey; + this.privateKey = privateKey; + } + + public String getPublicKey() { + return publicKey; + } + + public String getPrivateKey() { + return privateKey; + } + + } +} diff --git a/eladmin-common/src/main/java/me/zhengjie/utils/SecurityUtils.java b/eladmin-common/src/main/java/me/zhengjie/utils/SecurityUtils.java new file mode 100644 index 0000000..28e4672 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/utils/SecurityUtils.java @@ -0,0 +1,99 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.utils; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import lombok.extern.slf4j.Slf4j; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.utils.enums.DataScopeEnum; +import org.springframework.http.HttpStatus; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import java.util.List; + +/** + * 获取当前登录的用户 + * @author Zheng Jie + * @date 2019-01-17 + */ +@Slf4j +public class SecurityUtils { + + /** + * 获取当前登录的用户 + * @return UserDetails + */ + public static UserDetails getCurrentUser() { + UserDetailsService userDetailsService = SpringContextHolder.getBean(UserDetailsService.class); + return userDetailsService.loadUserByUsername(getCurrentUsername()); + } + + /** + * 获取系统用户名称 + * + * @return 系统用户名称 + */ + public static String getCurrentUsername() { + final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication == null) { + throw new BadRequestException(HttpStatus.UNAUTHORIZED, "当前登录状态过期"); + } + if (authentication.getPrincipal() instanceof UserDetails) { + UserDetails userDetails = (UserDetails) authentication.getPrincipal(); + return userDetails.getUsername(); + } + throw new BadRequestException(HttpStatus.UNAUTHORIZED, "找不到当前登录的信息"); + } + + /** + * 获取系统用户ID + * @return 系统用户ID + */ + public static Long getCurrentUserId() { + UserDetails userDetails = getCurrentUser(); + // 将 Java 对象转换为 JSONObject 对象 + JSONObject jsonObject = (JSONObject) JSON.toJSON(userDetails); + return jsonObject.getJSONObject("user").getLong("id"); + } + + /** + * 获取当前用户的数据权限 + * @return / + */ + public static List getCurrentUserDataScope(){ + UserDetails userDetails = getCurrentUser(); + // 将 Java 对象转换为 JSONObject 对象 + JSONObject jsonObject = (JSONObject) JSON.toJSON(userDetails); + JSONArray jsonArray = jsonObject.getJSONArray("dataScopes"); + return JSON.parseArray(jsonArray.toJSONString(), Long.class); + } + + /** + * 获取数据权限级别 + * @return 级别 + */ + public static String getDataScopeType() { + List dataScopes = getCurrentUserDataScope(); + if(dataScopes.size() != 0){ + return ""; + } + return DataScopeEnum.ALL.getValue(); + } +} diff --git a/eladmin-common/src/main/java/me/zhengjie/utils/SpringContextHolder.java b/eladmin-common/src/main/java/me/zhengjie/utils/SpringContextHolder.java new file mode 100644 index 0000000..41ead38 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/utils/SpringContextHolder.java @@ -0,0 +1,156 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.utils; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Service; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @author Jie + * @date 2019-01-07 + */ +@Slf4j +public class SpringContextHolder implements ApplicationContextAware, DisposableBean { + + private static ApplicationContext applicationContext = null; + private static final List CALL_BACKS = new ArrayList<>(); + private static boolean addCallback = true; + + /** + * 针对 某些初始化方法,在SpringContextHolder 未初始化时 提交回调方法。 + * 在SpringContextHolder 初始化后,进行回调使用 + * + * @param callBack 回调函数 + */ + public synchronized static void addCallBacks(CallBack callBack) { + if (addCallback) { + SpringContextHolder.CALL_BACKS.add(callBack); + } else { + log.warn("CallBack:{} 已无法添加!立即执行", callBack.getCallBackName()); + callBack.executor(); + } + } + + /** + * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型. + */ + @SuppressWarnings("unchecked") + public static T getBean(String name) { + assertContextInjected(); + return (T) applicationContext.getBean(name); + } + + /** + * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型. + */ + public static T getBean(Class requiredType) { + assertContextInjected(); + return applicationContext.getBean(requiredType); + } + + /** + * 获取SpringBoot 配置信息 + * + * @param property 属性key + * @param defaultValue 默认值 + * @param requiredType 返回类型 + * @return / + */ + public static T getProperties(String property, T defaultValue, Class requiredType) { + T result = defaultValue; + try { + result = getBean(Environment.class).getProperty(property, requiredType); + } catch (Exception ignored) {} + return result; + } + + /** + * 获取SpringBoot 配置信息 + * + * @param property 属性key + * @return / + */ + public static String getProperties(String property) { + return getProperties(property, null, String.class); + } + + /** + * 获取SpringBoot 配置信息 + * + * @param property 属性key + * @param requiredType 返回类型 + * @return / + */ + public static T getProperties(String property, Class requiredType) { + return getProperties(property, null, requiredType); + } + + /** + * 检查ApplicationContext不为空. + */ + private static void assertContextInjected() { + if (applicationContext == null) { + throw new IllegalStateException("applicaitonContext属性未注入, 请在applicationContext" + + ".xml中定义SpringContextHolder或在SpringBoot启动类中注册SpringContextHolder."); + } + } + + /** + * 清除SpringContextHolder中的ApplicationContext为Null. + */ + private static void clearHolder() { + log.debug("清除SpringContextHolder中的ApplicationContext:" + + applicationContext); + applicationContext = null; + } + + @Override + public void destroy() { + SpringContextHolder.clearHolder(); + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + if (SpringContextHolder.applicationContext != null) { + log.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" + SpringContextHolder.applicationContext); + } + SpringContextHolder.applicationContext = applicationContext; + if (addCallback) { + for (CallBack callBack : SpringContextHolder.CALL_BACKS) { + callBack.executor(); + } + CALL_BACKS.clear(); + } + SpringContextHolder.addCallback = false; + } + + /** + * 获取 @Service 的所有 bean 名称 + * @return / + */ + public static List getAllServiceBeanName() { + return new ArrayList<>(Arrays.asList(applicationContext + .getBeanNamesForAnnotation(Service.class))); + } +} diff --git a/eladmin-common/src/main/java/me/zhengjie/utils/StringUtils.java b/eladmin-common/src/main/java/me/zhengjie/utils/StringUtils.java new file mode 100644 index 0000000..a27a47a --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/utils/StringUtils.java @@ -0,0 +1,232 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.utils; + +import cn.hutool.http.useragent.UserAgent; +import cn.hutool.http.useragent.UserAgentUtil; +import lombok.extern.slf4j.Slf4j; +import net.dreamlu.mica.ip2region.core.Ip2regionSearcher; +import net.dreamlu.mica.ip2region.core.IpInfo; +import javax.servlet.http.HttpServletRequest; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.UnknownHostException; +import java.util.Calendar; +import java.util.Date; +import java.util.Enumeration; + +/** + * @author Zheng Jie + * 字符串工具类, 继承org.apache.commons.lang3.StringUtils类 + */ +@Slf4j +public class StringUtils extends org.apache.commons.lang3.StringUtils { + + private static final char SEPARATOR = '_'; + private static final String UNKNOWN = "unknown"; + + /** + * 注入bean + */ + private final static Ip2regionSearcher IP_SEARCHER = SpringContextHolder.getBean(Ip2regionSearcher.class); + + /** + * 驼峰命名法工具 + * + * @return toCamelCase(" hello_world ") == "helloWorld" + * toCapitalizeCamelCase("hello_world") == "HelloWorld" + * toUnderScoreCase("helloWorld") = "hello_world" + */ + public static String toCamelCase(String s) { + if (s == null) { + return null; + } + + s = s.toLowerCase(); + + StringBuilder sb = new StringBuilder(s.length()); + boolean upperCase = false; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + + if (c == SEPARATOR) { + upperCase = true; + } else if (upperCase) { + sb.append(Character.toUpperCase(c)); + upperCase = false; + } else { + sb.append(c); + } + } + + return sb.toString(); + } + + /** + * 驼峰命名法工具 + * + * @return toCamelCase(" hello_world ") == "helloWorld" + * toCapitalizeCamelCase("hello_world") == "HelloWorld" + * toUnderScoreCase("helloWorld") = "hello_world" + */ + public static String toCapitalizeCamelCase(String s) { + if (s == null) { + return null; + } + s = toCamelCase(s); + return s.substring(0, 1).toUpperCase() + s.substring(1); + } + + /** + * 驼峰命名法工具 + * + * @return toCamelCase(" hello_world ") == "helloWorld" + * toCapitalizeCamelCase("hello_world") == "HelloWorld" + * toUnderScoreCase("helloWorld") = "hello_world" + */ + static String toUnderScoreCase(String s) { + if (s == null) { + return null; + } + + StringBuilder sb = new StringBuilder(); + boolean upperCase = false; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + + boolean nextUpperCase = true; + + if (i < (s.length() - 1)) { + nextUpperCase = Character.isUpperCase(s.charAt(i + 1)); + } + + if ((i > 0) && Character.isUpperCase(c)) { + if (!upperCase || !nextUpperCase) { + sb.append(SEPARATOR); + } + upperCase = true; + } else { + upperCase = false; + } + + sb.append(Character.toLowerCase(c)); + } + + return sb.toString(); + } + + /** + * 获取ip地址 + */ + public static String getIp(HttpServletRequest request) { + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + String comma = ","; + String localhost = "127.0.0.1"; + if (ip.contains(comma)) { + ip = ip.split(",")[0]; + } + if (localhost.equals(ip)) { + // 获取本机真正的ip地址 + try { + ip = InetAddress.getLocalHost().getHostAddress(); + } catch (UnknownHostException e) { + log.error(e.getMessage(), e); + } + } + return ip; + } + + /** + * 根据ip获取详细地址 + */ + public static String getCityInfo(String ip) { + IpInfo ipInfo = IP_SEARCHER.memorySearch(ip); + if(ipInfo != null){ + return ipInfo.getAddress(); + } + return null; + } + + public static String getBrowser(HttpServletRequest request) { + UserAgent ua = UserAgentUtil.parse(request.getHeader("User-Agent")); + String browser = ua.getBrowser().toString() + " " + ua.getVersion(); + return browser.replace(".0.0.0",""); + } + + /** + * 获得当天是周几 + */ + public static String getWeekDay() { + String[] weekDays = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + Calendar cal = Calendar.getInstance(); + cal.setTime(new Date()); + + int w = cal.get(Calendar.DAY_OF_WEEK) - 1; + if (w < 0) { + w = 0; + } + return weekDays[w]; + } + + /** + * 获取当前机器的IP + * + * @return / + */ + public static String getLocalIp() { + try { + InetAddress candidateAddress = null; + // 遍历所有的网络接口 + for (Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); interfaces.hasMoreElements();) { + NetworkInterface anInterface = interfaces.nextElement(); + // 在所有的接口下再遍历IP + for (Enumeration inetAddresses = anInterface.getInetAddresses(); inetAddresses.hasMoreElements();) { + InetAddress inetAddr = inetAddresses.nextElement(); + // 排除loopback类型地址 + if (!inetAddr.isLoopbackAddress()) { + if (inetAddr.isSiteLocalAddress()) { + // 如果是site-local地址,就是它了 + return inetAddr.getHostAddress(); + } else if (candidateAddress == null) { + // site-local类型的地址未被发现,先记录候选地址 + candidateAddress = inetAddr; + } + } + } + } + if (candidateAddress != null) { + return candidateAddress.getHostAddress(); + } + // 如果没有发现 non-loopback地址.只能用最次选的方案 + InetAddress jdkSuppliedAddress = InetAddress.getLocalHost(); + if (jdkSuppliedAddress == null) { + return ""; + } + return jdkSuppliedAddress.getHostAddress(); + } catch (Exception e) { + return ""; + } + } +} diff --git a/eladmin-common/src/main/java/me/zhengjie/utils/ThrowableUtil.java b/eladmin-common/src/main/java/me/zhengjie/utils/ThrowableUtil.java new file mode 100644 index 0000000..075a65c --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/utils/ThrowableUtil.java @@ -0,0 +1,37 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.utils; + +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * 异常工具 2019-01-06 + * @author Zheng Jie + */ +public class ThrowableUtil { + + /** + * 获取堆栈信息 + */ + public static String getStackTrace(Throwable throwable){ + StringWriter sw = new StringWriter(); + try (PrintWriter pw = new PrintWriter(sw)) { + throwable.printStackTrace(pw); + return sw.toString(); + } + } +} diff --git a/eladmin-common/src/main/java/me/zhengjie/utils/ValidationUtil.java b/eladmin-common/src/main/java/me/zhengjie/utils/ValidationUtil.java new file mode 100644 index 0000000..e561762 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/utils/ValidationUtil.java @@ -0,0 +1,46 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.utils; + +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.ObjectUtil; +import me.zhengjie.exception.BadRequestException; + +/** + * 验证工具 + * + * @author Zheng Jie + * @date 2018-11-23 + */ +public class ValidationUtil { + + /** + * 验证空 + */ + public static void isNull(Object obj, String entity, String parameter , Object value){ + if(ObjectUtil.isNull(obj)){ + String msg = entity + " 不存在: "+ parameter +" is "+ value; + throw new BadRequestException(msg); + } + } + + /** + * 验证是否为邮箱 + */ + public static boolean isEmail(String email) { + return Validator.isEmail(email); + } +} diff --git a/eladmin-common/src/main/java/me/zhengjie/utils/enums/CodeBiEnum.java b/eladmin-common/src/main/java/me/zhengjie/utils/enums/CodeBiEnum.java new file mode 100644 index 0000000..944bc71 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/utils/enums/CodeBiEnum.java @@ -0,0 +1,50 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.utils.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + *

+ * 验证码业务场景 + *

+ * @author Zheng Jie + * @date 2020-05-02 + */ +@Getter +@AllArgsConstructor +public enum CodeBiEnum { + + /* 旧邮箱修改邮箱 */ + ONE(1, "旧邮箱修改邮箱"), + + /* 通过邮箱修改密码 */ + TWO(2, "通过邮箱修改密码"); + + private final Integer code; + private final String description; + + public static CodeBiEnum find(Integer code) { + for (CodeBiEnum value : CodeBiEnum.values()) { + if (value.getCode().equals(code)) { + return value; + } + } + return null; + } + +} diff --git a/eladmin-common/src/main/java/me/zhengjie/utils/enums/CodeEnum.java b/eladmin-common/src/main/java/me/zhengjie/utils/enums/CodeEnum.java new file mode 100644 index 0000000..916862a --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/utils/enums/CodeEnum.java @@ -0,0 +1,46 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.utils.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + *

+ * 验证码业务场景对应的 Redis 中的 key + *

+ * @author Zheng Jie + * @date 2020-05-02 + */ +@Getter +@AllArgsConstructor +public enum CodeEnum { + + /* 通过手机号码重置邮箱 */ + PHONE_RESET_EMAIL_CODE("phone_reset_email_code_", "通过手机号码重置邮箱"), + + /* 通过旧邮箱重置邮箱 */ + EMAIL_RESET_EMAIL_CODE("email_reset_email_code_", "通过旧邮箱重置邮箱"), + + /* 通过手机号码重置密码 */ + PHONE_RESET_PWD_CODE("phone_reset_pwd_code_", "通过手机号码重置密码"), + + /* 通过邮箱重置密码 */ + EMAIL_RESET_PWD_CODE("email_reset_pwd_code_", "通过邮箱重置密码"); + + private final String key; + private final String description; +} diff --git a/eladmin-common/src/main/java/me/zhengjie/utils/enums/DataScopeEnum.java b/eladmin-common/src/main/java/me/zhengjie/utils/enums/DataScopeEnum.java new file mode 100644 index 0000000..465eef6 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/utils/enums/DataScopeEnum.java @@ -0,0 +1,53 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.utils.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + *

+ * 数据权限枚举 + *

+ * @author Zheng Jie + * @date 2020-05-07 + */ +@Getter +@AllArgsConstructor +public enum DataScopeEnum { + + /* 全部的数据权限 */ + ALL("全部", "全部的数据权限"), + + /* 自己部门的数据权限 */ + THIS_LEVEL("本级", "自己部门的数据权限"), + + /* 自定义的数据权限 */ + CUSTOMIZE("自定义", "自定义的数据权限"); + + private final String value; + private final String description; + + public static DataScopeEnum find(String val) { + for (DataScopeEnum dataScopeEnum : DataScopeEnum.values()) { + if (dataScopeEnum.getValue().equals(val)) { + return dataScopeEnum; + } + } + return null; + } + +} diff --git a/eladmin-common/src/main/java/me/zhengjie/utils/enums/RequestMethodEnum.java b/eladmin-common/src/main/java/me/zhengjie/utils/enums/RequestMethodEnum.java new file mode 100644 index 0000000..7de5146 --- /dev/null +++ b/eladmin-common/src/main/java/me/zhengjie/utils/enums/RequestMethodEnum.java @@ -0,0 +1,74 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.utils.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author Zheng Jie + * @website https://eladmin.vip + * @description + * @date 2020-06-10 + **/ +@Getter +@AllArgsConstructor +public enum RequestMethodEnum { + + /** + * 搜寻 @AnonymousGetMapping + */ + GET("GET"), + + /** + * 搜寻 @AnonymousPostMapping + */ + POST("POST"), + + /** + * 搜寻 @AnonymousPutMapping + */ + PUT("PUT"), + + /** + * 搜寻 @AnonymousPatchMapping + */ + PATCH("PATCH"), + + /** + * 搜寻 @AnonymousDeleteMapping + */ + DELETE("DELETE"), + + /** + * 否则就是所有 Request 接口都放行 + */ + ALL("All"); + + /** + * Request 类型 + */ + private final String type; + + public static RequestMethodEnum find(String type) { + for (RequestMethodEnum value : RequestMethodEnum.values()) { + if (value.getType().equals(type)) { + return value; + } + } + return ALL; + } +} diff --git a/eladmin-common/src/test/java/me/zhengjie/utils/DateUtilsTest.java b/eladmin-common/src/test/java/me/zhengjie/utils/DateUtilsTest.java new file mode 100644 index 0000000..dfe01e0 --- /dev/null +++ b/eladmin-common/src/test/java/me/zhengjie/utils/DateUtilsTest.java @@ -0,0 +1,26 @@ +package me.zhengjie.utils; + +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; +import java.util.Date; + +public class DateUtilsTest { + @Test + public void test1() { + long l = System.currentTimeMillis() / 1000; + LocalDateTime localDateTime = DateUtil.fromTimeStamp(l); + System.out.print(DateUtil.localDateTimeFormatyMdHms(localDateTime)); + } + + @Test + public void test2() { + LocalDateTime now = LocalDateTime.now(); + System.out.println(DateUtil.localDateTimeFormatyMdHms(now)); + Date date = DateUtil.toDate(now); + LocalDateTime localDateTime = DateUtil.toLocalDateTime(date); + System.out.println(DateUtil.localDateTimeFormatyMdHms(localDateTime)); + LocalDateTime localDateTime1 = DateUtil.fromTimeStamp(date.getTime() / 1000); + System.out.println(DateUtil.localDateTimeFormatyMdHms(localDateTime1)); + } +} diff --git a/eladmin-common/src/test/java/me/zhengjie/utils/EncryptUtilsTest.java b/eladmin-common/src/test/java/me/zhengjie/utils/EncryptUtilsTest.java new file mode 100644 index 0000000..3ec7375 --- /dev/null +++ b/eladmin-common/src/test/java/me/zhengjie/utils/EncryptUtilsTest.java @@ -0,0 +1,33 @@ +package me.zhengjie.utils; + +import org.junit.jupiter.api.Test; + +import static me.zhengjie.utils.EncryptUtils.*; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class EncryptUtilsTest { + + /** + * 对称加密 + */ + @Test + public void testDesEncrypt() { + try { + assertEquals("7772841DC6099402", desEncrypt("123456")); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 对称解密 + */ + @Test + public void testDesDecrypt() { + try { + assertEquals("123456", desDecrypt("7772841DC6099402")); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/eladmin-common/src/test/java/me/zhengjie/utils/FileUtilTest.java b/eladmin-common/src/test/java/me/zhengjie/utils/FileUtilTest.java new file mode 100644 index 0000000..48e06bd --- /dev/null +++ b/eladmin-common/src/test/java/me/zhengjie/utils/FileUtilTest.java @@ -0,0 +1,36 @@ +package me.zhengjie.utils; + +import org.junit.jupiter.api.Test; +import org.springframework.mock.web.MockMultipartFile; + +import static me.zhengjie.utils.FileUtil.*; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class FileUtilTest { + + @Test + public void testToFile() { + long retval = toFile(new MockMultipartFile("foo", (byte[]) null)).getTotalSpace(); + assertEquals(500695072768L, retval); + } + + @Test + public void testGetExtensionName() { + assertEquals("foo", getExtensionName("foo")); + assertEquals("exe", getExtensionName("bar.exe")); + } + + @Test + public void testGetFileNameNoEx() { + assertEquals("foo", getFileNameNoEx("foo")); + assertEquals("bar", getFileNameNoEx("bar.txt")); + } + + @Test + public void testGetSize() { + assertEquals("1000B ", getSize(1000)); + assertEquals("1.00KB ", getSize(1024)); + assertEquals("1.00MB ", getSize(1048576)); + assertEquals("1.00GB ", getSize(1073741824)); + } +} diff --git a/eladmin-common/src/test/java/me/zhengjie/utils/StringUtilsTest.java b/eladmin-common/src/test/java/me/zhengjie/utils/StringUtilsTest.java new file mode 100644 index 0000000..ffb2cf8 --- /dev/null +++ b/eladmin-common/src/test/java/me/zhengjie/utils/StringUtilsTest.java @@ -0,0 +1,48 @@ +package me.zhengjie.utils; + +import org.junit.jupiter.api.Test; +import org.springframework.mock.web.MockHttpServletRequest; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import static me.zhengjie.utils.StringUtils.getIp; +import static me.zhengjie.utils.StringUtils.getWeekDay; +import static me.zhengjie.utils.StringUtils.toCamelCase; +import static me.zhengjie.utils.StringUtils.toCapitalizeCamelCase; +import static me.zhengjie.utils.StringUtils.toUnderScoreCase; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class StringUtilsTest { + + @Test + public void testToCamelCase() { + assertNull(toCamelCase(null)); + } + + @Test + public void testToCapitalizeCamelCase() { + assertNull(StringUtils.toCapitalizeCamelCase(null)); + assertEquals("HelloWorld", toCapitalizeCamelCase("hello_world")); + } + + @Test + public void testToUnderScoreCase() { + assertNull(StringUtils.toUnderScoreCase(null)); + assertEquals("hello_world", toUnderScoreCase("helloWorld")); + assertEquals("\u0000\u0000", toUnderScoreCase("\u0000\u0000")); + assertEquals("\u0000_a", toUnderScoreCase("\u0000A")); + } + + @Test + public void testGetWeekDay() { + SimpleDateFormat simpleDateformat = new SimpleDateFormat("E"); + assertEquals(simpleDateformat.format(new Date()), getWeekDay()); + } + + @Test + public void testGetIP() { + assertEquals("127.0.0.1", getIp(new MockHttpServletRequest())); + } +} diff --git a/eladmin-generator/pom.xml b/eladmin-generator/pom.xml new file mode 100644 index 0000000..3584959 --- /dev/null +++ b/eladmin-generator/pom.xml @@ -0,0 +1,39 @@ + + + + eladmin + me.zhengjie + 2.7 + + 4.0.0 + + eladmin-generator + 代码生成模块 + + + 1.10 + + + + + me.zhengjie + eladmin-common + 2.7 + + + + + org.springframework.boot + spring-boot-starter-freemarker + + + + + commons-configuration + commons-configuration + ${configuration.version} + + + \ No newline at end of file diff --git a/eladmin-generator/src/main/java/me/zhengjie/domain/ColumnInfo.java b/eladmin-generator/src/main/java/me/zhengjie/domain/ColumnInfo.java new file mode 100644 index 0000000..fdb962a --- /dev/null +++ b/eladmin-generator/src/main/java/me/zhengjie/domain/ColumnInfo.java @@ -0,0 +1,97 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.domain; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import me.zhengjie.utils.GenUtil; +import javax.persistence.*; +import java.io.Serializable; + +/** + * 列的数据信息 + * @author Zheng Jie + * @date 2019-01-02 + */ +@Getter +@Setter +@Entity +@NoArgsConstructor +@Table(name = "code_column_config") +public class ColumnInfo implements Serializable { + + @Id + @Column(name = "column_id") + @ApiModelProperty(value = "ID", hidden = true) + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ApiModelProperty(value = "表名") + private String tableName; + + @ApiModelProperty(value = "数据库字段名称") + private String columnName; + + @ApiModelProperty(value = "数据库字段类型") + private String columnType; + + @ApiModelProperty(value = "数据库字段键类型") + private String keyType; + + @ApiModelProperty(value = "字段额外的参数") + private String extra; + + @ApiModelProperty(value = "数据库字段描述") + private String remark; + + @ApiModelProperty(value = "是否必填") + private Boolean notNull; + + @ApiModelProperty(value = "是否在列表显示") + private Boolean listShow; + + @ApiModelProperty(value = "是否表单显示") + private Boolean formShow; + + @ApiModelProperty(value = "表单类型") + private String formType; + + @ApiModelProperty(value = "查询 1:模糊 2:精确") + private String queryType; + + @ApiModelProperty(value = "字典名称") + private String dictName; + + @ApiModelProperty(value = "日期注解") + private String dateAnnotation; + + public ColumnInfo(String tableName, String columnName, Boolean notNull, String columnType, String remark, String keyType, String extra) { + this.tableName = tableName; + this.columnName = columnName; + this.columnType = columnType; + this.keyType = keyType; + this.extra = extra; + this.notNull = notNull; + if(GenUtil.PK.equalsIgnoreCase(keyType) && GenUtil.EXTRA.equalsIgnoreCase(extra)){ + this.notNull = false; + } + this.remark = remark; + this.listShow = true; + this.formShow = true; + } +} diff --git a/eladmin-generator/src/main/java/me/zhengjie/domain/GenConfig.java b/eladmin-generator/src/main/java/me/zhengjie/domain/GenConfig.java new file mode 100644 index 0000000..a2d6706 --- /dev/null +++ b/eladmin-generator/src/main/java/me/zhengjie/domain/GenConfig.java @@ -0,0 +1,78 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.domain; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import javax.persistence.*; +import javax.validation.constraints.NotBlank; +import java.io.Serializable; + +/** + * 代码生成配置 + * @author Zheng Jie + * @date 2019-01-03 + */ +@Getter +@Setter +@Entity +@NoArgsConstructor +@Table(name = "code_gen_config") +public class GenConfig implements Serializable { + + public GenConfig(String tableName) { + this.tableName = tableName; + } + + @Id + @Column(name = "config_id") + @ApiModelProperty(value = "ID", hidden = true) + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotBlank + @ApiModelProperty(value = "表名") + private String tableName; + + @ApiModelProperty(value = "接口名称") + private String apiAlias; + + @NotBlank + @ApiModelProperty(value = "包路径") + private String pack; + + @NotBlank + @ApiModelProperty(value = "模块名") + private String moduleName; + + @NotBlank + @ApiModelProperty(value = "前端文件路径") + private String path; + + @ApiModelProperty(value = "前端文件路径") + private String apiPath; + + @ApiModelProperty(value = "作者") + private String author; + + @ApiModelProperty(value = "表前缀") + private String prefix; + + @ApiModelProperty(value = "是否覆盖") + private Boolean cover = false; +} diff --git a/eladmin-generator/src/main/java/me/zhengjie/domain/vo/TableInfo.java b/eladmin-generator/src/main/java/me/zhengjie/domain/vo/TableInfo.java new file mode 100644 index 0000000..1d3967b --- /dev/null +++ b/eladmin-generator/src/main/java/me/zhengjie/domain/vo/TableInfo.java @@ -0,0 +1,48 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.domain.vo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 表的数据信息 + * @author Zheng Jie + * @date 2019-01-02 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class TableInfo { + + /** 表名称 */ + private Object tableName; + + /** 创建日期 */ + private Object createTime; + + /** 数据库引擎 */ + private Object engine; + + /** 编码集 */ + private Object coding; + + /** 备注 */ + private Object remark; + + +} diff --git a/eladmin-generator/src/main/java/me/zhengjie/repository/ColumnInfoRepository.java b/eladmin-generator/src/main/java/me/zhengjie/repository/ColumnInfoRepository.java new file mode 100644 index 0000000..4638be2 --- /dev/null +++ b/eladmin-generator/src/main/java/me/zhengjie/repository/ColumnInfoRepository.java @@ -0,0 +1,34 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.repository; + +import me.zhengjie.domain.ColumnInfo; +import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; + +/** + * @author Zheng Jie + * @date 2019-01-14 + */ +public interface ColumnInfoRepository extends JpaRepository { + + /** + * 查询表信息 + * @param tableName 表格名 + * @return 表信息 + */ + List findByTableNameOrderByIdAsc(String tableName); +} diff --git a/eladmin-generator/src/main/java/me/zhengjie/repository/GenConfigRepository.java b/eladmin-generator/src/main/java/me/zhengjie/repository/GenConfigRepository.java new file mode 100644 index 0000000..18c9a0c --- /dev/null +++ b/eladmin-generator/src/main/java/me/zhengjie/repository/GenConfigRepository.java @@ -0,0 +1,33 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.repository; + +import me.zhengjie.domain.GenConfig; +import org.springframework.data.jpa.repository.JpaRepository; + +/** + * @author Zheng Jie + * @date 2019-01-14 + */ +public interface GenConfigRepository extends JpaRepository { + + /** + * 查询表配置 + * @param tableName 表名 + * @return / + */ + GenConfig findByTableName(String tableName); +} diff --git a/eladmin-generator/src/main/java/me/zhengjie/rest/GenConfigController.java b/eladmin-generator/src/main/java/me/zhengjie/rest/GenConfigController.java new file mode 100644 index 0000000..d4cbec9 --- /dev/null +++ b/eladmin-generator/src/main/java/me/zhengjie/rest/GenConfigController.java @@ -0,0 +1,51 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.rest; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import me.zhengjie.domain.GenConfig; +import me.zhengjie.service.GenConfigService; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +/** + * @author Zheng Jie + * @date 2019-01-14 + */ +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/genConfig") +@Api(tags = "系统:代码生成器配置管理") +public class GenConfigController { + + private final GenConfigService genConfigService; + + @ApiOperation("查询") + @GetMapping(value = "/{tableName}") + public ResponseEntity queryGenConfig(@PathVariable String tableName){ + return new ResponseEntity<>(genConfigService.find(tableName), HttpStatus.OK); + } + + @PutMapping + @ApiOperation("修改") + public ResponseEntity updateGenConfig(@Validated @RequestBody GenConfig genConfig){ + return new ResponseEntity<>(genConfigService.update(genConfig.getTableName(), genConfig),HttpStatus.OK); + } +} diff --git a/eladmin-generator/src/main/java/me/zhengjie/rest/GeneratorController.java b/eladmin-generator/src/main/java/me/zhengjie/rest/GeneratorController.java new file mode 100644 index 0000000..4a1f1e4 --- /dev/null +++ b/eladmin-generator/src/main/java/me/zhengjie/rest/GeneratorController.java @@ -0,0 +1,109 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.rest; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import me.zhengjie.domain.ColumnInfo; +import me.zhengjie.domain.vo.TableInfo; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.service.GenConfigService; +import me.zhengjie.service.GeneratorService; +import me.zhengjie.utils.PageResult; +import me.zhengjie.utils.PageUtil; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * @author Zheng Jie + * @date 2019-01-02 + */ +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/generator") +@Api(tags = "系统:代码生成管理") +public class GeneratorController { + + private final GeneratorService generatorService; + private final GenConfigService genConfigService; + + @Value("${generator.enabled}") + private Boolean generatorEnabled; + + @ApiOperation("查询数据库数据") + @GetMapping(value = "/tables/all") + public ResponseEntity queryAllTables(){ + return new ResponseEntity<>(generatorService.getTables(), HttpStatus.OK); + } + + @ApiOperation("查询数据库数据") + @GetMapping(value = "/tables") + public ResponseEntity> queryTables(@RequestParam(defaultValue = "") String name, + @RequestParam(defaultValue = "0")Integer page, + @RequestParam(defaultValue = "10")Integer size){ + int[] startEnd = PageUtil.transToStartEnd(page, size); + return new ResponseEntity<>(generatorService.getTables(name,startEnd), HttpStatus.OK); + } + + @ApiOperation("查询字段数据") + @GetMapping(value = "/columns") + public ResponseEntity> queryColumns(@RequestParam String tableName){ + List columnInfos = generatorService.getColumns(tableName); + return new ResponseEntity<>(PageUtil.toPage(columnInfos,columnInfos.size()), HttpStatus.OK); + } + + @ApiOperation("保存字段数据") + @PutMapping + public ResponseEntity saveColumn(@RequestBody List columnInfos){ + generatorService.save(columnInfos); + return new ResponseEntity<>(HttpStatus.OK); + } + + @ApiOperation("同步字段数据") + @PostMapping(value = "sync") + public ResponseEntity syncColumn(@RequestBody List tables){ + for (String table : tables) { + generatorService.sync(generatorService.getColumns(table), generatorService.query(table)); + } + return new ResponseEntity<>(HttpStatus.OK); + } + + @ApiOperation("生成代码") + @PostMapping(value = "/{tableName}/{type}") + public ResponseEntity generatorCode(@PathVariable String tableName, @PathVariable Integer type, HttpServletRequest request, HttpServletResponse response){ + if(!generatorEnabled && type == 0){ + throw new BadRequestException("此环境不允许生成代码,请选择预览或者下载查看!"); + } + switch (type){ + // 生成代码 + case 0: generatorService.generator(genConfigService.find(tableName), generatorService.getColumns(tableName)); + break; + // 预览 + case 1: return generatorService.preview(genConfigService.find(tableName), generatorService.getColumns(tableName)); + // 打包 + case 2: generatorService.download(genConfigService.find(tableName), generatorService.getColumns(tableName), request, response); + break; + default: throw new BadRequestException("没有这个选项"); + } + return new ResponseEntity<>(HttpStatus.OK); + } +} diff --git a/eladmin-generator/src/main/java/me/zhengjie/service/GenConfigService.java b/eladmin-generator/src/main/java/me/zhengjie/service/GenConfigService.java new file mode 100644 index 0000000..b5711f4 --- /dev/null +++ b/eladmin-generator/src/main/java/me/zhengjie/service/GenConfigService.java @@ -0,0 +1,40 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.service; + +import me.zhengjie.domain.GenConfig; + +/** + * @author Zheng Jie + * @date 2019-01-14 + */ +public interface GenConfigService { + + /** + * 查询表配置 + * @param tableName 表名 + * @return 表配置 + */ + GenConfig find(String tableName); + + /** + * 更新表配置 + * @param tableName 表名 + * @param genConfig 表配置 + * @return 表配置 + */ + GenConfig update(String tableName, GenConfig genConfig); +} diff --git a/eladmin-generator/src/main/java/me/zhengjie/service/GeneratorService.java b/eladmin-generator/src/main/java/me/zhengjie/service/GeneratorService.java new file mode 100644 index 0000000..b9c2a2f --- /dev/null +++ b/eladmin-generator/src/main/java/me/zhengjie/service/GeneratorService.java @@ -0,0 +1,98 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.service; + +import me.zhengjie.domain.GenConfig; +import me.zhengjie.domain.ColumnInfo; +import me.zhengjie.domain.vo.TableInfo; +import me.zhengjie.utils.PageResult; +import org.springframework.http.ResponseEntity; +import org.springframework.scheduling.annotation.Async; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * @author Zheng Jie + * @date 2019-01-02 + */ +public interface GeneratorService { + + /** + * 查询数据库元数据 + * @param name 表名 + * @param startEnd 分页参数 + * @return / + */ + PageResult getTables(String name, int[] startEnd); + + /** + * 得到数据表的元数据 + * @param name 表名 + * @return / + */ + List getColumns(String name); + + /** + * 同步表数据 + * @param columnInfos / + * @param columnInfoList / + */ + void sync(List columnInfos, List columnInfoList); + + /** + * 保持数据 + * @param columnInfos / + */ + void save(List columnInfos); + + /** + * 获取所有table + * @return / + */ + Object getTables(); + + /** + * 代码生成 + * @param genConfig 配置信息 + * @param columns 字段信息 + */ + void generator(GenConfig genConfig, List columns); + + /** + * 预览 + * @param genConfig 配置信息 + * @param columns 字段信息 + * @return / + */ + ResponseEntity preview(GenConfig genConfig, List columns); + + /** + * 打包下载 + * @param genConfig 配置信息 + * @param columns 字段信息 + * @param request / + * @param response / + */ + void download(GenConfig genConfig, List columns, HttpServletRequest request, HttpServletResponse response); + + /** + * 查询数据库的表字段数据数据 + * @param table / + * @return / + */ + List query(String table); +} diff --git a/eladmin-generator/src/main/java/me/zhengjie/service/impl/GenConfigServiceImpl.java b/eladmin-generator/src/main/java/me/zhengjie/service/impl/GenConfigServiceImpl.java new file mode 100644 index 0000000..bc2d061 --- /dev/null +++ b/eladmin-generator/src/main/java/me/zhengjie/service/impl/GenConfigServiceImpl.java @@ -0,0 +1,67 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.service.impl; + +import lombok.RequiredArgsConstructor; +import me.zhengjie.domain.GenConfig; +import me.zhengjie.repository.GenConfigRepository; +import me.zhengjie.service.GenConfigService; +import me.zhengjie.utils.StringUtils; +import org.springframework.stereotype.Service; +import java.io.File; + +/** + * @author Zheng Jie + * @date 2019-01-14 + */ +@Service +@RequiredArgsConstructor +public class GenConfigServiceImpl implements GenConfigService { + + private final GenConfigRepository genConfigRepository; + + @Override + public GenConfig find(String tableName) { + GenConfig genConfig = genConfigRepository.findByTableName(tableName); + if(genConfig == null){ + return new GenConfig(tableName); + } + return genConfig; + } + + @Override + public GenConfig update(String tableName, GenConfig genConfig) { + String separator = File.separator; + String[] paths; + String symbol = "\\"; + if (symbol.equals(separator)) { + paths = genConfig.getPath().split("\\\\"); + } else { + paths = genConfig.getPath().split(File.separator); + } + StringBuilder api = new StringBuilder(); + for (String path : paths) { + api.append(path); + api.append(separator); + if ("src".equals(path)) { + api.append("api"); + break; + } + } + genConfig.setApiPath(api.toString()); + return genConfigRepository.save(genConfig); + } +} diff --git a/eladmin-generator/src/main/java/me/zhengjie/service/impl/GeneratorServiceImpl.java b/eladmin-generator/src/main/java/me/zhengjie/service/impl/GeneratorServiceImpl.java new file mode 100644 index 0000000..604cf2d --- /dev/null +++ b/eladmin-generator/src/main/java/me/zhengjie/service/impl/GeneratorServiceImpl.java @@ -0,0 +1,204 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.ZipUtil; +import lombok.RequiredArgsConstructor; +import me.zhengjie.domain.GenConfig; +import me.zhengjie.domain.ColumnInfo; +import me.zhengjie.domain.vo.TableInfo; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.repository.ColumnInfoRepository; +import me.zhengjie.service.GeneratorService; +import me.zhengjie.utils.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.Query; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.IOException; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author Zheng Jie + * @date 2019-01-02 + */ +@Service +@RequiredArgsConstructor +public class GeneratorServiceImpl implements GeneratorService { + private static final Logger log = LoggerFactory.getLogger(GeneratorServiceImpl.class); + @PersistenceContext + private EntityManager em; + + private final ColumnInfoRepository columnInfoRepository; + + private final String CONFIG_MESSAGE = "请先配置生成器"; + @Override + public Object getTables() { + // 使用预编译防止sql注入 + String sql = "select table_name ,create_time , engine, table_collation, table_comment from information_schema.tables " + + "where table_schema = (select database()) " + + "order by create_time desc"; + Query query = em.createNativeQuery(sql); + return query.getResultList(); + } + + @Override + public PageResult getTables(String name, int[] startEnd) { + // 使用预编译防止sql注入 + String sql = "select table_name ,create_time , engine, table_collation, table_comment from information_schema.tables " + + "where table_schema = (select database()) " + + "and table_name like :table order by create_time desc"; + Query query = em.createNativeQuery(sql); + query.setFirstResult(startEnd[0]); + query.setMaxResults(startEnd[1] - startEnd[0]); + query.setParameter("table", StringUtils.isNotBlank(name) ? ("%" + name + "%") : "%%"); + List result = query.getResultList(); + List tableInfos = new ArrayList<>(); + for (Object obj : result) { + Object[] arr = (Object[]) obj; + tableInfos.add(new TableInfo(arr[0], arr[1], arr[2], arr[3], ObjectUtil.isNotEmpty(arr[4]) ? arr[4] : "-")); + } + String countSql = "select count(1) from information_schema.tables " + + "where table_schema = (select database()) and table_name like :table"; + Query queryCount = em.createNativeQuery(countSql); + queryCount.setParameter("table", StringUtils.isNotBlank(name) ? ("%" + name + "%") : "%%"); + BigInteger totalElements = (BigInteger) queryCount.getSingleResult(); + return PageUtil.toPage(tableInfos, totalElements.longValue()); + } + + @Override + public List getColumns(String tableName) { + List columnInfos = columnInfoRepository.findByTableNameOrderByIdAsc(tableName); + if (CollectionUtil.isNotEmpty(columnInfos)) { + return columnInfos; + } else { + columnInfos = query(tableName); + return columnInfoRepository.saveAll(columnInfos); + } + } + + @Override + public List query(String tableName) { + // 使用预编译防止sql注入 + String sql = "select column_name, is_nullable, data_type, column_comment, column_key, extra from information_schema.columns " + + "where table_name = ? and table_schema = (select database()) order by ordinal_position"; + Query query = em.createNativeQuery(sql); + query.setParameter(1, tableName); + List result = query.getResultList(); + List columnInfos = new ArrayList<>(); + for (Object obj : result) { + Object[] arr = (Object[]) obj; + columnInfos.add( + new ColumnInfo( + tableName, + arr[0].toString(), + "NO".equals(arr[1]), + arr[2].toString(), + ObjectUtil.isNotNull(arr[3]) ? arr[3].toString() : null, + ObjectUtil.isNotNull(arr[4]) ? arr[4].toString() : null, + ObjectUtil.isNotNull(arr[5]) ? arr[5].toString() : null) + ); + } + return columnInfos; + } + + @Override + public void sync(List columnInfos, List columnInfoList) { + // 第一种情况,数据库类字段改变或者新增字段 + for (ColumnInfo columnInfo : columnInfoList) { + // 根据字段名称查找 + List columns = columnInfos.stream().filter(c -> c.getColumnName().equals(columnInfo.getColumnName())).collect(Collectors.toList()); + // 如果能找到,就修改部分可能被字段 + if (CollectionUtil.isNotEmpty(columns)) { + ColumnInfo column = columns.get(0); + column.setColumnType(columnInfo.getColumnType()); + column.setExtra(columnInfo.getExtra()); + column.setKeyType(columnInfo.getKeyType()); + if (StringUtils.isBlank(column.getRemark())) { + column.setRemark(columnInfo.getRemark()); + } + columnInfoRepository.save(column); + } else { + // 如果找不到,则保存新字段信息 + columnInfoRepository.save(columnInfo); + } + } + // 第二种情况,数据库字段删除了 + for (ColumnInfo columnInfo : columnInfos) { + // 根据字段名称查找 + List columns = columnInfoList.stream().filter(c -> c.getColumnName().equals(columnInfo.getColumnName())).collect(Collectors.toList()); + // 如果找不到,就代表字段被删除了,则需要删除该字段 + if (CollectionUtil.isEmpty(columns)) { + columnInfoRepository.delete(columnInfo); + } + } + } + + @Override + public void save(List columnInfos) { + columnInfoRepository.saveAll(columnInfos); + } + + @Override + public void generator(GenConfig genConfig, List columns) { + if (genConfig.getId() == null) { + throw new BadRequestException(CONFIG_MESSAGE); + } + try { + GenUtil.generatorCode(columns, genConfig); + } catch (IOException e) { + log.error(e.getMessage(), e); + throw new BadRequestException("生成失败,请手动处理已生成的文件"); + } + } + + @Override + public ResponseEntity preview(GenConfig genConfig, List columns) { + if (genConfig.getId() == null) { + throw new BadRequestException(CONFIG_MESSAGE); + } + List> genList = GenUtil.preview(columns, genConfig); + return new ResponseEntity<>(genList, HttpStatus.OK); + } + + @Override + public void download(GenConfig genConfig, List columns, HttpServletRequest request, HttpServletResponse response) { + if (genConfig.getId() == null) { + throw new BadRequestException(CONFIG_MESSAGE); + } + try { + File file = new File(GenUtil.download(columns, genConfig)); + String zipPath = file.getPath() + ".zip"; + ZipUtil.zip(file.getPath(), zipPath); + FileUtil.downloadFile(request, response, new File(zipPath), true); + } catch (IOException e) { + throw new BadRequestException("打包失败"); + } + } +} diff --git a/eladmin-generator/src/main/java/me/zhengjie/utils/ColUtil.java b/eladmin-generator/src/main/java/me/zhengjie/utils/ColUtil.java new file mode 100644 index 0000000..b5fcd6b --- /dev/null +++ b/eladmin-generator/src/main/java/me/zhengjie/utils/ColUtil.java @@ -0,0 +1,54 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.utils; + +import org.apache.commons.configuration.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * sql字段转java + * + * @author Zheng Jie + * @date 2019-01-03 + */ +public class ColUtil { + private static final Logger log = LoggerFactory.getLogger(ColUtil.class); + + /** + * 转换mysql数据类型为java数据类型 + * + * @param type 数据库字段类型 + * @return String + */ + static String cloToJava(String type) { + Configuration config = getConfig(); + assert config != null; + return config.getString(type, "unknowType"); + } + + /** + * 获取配置信息 + */ + public static PropertiesConfiguration getConfig() { + try { + return new PropertiesConfiguration("generator.properties"); + } catch (ConfigurationException e) { + log.error(e.getMessage(), e); + } + return null; + } +} diff --git a/eladmin-generator/src/main/java/me/zhengjie/utils/GenUtil.java b/eladmin-generator/src/main/java/me/zhengjie/utils/GenUtil.java new file mode 100644 index 0000000..8c8a95a --- /dev/null +++ b/eladmin-generator/src/main/java/me/zhengjie/utils/GenUtil.java @@ -0,0 +1,422 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.utils; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.template.*; +import lombok.extern.slf4j.Slf4j; +import me.zhengjie.domain.GenConfig; +import me.zhengjie.domain.ColumnInfo; +import org.springframework.util.ObjectUtils; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; +import java.time.LocalDate; +import java.util.*; + +import static me.zhengjie.utils.FileUtil.SYS_TEM_DIR; + +/** + * 代码生成 + * + * @author Zheng Jie + * @date 2019-01-02 + */ +@Slf4j +@SuppressWarnings({"unchecked", "all"}) +public class GenUtil { + + private static final String TIMESTAMP = "Timestamp"; + + private static final String BIGDECIMAL = "BigDecimal"; + + public static final String PK = "PRI"; + + public static final String EXTRA = "auto_increment"; + + /** + * 获取后端代码模板名称 + * + * @return List + */ + private static List getAdminTemplateNames() { + List templateNames = new ArrayList<>(); + templateNames.add("Entity"); + templateNames.add("Dto"); + templateNames.add("Mapper"); + templateNames.add("Controller"); + templateNames.add("QueryCriteria"); + templateNames.add("Service"); + templateNames.add("ServiceImpl"); + templateNames.add("Repository"); + return templateNames; + } + + /** + * 获取前端代码模板名称 + * + * @return List + */ + private static List getFrontTemplateNames() { + List templateNames = new ArrayList<>(); + templateNames.add("index"); + templateNames.add("api"); + return templateNames; + } + + public static List> preview(List columns, GenConfig genConfig) { + Map genMap = getGenMap(columns, genConfig); + List> genList = new ArrayList<>(); + // 获取后端模版 + List templates = getAdminTemplateNames(); + TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("template", TemplateConfig.ResourceMode.CLASSPATH)); + for (String templateName : templates) { + Map map = new HashMap<>(1); + Template template = engine.getTemplate("admin/" + templateName + ".ftl"); + map.put("content", template.render(genMap)); + map.put("name", templateName); + genList.add(map); + } + // 获取前端模版 + templates = getFrontTemplateNames(); + for (String templateName : templates) { + Map map = new HashMap<>(1); + Template template = engine.getTemplate("front/" + templateName + ".ftl"); + map.put(templateName, template.render(genMap)); + map.put("content", template.render(genMap)); + map.put("name", templateName); + genList.add(map); + } + return genList; + } + + public static String download(List columns, GenConfig genConfig) throws IOException { + // 拼接的路径:/tmpeladmin-gen-temp/,这个路径在Linux下需要root用户才有权限创建,非root用户会权限错误而失败,更改为: /tmp/eladmin-gen-temp/ + // String tempPath =SYS_TEM_DIR + "eladmin-gen-temp" + File.separator + genConfig.getTableName() + File.separator; + String tempPath = SYS_TEM_DIR + "eladmin-gen-temp" + File.separator + genConfig.getTableName() + File.separator; + Map genMap = getGenMap(columns, genConfig); + TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("template", TemplateConfig.ResourceMode.CLASSPATH)); + // 生成后端代码 + List templates = getAdminTemplateNames(); + for (String templateName : templates) { + Template template = engine.getTemplate("admin/" + templateName + ".ftl"); + String filePath = getAdminFilePath(templateName, genConfig, genMap.get("className").toString(), tempPath + "eladmin" + File.separator); + assert filePath != null; + File file = new File(filePath); + // 如果非覆盖生成 + if (!genConfig.getCover() && FileUtil.exist(file)) { + continue; + } + // 生成代码 + genFile(file, template, genMap); + } + // 生成前端代码 + templates = getFrontTemplateNames(); + for (String templateName : templates) { + Template template = engine.getTemplate("front/" + templateName + ".ftl"); + String path = tempPath + "eladmin-web" + File.separator; + String apiPath = path + "src" + File.separator + "api" + File.separator; + String srcPath = path + "src" + File.separator + "views" + File.separator + genMap.get("changeClassName").toString() + File.separator; + String filePath = getFrontFilePath(templateName, apiPath, srcPath, genMap.get("changeClassName").toString()); + assert filePath != null; + File file = new File(filePath); + // 如果非覆盖生成 + if (!genConfig.getCover() && FileUtil.exist(file)) { + continue; + } + // 生成代码 + genFile(file, template, genMap); + } + return tempPath; + } + + public static void generatorCode(List columnInfos, GenConfig genConfig) throws IOException { + Map genMap = getGenMap(columnInfos, genConfig); + TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("template", TemplateConfig.ResourceMode.CLASSPATH)); + // 生成后端代码 + List templates = getAdminTemplateNames(); + for (String templateName : templates) { + Template template = engine.getTemplate("admin/" + templateName + ".ftl"); + String rootPath = System.getProperty("user.dir"); + String filePath = getAdminFilePath(templateName, genConfig, genMap.get("className").toString(), rootPath); + + assert filePath != null; + File file = new File(filePath); + + // 如果非覆盖生成 + if (!genConfig.getCover() && FileUtil.exist(file)) { + continue; + } + // 生成代码 + genFile(file, template, genMap); + } + + // 生成前端代码 + templates = getFrontTemplateNames(); + for (String templateName : templates) { + Template template = engine.getTemplate("front/" + templateName + ".ftl"); + String filePath = getFrontFilePath(templateName, genConfig.getApiPath(), genConfig.getPath(), genMap.get("changeClassName").toString()); + + assert filePath != null; + File file = new File(filePath); + + // 如果非覆盖生成 + if (!genConfig.getCover() && FileUtil.exist(file)) { + continue; + } + // 生成代码 + genFile(file, template, genMap); + } + } + + // 获取模版数据 + private static Map getGenMap(List columnInfos, GenConfig genConfig) { + // 存储模版字段数据 + Map genMap = new HashMap<>(16); + // 接口别名 + genMap.put("apiAlias", genConfig.getApiAlias()); + // 包名称 + genMap.put("package", genConfig.getPack()); + // 模块名称 + genMap.put("moduleName", genConfig.getModuleName()); + // 作者 + genMap.put("author", genConfig.getAuthor()); + // 创建日期 + genMap.put("date", LocalDate.now().toString()); + // 表名 + genMap.put("tableName", genConfig.getTableName()); + // 大写开头的类名 + String className = StringUtils.toCapitalizeCamelCase(genConfig.getTableName()); + // 小写开头的类名 + String changeClassName = StringUtils.toCamelCase(genConfig.getTableName()); + // 判断是否去除表前缀 + if (StringUtils.isNotEmpty(genConfig.getPrefix())) { + className = StringUtils.toCapitalizeCamelCase(StrUtil.removePrefix(genConfig.getTableName(), genConfig.getPrefix())); + changeClassName = StringUtils.toCamelCase(StrUtil.removePrefix(genConfig.getTableName(), genConfig.getPrefix())); + changeClassName = StringUtils.uncapitalize(changeClassName); + } + // 保存类名 + genMap.put("className", className); + // 保存小写开头的类名 + genMap.put("changeClassName", changeClassName); + // 存在 Timestamp 字段 + genMap.put("hasTimestamp", false); + // 查询类中存在 Timestamp 字段 + genMap.put("queryHasTimestamp", false); + // 存在 BigDecimal 字段 + genMap.put("hasBigDecimal", false); + // 查询类中存在 BigDecimal 字段 + genMap.put("queryHasBigDecimal", false); + // 是否需要创建查询 + genMap.put("hasQuery", false); + // 自增主键 + genMap.put("auto", false); + // 存在字典 + genMap.put("hasDict", false); + // 存在日期注解 + genMap.put("hasDateAnnotation", false); + // 保存字段信息 + List> columns = new ArrayList<>(); + // 保存查询字段的信息 + List> queryColumns = new ArrayList<>(); + // 存储字典信息 + List dicts = new ArrayList<>(); + // 存储 between 信息 + List> betweens = new ArrayList<>(); + // 存储不为空的字段信息 + List> isNotNullColumns = new ArrayList<>(); + + for (ColumnInfo column : columnInfos) { + Map listMap = new HashMap<>(16); + // 字段描述 + listMap.put("remark", column.getRemark()); + // 字段类型 + listMap.put("columnKey", column.getKeyType()); + // 主键类型 + String colType = ColUtil.cloToJava(column.getColumnType()); + // 小写开头的字段名 + String changeColumnName = StringUtils.toCamelCase(column.getColumnName()); + // 大写开头的字段名 + String capitalColumnName = StringUtils.toCapitalizeCamelCase(column.getColumnName()); + if (PK.equals(column.getKeyType())) { + // 存储主键类型 + genMap.put("pkColumnType", colType); + // 存储小写开头的字段名 + genMap.put("pkChangeColName", changeColumnName); + // 存储大写开头的字段名 + genMap.put("pkCapitalColName", capitalColumnName); + } + // 是否存在 Timestamp 类型的字段 + if (TIMESTAMP.equals(colType)) { + genMap.put("hasTimestamp", true); + } + // 是否存在 BigDecimal 类型的字段 + if (BIGDECIMAL.equals(colType)) { + genMap.put("hasBigDecimal", true); + } + // 主键是否自增 + if (EXTRA.equals(column.getExtra())) { + genMap.put("auto", true); + } + // 主键存在字典 + if (StringUtils.isNotBlank(column.getDictName())) { + genMap.put("hasDict", true); + if(!dicts.contains(column.getDictName())) + dicts.add(column.getDictName()); + } + + // 存储字段类型 + listMap.put("columnType", colType); + // 存储字原始段名称 + listMap.put("columnName", column.getColumnName()); + // 不为空 + listMap.put("istNotNull", column.getNotNull()); + // 字段列表显示 + listMap.put("columnShow", column.getListShow()); + // 表单显示 + listMap.put("formShow", column.getFormShow()); + // 表单组件类型 + listMap.put("formType", StringUtils.isNotBlank(column.getFormType()) ? column.getFormType() : "Input"); + // 小写开头的字段名称 + listMap.put("changeColumnName", changeColumnName); + //大写开头的字段名称 + listMap.put("capitalColumnName", capitalColumnName); + // 字典名称 + listMap.put("dictName", column.getDictName()); + // 日期注解 + listMap.put("dateAnnotation", column.getDateAnnotation()); + if (StringUtils.isNotBlank(column.getDateAnnotation())) { + genMap.put("hasDateAnnotation", true); + } + // 添加非空字段信息 + if (column.getNotNull()) { + isNotNullColumns.add(listMap); + } + // 判断是否有查询,如有则把查询的字段set进columnQuery + if (!StringUtils.isBlank(column.getQueryType())) { + // 查询类型 + listMap.put("queryType", column.getQueryType()); + // 是否存在查询 + genMap.put("hasQuery", true); + if (TIMESTAMP.equals(colType)) { + // 查询中存储 Timestamp 类型 + genMap.put("queryHasTimestamp", true); + } + if (BIGDECIMAL.equals(colType)) { + // 查询中存储 BigDecimal 类型 + genMap.put("queryHasBigDecimal", true); + } + if ("between".equalsIgnoreCase(column.getQueryType())) { + betweens.add(listMap); + } else { + // 添加到查询列表中 + queryColumns.add(listMap); + } + } + // 添加到字段列表中 + columns.add(listMap); + } + // 保存字段列表 + genMap.put("columns", columns); + // 保存查询列表 + genMap.put("queryColumns", queryColumns); + // 保存字段列表 + genMap.put("dicts", dicts); + // 保存查询列表 + genMap.put("betweens", betweens); + // 保存非空字段信息 + genMap.put("isNotNullColumns", isNotNullColumns); + return genMap; + } + + /** + * 定义后端文件路径以及名称 + */ + private static String getAdminFilePath(String templateName, GenConfig genConfig, String className, String rootPath) { + String projectPath = rootPath + File.separator + genConfig.getModuleName(); + String packagePath = projectPath + File.separator + "src" + File.separator + "main" + File.separator + "java" + File.separator; + if (!ObjectUtils.isEmpty(genConfig.getPack())) { + packagePath += genConfig.getPack().replace(".", File.separator) + File.separator; + } + + if ("Entity".equals(templateName)) { + return packagePath + "domain" + File.separator + className + ".java"; + } + + if ("Controller".equals(templateName)) { + return packagePath + "rest" + File.separator + className + "Controller.java"; + } + + if ("Service".equals(templateName)) { + return packagePath + "service" + File.separator + className + "Service.java"; + } + + if ("ServiceImpl".equals(templateName)) { + return packagePath + "service" + File.separator + "impl" + File.separator + className + "ServiceImpl.java"; + } + + if ("Dto".equals(templateName)) { + return packagePath + "service" + File.separator + "dto" + File.separator + className + "Dto.java"; + } + + if ("QueryCriteria".equals(templateName)) { + return packagePath + "service" + File.separator + "dto" + File.separator + className + "QueryCriteria.java"; + } + + if ("Mapper".equals(templateName)) { + return packagePath + "service" + File.separator + "mapstruct" + File.separator + className + "Mapper.java"; + } + + if ("Repository".equals(templateName)) { + return packagePath + "repository" + File.separator + className + "Repository.java"; + } + + return null; + } + + /** + * 定义前端文件路径以及名称 + */ + private static String getFrontFilePath(String templateName, String apiPath, String path, String apiName) { + + if ("api".equals(templateName)) { + return apiPath + File.separator + apiName + ".js"; + } + + if ("index".equals(templateName)) { + return path + File.separator + "index.vue"; + } + + return null; + } + + private static void genFile(File file, Template template, Map map) throws IOException { + // 生成目标文件 + Writer writer = null; + try { + FileUtil.touch(file); + writer = new FileWriter(file); + template.render(map, writer); + } catch (TemplateException | IOException e) { + throw new RuntimeException(e); + } finally { + assert writer != null; + writer.close(); + } + } +} diff --git a/eladmin-generator/src/main/resources/generator.properties b/eladmin-generator/src/main/resources/generator.properties new file mode 100644 index 0000000..e64d060 --- /dev/null +++ b/eladmin-generator/src/main/resources/generator.properties @@ -0,0 +1,27 @@ +# Database type to Java type +tinyint=Integer +smallint=Integer +mediumint=Integer +int=Integer +integer=Integer + +bigint=Long + +float=Float + +double=Double + +decimal=BigDecimal + +bit=Boolean + +char=String +varchar=String +tinytext=String +text=String +mediumtext=String +longtext=String + +date=Timestamp +datetime=Timestamp +timestamp=Timestamp \ No newline at end of file diff --git a/eladmin-generator/src/main/resources/template/admin/Controller.ftl b/eladmin-generator/src/main/resources/template/admin/Controller.ftl new file mode 100644 index 0000000..686d974 --- /dev/null +++ b/eladmin-generator/src/main/resources/template/admin/Controller.ftl @@ -0,0 +1,89 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package ${package}.rest; + +import me.zhengjie.annotation.Log; +import ${package}.domain.${className}; +import ${package}.service.${className}Service; +import ${package}.service.dto.${className}QueryCriteria; +import org.springframework.data.domain.Pageable; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import io.swagger.annotations.*; +import java.io.IOException; +import javax.servlet.http.HttpServletResponse; +import me.zhengjie.utils.PageResult; +import ${package}.service.dto.${className}Dto; + +/** +* @website https://eladmin.vip +* @author ${author} +**/ +@RestController +@RequiredArgsConstructor +@Api(tags = "${apiAlias}管理") +@RequestMapping("/api/${changeClassName}") +public class ${className}Controller { + + private final ${className}Service ${changeClassName}Service; + + @Log("导出数据") + @ApiOperation("导出数据") + @GetMapping(value = "/download") + @PreAuthorize("@el.check('${changeClassName}:list')") + public void export${className}(HttpServletResponse response, ${className}QueryCriteria criteria) throws IOException { + ${changeClassName}Service.download(${changeClassName}Service.queryAll(criteria), response); + } + + @GetMapping + @Log("查询${apiAlias}") + @ApiOperation("查询${apiAlias}") + @PreAuthorize("@el.check('${changeClassName}:list')") + public ResponseEntity> query${className}(${className}QueryCriteria criteria, Pageable pageable){ + return new ResponseEntity<>(${changeClassName}Service.queryAll(criteria,pageable),HttpStatus.OK); + } + + @PostMapping + @Log("新增${apiAlias}") + @ApiOperation("新增${apiAlias}") + @PreAuthorize("@el.check('${changeClassName}:add')") + public ResponseEntity create${className}(@Validated @RequestBody ${className} resources){ + ${changeClassName}Service.create(resources); + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @PutMapping + @Log("修改${apiAlias}") + @ApiOperation("修改${apiAlias}") + @PreAuthorize("@el.check('${changeClassName}:edit')") + public ResponseEntity update${className}(@Validated @RequestBody ${className} resources){ + ${changeClassName}Service.update(resources); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @DeleteMapping + @Log("删除${apiAlias}") + @ApiOperation("删除${apiAlias}") + @PreAuthorize("@el.check('${changeClassName}:del')") + public ResponseEntity delete${className}(@RequestBody ${pkColumnType}[] ids) { + ${changeClassName}Service.deleteAll(ids); + return new ResponseEntity<>(HttpStatus.OK); + } +} \ No newline at end of file diff --git a/eladmin-generator/src/main/resources/template/admin/Dto.ftl b/eladmin-generator/src/main/resources/template/admin/Dto.ftl new file mode 100644 index 0000000..1b010cc --- /dev/null +++ b/eladmin-generator/src/main/resources/template/admin/Dto.ftl @@ -0,0 +1,53 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package ${package}.service.dto; + +import lombok.Data; +<#if hasTimestamp> +import java.sql.Timestamp; + +<#if hasBigDecimal> +import java.math.BigDecimal; + +import java.io.Serializable; +<#if !auto && pkColumnType = 'Long'> +import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson.serializer.ToStringSerializer; + + +/** +* @website https://eladmin.vip +* @description / +* @author ${author} +**/ +@Data +public class ${className}Dto implements Serializable { +<#if columns??> + <#list columns as column> + + <#if column.remark != ''> + /** ${column.remark} */ + + <#if column.columnKey = 'PRI'> + <#if !auto && pkColumnType = 'Long'> + /** 防止精度丢失 */ + @JSONField(serializeUsing = ToStringSerializer.class) + + + private ${column.columnType} ${column.changeColumnName}; + + +} \ No newline at end of file diff --git a/eladmin-generator/src/main/resources/template/admin/Entity.ftl b/eladmin-generator/src/main/resources/template/admin/Entity.ftl new file mode 100644 index 0000000..fa96624 --- /dev/null +++ b/eladmin-generator/src/main/resources/template/admin/Entity.ftl @@ -0,0 +1,84 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package ${package}.domain; + +import lombok.Data; +import cn.hutool.core.bean.BeanUtil; +import io.swagger.annotations.ApiModelProperty; +import cn.hutool.core.bean.copier.CopyOptions; +import javax.persistence.*; +<#if isNotNullColumns??> +import javax.validation.constraints.*; + +<#if hasDateAnnotation> +import javax.persistence.Entity; +import javax.persistence.Table; +import org.hibernate.annotations.*; + +<#if hasTimestamp> +import java.sql.Timestamp; + +<#if hasBigDecimal> +import java.math.BigDecimal; + +import java.io.Serializable; + +/** +* @website https://eladmin.vip +* @description / +* @author ${author} +**/ +@Entity +@Data +@Table(name="${tableName}") +public class ${className} implements Serializable { +<#if columns??> + <#list columns as column> + + <#if column.columnKey = 'PRI'> + @Id + <#if auto> + @GeneratedValue(strategy = GenerationType.IDENTITY) + + + @Column(name = "`${column.columnName}`"<#if column.columnKey = 'UNI'>,unique = true<#if column.istNotNull && column.columnKey != 'PRI'>,nullable = false) + <#if column.istNotNull && column.columnKey != 'PRI'> + <#if column.columnType = 'String'> + @NotBlank + <#else> + @NotNull + + + <#if (column.dateAnnotation)?? && column.dateAnnotation != ''> + <#if column.dateAnnotation = 'CreationTimestamp'> + @CreationTimestamp + <#else> + @UpdateTimestamp + + + <#if column.remark != ''> + @ApiModelProperty(value = "${column.remark}") + <#else> + @ApiModelProperty(value = "${column.changeColumnName}") + + private ${column.columnType} ${column.changeColumnName}; + + + + public void copy(${className} source){ + BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true)); + } +} diff --git a/eladmin-generator/src/main/resources/template/admin/Mapper.ftl b/eladmin-generator/src/main/resources/template/admin/Mapper.ftl new file mode 100644 index 0000000..af04eca --- /dev/null +++ b/eladmin-generator/src/main/resources/template/admin/Mapper.ftl @@ -0,0 +1,31 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package ${package}.service.mapstruct; + +import me.zhengjie.base.BaseMapper; +import ${package}.domain.${className}; +import ${package}.service.dto.${className}Dto; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** +* @website https://eladmin.vip +* @author ${author} +**/ +@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface ${className}Mapper extends BaseMapper<${className}Dto, ${className}> { + +} \ No newline at end of file diff --git a/eladmin-generator/src/main/resources/template/admin/QueryCriteria.ftl b/eladmin-generator/src/main/resources/template/admin/QueryCriteria.ftl new file mode 100644 index 0000000..1e9a989 --- /dev/null +++ b/eladmin-generator/src/main/resources/template/admin/QueryCriteria.ftl @@ -0,0 +1,80 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package ${package}.service.dto; + +import lombok.Data; +<#if queryHasTimestamp> +import java.sql.Timestamp; + +<#if queryHasBigDecimal> +import java.math.BigDecimal; + +<#if betweens??> +import java.util.List; + +<#if queryColumns??> +import me.zhengjie.annotation.Query; + + +/** +* @website https://eladmin.vip +* @author ${author} +**/ +@Data +public class ${className}QueryCriteria{ +<#if queryColumns??> + <#list queryColumns as column> + +<#if column.queryType = '='> + /** 精确 */ + @Query + private ${column.columnType} ${column.changeColumnName}; + +<#if column.queryType = 'Like'> + /** 模糊 */ + @Query(type = Query.Type.INNER_LIKE) + private ${column.columnType} ${column.changeColumnName}; + +<#if column.queryType = '!='> + /** 不等于 */ + @Query(type = Query.Type.NOT_EQUAL) + private ${column.columnType} ${column.changeColumnName}; + +<#if column.queryType = 'NotNull'> + /** 不为空 */ + @Query(type = Query.Type.NOT_NULL) + private ${column.columnType} ${column.changeColumnName}; + +<#if column.queryType = '>='> + /** 大于等于 */ + @Query(type = Query.Type.GREATER_THAN) + private ${column.columnType} ${column.changeColumnName}; + +<#if column.queryType = '<='> + /** 小于等于 */ + @Query(type = Query.Type.LESS_THAN) + private ${column.columnType} ${column.changeColumnName}; + + + +<#if betweens??> + <#list betweens as column> + /** BETWEEN */ + @Query(type = Query.Type.BETWEEN) + private List<${column.columnType}> ${column.changeColumnName}; + + +} \ No newline at end of file diff --git a/eladmin-generator/src/main/resources/template/admin/Repository.ftl b/eladmin-generator/src/main/resources/template/admin/Repository.ftl new file mode 100644 index 0000000..4e7398a --- /dev/null +++ b/eladmin-generator/src/main/resources/template/admin/Repository.ftl @@ -0,0 +1,39 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package ${package}.repository; + +import ${package}.domain.${className}; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +/** +* @website https://eladmin.vip +* @author ${author} +**/ +public interface ${className}Repository extends JpaRepository<${className}, ${pkColumnType}>, JpaSpecificationExecutor<${className}> { +<#if columns??> + <#list columns as column> + <#if column.columnKey = 'UNI'> + /** + * 根据 ${column.capitalColumnName} 查询 + * @param ${column.columnName} / + * @return / + */ + ${className} findBy${column.capitalColumnName}(${column.columnType} ${column.columnName}); + + + +} \ No newline at end of file diff --git a/eladmin-generator/src/main/resources/template/admin/Service.ftl b/eladmin-generator/src/main/resources/template/admin/Service.ftl new file mode 100644 index 0000000..83482ef --- /dev/null +++ b/eladmin-generator/src/main/resources/template/admin/Service.ftl @@ -0,0 +1,82 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package ${package}.service; + +import ${package}.domain.${className}; +import ${package}.service.dto.${className}Dto; +import ${package}.service.dto.${className}QueryCriteria; +import org.springframework.data.domain.Pageable; +import java.util.Map; +import java.util.List; +import java.io.IOException; +import javax.servlet.http.HttpServletResponse; +import me.zhengjie.utils.PageResult; + +/** +* @website https://eladmin.vip +* @description 服务接口 +* @author ${author} +**/ +public interface ${className}Service { + + /** + * 查询数据分页 + * @param criteria 条件 + * @param pageable 分页参数 + * @return Map + */ + PageResult<${className}Dto> queryAll(${className}QueryCriteria criteria, Pageable pageable); + + /** + * 查询所有数据不分页 + * @param criteria 条件参数 + * @return List<${className}Dto> + */ + List<${className}Dto> queryAll(${className}QueryCriteria criteria); + + /** + * 根据ID查询 + * @param ${pkChangeColName} ID + * @return ${className}Dto + */ + ${className}Dto findById(${pkColumnType} ${pkChangeColName}); + + /** + * 创建 + * @param resources / + */ + void create(${className} resources); + + /** + * 编辑 + * @param resources / + */ + void update(${className} resources); + + /** + * 多选删除 + * @param ids / + */ + void deleteAll(${pkColumnType}[] ids); + + /** + * 导出数据 + * @param all 待导出的数据 + * @param response / + * @throws IOException / + */ + void download(List<${className}Dto> all, HttpServletResponse response) throws IOException; +} \ No newline at end of file diff --git a/eladmin-generator/src/main/resources/template/admin/ServiceImpl.ftl b/eladmin-generator/src/main/resources/template/admin/ServiceImpl.ftl new file mode 100644 index 0000000..1ead242 --- /dev/null +++ b/eladmin-generator/src/main/resources/template/admin/ServiceImpl.ftl @@ -0,0 +1,157 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package ${package}.service.impl; + +import ${package}.domain.${className}; +<#if columns??> + <#list columns as column> + <#if column.columnKey = 'UNI'> + <#if column_index = 1> +import me.zhengjie.exception.EntityExistException; + + + + +import me.zhengjie.utils.ValidationUtil; +import me.zhengjie.utils.FileUtil; +import lombok.RequiredArgsConstructor; +import ${package}.repository.${className}Repository; +import ${package}.service.${className}Service; +import ${package}.service.dto.${className}Dto; +import ${package}.service.dto.${className}QueryCriteria; +import ${package}.service.mapstruct.${className}Mapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +<#if !auto && pkColumnType = 'Long'> +import cn.hutool.core.lang.Snowflake; +import cn.hutool.core.util.IdUtil; + +<#if !auto && pkColumnType = 'String'> +import cn.hutool.core.util.IdUtil; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import me.zhengjie.utils.PageUtil; +import me.zhengjie.utils.QueryHelp; +import java.util.List; +import java.util.Map; +import java.io.IOException; +import javax.servlet.http.HttpServletResponse; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import me.zhengjie.utils.PageResult; + +/** +* @website https://eladmin.vip +* @description 服务实现 +* @author ${author} +**/ +@Service +@RequiredArgsConstructor +public class ${className}ServiceImpl implements ${className}Service { + + private final ${className}Repository ${changeClassName}Repository; + private final ${className}Mapper ${changeClassName}Mapper; + + @Override + public PageResult<${className}Dto> queryAll(${className}QueryCriteria criteria, Pageable pageable){ + Page<${className}> page = ${changeClassName}Repository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable); + return PageUtil.toPage(page.map(${changeClassName}Mapper::toDto)); + } + + @Override + public List<${className}Dto> queryAll(${className}QueryCriteria criteria){ + return ${changeClassName}Mapper.toDto(${changeClassName}Repository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder))); + } + + @Override + @Transactional + public ${className}Dto findById(${pkColumnType} ${pkChangeColName}) { + ${className} ${changeClassName} = ${changeClassName}Repository.findById(${pkChangeColName}).orElseGet(${className}::new); + ValidationUtil.isNull(${changeClassName}.get${pkCapitalColName}(),"${className}","${pkChangeColName}",${pkChangeColName}); + return ${changeClassName}Mapper.toDto(${changeClassName}); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void create(${className} resources) { +<#if !auto && pkColumnType = 'Long'> + Snowflake snowflake = IdUtil.createSnowflake(1, 1); + resources.set${pkCapitalColName}(snowflake.nextId()); + +<#if !auto && pkColumnType = 'String'> + resources.set${pkCapitalColName}(IdUtil.simpleUUID()); + +<#if columns??> + <#list columns as column> + <#if column.columnKey = 'UNI'> + if(${changeClassName}Repository.findBy${column.capitalColumnName}(resources.get${column.capitalColumnName}()) != null){ + throw new EntityExistException(${className}.class,"${column.columnName}",resources.get${column.capitalColumnName}()); + } + + + + ${changeClassName}Repository.save(resources); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(${className} resources) { + ${className} ${changeClassName} = ${changeClassName}Repository.findById(resources.get${pkCapitalColName}()).orElseGet(${className}::new); + ValidationUtil.isNull( ${changeClassName}.get${pkCapitalColName}(),"${className}","id",resources.get${pkCapitalColName}()); +<#if columns??> + <#list columns as column> + <#if column.columnKey = 'UNI'> + <#if column_index = 1> + ${className} ${changeClassName}1 = null; + + ${changeClassName}1 = ${changeClassName}Repository.findBy${column.capitalColumnName}(resources.get${column.capitalColumnName}()); + if(${changeClassName}1 != null && !${changeClassName}1.get${pkCapitalColName}().equals(${changeClassName}.get${pkCapitalColName}())){ + throw new EntityExistException(${className}.class,"${column.columnName}",resources.get${column.capitalColumnName}()); + } + + + + ${changeClassName}.copy(resources); + ${changeClassName}Repository.save(${changeClassName}); + } + + @Override + public void deleteAll(${pkColumnType}[] ids) { + for (${pkColumnType} ${pkChangeColName} : ids) { + ${changeClassName}Repository.deleteById(${pkChangeColName}); + } + } + + @Override + public void download(List<${className}Dto> all, HttpServletResponse response) throws IOException { + List> list = new ArrayList<>(); + for (${className}Dto ${changeClassName} : all) { + Map map = new LinkedHashMap<>(); + <#list columns as column> + <#if column.columnKey != 'PRI'> + <#if column.remark != ''> + map.put("${column.remark}", ${changeClassName}.get${column.capitalColumnName}()); + <#else> + map.put(" ${column.changeColumnName}", ${changeClassName}.get${column.capitalColumnName}()); + + + + list.add(map); + } + FileUtil.downloadExcel(list, response); + } +} \ No newline at end of file diff --git a/eladmin-generator/src/main/resources/template/front/api.ftl b/eladmin-generator/src/main/resources/template/front/api.ftl new file mode 100644 index 0000000..9587d0d --- /dev/null +++ b/eladmin-generator/src/main/resources/template/front/api.ftl @@ -0,0 +1,27 @@ +import request from '@/utils/request' + +export function add(data) { + return request({ + url: 'api/${changeClassName}', + method: 'post', + data + }) +} + +export function del(ids) { + return request({ + url: 'api/${changeClassName}/', + method: 'delete', + data: ids + }) +} + +export function edit(data) { + return request({ + url: 'api/${changeClassName}', + method: 'put', + data + }) +} + +export default { add, edit, del } diff --git a/eladmin-generator/src/main/resources/template/front/index.ftl b/eladmin-generator/src/main/resources/template/front/index.ftl new file mode 100644 index 0000000..9b7084c --- /dev/null +++ b/eladmin-generator/src/main/resources/template/front/index.ftl @@ -0,0 +1,169 @@ +<#--noinspection ALL--> + + + + + diff --git a/eladmin-logging/pom.xml b/eladmin-logging/pom.xml new file mode 100644 index 0000000..8613702 --- /dev/null +++ b/eladmin-logging/pom.xml @@ -0,0 +1,22 @@ + + + + eladmin + me.zhengjie + 2.7 + + 4.0.0 + + eladmin-logging + 日志模块 + + + + me.zhengjie + eladmin-common + 2.7 + + + \ No newline at end of file diff --git a/eladmin-logging/src/main/java/me/zhengjie/annotation/Log.java b/eladmin-logging/src/main/java/me/zhengjie/annotation/Log.java new file mode 100644 index 0000000..6c001cd --- /dev/null +++ b/eladmin-logging/src/main/java/me/zhengjie/annotation/Log.java @@ -0,0 +1,31 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Zheng Jie + * @date 2018-11-24 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Log { + String value() default ""; +} diff --git a/eladmin-logging/src/main/java/me/zhengjie/aspect/LogAspect.java b/eladmin-logging/src/main/java/me/zhengjie/aspect/LogAspect.java new file mode 100644 index 0000000..107aefc --- /dev/null +++ b/eladmin-logging/src/main/java/me/zhengjie/aspect/LogAspect.java @@ -0,0 +1,98 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.aspect; + +import lombok.extern.slf4j.Slf4j; +import me.zhengjie.domain.SysLog; +import me.zhengjie.service.SysLogService; +import me.zhengjie.utils.RequestHolder; +import me.zhengjie.utils.SecurityUtils; +import me.zhengjie.utils.StringUtils; +import me.zhengjie.utils.ThrowableUtil; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.stereotype.Component; +import javax.servlet.http.HttpServletRequest; + +/** + * @author Zheng Jie + * @date 2018-11-24 + */ +@Component +@Aspect +@Slf4j +public class LogAspect { + + private final SysLogService sysLogService; + + ThreadLocal currentTime = new ThreadLocal<>(); + + public LogAspect(SysLogService sysLogService) { + this.sysLogService = sysLogService; + } + + /** + * 配置切入点 + */ + @Pointcut("@annotation(me.zhengjie.annotation.Log)") + public void logPointcut() { + // 该方法无方法体,主要为了让同类中其他方法使用此切入点 + } + + /** + * 配置环绕通知,使用在方法logPointcut()上注册的切入点 + * + * @param joinPoint join point for advice + */ + @Around("logPointcut()") + public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { + Object result; + currentTime.set(System.currentTimeMillis()); + result = joinPoint.proceed(); + SysLog sysLog = new SysLog("INFO",System.currentTimeMillis() - currentTime.get()); + currentTime.remove(); + HttpServletRequest request = RequestHolder.getHttpServletRequest(); + sysLogService.save(getUsername(), StringUtils.getBrowser(request), StringUtils.getIp(request),joinPoint, sysLog); + return result; + } + + /** + * 配置异常通知 + * + * @param joinPoint join point for advice + * @param e exception + */ + @AfterThrowing(pointcut = "logPointcut()", throwing = "e") + public void logAfterThrowing(JoinPoint joinPoint, Throwable e) { + SysLog sysLog = new SysLog("ERROR",System.currentTimeMillis() - currentTime.get()); + currentTime.remove(); + sysLog.setExceptionDetail(ThrowableUtil.getStackTrace(e).getBytes()); + HttpServletRequest request = RequestHolder.getHttpServletRequest(); + sysLogService.save(getUsername(), StringUtils.getBrowser(request), StringUtils.getIp(request), (ProceedingJoinPoint)joinPoint, sysLog); + } + + public String getUsername() { + try { + return SecurityUtils.getCurrentUsername(); + }catch (Exception e){ + return ""; + } + } +} diff --git a/eladmin-logging/src/main/java/me/zhengjie/domain/SysLog.java b/eladmin-logging/src/main/java/me/zhengjie/domain/SysLog.java new file mode 100644 index 0000000..a6b2e47 --- /dev/null +++ b/eladmin-logging/src/main/java/me/zhengjie/domain/SysLog.java @@ -0,0 +1,80 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.domain; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.annotations.CreationTimestamp; +import javax.persistence.*; +import java.io.Serializable; +import java.sql.Timestamp; + +/** + * @author Zheng Jie + * @date 2018-11-24 + */ +@Entity +@Getter +@Setter +@Table(name = "sys_log") +@NoArgsConstructor +public class SysLog implements Serializable { + + @Id + @Column(name = "log_id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + /** 操作用户 */ + private String username; + + /** 描述 */ + private String description; + + /** 方法名 */ + private String method; + + /** 参数 */ + private String params; + + /** 日志类型 */ + private String logType; + + /** 请求ip */ + private String requestIp; + + /** 地址 */ + private String address; + + /** 浏览器 */ + private String browser; + + /** 请求耗时 */ + private Long time; + + /** 异常详细 */ + private byte[] exceptionDetail; + + /** 创建日期 */ + @CreationTimestamp + private Timestamp createTime; + + public SysLog(String logType, Long time) { + this.logType = logType; + this.time = time; + } +} diff --git a/eladmin-logging/src/main/java/me/zhengjie/repository/LogRepository.java b/eladmin-logging/src/main/java/me/zhengjie/repository/LogRepository.java new file mode 100644 index 0000000..805f97b --- /dev/null +++ b/eladmin-logging/src/main/java/me/zhengjie/repository/LogRepository.java @@ -0,0 +1,39 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.repository; + +import me.zhengjie.domain.SysLog; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +/** + * @author Zheng Jie + * @date 2018-11-24 + */ +@Repository +public interface LogRepository extends JpaRepository, JpaSpecificationExecutor { + + /** + * 根据日志类型删除信息 + * @param logType 日志类型 + */ + @Modifying + @Query(value = "delete from sys_log where log_type = ?1", nativeQuery = true) + void deleteByLogType(String logType); +} diff --git a/eladmin-logging/src/main/java/me/zhengjie/rest/SysLogController.java b/eladmin-logging/src/main/java/me/zhengjie/rest/SysLogController.java new file mode 100644 index 0000000..fa17370 --- /dev/null +++ b/eladmin-logging/src/main/java/me/zhengjie/rest/SysLogController.java @@ -0,0 +1,111 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.rest; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import me.zhengjie.annotation.Log; +import me.zhengjie.service.SysLogService; +import me.zhengjie.service.dto.SysLogQueryCriteria; +import me.zhengjie.service.dto.SysLogSmallDto; +import me.zhengjie.utils.PageResult; +import me.zhengjie.utils.SecurityUtils; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * @author Zheng Jie + * @date 2018-11-24 + */ +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/logs") +@Api(tags = "系统:日志管理") +public class SysLogController { + + private final SysLogService sysLogService; + + @Log("导出数据") + @ApiOperation("导出数据") + @GetMapping(value = "/download") + @PreAuthorize("@el.check()") + public void exportLog(HttpServletResponse response, SysLogQueryCriteria criteria) throws IOException { + criteria.setLogType("INFO"); + sysLogService.download(sysLogService.queryAll(criteria), response); + } + + @Log("导出错误数据") + @ApiOperation("导出错误数据") + @GetMapping(value = "/error/download") + @PreAuthorize("@el.check()") + public void exportErrorLog(HttpServletResponse response, SysLogQueryCriteria criteria) throws IOException { + criteria.setLogType("ERROR"); + sysLogService.download(sysLogService.queryAll(criteria), response); + } + @GetMapping + @ApiOperation("日志查询") + @PreAuthorize("@el.check()") + public ResponseEntity queryLog(SysLogQueryCriteria criteria, Pageable pageable){ + criteria.setLogType("INFO"); + return new ResponseEntity<>(sysLogService.queryAll(criteria,pageable), HttpStatus.OK); + } + + @GetMapping(value = "/user") + @ApiOperation("用户日志查询") + public ResponseEntity> queryUserLog(SysLogQueryCriteria criteria, Pageable pageable){ + criteria.setLogType("INFO"); + criteria.setUsername(SecurityUtils.getCurrentUsername()); + return new ResponseEntity<>(sysLogService.queryAllByUser(criteria,pageable), HttpStatus.OK); + } + + @GetMapping(value = "/error") + @ApiOperation("错误日志查询") + @PreAuthorize("@el.check()") + public ResponseEntity queryErrorLog(SysLogQueryCriteria criteria, Pageable pageable){ + criteria.setLogType("ERROR"); + return new ResponseEntity<>(sysLogService.queryAll(criteria,pageable), HttpStatus.OK); + } + + @GetMapping(value = "/error/{id}") + @ApiOperation("日志异常详情查询") + @PreAuthorize("@el.check()") + public ResponseEntity queryErrorLogDetail(@PathVariable Long id){ + return new ResponseEntity<>(sysLogService.findByErrDetail(id), HttpStatus.OK); + } + @DeleteMapping(value = "/del/error") + @Log("删除所有ERROR日志") + @ApiOperation("删除所有ERROR日志") + @PreAuthorize("@el.check()") + public ResponseEntity delAllErrorLog(){ + sysLogService.delAllByError(); + return new ResponseEntity<>(HttpStatus.OK); + } + + @DeleteMapping(value = "/del/info") + @Log("删除所有INFO日志") + @ApiOperation("删除所有INFO日志") + @PreAuthorize("@el.check()") + public ResponseEntity delAllInfoLog(){ + sysLogService.delAllByInfo(); + return new ResponseEntity<>(HttpStatus.OK); + } +} diff --git a/eladmin-logging/src/main/java/me/zhengjie/service/SysLogService.java b/eladmin-logging/src/main/java/me/zhengjie/service/SysLogService.java new file mode 100644 index 0000000..80f2d1c --- /dev/null +++ b/eladmin-logging/src/main/java/me/zhengjie/service/SysLogService.java @@ -0,0 +1,94 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.service; + +import me.zhengjie.domain.SysLog; +import me.zhengjie.service.dto.SysLogQueryCriteria; +import me.zhengjie.service.dto.SysLogSmallDto; +import me.zhengjie.utils.PageResult; +import org.aspectj.lang.ProceedingJoinPoint; +import org.springframework.data.domain.Pageable; +import org.springframework.scheduling.annotation.Async; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; + +/** + * @author Zheng Jie + * @date 2018-11-24 + */ +public interface SysLogService { + + /** + * 分页查询 + * @param criteria 查询条件 + * @param pageable 分页参数 + * @return / + */ + Object queryAll(SysLogQueryCriteria criteria, Pageable pageable); + + /** + * 查询全部数据 + * @param criteria 查询条件 + * @return / + */ + List queryAll(SysLogQueryCriteria criteria); + + /** + * 查询用户日志 + * @param criteria 查询条件 + * @param pageable 分页参数 + * @return - + */ + PageResult queryAllByUser(SysLogQueryCriteria criteria, Pageable pageable); + + /** + * 保存日志数据 + * @param username 用户 + * @param browser 浏览器 + * @param ip 请求IP + * @param joinPoint / + * @param sysLog 日志实体 + */ + @Async + void save(String username, String browser, String ip, ProceedingJoinPoint joinPoint, SysLog sysLog); + + /** + * 查询异常详情 + * @param id 日志ID + * @return Object + */ + Object findByErrDetail(Long id); + + /** + * 导出日志 + * @param sysLogs 待导出的数据 + * @param response / + * @throws IOException / + */ + void download(List sysLogs, HttpServletResponse response) throws IOException; + + /** + * 删除所有错误日志 + */ + void delAllByError(); + + /** + * 删除所有INFO日志 + */ + void delAllByInfo(); +} diff --git a/eladmin-logging/src/main/java/me/zhengjie/service/dto/SysLogErrorDto.java b/eladmin-logging/src/main/java/me/zhengjie/service/dto/SysLogErrorDto.java new file mode 100644 index 0000000..1fde4fb --- /dev/null +++ b/eladmin-logging/src/main/java/me/zhengjie/service/dto/SysLogErrorDto.java @@ -0,0 +1,46 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.service.dto; + +import lombok.Data; +import java.io.Serializable; +import java.sql.Timestamp; + +/** +* @author Zheng Jie +* @date 2019-5-22 +*/ +@Data +public class SysLogErrorDto implements Serializable { + + private Long id; + + private String username; + + private String description; + + private String method; + + private String params; + + private String browser; + + private String requestIp; + + private String address; + + private Timestamp createTime; +} \ No newline at end of file diff --git a/eladmin-logging/src/main/java/me/zhengjie/service/dto/SysLogQueryCriteria.java b/eladmin-logging/src/main/java/me/zhengjie/service/dto/SysLogQueryCriteria.java new file mode 100644 index 0000000..0588f64 --- /dev/null +++ b/eladmin-logging/src/main/java/me/zhengjie/service/dto/SysLogQueryCriteria.java @@ -0,0 +1,42 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.service.dto; + +import lombok.Data; +import me.zhengjie.annotation.Query; +import java.sql.Timestamp; +import java.util.List; + +/** + * 日志查询类 + * @author Zheng Jie + * @date 2019-6-4 09:23:07 + */ +@Data +public class SysLogQueryCriteria { + + @Query(blurry = "username,description,address,requestIp,method,params") + private String blurry; + + @Query + private String username; + + @Query + private String logType; + + @Query(type = Query.Type.BETWEEN) + private List createTime; +} diff --git a/eladmin-logging/src/main/java/me/zhengjie/service/dto/SysLogSmallDto.java b/eladmin-logging/src/main/java/me/zhengjie/service/dto/SysLogSmallDto.java new file mode 100644 index 0000000..d7312cd --- /dev/null +++ b/eladmin-logging/src/main/java/me/zhengjie/service/dto/SysLogSmallDto.java @@ -0,0 +1,40 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.service.dto; + +import lombok.Data; +import java.io.Serializable; +import java.sql.Timestamp; + +/** + * @author Zheng Jie + * @date 2019-5-22 + */ +@Data +public class SysLogSmallDto implements Serializable { + + private String description; + + private String requestIp; + + private Long time; + + private String address; + + private String browser; + + private Timestamp createTime; +} diff --git a/eladmin-logging/src/main/java/me/zhengjie/service/impl/SysLogServiceImpl.java b/eladmin-logging/src/main/java/me/zhengjie/service/impl/SysLogServiceImpl.java new file mode 100644 index 0000000..95ea4a0 --- /dev/null +++ b/eladmin-logging/src/main/java/me/zhengjie/service/impl/SysLogServiceImpl.java @@ -0,0 +1,180 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.service.impl; + +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import lombok.RequiredArgsConstructor; +import me.zhengjie.domain.SysLog; +import me.zhengjie.repository.LogRepository; +import me.zhengjie.service.SysLogService; +import me.zhengjie.service.dto.SysLogQueryCriteria; +import me.zhengjie.service.dto.SysLogSmallDto; +import me.zhengjie.service.mapstruct.LogErrorMapper; +import me.zhengjie.service.mapstruct.LogSmallMapper; +import me.zhengjie.utils.*; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.multipart.MultipartFile; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.*; + +/** + * @author Zheng Jie + * @date 2018-11-24 + */ +@Service +@RequiredArgsConstructor +public class SysLogServiceImpl implements SysLogService { + private final LogRepository logRepository; + private final LogErrorMapper logErrorMapper; + private final LogSmallMapper logSmallMapper; + + @Override + public Object queryAll(SysLogQueryCriteria criteria, Pageable pageable) { + Page page = logRepository.findAll(((root, criteriaQuery, cb) -> QueryHelp.getPredicate(root, criteria, cb)), pageable); + String status = "ERROR"; + if (status.equals(criteria.getLogType())) { + return PageUtil.toPage(page.map(logErrorMapper::toDto)); + } + return PageUtil.toPage(page); + } + + @Override + public List queryAll(SysLogQueryCriteria criteria) { + return logRepository.findAll(((root, criteriaQuery, cb) -> QueryHelp.getPredicate(root, criteria, cb))); + } + + @Override + public PageResult queryAllByUser(SysLogQueryCriteria criteria, Pageable pageable) { + Page page = logRepository.findAll(((root, criteriaQuery, cb) -> QueryHelp.getPredicate(root, criteria, cb)), pageable); + return PageUtil.toPage(page.map(logSmallMapper::toDto)); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(String username, String browser, String ip, ProceedingJoinPoint joinPoint, SysLog sysLog) { + if (sysLog == null) { + throw new IllegalArgumentException("Log 不能为 null!"); + } + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = signature.getMethod(); + me.zhengjie.annotation.Log aopLog = method.getAnnotation(me.zhengjie.annotation.Log.class); + + // 方法路径 + String methodName = joinPoint.getTarget().getClass().getName() + "." + signature.getName() + "()"; + + // 描述 + sysLog.setDescription(aopLog.value()); + + sysLog.setRequestIp(ip); + sysLog.setAddress(StringUtils.getCityInfo(sysLog.getRequestIp())); + sysLog.setMethod(methodName); + sysLog.setUsername(username); + sysLog.setParams(getParameter(method, joinPoint.getArgs())); + // 记录登录用户,隐藏密码信息 + if(signature.getName().equals("login") && StringUtils.isNotEmpty(sysLog.getParams())){ + JSONObject obj = JSON.parseObject(sysLog.getParams()); + sysLog.setUsername(obj.getString("username")); + sysLog.setParams(JSON.toJSONString(Dict.create().set("username", sysLog.getUsername()))); + } + sysLog.setBrowser(browser); + logRepository.save(sysLog); + } + + /** + * 根据方法和传入的参数获取请求参数 + */ + private String getParameter(Method method, Object[] args) { + List argList = new ArrayList<>(); + Parameter[] parameters = method.getParameters(); + for (int i = 0; i < parameters.length; i++) { + // 过滤掉不能序列化的类型: MultiPartFile + if (args[i] instanceof MultipartFile) { + continue; + } + //将RequestBody注解修饰的参数作为请求参数 + RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class); + if (requestBody != null) { + argList.add(args[i]); + } + //将RequestParam注解修饰的参数作为请求参数 + RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class); + if (requestParam != null) { + Map map = new HashMap<>(2); + String key = parameters[i].getName(); + if (!StringUtils.isEmpty(requestParam.value())) { + key = requestParam.value(); + } + map.put(key, args[i]); + argList.add(map); + } + } + if (argList.isEmpty()) { + return ""; + } + return argList.size() == 1 ? JSON.toJSONString(argList.get(0)) : JSON.toJSONString(argList); + } + + @Override + public Object findByErrDetail(Long id) { + SysLog sysLog = logRepository.findById(id).orElseGet(SysLog::new); + ValidationUtil.isNull(sysLog.getId(), "Log", "id", id); + byte[] details = sysLog.getExceptionDetail(); + return Dict.create().set("exception", new String(ObjectUtil.isNotNull(details) ? details : "".getBytes())); + } + + @Override + public void download(List sysLogs, HttpServletResponse response) throws IOException { + List> list = new ArrayList<>(); + for (SysLog sysLog : sysLogs) { + Map map = new LinkedHashMap<>(); + map.put("用户名", sysLog.getUsername()); + map.put("IP", sysLog.getRequestIp()); + map.put("IP来源", sysLog.getAddress()); + map.put("描述", sysLog.getDescription()); + map.put("浏览器", sysLog.getBrowser()); + map.put("请求耗时/毫秒", sysLog.getTime()); + map.put("异常详情", new String(ObjectUtil.isNotNull(sysLog.getExceptionDetail()) ? sysLog.getExceptionDetail() : "".getBytes())); + map.put("创建日期", sysLog.getCreateTime()); + list.add(map); + } + FileUtil.downloadExcel(list, response); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delAllByError() { + logRepository.deleteByLogType("ERROR"); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delAllByInfo() { + logRepository.deleteByLogType("INFO"); + } +} diff --git a/eladmin-logging/src/main/java/me/zhengjie/service/mapstruct/LogErrorMapper.java b/eladmin-logging/src/main/java/me/zhengjie/service/mapstruct/LogErrorMapper.java new file mode 100644 index 0000000..c777d11 --- /dev/null +++ b/eladmin-logging/src/main/java/me/zhengjie/service/mapstruct/LogErrorMapper.java @@ -0,0 +1,31 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.service.mapstruct; + +import me.zhengjie.base.BaseMapper; +import me.zhengjie.domain.SysLog; +import me.zhengjie.service.dto.SysLogErrorDto; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** + * @author Zheng Jie + * @date 2019-5-22 + */ +@Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface LogErrorMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/eladmin-logging/src/main/java/me/zhengjie/service/mapstruct/LogSmallMapper.java b/eladmin-logging/src/main/java/me/zhengjie/service/mapstruct/LogSmallMapper.java new file mode 100644 index 0000000..eeb5433 --- /dev/null +++ b/eladmin-logging/src/main/java/me/zhengjie/service/mapstruct/LogSmallMapper.java @@ -0,0 +1,31 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.service.mapstruct; + +import me.zhengjie.base.BaseMapper; +import me.zhengjie.domain.SysLog; +import me.zhengjie.service.dto.SysLogSmallDto; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** + * @author Zheng Jie + * @date 2019-5-22 + */ +@Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface LogSmallMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/eladmin-system/pom.xml b/eladmin-system/pom.xml new file mode 100644 index 0000000..9119bc1 --- /dev/null +++ b/eladmin-system/pom.xml @@ -0,0 +1,108 @@ + + + + eladmin + me.zhengjie + 2.7 + + 4.0.0 + + eladmin-system + 核心模块 + + + 0.11.5 + + 5.8.0 + + + + + + me.zhengjie + eladmin-generator + 2.7 + + + me.zhengjie + eladmin-common + + + + + + + me.zhengjie + eladmin-tools + 2.7 + + + + + org.springframework.boot + spring-boot-starter-websocket + + + + + io.jsonwebtoken + jjwt-api + ${jjwt.version} + + + io.jsonwebtoken + jjwt-impl + ${jjwt.version} + + + io.jsonwebtoken + jjwt-jackson + ${jjwt.version} + + + + + org.quartz-scheduler + quartz + + + + + ch.ethz.ganymed + ganymed-ssh2 + build210 + + + com.jcraft + jsch + 0.1.55 + + + + + com.github.oshi + oshi-core + 6.1.4 + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + + diff --git a/eladmin-system/src/main/java/me/zhengjie/AppRun.java b/eladmin-system/src/main/java/me/zhengjie/AppRun.java new file mode 100644 index 0000000..ad12601 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/AppRun.java @@ -0,0 +1,66 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie; + +import io.swagger.annotations.Api; +import me.zhengjie.annotation.rest.AnonymousGetMapping; +import me.zhengjie.utils.SpringContextHolder; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.ApplicationPidFileWriter; +import org.springframework.context.annotation.Bean; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.transaction.annotation.EnableTransactionManagement; +import org.springframework.web.bind.annotation.RestController; + +/** + * 开启审计功能 -> @EnableJpaAuditing + * + * @author Zheng Jie + * @date 2018/11/15 9:20:19 + */ +@EnableAsync +@RestController +@Api(hidden = true) +@SpringBootApplication +@EnableTransactionManagement +@EnableJpaAuditing(auditorAwareRef = "auditorAware") +public class AppRun { + + public static void main(String[] args) { + SpringApplication springApplication = new SpringApplication(AppRun.class); + // 监控应用的PID,启动时可指定PID路径:--spring.pid.file=/home/eladmin/app.pid + // 或者在 application.yml 添加文件路径,方便 kill,kill `cat /home/eladmin/app.pid` + springApplication.addListeners(new ApplicationPidFileWriter()); + springApplication.run(args); + } + + @Bean + public SpringContextHolder springContextHolder() { + return new SpringContextHolder(); + } + + /** + * 访问首页提示 + * + * @return / + */ + @AnonymousGetMapping("/") + public String index() { + return "Backend service started successfully"; + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/config/ConfigurerAdapter.java b/eladmin-system/src/main/java/me/zhengjie/config/ConfigurerAdapter.java new file mode 100644 index 0000000..a0093d6 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/config/ConfigurerAdapter.java @@ -0,0 +1,88 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.config; + +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.alibaba.fastjson.support.config.FastJsonConfig; +import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +/** + * WebMvcConfigurer + * + * @author Zheng Jie + * @date 2018-11-30 + */ +@Configuration +@EnableWebMvc +public class ConfigurerAdapter implements WebMvcConfigurer { + + /** 文件配置 */ + private final FileProperties properties; + + public ConfigurerAdapter(FileProperties properties) { + this.properties = properties; + } + + @Bean + public CorsFilter corsFilter() { + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + CorsConfiguration config = new CorsConfiguration(); + config.setAllowCredentials(true); + config.addAllowedOriginPattern("*"); + config.addAllowedHeader("*"); + config.addAllowedMethod("*"); + source.registerCorsConfiguration("/**", config); + return new CorsFilter(source); + } + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + FileProperties.ElPath path = properties.getPath(); + String avatarUtl = "file:" + path.getAvatar().replace("\\","/"); + String pathUtl = "file:" + path.getPath().replace("\\","/"); + registry.addResourceHandler("/avatar/**").addResourceLocations(avatarUtl).setCachePeriod(0); + registry.addResourceHandler("/file/**").addResourceLocations(pathUtl).setCachePeriod(0); + registry.addResourceHandler("/**").addResourceLocations("classpath:/META-INF/resources/").setCachePeriod(0); + } + + @Override + public void configureMessageConverters(List> converters) { + // 使用 fastjson 序列化,会导致 @JsonIgnore 失效,可以使用 @JSONField(serialize = false) 替换 + FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter(); + List supportMediaTypeList = new ArrayList<>(); + supportMediaTypeList.add(MediaType.APPLICATION_JSON); + FastJsonConfig config = new FastJsonConfig(); + config.setDateFormat("yyyy-MM-dd HH:mm:ss"); + config.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect); + converter.setFastJsonConfig(config); + converter.setSupportedMediaTypes(supportMediaTypeList); + converter.setDefaultCharset(StandardCharsets.UTF_8); + converters.add(converter); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/config/RelaxedQueryCharsConnectorCustomizer.java b/eladmin-system/src/main/java/me/zhengjie/config/RelaxedQueryCharsConnectorCustomizer.java new file mode 100644 index 0000000..14a6b20 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/config/RelaxedQueryCharsConnectorCustomizer.java @@ -0,0 +1,31 @@ +/* + * Copyright 2019-2023 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.config; + +import org.apache.catalina.connector.Connector; +import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer; +import org.springframework.context.annotation.Configuration; + +/** + * @author bearBoy80 + */ +@Configuration(proxyBeanMethods = false) +public class RelaxedQueryCharsConnectorCustomizer implements TomcatConnectorCustomizer { + @Override + public void customize(Connector connector) { + connector.setProperty("relaxedQueryChars", "[]{}"); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/config/WebSocketConfig.java b/eladmin-system/src/main/java/me/zhengjie/config/WebSocketConfig.java new file mode 100644 index 0000000..f55f5c6 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/config/WebSocketConfig.java @@ -0,0 +1,33 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; + +/** + * @author ZhangHouYing + * @date 2019-08-24 15:44 + */ +@Configuration +public class WebSocketConfig { + + @Bean + public ServerEndpointExporter serverEndpointExporter() { + return new ServerEndpointExporter(); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/config/thread/AsyncTaskProperties.java b/eladmin-system/src/main/java/me/zhengjie/config/thread/AsyncTaskProperties.java new file mode 100644 index 0000000..6e075ea --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/config/thread/AsyncTaskProperties.java @@ -0,0 +1,59 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.config.thread; + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 线程池配置属性类 + * @author https://juejin.im/entry/5abb8f6951882555677e9da2 + * @date 2019年10月31日14:58:18 + */ +@Data +@Component +public class AsyncTaskProperties { + + public static int corePoolSize; + + public static int maxPoolSize; + + public static int keepAliveSeconds; + + public static int queueCapacity; + + @Value("${task.pool.core-pool-size}") + public void setCorePoolSize(int corePoolSize) { + AsyncTaskProperties.corePoolSize = corePoolSize; + } + + @Value("${task.pool.max-pool-size}") + public void setMaxPoolSize(int maxPoolSize) { + AsyncTaskProperties.maxPoolSize = maxPoolSize; + } + + @Value("${task.pool.keep-alive-seconds}") + public void setKeepAliveSeconds(int keepAliveSeconds) { + AsyncTaskProperties.keepAliveSeconds = keepAliveSeconds; + } + + @Value("${task.pool.queue-capacity}") + public void setQueueCapacity(int queueCapacity) { + AsyncTaskProperties.queueCapacity = queueCapacity; + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/config/thread/CustomExecutorConfig.java b/eladmin-system/src/main/java/me/zhengjie/config/thread/CustomExecutorConfig.java new file mode 100644 index 0000000..ca8a8bc --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/config/thread/CustomExecutorConfig.java @@ -0,0 +1,52 @@ +package me.zhengjie.config.thread; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import java.util.concurrent.Executor; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * 创建自定义的线程池 + * @author Zheng Jie + * @description + * @date 2023-06-08 + **/ +@Configuration +public class CustomExecutorConfig { + + /** + * 自定义线程池,用法 @Async + * @return Executor + */ + @Bean + @Primary + public Executor elAsync() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(AsyncTaskProperties.corePoolSize); + executor.setMaxPoolSize(AsyncTaskProperties.maxPoolSize); + executor.setQueueCapacity(AsyncTaskProperties.queueCapacity); + executor.setThreadNamePrefix("el-async-"); + executor.setKeepAliveSeconds(AsyncTaskProperties.keepAliveSeconds); + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + executor.initialize(); + return executor; + } + + /** + * 自定义线程池,用法 @Async("otherAsync") + * @return Executor + */ + @Bean + public Executor otherAsync() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(15); + executor.setQueueCapacity(50); + executor.setKeepAliveSeconds(AsyncTaskProperties.keepAliveSeconds); + executor.setThreadNamePrefix("el-task-"); + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + executor.initialize(); + return executor; + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/domain/App.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/domain/App.java new file mode 100644 index 0000000..cf5789c --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/domain/App.java @@ -0,0 +1,67 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.domain; + +import io.swagger.annotations.ApiModelProperty; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.bean.copier.CopyOptions; +import lombok.Getter; +import lombok.Setter; +import me.zhengjie.base.BaseEntity; +import javax.persistence.*; +import java.io.Serializable; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +@Entity +@Getter +@Setter +@Table(name="mnt_app") +public class App extends BaseEntity implements Serializable { + + @Id + @Column(name = "app_id") + @ApiModelProperty(value = "ID", hidden = true) + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ApiModelProperty(value = "名称") + private String name; + + @ApiModelProperty(value = "端口") + private int port; + + @ApiModelProperty(value = "上传路径") + private String uploadPath; + + @ApiModelProperty(value = "部署路径") + private String deployPath; + + @ApiModelProperty(value = "备份路径") + private String backupPath; + + @ApiModelProperty(value = "启动脚本") + private String startScript; + + @ApiModelProperty(value = "部署脚本") + private String deployScript; + + public void copy(App source){ + BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true)); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/domain/Database.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/domain/Database.java new file mode 100644 index 0000000..6b3a68a --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/domain/Database.java @@ -0,0 +1,57 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.domain; + +import io.swagger.annotations.ApiModelProperty; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.bean.copier.CopyOptions; +import lombok.Getter; +import lombok.Setter; +import me.zhengjie.base.BaseEntity; +import javax.persistence.*; +import java.io.Serializable; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +@Entity +@Getter +@Setter +@Table(name="mnt_database") +public class Database extends BaseEntity implements Serializable { + + @Id + @Column(name = "db_id") + @ApiModelProperty(value = "ID", hidden = true) + private String id; + + @ApiModelProperty(value = "数据库名称") + private String name; + + @ApiModelProperty(value = "数据库连接地址") + private String jdbcUrl; + + @ApiModelProperty(value = "数据库密码") + private String pwd; + + @ApiModelProperty(value = "用户名") + private String userName; + + public void copy(Database source){ + BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true)); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/domain/Deploy.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/domain/Deploy.java new file mode 100644 index 0000000..bcf61e5 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/domain/Deploy.java @@ -0,0 +1,59 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.domain; + +import io.swagger.annotations.ApiModelProperty; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.bean.copier.CopyOptions; +import lombok.Getter; +import lombok.Setter; +import me.zhengjie.base.BaseEntity; +import javax.persistence.*; +import java.io.Serializable; +import java.util.Set; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +@Entity +@Getter +@Setter +@Table(name="mnt_deploy") +public class Deploy extends BaseEntity implements Serializable { + + @Id + @Column(name = "deploy_id") + @ApiModelProperty(value = "ID", hidden = true) + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToMany + @ApiModelProperty(name = "服务器", hidden = true) + @JoinTable(name = "mnt_deploy_server", + joinColumns = {@JoinColumn(name = "deploy_id",referencedColumnName = "deploy_id")}, + inverseJoinColumns = {@JoinColumn(name = "server_id",referencedColumnName = "server_id")}) + private Set deploys; + + @ManyToOne + @JoinColumn(name = "app_id") + @ApiModelProperty(value = "应用编号") + private App app; + + public void copy(Deploy source){ + BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true)); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/domain/DeployHistory.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/domain/DeployHistory.java new file mode 100644 index 0000000..6e07e13 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/domain/DeployHistory.java @@ -0,0 +1,62 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.domain; + +import io.swagger.annotations.ApiModelProperty; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.bean.copier.CopyOptions; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.annotations.CreationTimestamp; +import javax.persistence.*; +import java.io.Serializable; +import java.sql.Timestamp; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +@Entity +@Getter +@Setter +@Table(name="mnt_deploy_history") +public class DeployHistory implements Serializable { + + @Id + @Column(name = "history_id") + @ApiModelProperty(value = "ID", hidden = true) + private String id; + + @ApiModelProperty(value = "应用名称") + private String appName; + + @ApiModelProperty(value = "IP") + private String ip; + + @CreationTimestamp + @ApiModelProperty(value = "部署时间") + private Timestamp deployDate; + + @ApiModelProperty(value = "部署者") + private String deployUser; + + @ApiModelProperty(value = "部署ID") + private Long deployId; + + public void copy(DeployHistory source){ + BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true)); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/domain/ServerDeploy.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/domain/ServerDeploy.java new file mode 100644 index 0000000..f523562 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/domain/ServerDeploy.java @@ -0,0 +1,80 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.domain; + +import io.swagger.annotations.ApiModelProperty; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.bean.copier.CopyOptions; +import lombok.Getter; +import lombok.Setter; +import me.zhengjie.base.BaseEntity; +import javax.persistence.*; +import java.io.Serializable; +import java.util.Objects; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +@Entity +@Getter +@Setter +@Table(name="mnt_server") +public class ServerDeploy extends BaseEntity implements Serializable { + + @Id + @Column(name = "server_id") + @ApiModelProperty(value = "ID", hidden = true) + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ApiModelProperty(value = "服务器名称") + private String name; + + @ApiModelProperty(value = "IP") + private String ip; + + @ApiModelProperty(value = "端口") + private Integer port; + + @ApiModelProperty(value = "账号") + private String account; + + @ApiModelProperty(value = "密码") + private String password; + + public void copy(ServerDeploy source){ + BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true)); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ServerDeploy that = (ServerDeploy) o; + return Objects.equals(id, that.id) && + Objects.equals(name, that.name); + } + + @Override + public int hashCode() { + return Objects.hash(id, name); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/repository/AppRepository.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/repository/AppRepository.java new file mode 100644 index 0000000..41e5f5c --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/repository/AppRepository.java @@ -0,0 +1,27 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.repository; + +import me.zhengjie.modules.mnt.domain.App; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +public interface AppRepository extends JpaRepository, JpaSpecificationExecutor { +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/repository/DatabaseRepository.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/repository/DatabaseRepository.java new file mode 100644 index 0000000..695e0ad --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/repository/DatabaseRepository.java @@ -0,0 +1,27 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.repository; + +import me.zhengjie.modules.mnt.domain.Database; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +public interface DatabaseRepository extends JpaRepository, JpaSpecificationExecutor { +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/repository/DeployHistoryRepository.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/repository/DeployHistoryRepository.java new file mode 100644 index 0000000..3c8980e --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/repository/DeployHistoryRepository.java @@ -0,0 +1,27 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.repository; + +import me.zhengjie.modules.mnt.domain.DeployHistory; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +public interface DeployHistoryRepository extends JpaRepository, JpaSpecificationExecutor { +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/repository/DeployRepository.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/repository/DeployRepository.java new file mode 100644 index 0000000..2ea4498 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/repository/DeployRepository.java @@ -0,0 +1,27 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.repository; + +import me.zhengjie.modules.mnt.domain.Deploy; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +public interface DeployRepository extends JpaRepository, JpaSpecificationExecutor { +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/repository/ServerDeployRepository.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/repository/ServerDeployRepository.java new file mode 100644 index 0000000..4ca336c --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/repository/ServerDeployRepository.java @@ -0,0 +1,34 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.repository; + +import me.zhengjie.modules.mnt.domain.ServerDeploy; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +public interface ServerDeployRepository extends JpaRepository, JpaSpecificationExecutor { + + /** + * 根据IP查询 + * @param ip / + * @return / + */ + ServerDeploy findByIp(String ip); +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/rest/AppController.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/rest/AppController.java new file mode 100644 index 0000000..5dac15e --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/rest/AppController.java @@ -0,0 +1,89 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.rest; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import me.zhengjie.annotation.Log; +import me.zhengjie.modules.mnt.domain.App; +import me.zhengjie.modules.mnt.service.AppService; +import me.zhengjie.modules.mnt.service.dto.AppDto; +import me.zhengjie.modules.mnt.service.dto.AppQueryCriteria; +import me.zhengjie.utils.PageResult; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Set; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +@RestController +@RequiredArgsConstructor +@Api(tags = "运维:应用管理") +@RequestMapping("/api/app") +public class AppController { + + private final AppService appService; + + @ApiOperation("导出应用数据") + @GetMapping(value = "/download") + @PreAuthorize("@el.check('app:list')") + public void exportApp(HttpServletResponse response, AppQueryCriteria criteria) throws IOException { + appService.download(appService.queryAll(criteria), response); + } + + @ApiOperation(value = "查询应用") + @GetMapping + @PreAuthorize("@el.check('app:list')") + public ResponseEntity> queryApp(AppQueryCriteria criteria, Pageable pageable){ + return new ResponseEntity<>(appService.queryAll(criteria,pageable),HttpStatus.OK); + } + + @Log("新增应用") + @ApiOperation(value = "新增应用") + @PostMapping + @PreAuthorize("@el.check('app:add')") + public ResponseEntity createApp(@Validated @RequestBody App resources){ + appService.create(resources); + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @Log("修改应用") + @ApiOperation(value = "修改应用") + @PutMapping + @PreAuthorize("@el.check('app:edit')") + public ResponseEntity updateApp(@Validated @RequestBody App resources){ + appService.update(resources); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @Log("删除应用") + @ApiOperation(value = "删除应用") + @DeleteMapping + @PreAuthorize("@el.check('app:del')") + public ResponseEntity deleteApp(@RequestBody Set ids){ + appService.delete(ids); + return new ResponseEntity<>(HttpStatus.OK); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/rest/DatabaseController.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/rest/DatabaseController.java new file mode 100644 index 0000000..0ce1a4e --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/rest/DatabaseController.java @@ -0,0 +1,124 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.rest; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import me.zhengjie.annotation.Log; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.modules.mnt.domain.Database; +import me.zhengjie.modules.mnt.service.DatabaseService; +import me.zhengjie.modules.mnt.service.dto.DatabaseDto; +import me.zhengjie.modules.mnt.service.dto.DatabaseQueryCriteria; +import me.zhengjie.modules.mnt.util.SqlUtils; +import me.zhengjie.utils.FileUtil; +import me.zhengjie.utils.PageResult; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.IOException; +import java.util.Set; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +@Api(tags = "运维:数据库管理") +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/database") +public class DatabaseController { + + private final String fileSavePath = FileUtil.getTmpDirPath()+"/"; + private final DatabaseService databaseService; + + @ApiOperation("导出数据库数据") + @GetMapping(value = "/download") + @PreAuthorize("@el.check('database:list')") + public void exportDatabase(HttpServletResponse response, DatabaseQueryCriteria criteria) throws IOException { + databaseService.download(databaseService.queryAll(criteria), response); + } + + @ApiOperation(value = "查询数据库") + @GetMapping + @PreAuthorize("@el.check('database:list')") + public ResponseEntity> queryDatabase(DatabaseQueryCriteria criteria, Pageable pageable){ + return new ResponseEntity<>(databaseService.queryAll(criteria,pageable),HttpStatus.OK); + } + + @Log("新增数据库") + @ApiOperation(value = "新增数据库") + @PostMapping + @PreAuthorize("@el.check('database:add')") + public ResponseEntity createDatabase(@Validated @RequestBody Database resources){ + databaseService.create(resources); + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @Log("修改数据库") + @ApiOperation(value = "修改数据库") + @PutMapping + @PreAuthorize("@el.check('database:edit')") + public ResponseEntity updateDatabase(@Validated @RequestBody Database resources){ + databaseService.update(resources); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @Log("删除数据库") + @ApiOperation(value = "删除数据库") + @DeleteMapping + @PreAuthorize("@el.check('database:del')") + public ResponseEntity deleteDatabase(@RequestBody Set ids){ + databaseService.delete(ids); + return new ResponseEntity<>(HttpStatus.OK); + } + + @Log("测试数据库链接") + @ApiOperation(value = "测试数据库链接") + @PostMapping("/testConnect") + @PreAuthorize("@el.check('database:testConnect')") + public ResponseEntity testConnect(@Validated @RequestBody Database resources){ + return new ResponseEntity<>(databaseService.testConnection(resources),HttpStatus.CREATED); + } + + @Log("执行SQL脚本") + @ApiOperation(value = "执行SQL脚本") + @PostMapping(value = "/upload") + @PreAuthorize("@el.check('database:add')") + public ResponseEntity uploadDatabase(@RequestBody MultipartFile file, HttpServletRequest request)throws Exception{ + String id = request.getParameter("id"); + DatabaseDto database = databaseService.findById(id); + String fileName; + if(database != null){ + fileName = file.getOriginalFilename(); + File executeFile = new File(fileSavePath+fileName); + FileUtil.del(executeFile); + file.transferTo(executeFile); + String result = SqlUtils.executeFile(database.getJdbcUrl(), database.getUserName(), database.getPwd(), executeFile); + return new ResponseEntity<>(result,HttpStatus.OK); + }else{ + throw new BadRequestException("Database not exist"); + } + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/rest/DeployController.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/rest/DeployController.java new file mode 100644 index 0000000..3f13813 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/rest/DeployController.java @@ -0,0 +1,155 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.rest; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import me.zhengjie.annotation.Log; +import me.zhengjie.modules.mnt.domain.Deploy; +import me.zhengjie.modules.mnt.domain.DeployHistory; +import me.zhengjie.modules.mnt.service.DeployService; +import me.zhengjie.modules.mnt.service.dto.DeployDto; +import me.zhengjie.modules.mnt.service.dto.DeployQueryCriteria; +import me.zhengjie.utils.FileUtil; +import me.zhengjie.utils.PageResult; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +@RestController +@Api(tags = "运维:部署管理") +@RequiredArgsConstructor +@RequestMapping("/api/deploy") +public class DeployController { + + private final String fileSavePath = FileUtil.getTmpDirPath()+"/"; + private final DeployService deployService; + + + @ApiOperation("导出部署数据") + @GetMapping(value = "/download") + @PreAuthorize("@el.check('database:list')") + public void exportDeployData(HttpServletResponse response, DeployQueryCriteria criteria) throws IOException { + deployService.download(deployService.queryAll(criteria), response); + } + + @ApiOperation(value = "查询部署") + @GetMapping + @PreAuthorize("@el.check('deploy:list')") + public ResponseEntity> queryDeployData(DeployQueryCriteria criteria, Pageable pageable){ + return new ResponseEntity<>(deployService.queryAll(criteria,pageable),HttpStatus.OK); + } + + @Log("新增部署") + @ApiOperation(value = "新增部署") + @PostMapping + @PreAuthorize("@el.check('deploy:add')") + public ResponseEntity createDeploy(@Validated @RequestBody Deploy resources){ + deployService.create(resources); + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @Log("修改部署") + @ApiOperation(value = "修改部署") + @PutMapping + @PreAuthorize("@el.check('deploy:edit')") + public ResponseEntity updateDeploy(@Validated @RequestBody Deploy resources){ + deployService.update(resources); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @Log("删除部署") + @ApiOperation(value = "删除部署") + @DeleteMapping + @PreAuthorize("@el.check('deploy:del')") + public ResponseEntity deleteDeploy(@RequestBody Set ids){ + deployService.delete(ids); + return new ResponseEntity<>(HttpStatus.OK); + } + + @Log("上传文件部署") + @ApiOperation(value = "上传文件部署") + @PostMapping(value = "/upload") + @PreAuthorize("@el.check('deploy:edit')") + public ResponseEntity uploadDeploy(@RequestBody MultipartFile file, HttpServletRequest request)throws Exception{ + Long id = Long.valueOf(request.getParameter("id")); + String fileName = ""; + if(file != null){ + fileName = file.getOriginalFilename(); + File deployFile = new File(fileSavePath+fileName); + FileUtil.del(deployFile); + file.transferTo(deployFile); + //文件下一步要根据文件名字来 + deployService.deploy(fileSavePath+fileName ,id); + }else{ + System.out.println("没有找到相对应的文件"); + } + System.out.println("文件上传的原名称为:"+ Objects.requireNonNull(file).getOriginalFilename()); + Map map = new HashMap<>(2); + map.put("errno",0); + map.put("id",fileName); + return new ResponseEntity<>(map,HttpStatus.OK); + } + @Log("系统还原") + @ApiOperation(value = "系统还原") + @PostMapping(value = "/serverReduction") + @PreAuthorize("@el.check('deploy:edit')") + public ResponseEntity serverReduction(@Validated @RequestBody DeployHistory resources){ + String result = deployService.serverReduction(resources); + return new ResponseEntity<>(result,HttpStatus.OK); + } + @Log("服务运行状态") + @ApiOperation(value = "服务运行状态") + @PostMapping(value = "/serverStatus") + @PreAuthorize("@el.check('deploy:edit')") + public ResponseEntity serverStatus(@Validated @RequestBody Deploy resources){ + String result = deployService.serverStatus(resources); + return new ResponseEntity<>(result,HttpStatus.OK); + } + @Log("启动服务") + @ApiOperation(value = "启动服务") + @PostMapping(value = "/startServer") + @PreAuthorize("@el.check('deploy:edit')") + public ResponseEntity startServer(@Validated @RequestBody Deploy resources){ + String result = deployService.startServer(resources); + return new ResponseEntity<>(result,HttpStatus.OK); + } + @Log("停止服务") + @ApiOperation(value = "停止服务") + @PostMapping(value = "/stopServer") + @PreAuthorize("@el.check('deploy:edit')") + public ResponseEntity stopServer(@Validated @RequestBody Deploy resources){ + String result = deployService.stopServer(resources); + return new ResponseEntity<>(result,HttpStatus.OK); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/rest/DeployHistoryController.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/rest/DeployHistoryController.java new file mode 100644 index 0000000..8d3c902 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/rest/DeployHistoryController.java @@ -0,0 +1,69 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.rest; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import me.zhengjie.annotation.Log; +import me.zhengjie.modules.mnt.service.DeployHistoryService; +import me.zhengjie.modules.mnt.service.dto.DeployHistoryDto; +import me.zhengjie.modules.mnt.service.dto.DeployHistoryQueryCriteria; +import me.zhengjie.utils.PageResult; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Set; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +@RestController +@RequiredArgsConstructor +@Api(tags = "运维:部署历史管理") +@RequestMapping("/api/deployHistory") +public class DeployHistoryController { + + private final DeployHistoryService deployhistoryService; + + @ApiOperation("导出部署历史数据") + @GetMapping(value = "/download") + @PreAuthorize("@el.check('deployHistory:list')") + public void exportDeployHistory(HttpServletResponse response, DeployHistoryQueryCriteria criteria) throws IOException { + deployhistoryService.download(deployhistoryService.queryAll(criteria), response); + } + + @ApiOperation(value = "查询部署历史") + @GetMapping + @PreAuthorize("@el.check('deployHistory:list')") + public ResponseEntity> queryDeployHistory(DeployHistoryQueryCriteria criteria, Pageable pageable){ + return new ResponseEntity<>(deployhistoryService.queryAll(criteria,pageable),HttpStatus.OK); + } + + @Log("删除DeployHistory") + @ApiOperation(value = "删除部署历史") + @DeleteMapping + @PreAuthorize("@el.check('deployHistory:del')") + public ResponseEntity deleteDeployHistory(@RequestBody Set ids){ + deployhistoryService.delete(ids); + return new ResponseEntity<>(HttpStatus.OK); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/rest/ServerDeployController.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/rest/ServerDeployController.java new file mode 100644 index 0000000..5ccb0ed --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/rest/ServerDeployController.java @@ -0,0 +1,97 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.rest; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import me.zhengjie.annotation.Log; +import me.zhengjie.modules.mnt.domain.ServerDeploy; +import me.zhengjie.modules.mnt.service.ServerDeployService; +import me.zhengjie.modules.mnt.service.dto.ServerDeployDto; +import me.zhengjie.modules.mnt.service.dto.ServerDeployQueryCriteria; +import me.zhengjie.utils.PageResult; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Set; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +@RestController +@Api(tags = "运维:服务器管理") +@RequiredArgsConstructor +@RequestMapping("/api/serverDeploy") +public class ServerDeployController { + + private final ServerDeployService serverDeployService; + + @ApiOperation("导出服务器数据") + @GetMapping(value = "/download") + @PreAuthorize("@el.check('serverDeploy:list')") + public void exportServerDeploy(HttpServletResponse response, ServerDeployQueryCriteria criteria) throws IOException { + serverDeployService.download(serverDeployService.queryAll(criteria), response); + } + + @ApiOperation(value = "查询服务器") + @GetMapping + @PreAuthorize("@el.check('serverDeploy:list')") + public ResponseEntity> queryServerDeploy(ServerDeployQueryCriteria criteria, Pageable pageable){ + return new ResponseEntity<>(serverDeployService.queryAll(criteria,pageable),HttpStatus.OK); + } + + @Log("新增服务器") + @ApiOperation(value = "新增服务器") + @PostMapping + @PreAuthorize("@el.check('serverDeploy:add')") + public ResponseEntity createServerDeploy(@Validated @RequestBody ServerDeploy resources){ + serverDeployService.create(resources); + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @Log("修改服务器") + @ApiOperation(value = "修改服务器") + @PutMapping + @PreAuthorize("@el.check('serverDeploy:edit')") + public ResponseEntity updateServerDeploy(@Validated @RequestBody ServerDeploy resources){ + serverDeployService.update(resources); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @Log("删除服务器") + @ApiOperation(value = "删除Server") + @DeleteMapping + @PreAuthorize("@el.check('serverDeploy:del')") + public ResponseEntity deleteServerDeploy(@RequestBody Set ids){ + serverDeployService.delete(ids); + return new ResponseEntity<>(HttpStatus.OK); + } + + @Log("测试连接服务器") + @ApiOperation(value = "测试连接服务器") + @PostMapping("/testConnect") + @PreAuthorize("@el.check('serverDeploy:add')") + public ResponseEntity testConnectServerDeploy(@Validated @RequestBody ServerDeploy resources){ + return new ResponseEntity<>(serverDeployService.testConnect(resources),HttpStatus.CREATED); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/AppService.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/AppService.java new file mode 100644 index 0000000..9bded4d --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/AppService.java @@ -0,0 +1,82 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.service; + +import me.zhengjie.modules.mnt.domain.App; +import me.zhengjie.modules.mnt.service.dto.AppDto; +import me.zhengjie.modules.mnt.service.dto.AppQueryCriteria; +import me.zhengjie.utils.PageResult; +import org.springframework.data.domain.Pageable; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; +import java.util.Set; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +public interface AppService { + + /** + * 分页查询 + * @param criteria 条件 + * @param pageable 分页参数 + * @return / + */ + PageResult queryAll(AppQueryCriteria criteria, Pageable pageable); + + /** + * 查询全部数据 + * @param criteria 条件 + * @return / + */ + List queryAll(AppQueryCriteria criteria); + + /** + * 根据ID查询 + * @param id / + * @return / + */ + AppDto findById(Long id); + + /** + * 创建 + * @param resources / + */ + void create(App resources); + + /** + * 编辑 + * @param resources / + */ + void update(App resources); + + /** + * 删除 + * @param ids / + */ + void delete(Set ids); + + /** + * 导出数据 + * @param queryAll / + * @param response / + * @throws IOException / + */ + void download(List queryAll, HttpServletResponse response) throws IOException; +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/DatabaseService.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/DatabaseService.java new file mode 100644 index 0000000..904f73c --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/DatabaseService.java @@ -0,0 +1,89 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.service; + +import me.zhengjie.modules.mnt.domain.Database; +import me.zhengjie.modules.mnt.service.dto.DatabaseDto; +import me.zhengjie.modules.mnt.service.dto.DatabaseQueryCriteria; +import me.zhengjie.utils.PageResult; +import org.springframework.data.domain.Pageable; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; +import java.util.Set; + +/** + * @author ZhangHouYing + * @date 2019-08-24 + */ +public interface DatabaseService { + + /** + * 分页查询 + * @param criteria 条件 + * @param pageable 分页参数 + * @return / + */ + PageResult queryAll(DatabaseQueryCriteria criteria, Pageable pageable); + + /** + * 查询全部 + * @param criteria 条件 + * @return / + */ + List queryAll(DatabaseQueryCriteria criteria); + + /** + * 根据ID查询 + * @param id / + * @return / + */ + DatabaseDto findById(String id); + + /** + * 创建 + * @param resources / + */ + void create(Database resources); + + /** + * 编辑 + * @param resources / + */ + void update(Database resources); + + /** + * 删除 + * @param ids / + */ + void delete(Set ids); + + /** + * 测试连接数据库 + * @param resources / + * @return / + */ + boolean testConnection(Database resources); + + /** + * 导出数据 + * @param queryAll / + * @param response / + * @throws IOException e + */ + void download(List queryAll, HttpServletResponse response) throws IOException; +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/DeployHistoryService.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/DeployHistoryService.java new file mode 100644 index 0000000..07bcb64 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/DeployHistoryService.java @@ -0,0 +1,75 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.service; + +import me.zhengjie.modules.mnt.domain.DeployHistory; +import me.zhengjie.modules.mnt.service.dto.DeployHistoryDto; +import me.zhengjie.modules.mnt.service.dto.DeployHistoryQueryCriteria; +import me.zhengjie.utils.PageResult; +import org.springframework.data.domain.Pageable; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; +import java.util.Set; + +/** + * @author zhanghouying + */ +public interface DeployHistoryService { + + /** + * 分页查询 + * @param criteria 条件 + * @param pageable 分页参数 + * @return / + */ + PageResult queryAll(DeployHistoryQueryCriteria criteria, Pageable pageable); + + /** + * 查询全部 + * @param criteria 条件 + * @return / + */ + List queryAll(DeployHistoryQueryCriteria criteria); + + /** + * 根据ID查询 + * @param id / + * @return / + */ + DeployHistoryDto findById(String id); + + /** + * 创建 + * @param resources / + */ + void create(DeployHistory resources); + + /** + * 删除 + * @param ids / + */ + void delete(Set ids); + + /** + * 导出数据 + * @param queryAll / + * @param response / + * @throws IOException / + */ + void download(List queryAll, HttpServletResponse response) throws IOException; +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/DeployService.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/DeployService.java new file mode 100644 index 0000000..7fb78f5 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/DeployService.java @@ -0,0 +1,117 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.service; + +import me.zhengjie.modules.mnt.domain.Deploy; +import me.zhengjie.modules.mnt.domain.DeployHistory; +import me.zhengjie.modules.mnt.service.dto.DeployDto; +import me.zhengjie.modules.mnt.service.dto.DeployQueryCriteria; +import me.zhengjie.utils.PageResult; +import org.springframework.data.domain.Pageable; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; +import java.util.Set; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +public interface DeployService { + + /** + * 分页查询 + * @param criteria 条件 + * @param pageable 分页参数 + * @return / + */ + PageResult queryAll(DeployQueryCriteria criteria, Pageable pageable); + + /** + * 查询全部数据 + * @param criteria 条件 + * @return / + */ + List queryAll(DeployQueryCriteria criteria); + + /** + * 根据ID查询 + * @param id / + * @return / + */ + DeployDto findById(Long id); + + /** + * 创建 + * @param resources / + */ + void create(Deploy resources); + + + /** + * 编辑 + * @param resources / + */ + void update(Deploy resources); + + /** + * 删除 + * @param ids / + */ + void delete(Set ids); + + /** + * 部署文件到服务器 + * @param fileSavePath 文件路径 + * @param appId 应用ID + */ + void deploy(String fileSavePath, Long appId); + + /** + * 查询部署状态 + * @param resources / + * @return / + */ + String serverStatus(Deploy resources); + /** + * 启动服务 + * @param resources / + * @return / + */ + String startServer(Deploy resources); + /** + * 停止服务 + * @param resources / + * @return / + */ + String stopServer(Deploy resources); + + /** + * 停止服务 + * @param resources / + * @return / + */ + String serverReduction(DeployHistory resources); + + /** + * 导出数据 + * @param queryAll / + * @param response / + * @throws IOException / + */ + void download(List queryAll, HttpServletResponse response) throws IOException; +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/ServerDeployService.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/ServerDeployService.java new file mode 100644 index 0000000..6dd343f --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/ServerDeployService.java @@ -0,0 +1,96 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.service; + +import me.zhengjie.modules.mnt.domain.ServerDeploy; +import me.zhengjie.modules.mnt.service.dto.ServerDeployDto; +import me.zhengjie.modules.mnt.service.dto.ServerDeployQueryCriteria; +import me.zhengjie.utils.PageResult; +import org.springframework.data.domain.Pageable; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; +import java.util.Set; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +public interface ServerDeployService { + + /** + * 分页查询 + * @param criteria 条件 + * @param pageable 分页参数 + * @return / + */ + PageResult queryAll(ServerDeployQueryCriteria criteria, Pageable pageable); + + /** + * 查询全部数据 + * @param criteria 条件 + * @return / + */ + List queryAll(ServerDeployQueryCriteria criteria); + + /** + * 根据ID查询 + * @param id / + * @return / + */ + ServerDeployDto findById(Long id); + + /** + * 创建 + * @param resources / + */ + void create(ServerDeploy resources); + + /** + * 编辑 + * @param resources / + */ + void update(ServerDeploy resources); + + /** + * 删除 + * @param ids / + */ + void delete(Set ids); + + /** + * 根据IP查询 + * @param ip / + * @return / + */ + ServerDeployDto findByIp(String ip); + + /** + * 测试登录服务器 + * @param resources / + * @return / + */ + Boolean testConnect(ServerDeploy resources); + + /** + * 导出数据 + * @param queryAll / + * @param response / + * @throws IOException / + */ + void download(List queryAll, HttpServletResponse response) throws IOException; +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/dto/AppDto.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/dto/AppDto.java new file mode 100644 index 0000000..c6fd6f7 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/dto/AppDto.java @@ -0,0 +1,71 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.service.dto; + +import lombok.Getter; +import lombok.Setter; +import me.zhengjie.base.BaseDTO; +import java.io.Serializable; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +@Getter +@Setter +public class AppDto extends BaseDTO implements Serializable { + + /** + * 应用编号 + */ + private Long id; + + /** + * 应用名称 + */ + private String name; + + /** + * 端口 + */ + private Integer port; + + /** + * 上传目录 + */ + private String uploadPath; + + /** + * 部署目录 + */ + private String deployPath; + + /** + * 备份目录 + */ + private String backupPath; + + /** + * 启动脚本 + */ + private String startScript; + + /** + * 部署脚本 + */ + private String deployScript; + +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/dto/AppQueryCriteria.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/dto/AppQueryCriteria.java new file mode 100644 index 0000000..17f358f --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/dto/AppQueryCriteria.java @@ -0,0 +1,38 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.service.dto; + +import lombok.Data; +import me.zhengjie.annotation.Query; +import java.sql.Timestamp; +import java.util.List; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +@Data +public class AppQueryCriteria{ + + /** + * 模糊 + */ + @Query(type = Query.Type.INNER_LIKE) + private String name; + + @Query(type = Query.Type.BETWEEN) + private List createTime; +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/dto/DatabaseDto.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/dto/DatabaseDto.java new file mode 100644 index 0000000..689b06b --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/dto/DatabaseDto.java @@ -0,0 +1,55 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.service.dto; + +import lombok.Getter; +import lombok.Setter; +import me.zhengjie.base.BaseDTO; +import java.io.Serializable; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +@Getter +@Setter +public class DatabaseDto extends BaseDTO implements Serializable { + + /** + * id + */ + private String id; + + /** + * 数据库名称 + */ + private String name; + + /** + * 数据库连接地址 + */ + private String jdbcUrl; + + /** + * 数据库密码 + */ + private String pwd; + + /** + * 用户名 + */ + private String userName; +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/dto/DatabaseQueryCriteria.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/dto/DatabaseQueryCriteria.java new file mode 100644 index 0000000..53d619d --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/dto/DatabaseQueryCriteria.java @@ -0,0 +1,44 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.service.dto; + +import lombok.Data; +import me.zhengjie.annotation.Query; +import java.sql.Timestamp; +import java.util.List; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +@Data +public class DatabaseQueryCriteria{ + + /** + * 模糊 + */ + @Query(type = Query.Type.INNER_LIKE) + private String name; + + /** + * 精确 + */ + @Query + private String jdbcUrl; + + @Query(type = Query.Type.BETWEEN) + private List createTime; +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/dto/DeployDto.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/dto/DeployDto.java new file mode 100644 index 0000000..f3d77b8 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/dto/DeployDto.java @@ -0,0 +1,78 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.service.dto; + +import cn.hutool.core.collection.CollectionUtil; +import lombok.Getter; +import lombok.Setter; +import me.zhengjie.base.BaseDTO; +import java.io.Serializable; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +@Getter +@Setter +public class DeployDto extends BaseDTO implements Serializable { + + /** + * 部署编号 + */ + private String id; + + private AppDto app; + + /** + * 服务器 + */ + private Set deploys; + + private String servers; + + /** + * 服务状态 + */ + private String status; + + public String getServers() { + if(CollectionUtil.isNotEmpty(deploys)){ + return deploys.stream().map(ServerDeployDto::getName).collect(Collectors.joining(",")); + } + return servers; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DeployDto deployDto = (DeployDto) o; + return Objects.equals(id, deployDto.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/dto/DeployHistoryDto.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/dto/DeployHistoryDto.java new file mode 100644 index 0000000..a9f480c --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/dto/DeployHistoryDto.java @@ -0,0 +1,58 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.service.dto; + +import lombok.Data; +import java.io.Serializable; +import java.sql.Timestamp; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +@Data +public class DeployHistoryDto implements Serializable { + + /** + * 编号 + */ + private String id; + + /** + * 应用名称 + */ + private String appName; + + /** + * 部署IP + */ + private String ip; + + /** + * 部署时间 + */ + private Timestamp deployDate; + + /** + * 部署人员 + */ + private String deployUser; + + /** + * 部署编号 + */ + private Long deployId; +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/dto/DeployHistoryQueryCriteria.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/dto/DeployHistoryQueryCriteria.java new file mode 100644 index 0000000..c34f124 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/dto/DeployHistoryQueryCriteria.java @@ -0,0 +1,41 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.service.dto; + +import lombok.Data; +import me.zhengjie.annotation.Query; +import java.sql.Timestamp; +import java.util.List; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +@Data +public class DeployHistoryQueryCriteria{ + + /** + * 精确 + */ + @Query(blurry = "appName,ip,deployUser") + private String blurry; + + @Query + private Long deployId; + + @Query(type = Query.Type.BETWEEN) + private List deployDate; +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/dto/DeployQueryCriteria.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/dto/DeployQueryCriteria.java new file mode 100644 index 0000000..c404620 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/dto/DeployQueryCriteria.java @@ -0,0 +1,39 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.service.dto; + +import lombok.Data; +import me.zhengjie.annotation.Query; +import java.sql.Timestamp; +import java.util.List; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +@Data +public class DeployQueryCriteria{ + + /** + * 模糊 + */ + @Query(type = Query.Type.INNER_LIKE, propName = "name", joinName = "app") + private String appName; + + @Query(type = Query.Type.BETWEEN) + private List createTime; + +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/dto/ServerDeployDto.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/dto/ServerDeployDto.java new file mode 100644 index 0000000..a49c795 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/dto/ServerDeployDto.java @@ -0,0 +1,61 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.service.dto; + +import lombok.Getter; +import lombok.Setter; +import me.zhengjie.base.BaseDTO; +import java.io.Serializable; +import java.util.Objects; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +@Getter +@Setter +public class ServerDeployDto extends BaseDTO implements Serializable { + + private Long id; + + private String name; + + private String ip; + + private Integer port; + + private String account; + + private String password; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ServerDeployDto that = (ServerDeployDto) o; + return Objects.equals(id, that.id) && + Objects.equals(name, that.name); + } + + @Override + public int hashCode() { + return Objects.hash(id, name); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/dto/ServerDeployQueryCriteria.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/dto/ServerDeployQueryCriteria.java new file mode 100644 index 0000000..bb8bd41 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/dto/ServerDeployQueryCriteria.java @@ -0,0 +1,38 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.service.dto; + +import lombok.Data; +import me.zhengjie.annotation.Query; +import java.sql.Timestamp; +import java.util.List; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +@Data +public class ServerDeployQueryCriteria{ + + /** + * 模糊 + */ + @Query(blurry = "name,ip,account") + private String blurry; + + @Query(type = Query.Type.BETWEEN) + private List createTime; +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/impl/AppServiceImpl.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/impl/AppServiceImpl.java new file mode 100644 index 0000000..7d66d0b --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/impl/AppServiceImpl.java @@ -0,0 +1,120 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.service.impl; + +import lombok.RequiredArgsConstructor; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.modules.mnt.domain.App; +import me.zhengjie.modules.mnt.repository.AppRepository; +import me.zhengjie.modules.mnt.service.AppService; +import me.zhengjie.modules.mnt.service.dto.AppDto; +import me.zhengjie.modules.mnt.service.dto.AppQueryCriteria; +import me.zhengjie.modules.mnt.service.mapstruct.AppMapper; +import me.zhengjie.utils.*; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.*; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +@Service +@RequiredArgsConstructor +public class AppServiceImpl implements AppService { + + private final AppRepository appRepository; + private final AppMapper appMapper; + + @Override + public PageResult queryAll(AppQueryCriteria criteria, Pageable pageable){ + Page page = appRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable); + return PageUtil.toPage(page.map(appMapper::toDto)); + } + + @Override + public List queryAll(AppQueryCriteria criteria){ + return appMapper.toDto(appRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder))); + } + + @Override + public AppDto findById(Long id) { + App app = appRepository.findById(id).orElseGet(App::new); + ValidationUtil.isNull(app.getId(),"App","id",id); + return appMapper.toDto(app); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void create(App resources) { + verification(resources); + appRepository.save(resources); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(App resources) { + verification(resources); + App app = appRepository.findById(resources.getId()).orElseGet(App::new); + ValidationUtil.isNull(app.getId(),"App","id",resources.getId()); + app.copy(resources); + appRepository.save(app); + } + + private void verification(App resources){ + String opt = "/opt"; + String home = "/home"; + if (!(resources.getUploadPath().startsWith(opt) || resources.getUploadPath().startsWith(home))) { + throw new BadRequestException("文件只能上传在opt目录或者home目录 "); + } + if (!(resources.getDeployPath().startsWith(opt) || resources.getDeployPath().startsWith(home))) { + throw new BadRequestException("文件只能部署在opt目录或者home目录 "); + } + if (!(resources.getBackupPath().startsWith(opt) || resources.getBackupPath().startsWith(home))) { + throw new BadRequestException("文件只能备份在opt目录或者home目录 "); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Set ids) { + for (Long id : ids) { + appRepository.deleteById(id); + } + } + + @Override + public void download(List queryAll, HttpServletResponse response) throws IOException { + List> list = new ArrayList<>(); + for (AppDto appDto : queryAll) { + Map map = new LinkedHashMap<>(); + map.put("应用名称", appDto.getName()); + map.put("端口", appDto.getPort()); + map.put("上传目录", appDto.getUploadPath()); + map.put("部署目录", appDto.getDeployPath()); + map.put("备份目录", appDto.getBackupPath()); + map.put("启动脚本", appDto.getStartScript()); + map.put("部署脚本", appDto.getDeployScript()); + map.put("创建日期", appDto.getCreateTime()); + list.add(map); + } + FileUtil.downloadExcel(list, response); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/impl/DatabaseServiceImpl.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/impl/DatabaseServiceImpl.java new file mode 100644 index 0000000..fa55782 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/impl/DatabaseServiceImpl.java @@ -0,0 +1,114 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.service.impl; + +import cn.hutool.core.util.IdUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import me.zhengjie.modules.mnt.domain.Database; +import me.zhengjie.modules.mnt.repository.DatabaseRepository; +import me.zhengjie.modules.mnt.service.DatabaseService; +import me.zhengjie.modules.mnt.service.dto.DatabaseDto; +import me.zhengjie.modules.mnt.service.dto.DatabaseQueryCriteria; +import me.zhengjie.modules.mnt.service.mapstruct.DatabaseMapper; +import me.zhengjie.modules.mnt.util.SqlUtils; +import me.zhengjie.utils.*; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.*; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +@Slf4j +@Service +@RequiredArgsConstructor +public class DatabaseServiceImpl implements DatabaseService { + + private final DatabaseRepository databaseRepository; + private final DatabaseMapper databaseMapper; + + @Override + public PageResult queryAll(DatabaseQueryCriteria criteria, Pageable pageable){ + Page page = databaseRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable); + return PageUtil.toPage(page.map(databaseMapper::toDto)); + } + + @Override + public List queryAll(DatabaseQueryCriteria criteria){ + return databaseMapper.toDto(databaseRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder))); + } + + @Override + public DatabaseDto findById(String id) { + Database database = databaseRepository.findById(id).orElseGet(Database::new); + ValidationUtil.isNull(database.getId(),"Database","id",id); + return databaseMapper.toDto(database); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void create(Database resources) { + resources.setId(IdUtil.simpleUUID()); + databaseRepository.save(resources); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(Database resources) { + Database database = databaseRepository.findById(resources.getId()).orElseGet(Database::new); + ValidationUtil.isNull(database.getId(),"Database","id",resources.getId()); + database.copy(resources); + databaseRepository.save(database); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Set ids) { + for (String id : ids) { + databaseRepository.deleteById(id); + } + } + + @Override + public boolean testConnection(Database resources) { + try { + return SqlUtils.testConnection(resources.getJdbcUrl(), resources.getUserName(), resources.getPwd()); + } catch (Exception e) { + log.error(e.getMessage()); + return false; + } + } + + @Override + public void download(List queryAll, HttpServletResponse response) throws IOException { + List> list = new ArrayList<>(); + for (DatabaseDto databaseDto : queryAll) { + Map map = new LinkedHashMap<>(); + map.put("数据库名称", databaseDto.getName()); + map.put("数据库连接地址", databaseDto.getJdbcUrl()); + map.put("用户名", databaseDto.getUserName()); + map.put("创建日期", databaseDto.getCreateTime()); + list.add(map); + } + FileUtil.downloadExcel(list, response); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/impl/DeployHistoryServiceImpl.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/impl/DeployHistoryServiceImpl.java new file mode 100644 index 0000000..762b783 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/impl/DeployHistoryServiceImpl.java @@ -0,0 +1,93 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.service.impl; + +import cn.hutool.core.util.IdUtil; +import lombok.RequiredArgsConstructor; +import me.zhengjie.modules.mnt.domain.DeployHistory; +import me.zhengjie.modules.mnt.repository.DeployHistoryRepository; +import me.zhengjie.modules.mnt.service.DeployHistoryService; +import me.zhengjie.modules.mnt.service.dto.DeployHistoryDto; +import me.zhengjie.modules.mnt.service.dto.DeployHistoryQueryCriteria; +import me.zhengjie.modules.mnt.service.mapstruct.DeployHistoryMapper; +import me.zhengjie.utils.*; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.*; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +@Service +@RequiredArgsConstructor +public class DeployHistoryServiceImpl implements DeployHistoryService { + + private final DeployHistoryRepository deployhistoryRepository; + private final DeployHistoryMapper deployhistoryMapper; + + @Override + public PageResult queryAll(DeployHistoryQueryCriteria criteria, Pageable pageable){ + Page page = deployhistoryRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable); + return PageUtil.toPage(page.map(deployhistoryMapper::toDto)); + } + + @Override + public List queryAll(DeployHistoryQueryCriteria criteria){ + return deployhistoryMapper.toDto(deployhistoryRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder))); + } + + @Override + public DeployHistoryDto findById(String id) { + DeployHistory deployhistory = deployhistoryRepository.findById(id).orElseGet(DeployHistory::new); + ValidationUtil.isNull(deployhistory.getId(),"DeployHistory","id",id); + return deployhistoryMapper.toDto(deployhistory); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void create(DeployHistory resources) { + resources.setId(IdUtil.simpleUUID()); + deployhistoryRepository.save(resources); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Set ids) { + for (String id : ids) { + deployhistoryRepository.deleteById(id); + } + } + + @Override + public void download(List queryAll, HttpServletResponse response) throws IOException { + List> list = new ArrayList<>(); + for (DeployHistoryDto deployHistoryDto : queryAll) { + Map map = new LinkedHashMap<>(); + map.put("部署编号", deployHistoryDto.getDeployId()); + map.put("应用名称", deployHistoryDto.getAppName()); + map.put("部署IP", deployHistoryDto.getIp()); + map.put("部署时间", deployHistoryDto.getDeployDate()); + map.put("部署人员", deployHistoryDto.getDeployUser()); + list.add(map); + } + FileUtil.downloadExcel(list, response); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/impl/DeployServiceImpl.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/impl/DeployServiceImpl.java new file mode 100644 index 0000000..1c16f9c --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/impl/DeployServiceImpl.java @@ -0,0 +1,430 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.service.impl; + +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.modules.mnt.domain.App; +import me.zhengjie.modules.mnt.domain.Deploy; +import me.zhengjie.modules.mnt.domain.DeployHistory; +import me.zhengjie.modules.mnt.domain.ServerDeploy; +import me.zhengjie.modules.mnt.repository.DeployRepository; +import me.zhengjie.modules.mnt.service.DeployHistoryService; +import me.zhengjie.modules.mnt.service.DeployService; +import me.zhengjie.modules.mnt.service.ServerDeployService; +import me.zhengjie.modules.mnt.service.dto.AppDto; +import me.zhengjie.modules.mnt.service.dto.DeployDto; +import me.zhengjie.modules.mnt.service.dto.DeployQueryCriteria; +import me.zhengjie.modules.mnt.service.dto.ServerDeployDto; +import me.zhengjie.modules.mnt.service.mapstruct.DeployMapper; +import me.zhengjie.modules.mnt.util.ExecuteShellUtil; +import me.zhengjie.modules.mnt.util.ScpClientUtil; +import me.zhengjie.modules.mnt.websocket.MsgType; +import me.zhengjie.modules.mnt.websocket.SocketMsg; +import me.zhengjie.modules.mnt.websocket.WebSocketServer; +import me.zhengjie.utils.*; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.*; + +/** + * @author zhanghouying + * @date 2019-08-24 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class DeployServiceImpl implements DeployService { + + private final String FILE_SEPARATOR = "/"; + private final DeployRepository deployRepository; + private final DeployMapper deployMapper; + private final ServerDeployService serverDeployService; + private final DeployHistoryService deployHistoryService; + /** + * 循环次数 + */ + private final Integer count = 30; + + + @Override + public PageResult queryAll(DeployQueryCriteria criteria, Pageable pageable) { + Page page = deployRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder), pageable); + return PageUtil.toPage(page.map(deployMapper::toDto)); + } + + @Override + public List queryAll(DeployQueryCriteria criteria) { + return deployMapper.toDto(deployRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder))); + } + + @Override + public DeployDto findById(Long id) { + Deploy deploy = deployRepository.findById(id).orElseGet(Deploy::new); + ValidationUtil.isNull(deploy.getId(), "Deploy", "id", id); + return deployMapper.toDto(deploy); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void create(Deploy resources) { + deployRepository.save(resources); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(Deploy resources) { + Deploy deploy = deployRepository.findById(resources.getId()).orElseGet(Deploy::new); + ValidationUtil.isNull(deploy.getId(), "Deploy", "id", resources.getId()); + deploy.copy(resources); + deployRepository.save(deploy); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Set ids) { + for (Long id : ids) { + deployRepository.deleteById(id); + } + } + + @Override + public void deploy(String fileSavePath, Long id) { + deployApp(fileSavePath, id); + } + + /** + * @param fileSavePath 本机路径 + * @param id ID + */ + private void deployApp(String fileSavePath, Long id) { + + DeployDto deploy = findById(id); + if (deploy == null) { + sendMsg("部署信息不存在", MsgType.ERROR); + throw new BadRequestException("部署信息不存在"); + } + AppDto app = deploy.getApp(); + if (app == null) { + sendMsg("包对应应用信息不存在", MsgType.ERROR); + throw new BadRequestException("包对应应用信息不存在"); + } + int port = app.getPort(); + //这个是服务器部署路径 + String uploadPath = app.getUploadPath(); + StringBuilder sb = new StringBuilder(); + String msg; + Set deploys = deploy.getDeploys(); + for (ServerDeployDto deployDTO : deploys) { + String ip = deployDTO.getIp(); + ExecuteShellUtil executeShellUtil = getExecuteShellUtil(ip); + //判断是否第一次部署 + boolean flag = checkFile(executeShellUtil, app); + //第一步要确认服务器上有这个目录 + executeShellUtil.execute("mkdir -p " + app.getUploadPath()); + executeShellUtil.execute("mkdir -p " + app.getBackupPath()); + executeShellUtil.execute("mkdir -p " + app.getDeployPath()); + //上传文件 + msg = String.format("登陆到服务器:%s", ip); + ScpClientUtil scpClientUtil = getScpClientUtil(ip); + log.info(msg); + sendMsg(msg, MsgType.INFO); + msg = String.format("上传文件到服务器:%s
目录:%s下,请稍等...", ip, uploadPath); + sendMsg(msg, MsgType.INFO); + scpClientUtil.putFile(fileSavePath, uploadPath); + if (flag) { + sendMsg("停止原来应用", MsgType.INFO); + //停止应用 + stopApp(port, executeShellUtil); + sendMsg("备份原来应用", MsgType.INFO); + //备份应用 + backupApp(executeShellUtil, ip, app.getDeployPath()+FILE_SEPARATOR, app.getName(), app.getBackupPath()+FILE_SEPARATOR, id); + } + sendMsg("部署应用", MsgType.INFO); + //部署文件,并启动应用 + String deployScript = app.getDeployScript(); + executeShellUtil.execute(deployScript); + sleep(3); + sendMsg("应用部署中,请耐心等待部署结果,或者稍后手动查看部署状态", MsgType.INFO); + int i = 0; + boolean result = false; + // 由于启动应用需要时间,所以需要循环获取状态,如果超过30次,则认为是启动失败 + while (i++ < count){ + result = checkIsRunningStatus(port, executeShellUtil); + if(result){ + break; + } + // 休眠6秒 + sleep(6); + } + sb.append("服务器:").append(deployDTO.getName()).append("
应用:").append(app.getName()); + sendResultMsg(result, sb); + executeShellUtil.close(); + } + } + + private void sleep(int second) { + try { + Thread.sleep(second * 1000); + } catch (InterruptedException e) { + log.error(e.getMessage(),e); + } + } + + private void backupApp(ExecuteShellUtil executeShellUtil, String ip, String fileSavePath, String appName, String backupPath, Long id) { + String deployDate = DateUtil.format(new Date(), DatePattern.PURE_DATETIME_PATTERN); + StringBuilder sb = new StringBuilder(); + backupPath += appName + FILE_SEPARATOR + deployDate + "\n"; + sb.append("mkdir -p ").append(backupPath); + sb.append("mv -f ").append(fileSavePath); + sb.append(appName).append(" ").append(backupPath); + log.info("备份应用脚本:" + sb.toString()); + executeShellUtil.execute(sb.toString()); + //还原信息入库 + DeployHistory deployHistory = new DeployHistory(); + deployHistory.setAppName(appName); + deployHistory.setDeployUser(SecurityUtils.getCurrentUsername()); + deployHistory.setIp(ip); + deployHistory.setDeployId(id); + deployHistoryService.create(deployHistory); + } + + /** + * 停App + * + * @param port 端口 + * @param executeShellUtil / + */ + private void stopApp(int port, ExecuteShellUtil executeShellUtil) { + //发送停止命令 + executeShellUtil.execute(String.format("lsof -i :%d|grep -v \"PID\"|awk '{print \"kill -9\",$2}'|sh", port)); + + } + + /** + * 指定端口程序是否在运行 + * + * @param port 端口 + * @param executeShellUtil / + * @return true 正在运行 false 已经停止 + */ + private boolean checkIsRunningStatus(int port, ExecuteShellUtil executeShellUtil) { + String result = executeShellUtil.executeForResult(String.format("fuser -n tcp %d", port)); + return result.indexOf("/tcp:")>0; + } + + private void sendMsg(String msg, MsgType msgType) { + try { + WebSocketServer.sendInfo(new SocketMsg(msg, msgType), "deploy"); + } catch (IOException e) { + log.error(e.getMessage(),e); + } + } + + @Override + public String serverStatus(Deploy resources) { + Set serverDeploys = resources.getDeploys(); + App app = resources.getApp(); + for (ServerDeploy serverDeploy : serverDeploys) { + StringBuilder sb = new StringBuilder(); + ExecuteShellUtil executeShellUtil = getExecuteShellUtil(serverDeploy.getIp()); + sb.append("服务器:").append(serverDeploy.getName()).append("
应用:").append(app.getName()); + boolean result = checkIsRunningStatus(app.getPort(), executeShellUtil); + if (result) { + sb.append("
正在运行"); + sendMsg(sb.toString(), MsgType.INFO); + } else { + sb.append("
已停止!"); + sendMsg(sb.toString(), MsgType.ERROR); + } + log.info(sb.toString()); + executeShellUtil.close(); + } + return "执行完毕"; + } + + private boolean checkFile(ExecuteShellUtil executeShellUtil, AppDto appDTO) { + String result = executeShellUtil.executeForResult("find " + appDTO.getDeployPath() + " -name " + appDTO.getName()); + return result.indexOf(appDTO.getName())>0; + } + + /** + * 启动服务 + * @param resources / + * @return / + */ + @Override + public String startServer(Deploy resources) { + Set deploys = resources.getDeploys(); + App app = resources.getApp(); + for (ServerDeploy deploy : deploys) { + StringBuilder sb = new StringBuilder(); + ExecuteShellUtil executeShellUtil = getExecuteShellUtil(deploy.getIp()); + //为了防止重复启动,这里先停止应用 + stopApp(app.getPort(), executeShellUtil); + sb.append("服务器:").append(deploy.getName()).append("
应用:").append(app.getName()); + sendMsg("下发启动命令", MsgType.INFO); + executeShellUtil.execute(app.getStartScript()); + sleep(3); + sendMsg("应用启动中,请耐心等待启动结果,或者稍后手动查看运行状态", MsgType.INFO); + int i = 0; + boolean result = false; + // 由于启动应用需要时间,所以需要循环获取状态,如果超过30次,则认为是启动失败 + while (i++ < count){ + result = checkIsRunningStatus(app.getPort(), executeShellUtil); + if(result){ + break; + } + // 休眠6秒 + sleep(6); + } + sendResultMsg(result, sb); + log.info(sb.toString()); + executeShellUtil.close(); + } + return "执行完毕"; + } + + /** + * 停止服务 + * @param resources / + * @return / + */ + @Override + public String stopServer(Deploy resources) { + Set deploys = resources.getDeploys(); + App app = resources.getApp(); + for (ServerDeploy deploy : deploys) { + StringBuilder sb = new StringBuilder(); + ExecuteShellUtil executeShellUtil = getExecuteShellUtil(deploy.getIp()); + sb.append("服务器:").append(deploy.getName()).append("
应用:").append(app.getName()); + sendMsg("下发停止命令", MsgType.INFO); + //停止应用 + stopApp(app.getPort(), executeShellUtil); + sleep(1); + boolean result = checkIsRunningStatus(app.getPort(), executeShellUtil); + if (result) { + sb.append("
关闭失败!"); + sendMsg(sb.toString(), MsgType.ERROR); + } else { + sb.append("
关闭成功!"); + sendMsg(sb.toString(), MsgType.INFO); + } + log.info(sb.toString()); + executeShellUtil.close(); + } + return "执行完毕"; + } + + @Override + public String serverReduction(DeployHistory resources) { + Long deployId = resources.getDeployId(); + Deploy deployInfo = deployRepository.findById(deployId).orElseGet(Deploy::new); + String deployDate = DateUtil.format(resources.getDeployDate(), DatePattern.PURE_DATETIME_PATTERN); + App app = deployInfo.getApp(); + if (app == null) { + sendMsg("应用信息不存在:" + resources.getAppName(), MsgType.ERROR); + throw new BadRequestException("应用信息不存在:" + resources.getAppName()); + } + String backupPath = app.getBackupPath()+FILE_SEPARATOR; + backupPath += resources.getAppName() + FILE_SEPARATOR + deployDate; + //这个是服务器部署路径 + String deployPath = app.getDeployPath(); + String ip = resources.getIp(); + ExecuteShellUtil executeShellUtil = getExecuteShellUtil(ip); + String msg; + + msg = String.format("登陆到服务器:%s", ip); + log.info(msg); + sendMsg(msg, MsgType.INFO); + sendMsg("停止原来应用", MsgType.INFO); + //停止应用 + stopApp(app.getPort(), executeShellUtil); + //删除原来应用 + sendMsg("删除应用", MsgType.INFO); + executeShellUtil.execute("rm -rf " + deployPath + FILE_SEPARATOR + resources.getAppName()); + //还原应用 + sendMsg("还原应用", MsgType.INFO); + executeShellUtil.execute("cp -r " + backupPath + "/. " + deployPath); + sendMsg("启动应用", MsgType.INFO); + executeShellUtil.execute(app.getStartScript()); + sendMsg("应用启动中,请耐心等待启动结果,或者稍后手动查看启动状态", MsgType.INFO); + int i = 0; + boolean result = false; + // 由于启动应用需要时间,所以需要循环获取状态,如果超过30次,则认为是启动失败 + while (i++ < count){ + result = checkIsRunningStatus(app.getPort(), executeShellUtil); + if(result){ + break; + } + // 休眠6秒 + sleep(6); + } + StringBuilder sb = new StringBuilder(); + sb.append("服务器:").append(ip).append("
应用:").append(resources.getAppName()); + sendResultMsg(result, sb); + executeShellUtil.close(); + return ""; + } + + private ExecuteShellUtil getExecuteShellUtil(String ip) { + ServerDeployDto serverDeployDTO = serverDeployService.findByIp(ip); + if (serverDeployDTO == null) { + sendMsg("IP对应服务器信息不存在:" + ip, MsgType.ERROR); + throw new BadRequestException("IP对应服务器信息不存在:" + ip); + } + return new ExecuteShellUtil(ip, serverDeployDTO.getAccount(), serverDeployDTO.getPassword(),serverDeployDTO.getPort()); + } + + private ScpClientUtil getScpClientUtil(String ip) { + ServerDeployDto serverDeployDTO = serverDeployService.findByIp(ip); + if (serverDeployDTO == null) { + sendMsg("IP对应服务器信息不存在:" + ip, MsgType.ERROR); + throw new BadRequestException("IP对应服务器信息不存在:" + ip); + } + return ScpClientUtil.getInstance(ip, serverDeployDTO.getPort(), serverDeployDTO.getAccount(), serverDeployDTO.getPassword()); + } + + private void sendResultMsg(boolean result, StringBuilder sb) { + if (result) { + sb.append("
启动成功!"); + sendMsg(sb.toString(), MsgType.INFO); + } else { + sb.append("
启动失败!"); + sendMsg(sb.toString(), MsgType.ERROR); + } + } + + @Override + public void download(List queryAll, HttpServletResponse response) throws IOException { + List> list = new ArrayList<>(); + for (DeployDto deployDto : queryAll) { + Map map = new LinkedHashMap<>(); + map.put("应用名称", deployDto.getApp().getName()); + map.put("服务器", deployDto.getServers()); + map.put("部署日期", deployDto.getCreateTime()); + list.add(map); + } + FileUtil.downloadExcel(list, response); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/impl/ServerDeployServiceImpl.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/impl/ServerDeployServiceImpl.java new file mode 100644 index 0000000..9e25eb4 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/impl/ServerDeployServiceImpl.java @@ -0,0 +1,122 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.service.impl; + +import lombok.RequiredArgsConstructor; +import me.zhengjie.modules.mnt.domain.ServerDeploy; +import me.zhengjie.modules.mnt.repository.ServerDeployRepository; +import me.zhengjie.modules.mnt.service.ServerDeployService; +import me.zhengjie.modules.mnt.service.dto.ServerDeployDto; +import me.zhengjie.modules.mnt.service.dto.ServerDeployQueryCriteria; +import me.zhengjie.modules.mnt.service.mapstruct.ServerDeployMapper; +import me.zhengjie.modules.mnt.util.ExecuteShellUtil; +import me.zhengjie.utils.*; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.*; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +@Service +@RequiredArgsConstructor +public class ServerDeployServiceImpl implements ServerDeployService { + + private final ServerDeployRepository serverDeployRepository; + private final ServerDeployMapper serverDeployMapper; + + @Override + public PageResult queryAll(ServerDeployQueryCriteria criteria, Pageable pageable){ + Page page = serverDeployRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable); + return PageUtil.toPage(page.map(serverDeployMapper::toDto)); + } + + @Override + public List queryAll(ServerDeployQueryCriteria criteria){ + return serverDeployMapper.toDto(serverDeployRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder))); + } + + @Override + public ServerDeployDto findById(Long id) { + ServerDeploy server = serverDeployRepository.findById(id).orElseGet(ServerDeploy::new); + ValidationUtil.isNull(server.getId(),"ServerDeploy","id",id); + return serverDeployMapper.toDto(server); + } + + @Override + public ServerDeployDto findByIp(String ip) { + ServerDeploy deploy = serverDeployRepository.findByIp(ip); + return serverDeployMapper.toDto(deploy); + } + + @Override + public Boolean testConnect(ServerDeploy resources) { + ExecuteShellUtil executeShellUtil = null; + try { + executeShellUtil = new ExecuteShellUtil(resources.getIp(), resources.getAccount(), resources.getPassword(),resources.getPort()); + return executeShellUtil.execute("ls")==0; + } catch (Exception e) { + return false; + }finally { + if (executeShellUtil != null) { + executeShellUtil.close(); + } + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void create(ServerDeploy resources) { + serverDeployRepository.save(resources); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(ServerDeploy resources) { + ServerDeploy serverDeploy = serverDeployRepository.findById(resources.getId()).orElseGet(ServerDeploy::new); + ValidationUtil.isNull( serverDeploy.getId(),"ServerDeploy","id",resources.getId()); + serverDeploy.copy(resources); + serverDeployRepository.save(serverDeploy); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Set ids) { + for (Long id : ids) { + serverDeployRepository.deleteById(id); + } + } + + @Override + public void download(List queryAll, HttpServletResponse response) throws IOException { + List> list = new ArrayList<>(); + for (ServerDeployDto deployDto : queryAll) { + Map map = new LinkedHashMap<>(); + map.put("服务器名称", deployDto.getName()); + map.put("服务器IP", deployDto.getIp()); + map.put("端口", deployDto.getPort()); + map.put("账号", deployDto.getAccount()); + map.put("创建日期", deployDto.getCreateTime()); + list.add(map); + } + FileUtil.downloadExcel(list, response); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/mapstruct/AppMapper.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/mapstruct/AppMapper.java new file mode 100644 index 0000000..fc39eeb --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/mapstruct/AppMapper.java @@ -0,0 +1,31 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.service.mapstruct; + +import me.zhengjie.base.BaseMapper; +import me.zhengjie.modules.mnt.domain.App; +import me.zhengjie.modules.mnt.service.dto.AppDto; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +@Mapper(componentModel = "spring",uses = {},unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface AppMapper extends BaseMapper { + +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/mapstruct/DatabaseMapper.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/mapstruct/DatabaseMapper.java new file mode 100644 index 0000000..3cc6e8d --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/mapstruct/DatabaseMapper.java @@ -0,0 +1,31 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.service.mapstruct; + +import me.zhengjie.base.BaseMapper; +import me.zhengjie.modules.mnt.domain.Database; +import me.zhengjie.modules.mnt.service.dto.DatabaseDto; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +@Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface DatabaseMapper extends BaseMapper { + +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/mapstruct/DeployHistoryMapper.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/mapstruct/DeployHistoryMapper.java new file mode 100644 index 0000000..2522ab0 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/mapstruct/DeployHistoryMapper.java @@ -0,0 +1,31 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.service.mapstruct; + +import me.zhengjie.base.BaseMapper; +import me.zhengjie.modules.mnt.domain.DeployHistory; +import me.zhengjie.modules.mnt.service.dto.DeployHistoryDto; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +@Mapper(componentModel = "spring",uses = {},unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface DeployHistoryMapper extends BaseMapper { + +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/mapstruct/DeployMapper.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/mapstruct/DeployMapper.java new file mode 100644 index 0000000..cd3edee --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/mapstruct/DeployMapper.java @@ -0,0 +1,31 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.service.mapstruct; + +import me.zhengjie.base.BaseMapper; +import me.zhengjie.modules.mnt.domain.Deploy; +import me.zhengjie.modules.mnt.service.dto.DeployDto; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +@Mapper(componentModel = "spring",uses = {AppMapper.class, ServerDeployMapper.class},unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface DeployMapper extends BaseMapper { + +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/mapstruct/ServerDeployMapper.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/mapstruct/ServerDeployMapper.java new file mode 100644 index 0000000..960b25b --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/service/mapstruct/ServerDeployMapper.java @@ -0,0 +1,31 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.service.mapstruct; + +import me.zhengjie.base.BaseMapper; +import me.zhengjie.modules.mnt.domain.ServerDeploy; +import me.zhengjie.modules.mnt.service.dto.ServerDeployDto; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** +* @author zhanghouying +* @date 2019-08-24 +*/ +@Mapper(componentModel = "spring",uses = {},unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface ServerDeployMapper extends BaseMapper { + +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/util/DataTypeEnum.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/util/DataTypeEnum.java new file mode 100644 index 0000000..e104b9e --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/util/DataTypeEnum.java @@ -0,0 +1,140 @@ +/* + * << + * Davinci + * == + * Copyright (C) 2016 - 2019 EDP + * == + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * >> + * + */ + +package me.zhengjie.modules.mnt.util; +import lombok.extern.slf4j.Slf4j; + +/** + * @author / + */ +@Slf4j +@SuppressWarnings({"unchecked","all"}) +public enum DataTypeEnum { + + /** mysql */ + MYSQL("mysql", "mysql", "com.mysql.jdbc.Driver", "`", "`", "'", "'"), + + /** oracle */ + ORACLE("oracle", "oracle", "oracle.jdbc.driver.OracleDriver", "\"", "\"", "\"", "\""), + + /** sql server */ + SQLSERVER("sqlserver", "sqlserver", "com.microsoft.sqlserver.jdbc.SQLServerDriver", "\"", "\"", "\"", "\""), + + /** h2 */ + H2("h2", "h2", "org.h2.Driver", "`", "`", "\"", "\""), + + /** phoenix */ + PHOENIX("phoenix", "hbase phoenix", "org.apache.phoenix.jdbc.PhoenixDriver", "", "", "\"", "\""), + + /** mongo */ + MONGODB("mongo", "mongodb", "mongodb.jdbc.MongoDriver", "`", "`", "\"", "\""), + + /** sql4es */ + ELASTICSEARCH("sql4es", "elasticsearch", "nl.anchormen.sql4es.jdbc.ESDriver", "", "", "'", "'"), + + /** presto */ + PRESTO("presto", "presto", "com.facebook.presto.jdbc.PrestoDriver", "", "", "\"", "\""), + + /** moonbox */ + MOONBOX("moonbox", "moonbox", "moonbox.jdbc.MbDriver", "`", "`", "`", "`"), + + /** cassandra */ + CASSANDRA("cassandra", "cassandra", "com.github.adejanovski.cassandra.jdbc.CassandraDriver", "", "", "'", "'"), + + /** click house */ + CLICKHOUSE("clickhouse", "clickhouse", "ru.yandex.clickhouse.ClickHouseDriver", "", "", "\"", "\""), + + /** kylin */ + KYLIN("kylin", "kylin", "org.apache.kylin.jdbc.Driver", "\"", "\"", "\"", "\""), + + /** vertica */ + VERTICA("vertica", "vertica", "com.vertica.jdbc.Driver", "", "", "'", "'"), + + /** sap */ + HANA("sap", "sap hana", "com.sap.db.jdbc.Driver", "", "", "'", "'"), + + /** impala */ + IMPALA("impala", "impala", "com.cloudera.impala.jdbc41.Driver", "", "", "'", "'"); + + private String feature; + private String desc; + private String driver; + private String keywordPrefix; + private String keywordSuffix; + private String aliasPrefix; + private String aliasSuffix; + + private static final String JDBC_URL_PREFIX = "jdbc:"; + + DataTypeEnum(String feature, String desc, String driver, String keywordPrefix, String keywordSuffix, String aliasPrefix, String aliasSuffix) { + this.feature = feature; + this.desc = desc; + this.driver = driver; + this.keywordPrefix = keywordPrefix; + this.keywordSuffix = keywordSuffix; + this.aliasPrefix = aliasPrefix; + this.aliasSuffix = aliasSuffix; + } + + public static DataTypeEnum urlOf(String jdbcUrl) { + String url = jdbcUrl.toLowerCase().trim(); + for (DataTypeEnum dataTypeEnum : values()) { + if (url.startsWith(JDBC_URL_PREFIX + dataTypeEnum.feature)) { + try { + Class aClass = Class.forName(dataTypeEnum.getDriver()); + if (null == aClass) { + throw new RuntimeException("Unable to get driver instance for jdbcUrl: " + jdbcUrl); + } + } catch (ClassNotFoundException e) { + throw new RuntimeException("Unable to get driver instance: " + jdbcUrl); + } + return dataTypeEnum; + } + } + return null; + } + + public String getFeature() { + return feature; + } + + public String getDesc() { + return desc; + } + + public String getDriver() { + return driver; + } + + public String getKeywordPrefix() { + return keywordPrefix; + } + + public String getKeywordSuffix() { + return keywordSuffix; + } + + public String getAliasPrefix() { + return aliasPrefix; + } + + public String getAliasSuffix() { + return aliasSuffix; + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/util/ExecuteShellUtil.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/util/ExecuteShellUtil.java new file mode 100644 index 0000000..a5d5b59 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/util/ExecuteShellUtil.java @@ -0,0 +1,101 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.util; + +import cn.hutool.core.io.IoUtil; +import com.jcraft.jsch.ChannelShell; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import lombok.extern.slf4j.Slf4j; + +import java.io.*; +import java.util.Vector; + +/** + * 执行shell命令 + * + * @author: ZhangHouYing + * @date: 2019/8/10 + */ +@Slf4j +public class ExecuteShellUtil { + + private Vector stdout; + + Session session; + + public ExecuteShellUtil(final String ipAddress, final String username, final String password,int port) { + try { + JSch jsch = new JSch(); + session = jsch.getSession(username, ipAddress, port); + session.setPassword(password); + session.setConfig("StrictHostKeyChecking", "no"); + session.connect(3000); + } catch (Exception e) { + log.error(e.getMessage(),e); + } + + } + + public int execute(final String command) { + int returnCode = 0; + ChannelShell channel = null; + PrintWriter printWriter = null; + BufferedReader input = null; + stdout = new Vector(); + try { + channel = (ChannelShell) session.openChannel("shell"); + channel.connect(); + input = new BufferedReader(new InputStreamReader(channel.getInputStream())); + printWriter = new PrintWriter(channel.getOutputStream()); + printWriter.println(command); + printWriter.println("exit"); + printWriter.flush(); + log.info("The remote command is: "); + String line; + while ((line = input.readLine()) != null) { + stdout.add(line); + System.out.println(line); + } + } catch (Exception e) { + log.error(e.getMessage(),e); + return -1; + }finally { + IoUtil.close(printWriter); + IoUtil.close(input); + if (channel != null) { + channel.disconnect(); + } + } + return returnCode; + } + + public void close(){ + if (session != null) { + session.disconnect(); + } + } + + public String executeForResult(String command) { + execute(command); + StringBuilder sb = new StringBuilder(); + for (String str : stdout) { + sb.append(str); + } + return sb.toString(); + } + +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/util/ScpClientUtil.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/util/ScpClientUtil.java new file mode 100644 index 0000000..7cb83aa --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/util/ScpClientUtil.java @@ -0,0 +1,105 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.util; + +import ch.ethz.ssh2.Connection; +import ch.ethz.ssh2.SCPClient; +import com.google.common.collect.Maps; + +import java.io.IOException; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * 远程执行linux命令 + * @author: ZhangHouYing + * @date: 2019-08-10 10:06 + */ +public class ScpClientUtil { + + static private Map instance = Maps.newHashMap(); + + static synchronized public ScpClientUtil getInstance(String ip, int port, String username, String password) { + if (instance.get(ip) == null) { + instance.put(ip, new ScpClientUtil(ip, port, username, password)); + } + return instance.get(ip); + } + + public ScpClientUtil(String ip, int port, String username, String password) { + this.ip = ip; + this.port = port; + this.username = username; + this.password = password; + } + + public void getFile(String remoteFile, String localTargetDirectory) { + Connection conn = new Connection(ip, port); + try { + conn.connect(); + boolean isAuthenticated = conn.authenticateWithPassword(username, password); + if (!isAuthenticated) { + System.err.println("authentication failed"); + } + SCPClient client = new SCPClient(conn); + client.get(remoteFile, localTargetDirectory); + } catch (IOException ex) { + Logger.getLogger(SCPClient.class.getName()).log(Level.SEVERE, null, ex); + }finally{ + conn.close(); + } + } + + public void putFile(String localFile, String remoteTargetDirectory) { + putFile(localFile, null, remoteTargetDirectory); + } + + public void putFile(String localFile, String remoteFileName, String remoteTargetDirectory) { + putFile(localFile, remoteFileName, remoteTargetDirectory,null); + } + + public void putFile(String localFile, String remoteFileName, String remoteTargetDirectory, String mode) { + Connection conn = new Connection(ip, port); + try { + conn.connect(); + boolean isAuthenticated = conn.authenticateWithPassword(username, password); + if (!isAuthenticated) { + System.err.println("authentication failed"); + } + SCPClient client = new SCPClient(conn); + if ((mode == null) || (mode.length() == 0)) { + mode = "0600"; + } + if (remoteFileName == null) { + client.put(localFile, remoteTargetDirectory); + } else { + client.put(localFile, remoteFileName, remoteTargetDirectory, mode); + } + } catch (IOException ex) { + Logger.getLogger(ScpClientUtil.class.getName()).log(Level.SEVERE, null, ex); + }finally{ + conn.close(); + } + } + + private String ip; + private int port; + private String username; + private String password; + + +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/util/SqlUtils.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/util/SqlUtils.java new file mode 100644 index 0000000..d7e06b1 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/util/SqlUtils.java @@ -0,0 +1,201 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.util; + +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.util.StringUtils; +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; +import me.zhengjie.utils.CloseUtil; +import javax.sql.DataSource; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.sql.*; +import java.util.List; + +/** + * @author / + */ +@Slf4j +public class SqlUtils { + + /** + * 获取数据源 + * + * @param jdbcUrl / + * @param userName / + * @param password / + * @return DataSource + */ + private static DataSource getDataSource(String jdbcUrl, String userName, String password) { + DruidDataSource druidDataSource = new DruidDataSource(); + String className; + try { + className = DriverManager.getDriver(jdbcUrl.trim()).getClass().getName(); + } catch (SQLException e) { + throw new RuntimeException("Get class name error: =" + jdbcUrl); + } + if (StringUtils.isEmpty(className)) { + DataTypeEnum dataTypeEnum = DataTypeEnum.urlOf(jdbcUrl); + if (null == dataTypeEnum) { + throw new RuntimeException("Not supported data type: jdbcUrl=" + jdbcUrl); + } + druidDataSource.setDriverClassName(dataTypeEnum.getDriver()); + } else { + druidDataSource.setDriverClassName(className); + } + + + druidDataSource.setUrl(jdbcUrl); + druidDataSource.setUsername(userName); + druidDataSource.setPassword(password); + // 配置获取连接等待超时的时间 + druidDataSource.setMaxWait(3000); + // 配置初始化大小、最小、最大 + druidDataSource.setInitialSize(1); + druidDataSource.setMinIdle(1); + druidDataSource.setMaxActive(1); + + // 如果链接出现异常则直接判定为失败而不是一直重试 + druidDataSource.setBreakAfterAcquireFailure(true); + try { + druidDataSource.init(); + } catch (SQLException e) { + log.error("Exception during pool initialization", e); + throw new RuntimeException(e.getMessage()); + } + + return druidDataSource; + } + + private static Connection getConnection(String jdbcUrl, String userName, String password) { + DataSource dataSource = getDataSource(jdbcUrl, userName, password); + Connection connection = null; + try { + connection = dataSource.getConnection(); + } catch (Exception ignored) {} + try { + int timeOut = 5; + if (null == connection || connection.isClosed() || !connection.isValid(timeOut)) { + log.info("connection is closed or invalid, retry get connection!"); + connection = dataSource.getConnection(); + } + } catch (Exception e) { + log.error("create connection error, jdbcUrl: {}", jdbcUrl); + throw new RuntimeException("create connection error, jdbcUrl: " + jdbcUrl); + } finally { + CloseUtil.close(connection); + } + return connection; + } + + private static void releaseConnection(Connection connection) { + if (null != connection) { + try { + connection.close(); + } catch (Exception e) { + log.error(e.getMessage(),e); + log.error("connection close error:" + e.getMessage()); + } + } + } + + public static boolean testConnection(String jdbcUrl, String userName, String password) { + Connection connection = null; + try { + connection = getConnection(jdbcUrl, userName, password); + if (null != connection) { + return true; + } + } catch (Exception e) { + log.info("Get connection failed:" + e.getMessage()); + } finally { + releaseConnection(connection); + } + return false; + } + + public static String executeFile(String jdbcUrl, String userName, String password, File sqlFile) { + Connection connection = getConnection(jdbcUrl, userName, password); + try { + batchExecute(connection, readSqlList(sqlFile)); + } catch (Exception e) { + log.error("sql脚本执行发生异常:{}",e.getMessage()); + return e.getMessage(); + }finally { + releaseConnection(connection); + } + return "success"; + } + + + /** + * 批量执行sql + * @param connection / + * @param sqlList / + */ + public static void batchExecute(Connection connection, List sqlList) { + Statement st = null; + try { + st = connection.createStatement(); + for (String sql : sqlList) { + if (sql.endsWith(";")) { + sql = sql.substring(0, sql.length() - 1); + } + st.addBatch(sql); + } + st.executeBatch(); + } catch (SQLException throwables) { + throwables.printStackTrace(); + } finally { + CloseUtil.close(st); + } + } + + /** + * 将文件中的sql语句以;为单位读取到列表中 + * @param sqlFile / + * @return / + * @throws Exception e + */ + private static List readSqlList(File sqlFile) throws Exception { + List sqlList = Lists.newArrayList(); + StringBuilder sb = new StringBuilder(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader( + new FileInputStream(sqlFile), StandardCharsets.UTF_8))) { + String tmp; + while ((tmp = reader.readLine()) != null) { + log.info("line:{}", tmp); + if (tmp.endsWith(";")) { + sb.append(tmp); + sqlList.add(sb.toString()); + sb.delete(0, sb.length()); + } else { + sb.append(tmp); + } + } + if (!"".endsWith(sb.toString().trim())) { + sqlList.add(sb.toString()); + } + } + + return sqlList; + } + +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/websocket/MsgType.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/websocket/MsgType.java new file mode 100644 index 0000000..2fc473d --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/websocket/MsgType.java @@ -0,0 +1,31 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.websocket; + +/** + * @author ZhangHouYing + * @date 2019-08-10 9:56 + */ +public enum MsgType { + /** 连接 */ + CONNECT, + /** 关闭 */ + CLOSE, + /** 信息 */ + INFO, + /** 错误 */ + ERROR +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/websocket/SocketMsg.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/websocket/SocketMsg.java new file mode 100644 index 0000000..ade33a2 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/websocket/SocketMsg.java @@ -0,0 +1,33 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.websocket; + +import lombok.Data; + +/** + * @author ZhangHouYing + * @date 2019-08-10 9:55 + */ +@Data +public class SocketMsg { + private String msg; + private MsgType msgType; + + public SocketMsg(String msg, MsgType msgType) { + this.msg = msg; + this.msgType = msgType; + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/mnt/websocket/WebSocketServer.java b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/websocket/WebSocketServer.java new file mode 100644 index 0000000..000a515 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/mnt/websocket/WebSocketServer.java @@ -0,0 +1,134 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.mnt.websocket; + +import com.alibaba.fastjson.JSON; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import javax.websocket.*; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; +import java.io.IOException; +import java.util.Objects; +import java.util.concurrent.CopyOnWriteArraySet; +/** + * @author ZhangHouYing + * @date 2019-08-10 15:46 + */ +@ServerEndpoint("/webSocket/{sid}") +@Slf4j +@Component +public class WebSocketServer { + + /** + * concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。 + */ + private static final CopyOnWriteArraySet webSocketSet = new CopyOnWriteArraySet(); + + /** + * 与某个客户端的连接会话,需要通过它来给客户端发送数据 + */ + private Session session; + + /** + * 接收sid + */ + private String sid=""; + /** + * 连接建立成功调用的方法 + * */ + @OnOpen + public void onOpen(Session session,@PathParam("sid") String sid) { + this.session = session; + //如果存在就先删除一个,防止重复推送消息 + webSocketSet.removeIf(webSocket -> webSocket.sid.equals(sid)); + webSocketSet.add(this); + this.sid=sid; + } + + /** + * 连接关闭调用的方法 + */ + @OnClose + public void onClose() { + webSocketSet.remove(this); + } + + /** + * 收到客户端消息后调用的方法 + * @param message 客户端发送过来的消息*/ + @OnMessage + public void onMessage(String message, Session session) { + log.info("收到来"+sid+"的信息:"+message); + //群发消息 + for (WebSocketServer item : webSocketSet) { + try { + item.sendMessage(message); + } catch (IOException e) { + log.error(e.getMessage(),e); + } + } + } + + @OnError + public void onError(Session session, Throwable error) { + log.error("发生错误"); + error.printStackTrace(); + } + /** + * 实现服务器主动推送 + */ + private void sendMessage(String message) throws IOException { + this.session.getBasicRemote().sendText(message); + } + + + /** + * 群发自定义消息 + * */ + public static void sendInfo(SocketMsg socketMsg,@PathParam("sid") String sid) throws IOException { + String message = JSON.toJSONString(socketMsg); + log.info("推送消息到"+sid+",推送内容:"+message); + for (WebSocketServer item : webSocketSet) { + try { + //这里可以设定只推送给这个sid的,为null则全部推送 + if(sid==null) { + item.sendMessage(message); + }else if(item.sid.equals(sid)){ + item.sendMessage(message); + } + } catch (IOException ignored) { } + } + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + WebSocketServer that = (WebSocketServer) o; + return Objects.equals(session, that.session) && + Objects.equals(sid, that.sid); + } + + @Override + public int hashCode() { + return Objects.hash(session, sid); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/quartz/config/JobRunner.java b/eladmin-system/src/main/java/me/zhengjie/modules/quartz/config/JobRunner.java new file mode 100644 index 0000000..a5a9c91 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/quartz/config/JobRunner.java @@ -0,0 +1,51 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.quartz.config; + +import lombok.RequiredArgsConstructor; +import me.zhengjie.modules.quartz.domain.QuartzJob; +import me.zhengjie.modules.quartz.repository.QuartzJobRepository; +import me.zhengjie.modules.quartz.utils.QuartzManage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.stereotype.Component; +import java.util.List; + +/** + * @author Zheng Jie + * @date 2019-01-07 + */ +@Component +@RequiredArgsConstructor +public class JobRunner implements ApplicationRunner { + private static final Logger log = LoggerFactory.getLogger(JobRunner.class); + private final QuartzJobRepository quartzJobRepository; + private final QuartzManage quartzManage; + + /** + * 项目启动时重新激活启用的定时任务 + * + * @param applicationArguments / + */ + @Override + public void run(ApplicationArguments applicationArguments) { + List quartzJobs = quartzJobRepository.findByIsPauseIsFalse(); + quartzJobs.forEach(quartzManage::addJob); + log.info("Timing task injection complete"); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/quartz/config/QuartzConfig.java b/eladmin-system/src/main/java/me/zhengjie/modules/quartz/config/QuartzConfig.java new file mode 100644 index 0000000..99f2e50 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/quartz/config/QuartzConfig.java @@ -0,0 +1,52 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.quartz.config; + +import org.quartz.spi.TriggerFiredBundle; +import org.springframework.beans.factory.config.AutowireCapableBeanFactory; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.quartz.AdaptableJobFactory; +import org.springframework.stereotype.Component; + +/** + * 定时任务配置 + * @author / + * @date 2019-01-07 + */ +@Configuration +public class QuartzConfig { + + /** + * 解决Job中注入Spring Bean为null的问题 + */ + @Component("quartzJobFactory") + public static class QuartzJobFactory extends AdaptableJobFactory { + + private final AutowireCapableBeanFactory capableBeanFactory; + + public QuartzJobFactory(AutowireCapableBeanFactory capableBeanFactory) { + this.capableBeanFactory = capableBeanFactory; + } + + @Override + protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { + //调用父类的方法,把Job注入到spring中 + Object jobInstance = super.createJobInstance(bundle); + capableBeanFactory.autowireBean(jobInstance); + return jobInstance; + } + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/quartz/domain/QuartzJob.java b/eladmin-system/src/main/java/me/zhengjie/modules/quartz/domain/QuartzJob.java new file mode 100644 index 0000000..39e11f4 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/quartz/domain/QuartzJob.java @@ -0,0 +1,85 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.quartz.domain; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; +import me.zhengjie.base.BaseEntity; +import javax.persistence.*; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.io.Serializable; + +/** + * @author Zheng Jie + * @date 2019-01-07 + */ +@Getter +@Setter +@Entity +@Table(name = "sys_quartz_job") +public class QuartzJob extends BaseEntity implements Serializable { + + public static final String JOB_KEY = "JOB_KEY"; + + @Id + @Column(name = "job_id") + @NotNull(groups = {Update.class}) + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Transient + @ApiModelProperty(value = "用于子任务唯一标识", hidden = true) + private String uuid; + + @ApiModelProperty(value = "定时器名称") + private String jobName; + + @NotBlank + @ApiModelProperty(value = "Bean名称") + private String beanName; + + @NotBlank + @ApiModelProperty(value = "方法名称") + private String methodName; + + @ApiModelProperty(value = "参数") + private String params; + + @NotBlank + @ApiModelProperty(value = "cron表达式") + private String cronExpression; + + @ApiModelProperty(value = "状态,暂时或启动") + private Boolean isPause = false; + + @ApiModelProperty(value = "负责人") + private String personInCharge; + + @ApiModelProperty(value = "报警邮箱") + private String email; + + @ApiModelProperty(value = "子任务") + private String subTask; + + @ApiModelProperty(value = "失败后暂停") + private Boolean pauseAfterFailure; + + @NotBlank + @ApiModelProperty(value = "备注") + private String description; +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/quartz/domain/QuartzLog.java b/eladmin-system/src/main/java/me/zhengjie/modules/quartz/domain/QuartzLog.java new file mode 100644 index 0000000..4de57ec --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/quartz/domain/QuartzLog.java @@ -0,0 +1,67 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.quartz.domain; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.hibernate.annotations.CreationTimestamp; +import javax.persistence.*; +import java.io.Serializable; +import java.sql.Timestamp; + +/** + * @author Zheng Jie + * @date 2019-01-07 + */ +@Entity +@Data +@Table(name = "sys_quartz_log") +public class QuartzLog implements Serializable { + + @Id + @Column(name = "log_id") + @ApiModelProperty(value = "ID", hidden = true) + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ApiModelProperty(value = "任务名称", hidden = true) + private String jobName; + + @ApiModelProperty(value = "bean名称", hidden = true) + private String beanName; + + @ApiModelProperty(value = "方法名称", hidden = true) + private String methodName; + + @ApiModelProperty(value = "参数", hidden = true) + private String params; + + @ApiModelProperty(value = "cron表达式", hidden = true) + private String cronExpression; + + @ApiModelProperty(value = "状态", hidden = true) + private Boolean isSuccess; + + @ApiModelProperty(value = "异常详情", hidden = true) + private String exceptionDetail; + + @ApiModelProperty(value = "执行耗时", hidden = true) + private Long time; + + @CreationTimestamp + @ApiModelProperty(value = "创建时间", hidden = true) + private Timestamp createTime; +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/quartz/repository/QuartzJobRepository.java b/eladmin-system/src/main/java/me/zhengjie/modules/quartz/repository/QuartzJobRepository.java new file mode 100644 index 0000000..79b222b --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/quartz/repository/QuartzJobRepository.java @@ -0,0 +1,34 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.quartz.repository; + +import me.zhengjie.modules.quartz.domain.QuartzJob; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import java.util.List; + +/** + * @author Zheng Jie + * @date 2019-01-07 + */ +public interface QuartzJobRepository extends JpaRepository, JpaSpecificationExecutor { + + /** + * 查询启用的任务 + * @return List + */ + List findByIsPauseIsFalse(); +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/quartz/repository/QuartzLogRepository.java b/eladmin-system/src/main/java/me/zhengjie/modules/quartz/repository/QuartzLogRepository.java new file mode 100644 index 0000000..db724aa --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/quartz/repository/QuartzLogRepository.java @@ -0,0 +1,28 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.quartz.repository; + +import me.zhengjie.modules.quartz.domain.QuartzLog; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +/** + * @author Zheng Jie + * @date 2019-01-07 + */ +public interface QuartzLogRepository extends JpaRepository, JpaSpecificationExecutor { + +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/quartz/rest/QuartzJobController.java b/eladmin-system/src/main/java/me/zhengjie/modules/quartz/rest/QuartzJobController.java new file mode 100644 index 0000000..e1b5da7 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/quartz/rest/QuartzJobController.java @@ -0,0 +1,141 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.quartz.rest; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import me.zhengjie.annotation.Log; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.modules.quartz.domain.QuartzJob; +import me.zhengjie.modules.quartz.domain.QuartzLog; +import me.zhengjie.modules.quartz.service.QuartzJobService; +import me.zhengjie.modules.quartz.service.dto.JobQueryCriteria; +import me.zhengjie.utils.PageResult; +import me.zhengjie.utils.SpringContextHolder; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Set; + +/** + * @author Zheng Jie + * @date 2019-01-07 + */ +@Slf4j +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/jobs") +@Api(tags = "系统:定时任务管理") +public class QuartzJobController { + + private static final String ENTITY_NAME = "quartzJob"; + private final QuartzJobService quartzJobService; + + @ApiOperation("查询定时任务") + @GetMapping + @PreAuthorize("@el.check('timing:list')") + public ResponseEntity> queryQuartzJob(JobQueryCriteria criteria, Pageable pageable){ + return new ResponseEntity<>(quartzJobService.queryAll(criteria,pageable), HttpStatus.OK); + } + + @ApiOperation("导出任务数据") + @GetMapping(value = "/download") + @PreAuthorize("@el.check('timing:list')") + public void exportQuartzJob(HttpServletResponse response, JobQueryCriteria criteria) throws IOException { + quartzJobService.download(quartzJobService.queryAll(criteria), response); + } + + @ApiOperation("导出日志数据") + @GetMapping(value = "/logs/download") + @PreAuthorize("@el.check('timing:list')") + public void exportQuartzJobLog(HttpServletResponse response, JobQueryCriteria criteria) throws IOException { + quartzJobService.downloadLog(quartzJobService.queryAllLog(criteria), response); + } + + @ApiOperation("查询任务执行日志") + @GetMapping(value = "/logs") + @PreAuthorize("@el.check('timing:list')") + public ResponseEntity> queryQuartzJobLog(JobQueryCriteria criteria, Pageable pageable){ + return new ResponseEntity<>(quartzJobService.queryAllLog(criteria,pageable), HttpStatus.OK); + } + + @Log("新增定时任务") + @ApiOperation("新增定时任务") + @PostMapping + @PreAuthorize("@el.check('timing:add')") + public ResponseEntity createQuartzJob(@Validated @RequestBody QuartzJob resources){ + if (resources.getId() != null) { + throw new BadRequestException("A new "+ ENTITY_NAME +" cannot already have an ID"); + } + // 验证Bean是不是合法的,合法的定时任务 Bean 需要用 @Service 定义 + checkBean(resources.getBeanName()); + quartzJobService.create(resources); + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @Log("修改定时任务") + @ApiOperation("修改定时任务") + @PutMapping + @PreAuthorize("@el.check('timing:edit')") + public ResponseEntity updateQuartzJob(@Validated(QuartzJob.Update.class) @RequestBody QuartzJob resources){ + // 验证Bean是不是合法的,合法的定时任务 Bean 需要用 @Service 定义 + checkBean(resources.getBeanName()); + quartzJobService.update(resources); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @Log("更改定时任务状态") + @ApiOperation("更改定时任务状态") + @PutMapping(value = "/{id}") + @PreAuthorize("@el.check('timing:edit')") + public ResponseEntity updateQuartzJobStatus(@PathVariable Long id){ + quartzJobService.updateIsPause(quartzJobService.findById(id)); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @Log("执行定时任务") + @ApiOperation("执行定时任务") + @PutMapping(value = "/exec/{id}") + @PreAuthorize("@el.check('timing:edit')") + public ResponseEntity executionQuartzJob(@PathVariable Long id){ + quartzJobService.execution(quartzJobService.findById(id)); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @Log("删除定时任务") + @ApiOperation("删除定时任务") + @DeleteMapping + @PreAuthorize("@el.check('timing:del')") + public ResponseEntity deleteQuartzJob(@RequestBody Set ids){ + quartzJobService.delete(ids); + return new ResponseEntity<>(HttpStatus.OK); + } + + private void checkBean(String beanName){ + // 避免调用攻击者可以从SpringContextHolder获得控制jdbcTemplate类 + // 并使用getDeclaredMethod调用jdbcTemplate的queryForMap函数,执行任意sql命令。 + if(!SpringContextHolder.getAllServiceBeanName().contains(beanName)){ + throw new BadRequestException("非法的 Bean,请重新输入!"); + } + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/quartz/service/QuartzJobService.java b/eladmin-system/src/main/java/me/zhengjie/modules/quartz/service/QuartzJobService.java new file mode 100644 index 0000000..2e93c7b --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/quartz/service/QuartzJobService.java @@ -0,0 +1,123 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.quartz.service; + +import me.zhengjie.modules.quartz.domain.QuartzJob; +import me.zhengjie.modules.quartz.domain.QuartzLog; +import me.zhengjie.modules.quartz.service.dto.JobQueryCriteria; +import me.zhengjie.utils.PageResult; +import org.springframework.data.domain.Pageable; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; +import java.util.Set; + +/** + * @author Zheng Jie + * @date 2019-01-07 + */ +public interface QuartzJobService { + + /** + * 分页查询 + * @param criteria 条件 + * @param pageable 分页参数 + * @return / + */ + PageResult queryAll(JobQueryCriteria criteria, Pageable pageable); + + /** + * 查询全部 + * @param criteria 条件 + * @return / + */ + List queryAll(JobQueryCriteria criteria); + + /** + * 分页查询日志 + * @param criteria 条件 + * @param pageable 分页参数 + * @return / + */ + PageResult queryAllLog(JobQueryCriteria criteria, Pageable pageable); + + /** + * 查询全部 + * @param criteria 条件 + * @return / + */ + List queryAllLog(JobQueryCriteria criteria); + + /** + * 创建 + * @param resources / + */ + void create(QuartzJob resources); + + /** + * 编辑 + * @param resources / + */ + void update(QuartzJob resources); + + /** + * 删除任务 + * @param ids / + */ + void delete(Set ids); + + /** + * 根据ID查询 + * @param id ID + * @return / + */ + QuartzJob findById(Long id); + + /** + * 更改定时任务状态 + * @param quartzJob / + */ + void updateIsPause(QuartzJob quartzJob); + + /** + * 立即执行定时任务 + * @param quartzJob / + */ + void execution(QuartzJob quartzJob); + + /** + * 导出定时任务 + * @param queryAll 待导出的数据 + * @param response / + * @throws IOException / + */ + void download(List queryAll, HttpServletResponse response) throws IOException; + + /** + * 导出定时任务日志 + * @param queryAllLog 待导出的数据 + * @param response / + * @throws IOException / + */ + void downloadLog(List queryAllLog, HttpServletResponse response) throws IOException; + + /** + * 执行子任务 + * @param tasks / + * @throws InterruptedException / + */ + void executionSubJob(String[] tasks) throws InterruptedException; +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/quartz/service/dto/JobQueryCriteria.java b/eladmin-system/src/main/java/me/zhengjie/modules/quartz/service/dto/JobQueryCriteria.java new file mode 100644 index 0000000..6cec0f1 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/quartz/service/dto/JobQueryCriteria.java @@ -0,0 +1,38 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.quartz.service.dto; + +import lombok.Data; +import me.zhengjie.annotation.Query; +import java.sql.Timestamp; +import java.util.List; + +/** + * @author Zheng Jie + * @date 2019-6-4 10:33:02 + */ +@Data +public class JobQueryCriteria { + + @Query(type = Query.Type.INNER_LIKE) + private String jobName; + + @Query + private Boolean isSuccess; + + @Query(type = Query.Type.BETWEEN) + private List createTime; +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/quartz/service/impl/QuartzJobServiceImpl.java b/eladmin-system/src/main/java/me/zhengjie/modules/quartz/service/impl/QuartzJobServiceImpl.java new file mode 100644 index 0000000..06341e2 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/quartz/service/impl/QuartzJobServiceImpl.java @@ -0,0 +1,197 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.quartz.service.impl; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import lombok.RequiredArgsConstructor; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.modules.quartz.domain.QuartzJob; +import me.zhengjie.modules.quartz.domain.QuartzLog; +import me.zhengjie.modules.quartz.repository.QuartzJobRepository; +import me.zhengjie.modules.quartz.repository.QuartzLogRepository; +import me.zhengjie.modules.quartz.service.QuartzJobService; +import me.zhengjie.modules.quartz.service.dto.JobQueryCriteria; +import me.zhengjie.modules.quartz.utils.QuartzManage; +import me.zhengjie.utils.*; +import org.quartz.CronExpression; +import org.springframework.data.domain.Pageable; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.*; + +/** + * @author Zheng Jie + * @date 2019-01-07 + */ +@RequiredArgsConstructor +@Service(value = "quartzJobService") +public class QuartzJobServiceImpl implements QuartzJobService { + + private final QuartzJobRepository quartzJobRepository; + private final QuartzLogRepository quartzLogRepository; + private final QuartzManage quartzManage; + private final RedisUtils redisUtils; + + @Override + public PageResult queryAll(JobQueryCriteria criteria, Pageable pageable){ + return PageUtil.toPage(quartzJobRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable)); + } + + @Override + public PageResult queryAllLog(JobQueryCriteria criteria, Pageable pageable){ + return PageUtil.toPage(quartzLogRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable)); + } + + @Override + public List queryAll(JobQueryCriteria criteria) { + return quartzJobRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder)); + } + + @Override + public List queryAllLog(JobQueryCriteria criteria) { + return quartzLogRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder)); + } + + @Override + public QuartzJob findById(Long id) { + QuartzJob quartzJob = quartzJobRepository.findById(id).orElseGet(QuartzJob::new); + ValidationUtil.isNull(quartzJob.getId(),"QuartzJob","id",id); + return quartzJob; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void create(QuartzJob resources) { + if (!CronExpression.isValidExpression(resources.getCronExpression())){ + throw new BadRequestException("cron表达式格式错误"); + } + resources = quartzJobRepository.save(resources); + quartzManage.addJob(resources); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(QuartzJob resources) { + if (!CronExpression.isValidExpression(resources.getCronExpression())){ + throw new BadRequestException("cron表达式格式错误"); + } + if(StringUtils.isNotBlank(resources.getSubTask())){ + List tasks = Arrays.asList(resources.getSubTask().split("[,,]")); + if (tasks.contains(resources.getId().toString())) { + throw new BadRequestException("子任务中不能添加当前任务ID"); + } + } + resources = quartzJobRepository.save(resources); + quartzManage.updateJobCron(resources); + } + + @Override + public void updateIsPause(QuartzJob quartzJob) { + if (quartzJob.getIsPause()) { + quartzManage.resumeJob(quartzJob); + quartzJob.setIsPause(false); + } else { + quartzManage.pauseJob(quartzJob); + quartzJob.setIsPause(true); + } + quartzJobRepository.save(quartzJob); + } + + @Override + public void execution(QuartzJob quartzJob) { + quartzManage.runJobNow(quartzJob); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Set ids) { + for (Long id : ids) { + QuartzJob quartzJob = findById(id); + quartzManage.deleteJob(quartzJob); + quartzJobRepository.delete(quartzJob); + } + } + + @Async + @Override + @Transactional(rollbackFor = Exception.class) + public void executionSubJob(String[] tasks) throws InterruptedException { + for (String id : tasks) { + if (StrUtil.isBlank(id)) { + // 如果是手动清除子任务id,会出现id为空字符串的问题 + continue; + } + QuartzJob quartzJob = findById(Long.parseLong(id)); + // 执行任务 + String uuid = IdUtil.simpleUUID(); + quartzJob.setUuid(uuid); + // 执行任务 + execution(quartzJob); + // 获取执行状态,如果执行失败则停止后面的子任务执行 + Boolean result = (Boolean) redisUtils.get(uuid); + while (result == null) { + // 休眠5秒,再次获取子任务执行情况 + Thread.sleep(5000); + result = (Boolean) redisUtils.get(uuid); + } + if(!result){ + redisUtils.del(uuid); + break; + } + } + } + + @Override + public void download(List quartzJobs, HttpServletResponse response) throws IOException { + List> list = new ArrayList<>(); + for (QuartzJob quartzJob : quartzJobs) { + Map map = new LinkedHashMap<>(); + map.put("任务名称", quartzJob.getJobName()); + map.put("Bean名称", quartzJob.getBeanName()); + map.put("执行方法", quartzJob.getMethodName()); + map.put("参数", quartzJob.getParams()); + map.put("表达式", quartzJob.getCronExpression()); + map.put("状态", quartzJob.getIsPause() ? "暂停中" : "运行中"); + map.put("描述", quartzJob.getDescription()); + map.put("创建日期", quartzJob.getCreateTime()); + list.add(map); + } + FileUtil.downloadExcel(list, response); + } + + @Override + public void downloadLog(List queryAllLog, HttpServletResponse response) throws IOException { + List> list = new ArrayList<>(); + for (QuartzLog quartzLog : queryAllLog) { + Map map = new LinkedHashMap<>(); + map.put("任务名称", quartzLog.getJobName()); + map.put("Bean名称", quartzLog.getBeanName()); + map.put("执行方法", quartzLog.getMethodName()); + map.put("参数", quartzLog.getParams()); + map.put("表达式", quartzLog.getCronExpression()); + map.put("异常详情", quartzLog.getExceptionDetail()); + map.put("耗时/毫秒", quartzLog.getTime()); + map.put("状态", quartzLog.getIsSuccess() ? "成功" : "失败"); + map.put("创建日期", quartzLog.getCreateTime()); + list.add(map); + } + FileUtil.downloadExcel(list, response); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/quartz/task/TestTask.java b/eladmin-system/src/main/java/me/zhengjie/modules/quartz/task/TestTask.java new file mode 100644 index 0000000..4cf3434 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/quartz/task/TestTask.java @@ -0,0 +1,41 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.quartz.task; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +/** + * 测试用 + * @author Zheng Jie + * @date 2019-01-08 + */ +@Slf4j +@Service +public class TestTask { + + public void run(){ + log.info("run 执行成功"); + } + + public void run1(String str){ + log.info("run1 执行成功,参数为: {}" + str); + } + + public void run2(){ + log.info("run2 执行成功"); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/quartz/utils/ExecutionJob.java b/eladmin-system/src/main/java/me/zhengjie/modules/quartz/utils/ExecutionJob.java new file mode 100644 index 0000000..4de1120 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/quartz/utils/ExecutionJob.java @@ -0,0 +1,133 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.quartz.utils; + +import cn.hutool.extra.template.Template; +import cn.hutool.extra.template.TemplateConfig; +import cn.hutool.extra.template.TemplateEngine; +import cn.hutool.extra.template.TemplateUtil; +import me.zhengjie.domain.vo.EmailVo; +import me.zhengjie.modules.quartz.domain.QuartzJob; +import me.zhengjie.modules.quartz.domain.QuartzLog; +import me.zhengjie.modules.quartz.repository.QuartzLogRepository; +import me.zhengjie.modules.quartz.service.QuartzJobService; +import me.zhengjie.service.EmailService; +import me.zhengjie.utils.RedisUtils; +import me.zhengjie.utils.SpringContextHolder; +import me.zhengjie.utils.StringUtils; +import me.zhengjie.utils.ThrowableUtil; +import org.quartz.JobExecutionContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.Async; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.scheduling.quartz.QuartzJobBean; +import java.util.*; +import java.util.concurrent.*; + +/** + * 参考人人开源,... + * @author / + * @date 2019-01-07 + */ +@Async +public class ExecutionJob extends QuartzJobBean { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + // 此处仅供参考,可根据任务执行情况自定义线程池参数 + private final ThreadPoolTaskExecutor executor = SpringContextHolder.getBean("elAsync"); + + @Override + public void executeInternal(JobExecutionContext context) { + // 获取任务 + QuartzJob quartzJob = (QuartzJob) context.getMergedJobDataMap().get(QuartzJob.JOB_KEY); + // 获取spring bean + QuartzLogRepository quartzLogRepository = SpringContextHolder.getBean(QuartzLogRepository.class); + QuartzJobService quartzJobService = SpringContextHolder.getBean(QuartzJobService.class); + RedisUtils redisUtils = SpringContextHolder.getBean(RedisUtils.class); + + String uuid = quartzJob.getUuid(); + + QuartzLog log = new QuartzLog(); + log.setJobName(quartzJob.getJobName()); + log.setBeanName(quartzJob.getBeanName()); + log.setMethodName(quartzJob.getMethodName()); + log.setParams(quartzJob.getParams()); + long startTime = System.currentTimeMillis(); + log.setCronExpression(quartzJob.getCronExpression()); + try { + // 执行任务 + QuartzRunnable task = new QuartzRunnable(quartzJob.getBeanName(), quartzJob.getMethodName(), quartzJob.getParams()); + Future future = executor.submit(task); + future.get(); + long times = System.currentTimeMillis() - startTime; + log.setTime(times); + if(StringUtils.isNotBlank(uuid)) { + redisUtils.set(uuid, true); + } + // 任务状态 + log.setIsSuccess(true); + logger.info("任务执行成功,任务名称:" + quartzJob.getJobName() + ", 执行时间:" + times + "毫秒"); + // 判断是否存在子任务 + if(StringUtils.isNotBlank(quartzJob.getSubTask())){ + String[] tasks = quartzJob.getSubTask().split("[,,]"); + // 执行子任务 + quartzJobService.executionSubJob(tasks); + } + } catch (Exception e) { + if(StringUtils.isNotBlank(uuid)) { + redisUtils.set(uuid, false); + } + logger.error("任务执行失败,任务名称:" + quartzJob.getJobName()); + long times = System.currentTimeMillis() - startTime; + log.setTime(times); + // 任务状态 0:成功 1:失败 + log.setIsSuccess(false); + log.setExceptionDetail(ThrowableUtil.getStackTrace(e)); + // 任务如果失败了则暂停 + if(quartzJob.getPauseAfterFailure() != null && quartzJob.getPauseAfterFailure()){ + quartzJob.setIsPause(false); + //更新状态 + quartzJobService.updateIsPause(quartzJob); + } + if(quartzJob.getEmail() != null){ + EmailService emailService = SpringContextHolder.getBean(EmailService.class); + // 邮箱报警 + if(StringUtils.isNoneBlank(quartzJob.getEmail())){ + EmailVo emailVo = taskAlarm(quartzJob, ThrowableUtil.getStackTrace(e)); + emailService.send(emailVo, emailService.find()); + } + } + } finally { + quartzLogRepository.save(log); + } + } + + private EmailVo taskAlarm(QuartzJob quartzJob, String msg) { + EmailVo emailVo = new EmailVo(); + emailVo.setSubject("定时任务【"+ quartzJob.getJobName() +"】执行失败,请尽快处理!"); + Map data = new HashMap<>(16); + data.put("task", quartzJob); + data.put("msg", msg); + TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("template", TemplateConfig.ResourceMode.CLASSPATH)); + Template template = engine.getTemplate("taskAlarm.ftl"); + emailVo.setContent(template.render(data)); + List emails = Arrays.asList(quartzJob.getEmail().split("[,,]")); + emailVo.setTos(emails); + return emailVo; + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/quartz/utils/QuartzManage.java b/eladmin-system/src/main/java/me/zhengjie/modules/quartz/utils/QuartzManage.java new file mode 100644 index 0000000..e0cf1e0 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/quartz/utils/QuartzManage.java @@ -0,0 +1,174 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.quartz.utils; + +import lombok.extern.slf4j.Slf4j; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.modules.quartz.domain.QuartzJob; +import org.quartz.*; +import org.quartz.impl.triggers.CronTriggerImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import javax.annotation.Resource; +import java.util.Date; +import static org.quartz.TriggerBuilder.newTrigger; + +/** + * @author Zheng Jie + * @date 2019-01-07 + */ +@Slf4j +@Component +public class QuartzManage { + + private static final String JOB_NAME = "TASK_"; + + @Resource + private Scheduler scheduler; + + public void addJob(QuartzJob quartzJob){ + try { + // 构建job信息 + JobDetail jobDetail = JobBuilder.newJob(ExecutionJob.class). + withIdentity(JOB_NAME + quartzJob.getId()).build(); + + //通过触发器名和cron 表达式创建 Trigger + Trigger cronTrigger = newTrigger() + .withIdentity(JOB_NAME + quartzJob.getId()) + .startNow() + .withSchedule(CronScheduleBuilder.cronSchedule(quartzJob.getCronExpression())) + .build(); + + cronTrigger.getJobDataMap().put(QuartzJob.JOB_KEY, quartzJob); + + //重置启动时间 + ((CronTriggerImpl)cronTrigger).setStartTime(new Date()); + + //执行定时任务 + scheduler.scheduleJob(jobDetail,cronTrigger); + + // 暂停任务 + if (quartzJob.getIsPause()) { + pauseJob(quartzJob); + } + } catch (Exception e){ + log.error("创建定时任务失败", e); + throw new BadRequestException("创建定时任务失败"); + } + } + + /** + * 更新job cron表达式 + * @param quartzJob / + */ + public void updateJobCron(QuartzJob quartzJob){ + try { + TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId()); + CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); + // 如果不存在则创建一个定时任务 + if(trigger == null){ + addJob(quartzJob); + trigger = (CronTrigger) scheduler.getTrigger(triggerKey); + } + CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzJob.getCronExpression()); + trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build(); + //重置启动时间 + ((CronTriggerImpl)trigger).setStartTime(new Date()); + trigger.getJobDataMap().put(QuartzJob.JOB_KEY,quartzJob); + + scheduler.rescheduleJob(triggerKey, trigger); + // 暂停任务 + if (quartzJob.getIsPause()) { + pauseJob(quartzJob); + } + } catch (Exception e){ + log.error("更新定时任务失败", e); + throw new BadRequestException("更新定时任务失败"); + } + + } + + /** + * 删除一个job + * @param quartzJob / + */ + public void deleteJob(QuartzJob quartzJob){ + try { + JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId()); + scheduler.pauseJob(jobKey); + scheduler.deleteJob(jobKey); + } catch (Exception e){ + log.error("删除定时任务失败", e); + throw new BadRequestException("删除定时任务失败"); + } + } + + /** + * 恢复一个job + * @param quartzJob / + */ + public void resumeJob(QuartzJob quartzJob){ + try { + TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId()); + CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); + // 如果不存在则创建一个定时任务 + if(trigger == null) { + addJob(quartzJob); + } + JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId()); + scheduler.resumeJob(jobKey); + } catch (Exception e){ + log.error("恢复定时任务失败", e); + throw new BadRequestException("恢复定时任务失败"); + } + } + + /** + * 立即执行job + * @param quartzJob / + */ + public void runJobNow(QuartzJob quartzJob){ + try { + TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId()); + CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); + // 如果不存在则创建一个定时任务 + if(trigger == null) { + addJob(quartzJob); + } + JobDataMap dataMap = new JobDataMap(); + dataMap.put(QuartzJob.JOB_KEY, quartzJob); + JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId()); + scheduler.triggerJob(jobKey,dataMap); + } catch (Exception e){ + log.error("定时任务执行失败", e); + throw new BadRequestException("定时任务执行失败"); + } + } + + /** + * 暂停一个job + * @param quartzJob / + */ + public void pauseJob(QuartzJob quartzJob){ + try { + JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId()); + scheduler.pauseJob(jobKey); + } catch (Exception e){ + log.error("定时任务暂停失败", e); + throw new BadRequestException("定时任务暂停失败"); + } + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/quartz/utils/QuartzRunnable.java b/eladmin-system/src/main/java/me/zhengjie/modules/quartz/utils/QuartzRunnable.java new file mode 100644 index 0000000..31c3a0f --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/quartz/utils/QuartzRunnable.java @@ -0,0 +1,58 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.quartz.utils; + +import lombok.extern.slf4j.Slf4j; +import me.zhengjie.utils.SpringContextHolder; +import org.apache.commons.lang3.StringUtils; +import org.springframework.util.ReflectionUtils; +import java.lang.reflect.Method; +import java.util.concurrent.Callable; + +/** + * 执行定时任务 + * @author / + */ +@Slf4j +public class QuartzRunnable implements Callable { + + private final Object target; + private final Method method; + private final String params; + + QuartzRunnable(String beanName, String methodName, String params) + throws NoSuchMethodException, SecurityException { + this.target = SpringContextHolder.getBean(beanName); + this.params = params; + if (StringUtils.isNotBlank(params)) { + this.method = target.getClass().getDeclaredMethod(methodName, String.class); + } else { + this.method = target.getClass().getDeclaredMethod(methodName); + } + } + + @Override + @SuppressWarnings("all") + public Object call() throws Exception { + ReflectionUtils.makeAccessible(method); + if (StringUtils.isNotBlank(params)) { + method.invoke(target, params); + } else { + method.invoke(target); + } + return null; + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/config/ConfigBeanConfiguration.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/ConfigBeanConfiguration.java new file mode 100644 index 0000000..8cbc88d --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/ConfigBeanConfiguration.java @@ -0,0 +1,43 @@ +/* + * Copyright 2019-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.security.config; + +import me.zhengjie.modules.security.config.bean.LoginProperties; +import me.zhengjie.modules.security.config.bean.SecurityProperties; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @apiNote 配置文件转换Pojo类的 统一配置 类 + * @author: liaojinlong + * @date: 2020/6/10 19:04 + */ +@Configuration +public class ConfigBeanConfiguration { + + @Bean + @ConfigurationProperties(prefix = "login") + public LoginProperties loginProperties() { + return new LoginProperties(); + } + + @Bean + @ConfigurationProperties(prefix = "jwt") + public SecurityProperties securityProperties() { + return new SecurityProperties(); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/config/SpringSecurityConfig.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/SpringSecurityConfig.java new file mode 100644 index 0000000..ad886f3 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/SpringSecurityConfig.java @@ -0,0 +1,188 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.security.config; + +import lombok.RequiredArgsConstructor; +import me.zhengjie.annotation.AnonymousAccess; +import me.zhengjie.modules.security.config.bean.SecurityProperties; +import me.zhengjie.modules.security.security.*; +import me.zhengjie.modules.security.service.OnlineUserService; +import me.zhengjie.modules.security.service.UserCacheManager; +import me.zhengjie.utils.enums.RequestMethodEnum; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.core.GrantedAuthorityDefaults; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.filter.CorsFilter; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.mvc.method.RequestMappingInfo; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; +import java.util.*; + +/** + * @author Zheng Jie + */ +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor +@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) +public class SpringSecurityConfig extends WebSecurityConfigurerAdapter { + + private final TokenProvider tokenProvider; + private final CorsFilter corsFilter; + private final JwtAuthenticationEntryPoint authenticationErrorHandler; + private final JwtAccessDeniedHandler jwtAccessDeniedHandler; + private final ApplicationContext applicationContext; + private final SecurityProperties properties; + private final OnlineUserService onlineUserService; + private final UserCacheManager userCacheManager; + + @Bean + GrantedAuthorityDefaults grantedAuthorityDefaults() { + // 去除 ROLE_ 前缀 + return new GrantedAuthorityDefaults(""); + } + + @Bean + public PasswordEncoder passwordEncoder() { + // 密码加密方式 + return new BCryptPasswordEncoder(); + } + + @Override + protected void configure(HttpSecurity httpSecurity) throws Exception { + // 搜寻匿名标记 url: @AnonymousAccess + RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping) applicationContext.getBean("requestMappingHandlerMapping"); + Map handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods(); + // 获取匿名标记 + Map> anonymousUrls = getAnonymousUrl(handlerMethodMap); + httpSecurity + // 禁用 CSRF + .csrf().disable() + .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class) + // 授权异常 + .exceptionHandling() + .authenticationEntryPoint(authenticationErrorHandler) + .accessDeniedHandler(jwtAccessDeniedHandler) + // 防止iframe 造成跨域 + .and() + .headers() + .frameOptions() + .disable() + // 不创建会话 + .and() + .sessionManagement() + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .and() + .authorizeRequests() + // 静态资源等等 + .antMatchers( + HttpMethod.GET, + "/*.html", + "/**/*.html", + "/**/*.css", + "/**/*.js", + "/webSocket/**" + ).permitAll() + // swagger 文档 + .antMatchers("/swagger-ui.html").permitAll() + .antMatchers("/swagger-resources/**").permitAll() + .antMatchers("/webjars/**").permitAll() + .antMatchers("/*/api-docs").permitAll() + // 文件 + .antMatchers("/avatar/**").permitAll() + .antMatchers("/file/**").permitAll() + // 阿里巴巴 druid + .antMatchers("/druid/**").permitAll() + // 放行OPTIONS请求 + .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() + // 自定义匿名访问所有url放行:允许匿名和带Token访问,细腻化到每个 Request 类型 + // GET + .antMatchers(HttpMethod.GET, anonymousUrls.get(RequestMethodEnum.GET.getType()).toArray(new String[0])).permitAll() + // POST + .antMatchers(HttpMethod.POST, anonymousUrls.get(RequestMethodEnum.POST.getType()).toArray(new String[0])).permitAll() + // PUT + .antMatchers(HttpMethod.PUT, anonymousUrls.get(RequestMethodEnum.PUT.getType()).toArray(new String[0])).permitAll() + // PATCH + .antMatchers(HttpMethod.PATCH, anonymousUrls.get(RequestMethodEnum.PATCH.getType()).toArray(new String[0])).permitAll() + // DELETE + .antMatchers(HttpMethod.DELETE, anonymousUrls.get(RequestMethodEnum.DELETE.getType()).toArray(new String[0])).permitAll() + // 所有类型的接口都放行 + .antMatchers(anonymousUrls.get(RequestMethodEnum.ALL.getType()).toArray(new String[0])).permitAll() + // 所有请求都需要认证 + .anyRequest().authenticated() + .and().apply(securityConfigurerAdapter()); + } + + private TokenConfigurer securityConfigurerAdapter() { + return new TokenConfigurer(tokenProvider, properties, onlineUserService, userCacheManager); + } + + private Map> getAnonymousUrl(Map handlerMethodMap) { + Map> anonymousUrls = new HashMap<>(8); + Set get = new HashSet<>(); + Set post = new HashSet<>(); + Set put = new HashSet<>(); + Set patch = new HashSet<>(); + Set delete = new HashSet<>(); + Set all = new HashSet<>(); + for (Map.Entry infoEntry : handlerMethodMap.entrySet()) { + HandlerMethod handlerMethod = infoEntry.getValue(); + AnonymousAccess anonymousAccess = handlerMethod.getMethodAnnotation(AnonymousAccess.class); + if (null != anonymousAccess) { + List requestMethods = new ArrayList<>(infoEntry.getKey().getMethodsCondition().getMethods()); + RequestMethodEnum request = RequestMethodEnum.find(requestMethods.size() == 0 ? RequestMethodEnum.ALL.getType() : requestMethods.get(0).name()); + switch (Objects.requireNonNull(request)) { + case GET: + get.addAll(infoEntry.getKey().getPatternsCondition().getPatterns()); + break; + case POST: + post.addAll(infoEntry.getKey().getPatternsCondition().getPatterns()); + break; + case PUT: + put.addAll(infoEntry.getKey().getPatternsCondition().getPatterns()); + break; + case PATCH: + patch.addAll(infoEntry.getKey().getPatternsCondition().getPatterns()); + break; + case DELETE: + delete.addAll(infoEntry.getKey().getPatternsCondition().getPatterns()); + break; + default: + all.addAll(infoEntry.getKey().getPatternsCondition().getPatterns()); + break; + } + } + } + anonymousUrls.put(RequestMethodEnum.GET.getType(), get); + anonymousUrls.put(RequestMethodEnum.POST.getType(), post); + anonymousUrls.put(RequestMethodEnum.PUT.getType(), put); + anonymousUrls.put(RequestMethodEnum.PATCH.getType(), patch); + anonymousUrls.put(RequestMethodEnum.DELETE.getType(), delete); + anonymousUrls.put(RequestMethodEnum.ALL.getType(), all); + return anonymousUrls; + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/config/bean/LoginCode.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/bean/LoginCode.java new file mode 100644 index 0000000..fefd252 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/bean/LoginCode.java @@ -0,0 +1,61 @@ +/* + * Copyright 2019-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.security.config.bean; + +import lombok.Data; + +/** + * 登录验证码配置信息 + * + * @author liaojinlong + * @date 2020/6/10 18:53 + */ +@Data +public class LoginCode { + + /** + * 验证码配置 + */ + private LoginCodeEnum codeType; + /** + * 验证码有效期 分钟 + */ + private Long expiration = 2L; + /** + * 验证码内容长度 + */ + private int length = 2; + /** + * 验证码宽度 + */ + private int width = 111; + /** + * 验证码高度 + */ + private int height = 36; + /** + * 验证码字体 + */ + private String fontName; + /** + * 字体大小 + */ + private int fontSize = 25; + + public LoginCodeEnum getCodeType() { + return codeType; + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/config/bean/LoginCodeEnum.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/bean/LoginCodeEnum.java new file mode 100644 index 0000000..685ccbb --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/bean/LoginCodeEnum.java @@ -0,0 +1,43 @@ +/* + * Copyright 2019-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.security.config.bean; + +/** + * 验证码配置枚举 + * + * @author: liaojinlong + * @date: 2020/6/10 17:40 + */ + +public enum LoginCodeEnum { + /** + * 算数 + */ + ARITHMETIC, + /** + * 中文 + */ + CHINESE, + /** + * 中文闪图 + */ + CHINESE_GIF, + /** + * 闪图 + */ + GIF, + SPEC +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/config/bean/LoginProperties.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/bean/LoginProperties.java new file mode 100644 index 0000000..0201a13 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/bean/LoginProperties.java @@ -0,0 +1,125 @@ +/* + * Copyright 2019-2020 the original author or authors. + * + * Licensed under the Apache License, Version loginCode.length.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-loginCode.length.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.security.config.bean; + +import com.wf.captcha.*; +import com.wf.captcha.base.Captcha; +import lombok.Data; +import me.zhengjie.exception.BadConfigurationException; +import me.zhengjie.utils.StringUtils; +import java.awt.*; +import java.util.Objects; + +/** + * 配置文件读取 + * + * @author liaojinlong + * @date loginCode.length0loginCode.length0/6/10 17:loginCode.length6 + */ +@Data +public class LoginProperties { + + /** + * 账号单用户 登录 + */ + private boolean singleLogin = false; + + private LoginCode loginCode; + + public static final String cacheKey = "user-login-cache:"; + + public boolean isSingleLogin() { + return singleLogin; + } + + /** + * 获取验证码生产类 + * + * @return / + */ + public Captcha getCaptcha() { + if (Objects.isNull(loginCode)) { + loginCode = new LoginCode(); + if (Objects.isNull(loginCode.getCodeType())) { + loginCode.setCodeType(LoginCodeEnum.ARITHMETIC); + } + } + return switchCaptcha(loginCode); + } + + /** + * 依据配置信息生产验证码 + * + * @param loginCode 验证码配置信息 + * @return / + */ + private Captcha switchCaptcha(LoginCode loginCode) { + Captcha captcha; + switch (loginCode.getCodeType()) { + case ARITHMETIC: + // 算术类型 https://gitee.com/whvse/EasyCaptcha + captcha = new FixedArithmeticCaptcha(loginCode.getWidth(), loginCode.getHeight()); + // 几位数运算,默认是两位 + captcha.setLen(loginCode.getLength()); + break; + case CHINESE: + captcha = new ChineseCaptcha(loginCode.getWidth(), loginCode.getHeight()); + captcha.setLen(loginCode.getLength()); + break; + case CHINESE_GIF: + captcha = new ChineseGifCaptcha(loginCode.getWidth(), loginCode.getHeight()); + captcha.setLen(loginCode.getLength()); + break; + case GIF: + captcha = new GifCaptcha(loginCode.getWidth(), loginCode.getHeight()); + captcha.setLen(loginCode.getLength()); + break; + case SPEC: + captcha = new SpecCaptcha(loginCode.getWidth(), loginCode.getHeight()); + captcha.setLen(loginCode.getLength()); + break; + default: + throw new BadConfigurationException("验证码配置信息错误!正确配置查看 LoginCodeEnum "); + } + if(StringUtils.isNotBlank(loginCode.getFontName())){ + captcha.setFont(new Font(loginCode.getFontName(), Font.PLAIN, loginCode.getFontSize())); + } + return captcha; + } + + static class FixedArithmeticCaptcha extends ArithmeticCaptcha { + public FixedArithmeticCaptcha(int width, int height) { + super(width, height); + } + + @Override + protected char[] alphas() { + // 生成随机数字和运算符 + int n1 = num(1, 10), n2 = num(1, 10); + int opt = num(3); + + // 计算结果 + int res = new int[]{n1 + n2, n1 - n2, n1 * n2}[opt]; + // 转换为字符运算符 + char optChar = "+-x".charAt(opt); + + this.setArithmeticString(String.format("%s%c%s=?", n1, optChar, n2)); + this.chars = String.valueOf(res); + + return chars.toCharArray(); + } + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/config/bean/SecurityProperties.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/bean/SecurityProperties.java new file mode 100644 index 0000000..16ec3cf --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/bean/SecurityProperties.java @@ -0,0 +1,72 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.security.config.bean; + +import lombok.Data; + +/** + * Jwt参数配置 + * + * @author Zheng Jie + * @date 2019年11月28日 + */ +@Data +public class SecurityProperties { + + /** + * Request Headers : Authorization + */ + private String header; + + /** + * 令牌前缀,最后留个空格 Bearer + */ + private String tokenStartWith; + + /** + * 必须使用最少88位的Base64对该令牌进行编码 + */ + private String base64Secret; + + /** + * 令牌过期时间 此处单位/毫秒 + */ + private Long tokenValidityInSeconds; + + /** + * 在线用户 key,根据 key 查询 redis 中在线用户的数据 + */ + private String onlineKey; + + /** + * 验证码 key + */ + private String codeKey; + + /** + * token 续期检查 + */ + private Long detect; + + /** + * 续期时间 + */ + private Long renew; + + public String getTokenStartWith() { + return tokenStartWith + " "; + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/rest/AuthorizationController.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/rest/AuthorizationController.java new file mode 100644 index 0000000..effb15f --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/rest/AuthorizationController.java @@ -0,0 +1,149 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.security.rest; + +import cn.hutool.core.util.IdUtil; +import com.wf.captcha.base.Captcha; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import me.zhengjie.annotation.Log; +import me.zhengjie.annotation.rest.AnonymousDeleteMapping; +import me.zhengjie.annotation.rest.AnonymousGetMapping; +import me.zhengjie.annotation.rest.AnonymousPostMapping; +import me.zhengjie.config.RsaProperties; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.modules.security.config.bean.LoginCodeEnum; +import me.zhengjie.modules.security.config.bean.LoginProperties; +import me.zhengjie.modules.security.config.bean.SecurityProperties; +import me.zhengjie.modules.security.security.TokenProvider; +import me.zhengjie.modules.security.service.dto.AuthUserDto; +import me.zhengjie.modules.security.service.dto.JwtUserDto; +import me.zhengjie.modules.security.service.OnlineUserService; +import me.zhengjie.utils.RsaUtils; +import me.zhengjie.utils.RedisUtils; +import me.zhengjie.utils.SecurityUtils; +import me.zhengjie.utils.StringUtils; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * @author Zheng Jie + * @date 2018-11-23 + * 授权、根据token获取用户详细信息 + */ +@Slf4j +@RestController +@RequestMapping("/auth") +@RequiredArgsConstructor +@Api(tags = "系统:系统授权接口") +public class AuthorizationController { + private final SecurityProperties properties; + private final RedisUtils redisUtils; + private final OnlineUserService onlineUserService; + private final TokenProvider tokenProvider; + private final AuthenticationManagerBuilder authenticationManagerBuilder; + @Resource + private LoginProperties loginProperties; + + @Log("用户登录") + @ApiOperation("登录授权") + @AnonymousPostMapping(value = "/login") + public ResponseEntity login(@Validated @RequestBody AuthUserDto authUser, HttpServletRequest request) throws Exception { + // 密码解密 + String password = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, authUser.getPassword()); + // 查询验证码 + String code = (String) redisUtils.get(authUser.getUuid()); + // 清除验证码 + redisUtils.del(authUser.getUuid()); + if (StringUtils.isBlank(code)) { + throw new BadRequestException("验证码不存在或已过期"); + } + if (StringUtils.isBlank(authUser.getCode()) || !authUser.getCode().equalsIgnoreCase(code)) { + throw new BadRequestException("验证码错误"); + } + UsernamePasswordAuthenticationToken authenticationToken = + new UsernamePasswordAuthenticationToken(authUser.getUsername(), password); + Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken); + SecurityContextHolder.getContext().setAuthentication(authentication); + // 生成令牌与第三方系统获取令牌方式 + // UserDetails userDetails = userDetailsService.loadUserByUsername(userInfo.getUsername()); + // Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); + // SecurityContextHolder.getContext().setAuthentication(authentication); + String token = tokenProvider.createToken(authentication); + final JwtUserDto jwtUserDto = (JwtUserDto) authentication.getPrincipal(); + // 返回 token 与 用户信息 + Map authInfo = new HashMap(2) {{ + put("token", properties.getTokenStartWith() + token); + put("user", jwtUserDto); + }}; + if (loginProperties.isSingleLogin()) { + // 踢掉之前已经登录的token + onlineUserService.kickOutForUsername(authUser.getUsername()); + } + // 保存在线信息 + onlineUserService.save(jwtUserDto, token, request); + // 返回登录信息 + return ResponseEntity.ok(authInfo); + } + + @ApiOperation("获取用户信息") + @GetMapping(value = "/info") + public ResponseEntity getUserInfo() { + return ResponseEntity.ok(SecurityUtils.getCurrentUser()); + } + + @ApiOperation("获取验证码") + @AnonymousGetMapping(value = "/code") + public ResponseEntity getCode() { + // 获取运算的结果 + Captcha captcha = loginProperties.getCaptcha(); + String uuid = properties.getCodeKey() + IdUtil.simpleUUID(); + //当验证码类型为 arithmetic时且长度 >= 2 时,captcha.text()的结果有几率为浮点型 + String captchaValue = captcha.text(); + if (captcha.getCharType() - 1 == LoginCodeEnum.ARITHMETIC.ordinal() && captchaValue.contains(".")) { + captchaValue = captchaValue.split("\\.")[0]; + } + // 保存 + redisUtils.set(uuid, captchaValue, loginProperties.getLoginCode().getExpiration(), TimeUnit.MINUTES); + // 验证码信息 + Map imgResult = new HashMap(2) {{ + put("img", captcha.toBase64()); + put("uuid", uuid); + }}; + return ResponseEntity.ok(imgResult); + } + + @ApiOperation("退出登录") + @AnonymousDeleteMapping(value = "/logout") + public ResponseEntity logout(HttpServletRequest request) { + onlineUserService.logout(tokenProvider.getToken(request)); + return new ResponseEntity<>(HttpStatus.OK); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/rest/OnlineController.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/rest/OnlineController.java new file mode 100644 index 0000000..2e46859 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/rest/OnlineController.java @@ -0,0 +1,70 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.security.rest; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import me.zhengjie.modules.security.service.OnlineUserService; +import me.zhengjie.modules.security.service.dto.OnlineUserDto; +import me.zhengjie.utils.EncryptUtils; +import me.zhengjie.utils.PageResult; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Set; + +/** + * @author Zheng Jie + */ +@RestController +@RequiredArgsConstructor +@RequestMapping("/auth/online") +@Api(tags = "系统:在线用户管理") +public class OnlineController { + + private final OnlineUserService onlineUserService; + + @ApiOperation("查询在线用户") + @GetMapping + @PreAuthorize("@el.check()") + public ResponseEntity> queryOnlineUser(String username, Pageable pageable){ + return new ResponseEntity<>(onlineUserService.getAll(username, pageable),HttpStatus.OK); + } + + @ApiOperation("导出数据") + @GetMapping(value = "/download") + @PreAuthorize("@el.check()") + public void exportOnlineUser(HttpServletResponse response, String username) throws IOException { + onlineUserService.download(onlineUserService.getAll(username), response); + } + + @ApiOperation("踢出用户") + @DeleteMapping + @PreAuthorize("@el.check()") + public ResponseEntity deleteOnlineUser(@RequestBody Set keys) throws Exception { + for (String token : keys) { + // 解密Key + token = EncryptUtils.desDecrypt(token); + onlineUserService.logout(token); + } + return new ResponseEntity<>(HttpStatus.OK); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAccessDeniedHandler.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAccessDeniedHandler.java new file mode 100644 index 0000000..8b3f8c1 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAccessDeniedHandler.java @@ -0,0 +1,37 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.security.security; + +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * @author Zheng Jie + */ +@Component +public class JwtAccessDeniedHandler implements AccessDeniedHandler { + + @Override + public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException { + //当用户在没有授权的情况下访问受保护的REST资源时,将调用此方法发送403 Forbidden响应 + response.sendError(HttpServletResponse.SC_FORBIDDEN, accessDeniedException.getMessage()); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAuthenticationEntryPoint.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAuthenticationEntryPoint.java new file mode 100644 index 0000000..f881586 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAuthenticationEntryPoint.java @@ -0,0 +1,39 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.security.security; + +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * @author Zheng Jie + */ +@Component +public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { + + @Override + public void commence(HttpServletRequest request, + HttpServletResponse response, + AuthenticationException authException) throws IOException { + // 当用户尝试访问安全的REST资源而不提供任何凭据时,将调用此方法发送401 响应 + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException==null?"Unauthorized":authException.getMessage()); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenConfigurer.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenConfigurer.java new file mode 100644 index 0000000..cff5e1e --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenConfigurer.java @@ -0,0 +1,43 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.security.security; + +import lombok.RequiredArgsConstructor; +import me.zhengjie.modules.security.config.bean.SecurityProperties; +import me.zhengjie.modules.security.service.OnlineUserService; +import me.zhengjie.modules.security.service.UserCacheManager; +import org.springframework.security.config.annotation.SecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.web.DefaultSecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +/** + * @author / + */ +@RequiredArgsConstructor +public class TokenConfigurer extends SecurityConfigurerAdapter { + + private final TokenProvider tokenProvider; + private final SecurityProperties properties; + private final OnlineUserService onlineUserService; + private final UserCacheManager userCacheManager; + + @Override + public void configure(HttpSecurity http) { + TokenFilter customFilter = new TokenFilter(tokenProvider, properties, onlineUserService, userCacheManager); + http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenFilter.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenFilter.java new file mode 100644 index 0000000..7cb8a03 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenFilter.java @@ -0,0 +1,109 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.security.security; + +import cn.hutool.core.util.StrUtil; +import io.jsonwebtoken.ExpiredJwtException; +import me.zhengjie.modules.security.config.bean.SecurityProperties; +import me.zhengjie.modules.security.service.UserCacheManager; +import me.zhengjie.modules.security.service.dto.OnlineUserDto; +import me.zhengjie.modules.security.service.OnlineUserService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.util.StringUtils; +import org.springframework.web.filter.GenericFilterBean; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.util.Objects; + +/** + * @author / + */ +public class TokenFilter extends GenericFilterBean { + private static final Logger log = LoggerFactory.getLogger(TokenFilter.class); + + + private final TokenProvider tokenProvider; + private final SecurityProperties properties; + private final OnlineUserService onlineUserService; + private final UserCacheManager userCacheManager; + + /** + * @param tokenProvider Token + * @param properties JWT + * @param onlineUserService 用户在线 + * @param userCacheManager 用户缓存工具 + */ + public TokenFilter(TokenProvider tokenProvider, SecurityProperties properties, OnlineUserService onlineUserService, UserCacheManager userCacheManager) { + this.properties = properties; + this.onlineUserService = onlineUserService; + this.tokenProvider = tokenProvider; + this.userCacheManager = userCacheManager; + } + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) + throws IOException, ServletException { + HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; + String token = resolveToken(httpServletRequest); + // 对于 Token 为空的不需要去查 Redis + if (StrUtil.isNotBlank(token)) { + OnlineUserDto onlineUserDto = null; + boolean cleanUserCache = false; + try { + String loginKey = tokenProvider.loginKey(token); + onlineUserDto = onlineUserService.getOne(loginKey); + } catch (ExpiredJwtException e) { + log.error(e.getMessage()); + cleanUserCache = true; + } finally { + if (cleanUserCache || Objects.isNull(onlineUserDto)) { + userCacheManager.cleanUserCache(String.valueOf(tokenProvider.getClaims(token).get(TokenProvider.AUTHORITIES_KEY))); + } + } + if (onlineUserDto != null && StringUtils.hasText(token)) { + Authentication authentication = tokenProvider.getAuthentication(token); + SecurityContextHolder.getContext().setAuthentication(authentication); + // Token 续期 + tokenProvider.checkRenewal(token); + } + } + filterChain.doFilter(servletRequest, servletResponse); + } + + /** + * 初步检测Token + * + * @param request / + * @return / + */ + private String resolveToken(HttpServletRequest request) { + String bearerToken = request.getHeader(properties.getHeader()); + if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(properties.getTokenStartWith())) { + // 去掉令牌前缀 + return bearerToken.replace(properties.getTokenStartWith(), ""); + } else { + log.debug("非法Token:{}", bearerToken); + } + return null; + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenProvider.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenProvider.java new file mode 100644 index 0000000..d2db83c --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenProvider.java @@ -0,0 +1,135 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.security.security; + +import cn.hutool.core.date.DateField; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.crypto.digest.DigestUtil; +import io.jsonwebtoken.*; +import io.jsonwebtoken.io.Decoders; +import io.jsonwebtoken.security.Keys; +import lombok.extern.slf4j.Slf4j; +import me.zhengjie.modules.security.config.bean.SecurityProperties; +import me.zhengjie.utils.RedisUtils; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.User; +import org.springframework.stereotype.Component; +import javax.servlet.http.HttpServletRequest; +import java.security.Key; +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * @author / + */ +@Slf4j +@Component +public class TokenProvider implements InitializingBean { + + private final SecurityProperties properties; + private final RedisUtils redisUtils; + public static final String AUTHORITIES_KEY = "user"; + private JwtParser jwtParser; + private JwtBuilder jwtBuilder; + + public TokenProvider(SecurityProperties properties, RedisUtils redisUtils) { + this.properties = properties; + this.redisUtils = redisUtils; + } + + @Override + public void afterPropertiesSet() { + byte[] keyBytes = Decoders.BASE64.decode(properties.getBase64Secret()); + Key key = Keys.hmacShaKeyFor(keyBytes); + jwtParser = Jwts.parserBuilder() + .setSigningKey(key) + .build(); + jwtBuilder = Jwts.builder() + .signWith(key, SignatureAlgorithm.HS512); + } + + /** + * 创建Token 设置永不过期, + * Token 的时间有效性转到Redis 维护 + * + * @param authentication / + * @return / + */ + public String createToken(Authentication authentication) { + return jwtBuilder + // 加入ID确保生成的 Token 都不一致 + .setId(IdUtil.simpleUUID()) + .claim(AUTHORITIES_KEY, authentication.getName()) + .setSubject(authentication.getName()) + .compact(); + } + + /** + * 依据Token 获取鉴权信息 + * + * @param token / + * @return / + */ + Authentication getAuthentication(String token) { + Claims claims = getClaims(token); + User principal = new User(claims.getSubject(), "******", new ArrayList<>()); + return new UsernamePasswordAuthenticationToken(principal, token, new ArrayList<>()); + } + + public Claims getClaims(String token) { + return jwtParser + .parseClaimsJws(token) + .getBody(); + } + + /** + * @param token 需要检查的token + */ + public void checkRenewal(String token) { + // 判断是否续期token,计算token的过期时间 + long time = redisUtils.getExpire(properties.getOnlineKey() + token) * 1000; + Date expireDate = DateUtil.offset(new Date(), DateField.MILLISECOND, (int) time); + // 判断当前时间与过期时间的时间差 + long differ = expireDate.getTime() - System.currentTimeMillis(); + // 如果在续期检查的范围内,则续期 + if (differ <= properties.getDetect()) { + long renew = time + properties.getRenew(); + redisUtils.expire(properties.getOnlineKey() + token, renew, TimeUnit.MILLISECONDS); + } + } + + public String getToken(HttpServletRequest request) { + final String requestHeader = request.getHeader(properties.getHeader()); + if (requestHeader != null && requestHeader.startsWith(properties.getTokenStartWith())) { + return requestHeader.substring(7); + } + return null; + } + + /** + * 获取登录用户RedisKey + * @param token / + * @return key + */ + public String loginKey(String token) { + Claims claims = getClaims(token); + String md5Token = DigestUtil.md5Hex(token); + return properties.getOnlineKey() + claims.getSubject() + "-" + md5Token; + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/service/OnlineUserService.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/service/OnlineUserService.java new file mode 100644 index 0000000..ede7868 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/service/OnlineUserService.java @@ -0,0 +1,149 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.security.service; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import me.zhengjie.modules.security.security.TokenProvider; +import me.zhengjie.utils.PageResult; +import me.zhengjie.modules.security.config.bean.SecurityProperties; +import me.zhengjie.modules.security.service.dto.JwtUserDto; +import me.zhengjie.modules.security.service.dto.OnlineUserDto; +import me.zhengjie.utils.*; +import org.springframework.data.domain.Pageable; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * @author Zheng Jie + * @date 2019年10月26日21:56:27 + */ +@Service +@Slf4j +@AllArgsConstructor +public class OnlineUserService { + + private final SecurityProperties properties; + private final TokenProvider tokenProvider; + private final RedisUtils redisUtils; + + /** + * 保存在线用户信息 + * @param jwtUserDto / + * @param token / + * @param request / + */ + public void save(JwtUserDto jwtUserDto, String token, HttpServletRequest request){ + String dept = jwtUserDto.getUser().getDept().getName(); + String ip = StringUtils.getIp(request); + String browser = StringUtils.getBrowser(request); + String address = StringUtils.getCityInfo(ip); + OnlineUserDto onlineUserDto = null; + try { + onlineUserDto = new OnlineUserDto(jwtUserDto.getUsername(), jwtUserDto.getUser().getNickName(), dept, browser , ip, address, EncryptUtils.desEncrypt(token), new Date()); + } catch (Exception e) { + log.error(e.getMessage(),e); + } + String loginKey = tokenProvider.loginKey(token); + redisUtils.set(loginKey, onlineUserDto, properties.getTokenValidityInSeconds(), TimeUnit.MILLISECONDS); + } + + /** + * 查询全部数据 + * @param username / + * @param pageable / + * @return / + */ + public PageResult getAll(String username, Pageable pageable){ + List onlineUserDtos = getAll(username); + return PageUtil.toPage( + PageUtil.paging(pageable.getPageNumber(),pageable.getPageSize(), onlineUserDtos), + onlineUserDtos.size() + ); + } + + /** + * 查询全部数据,不分页 + * @param username / + * @return / + */ + public List getAll(String username){ + String loginKey = properties.getOnlineKey() + + (StringUtils.isBlank(username) ? "" : "*" + username); + List keys = redisUtils.scan(loginKey + "*"); + Collections.reverse(keys); + List onlineUserDtos = new ArrayList<>(); + for (String key : keys) { + onlineUserDtos.add((OnlineUserDto) redisUtils.get(key)); + } + onlineUserDtos.sort((o1, o2) -> o2.getLoginTime().compareTo(o1.getLoginTime())); + return onlineUserDtos; + } + + /** + * 退出登录 + * @param token / + */ + public void logout(String token) { + String loginKey = tokenProvider.loginKey(token); + redisUtils.del(loginKey); + } + + /** + * 导出 + * @param all / + * @param response / + * @throws IOException / + */ + public void download(List all, HttpServletResponse response) throws IOException { + List> list = new ArrayList<>(); + for (OnlineUserDto user : all) { + Map map = new LinkedHashMap<>(); + map.put("用户名", user.getUserName()); + map.put("部门", user.getDept()); + map.put("登录IP", user.getIp()); + map.put("登录地点", user.getAddress()); + map.put("浏览器", user.getBrowser()); + map.put("登录日期", user.getLoginTime()); + list.add(map); + } + FileUtil.downloadExcel(list, response); + } + + /** + * 查询用户 + * @param key / + * @return / + */ + public OnlineUserDto getOne(String key) { + return (OnlineUserDto)redisUtils.get(key); + } + + /** + * 根据用户名强退用户 + * @param username / + */ + @Async + public void kickOutForUsername(String username) { + String loginKey = properties.getOnlineKey() + username + "*"; + redisUtils.scanDel(loginKey); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/service/UserCacheManager.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/service/UserCacheManager.java new file mode 100644 index 0000000..0808e65 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/service/UserCacheManager.java @@ -0,0 +1,82 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.security.service; + +import cn.hutool.core.util.RandomUtil; +import me.zhengjie.modules.security.config.bean.LoginProperties; +import me.zhengjie.modules.security.service.dto.JwtUserDto; +import me.zhengjie.utils.RedisUtils; +import me.zhengjie.utils.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import javax.annotation.Resource; + +/** + * @author Zheng Jie + * @description 用户缓存管理 + * @date 2022-05-26 + **/ +@Component +public class UserCacheManager { + + @Resource + private RedisUtils redisUtils; + @Value("${login.user-cache.idle-time}") + private long idleTime; + + /** + * 返回用户缓存 + * @param userName 用户名 + * @return JwtUserDto + */ + public JwtUserDto getUserCache(String userName) { + if (StringUtils.isNotEmpty(userName)) { + // 获取数据 + Object obj = redisUtils.get(LoginProperties.cacheKey + userName); + if(obj != null){ + return (JwtUserDto)obj; + } + } + return null; + } + + /** + * 添加缓存到Redis + * @param userName 用户名 + */ + @Async + public void addUserCache(String userName, JwtUserDto user) { + if (StringUtils.isNotEmpty(userName)) { + // 添加数据, 避免数据同时过期 + long time = idleTime + RandomUtil.randomInt(900, 1800); + redisUtils.set(LoginProperties.cacheKey + userName, user, time); + } + } + + /** + * 清理用户缓存信息 + * 用户信息变更时 + * @param userName 用户名 + */ + @Async + public void cleanUserCache(String userName) { + if (StringUtils.isNotEmpty(userName)) { + // 清除数据 + redisUtils.del(LoginProperties.cacheKey + userName); + } + } +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/service/UserDetailsServiceImpl.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/service/UserDetailsServiceImpl.java new file mode 100644 index 0000000..70db5df --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/service/UserDetailsServiceImpl.java @@ -0,0 +1,72 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.security.service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.exception.EntityNotFoundException; +import me.zhengjie.modules.security.service.dto.JwtUserDto; +import me.zhengjie.modules.system.service.DataService; +import me.zhengjie.modules.system.service.RoleService; +import me.zhengjie.modules.system.service.UserService; +import me.zhengjie.modules.system.service.dto.UserLoginDto; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +/** + * @author Zheng Jie + * @date 2018-11-22 + */ +@Slf4j +@RequiredArgsConstructor +@Service("userDetailsService") +public class UserDetailsServiceImpl implements UserDetailsService { + private final UserService userService; + private final RoleService roleService; + private final DataService dataService; + private final UserCacheManager userCacheManager; + + @Override + public JwtUserDto loadUserByUsername(String username) { + JwtUserDto jwtUserDto = userCacheManager.getUserCache(username); + if(jwtUserDto == null){ + UserLoginDto user; + try { + user = userService.getLoginData(username); + } catch (EntityNotFoundException e) { + // SpringSecurity会自动转换UsernameNotFoundException为BadCredentialsException + throw new UsernameNotFoundException(username, e); + } + if (user == null) { + throw new UsernameNotFoundException(""); + } else { + if (!user.getEnabled()) { + throw new BadRequestException("账号未激活!"); + } + jwtUserDto = new JwtUserDto( + user, + dataService.getDeptIds(user), + roleService.mapToGrantedAuthorities(user) + ); + // 添加缓存数据 + userCacheManager.addUserCache(username, jwtUserDto); + } + } + return jwtUserDto; + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/service/dto/AuthUserDto.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/service/dto/AuthUserDto.java new file mode 100644 index 0000000..5219fc5 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/service/dto/AuthUserDto.java @@ -0,0 +1,39 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.security.service.dto; + +import lombok.Getter; +import lombok.Setter; +import javax.validation.constraints.NotBlank; + +/** + * @author Zheng Jie + * @date 2018-11-30 + */ +@Getter +@Setter +public class AuthUserDto { + + @NotBlank + private String username; + + @NotBlank + private String password; + + private String code; + + private String uuid = ""; +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/service/dto/AuthorityDto.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/service/dto/AuthorityDto.java new file mode 100644 index 0000000..888cceb --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/service/dto/AuthorityDto.java @@ -0,0 +1,34 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.security.service.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.security.core.GrantedAuthority; + +/** + * 避免序列化问题 + * @author Zheng Jie + * @date 2018-11-30 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class AuthorityDto implements GrantedAuthority { + + private String authority; +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/service/dto/JwtUserDto.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/service/dto/JwtUserDto.java new file mode 100644 index 0000000..5aa0c45 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/service/dto/JwtUserDto.java @@ -0,0 +1,80 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.security.service.dto; + +import com.alibaba.fastjson.annotation.JSONField; +import lombok.AllArgsConstructor; +import lombok.Getter; +import me.zhengjie.modules.system.service.dto.UserLoginDto; +import org.springframework.security.core.userdetails.UserDetails; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * @author Zheng Jie + * @date 2018-11-23 + */ +@Getter +@AllArgsConstructor +public class JwtUserDto implements UserDetails { + + private final UserLoginDto user; + + private final List dataScopes; + + private final List authorities; + + public Set getRoles() { + return authorities.stream().map(AuthorityDto::getAuthority).collect(Collectors.toSet()); + } + + @Override + @JSONField(serialize = false) + public String getPassword() { + return user.getPassword(); + } + + @Override + @JSONField(serialize = false) + public String getUsername() { + return user.getUsername(); + } + + @JSONField(serialize = false) + @Override + public boolean isAccountNonExpired() { + return true; + } + + @JSONField(serialize = false) + @Override + public boolean isAccountNonLocked() { + return true; + } + + @JSONField(serialize = false) + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + @JSONField(serialize = false) + public boolean isEnabled() { + return user.getEnabled(); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/service/dto/OnlineUserDto.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/service/dto/OnlineUserDto.java new file mode 100644 index 0000000..290ab6d --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/service/dto/OnlineUserDto.java @@ -0,0 +1,73 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.security.service.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import java.util.Date; + +/** + * 在线用户 + * @author Zheng Jie + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class OnlineUserDto { + + /** + * 用户名 + */ + private String userName; + + /** + * 昵称 + */ + private String nickName; + + /** + * 岗位 + */ + private String dept; + + /** + * 浏览器 + */ + private String browser; + + /** + * IP + */ + private String ip; + + /** + * 地址 + */ + private String address; + + /** + * token + */ + private String key; + + /** + * 登录时间 + */ + private Date loginTime; + + +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/Dept.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/Dept.java new file mode 100644 index 0000000..9f28af2 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/Dept.java @@ -0,0 +1,86 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.domain; + +import com.alibaba.fastjson.annotation.JSONField; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; +import me.zhengjie.base.BaseEntity; +import javax.persistence.*; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.Objects; +import java.util.Set; + +/** +* @author Zheng Jie +* @date 2019-03-25 +*/ +@Entity +@Getter +@Setter +@Table(name="sys_dept") +public class Dept extends BaseEntity implements Serializable { + + @Id + @Column(name = "dept_id") + @NotNull(groups = Update.class) + @ApiModelProperty(value = "ID", hidden = true) + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @JSONField(serialize = false) + @ManyToMany(mappedBy = "depts") + @ApiModelProperty(value = "角色") + private Set roles; + + @ApiModelProperty(value = "排序") + private Integer deptSort; + + @NotBlank + @ApiModelProperty(value = "部门名称") + private String name; + + @NotNull + @ApiModelProperty(value = "是否启用") + private Boolean enabled; + + @ApiModelProperty(value = "上级部门") + private Long pid; + + @ApiModelProperty(value = "子节点数目", hidden = true) + private Integer subCount = 0; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Dept dept = (Dept) o; + return Objects.equals(id, dept.id) && + Objects.equals(name, dept.name); + } + + @Override + public int hashCode() { + return Objects.hash(id, name); + } +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/Dict.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/Dict.java new file mode 100644 index 0000000..689cf2e --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/Dict.java @@ -0,0 +1,54 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.domain; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; +import me.zhengjie.base.BaseEntity; +import javax.persistence.*; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.List; + +/** +* @author Zheng Jie +* @date 2019-04-10 +*/ +@Entity +@Getter +@Setter +@Table(name="sys_dict") +public class Dict extends BaseEntity implements Serializable { + + @Id + @Column(name = "dict_id") + @NotNull(groups = Update.class) + @ApiModelProperty(value = "ID", hidden = true) + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @OneToMany(mappedBy = "dict",cascade={CascadeType.PERSIST,CascadeType.REMOVE}) + private List dictDetails; + + @NotBlank + @ApiModelProperty(value = "名称") + private String name; + + @ApiModelProperty(value = "描述") + private String description; +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/DictDetail.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/DictDetail.java new file mode 100644 index 0000000..554dde6 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/DictDetail.java @@ -0,0 +1,56 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.domain; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; +import me.zhengjie.base.BaseEntity; +import javax.persistence.*; +import javax.validation.constraints.NotNull; +import java.io.Serializable; + +/** +* @author Zheng Jie +* @date 2019-04-10 +*/ +@Entity +@Getter +@Setter +@Table(name="sys_dict_detail") +public class DictDetail extends BaseEntity implements Serializable { + + @Id + @Column(name = "detail_id") + @NotNull(groups = Update.class) + @ApiModelProperty(value = "ID", hidden = true) + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @JoinColumn(name = "dict_id") + @ManyToOne(fetch=FetchType.LAZY) + @ApiModelProperty(value = "字典", hidden = true) + private Dict dict; + + @ApiModelProperty(value = "字典标签") + private String label; + + @ApiModelProperty(value = "字典值") + private String value; + + @ApiModelProperty(value = "排序") + private Integer dictSort = 999; +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/Job.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/Job.java new file mode 100644 index 0000000..f2d358a --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/Job.java @@ -0,0 +1,73 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.domain; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; +import me.zhengjie.base.BaseEntity; +import javax.persistence.*; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.Objects; + +/** +* @author Zheng Jie +* @date 2019-03-29 +*/ +@Entity +@Getter +@Setter +@Table(name="sys_job") +public class Job extends BaseEntity implements Serializable { + + @Id + @Column(name = "job_id") + @NotNull(groups = Update.class) + @ApiModelProperty(value = "ID", hidden = true) + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotBlank + @ApiModelProperty(value = "岗位名称") + private String name; + + @NotNull + @ApiModelProperty(value = "岗位排序") + private Long jobSort; + + @NotNull + @ApiModelProperty(value = "是否启用") + private Boolean enabled; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Job job = (Job) o; + return Objects.equals(id, job.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/Menu.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/Menu.java new file mode 100644 index 0000000..b2ea225 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/Menu.java @@ -0,0 +1,110 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.domain; + +import com.alibaba.fastjson.annotation.JSONField; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; +import me.zhengjie.base.BaseEntity; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.Objects; +import java.util.Set; + +/** + * @author Zheng Jie + * @date 2018-12-17 + */ +@Entity +@Getter +@Setter +@Table(name = "sys_menu") +public class Menu extends BaseEntity implements Serializable { + + @Id + @Column(name = "menu_id") + @NotNull(groups = {Update.class}) + @ApiModelProperty(value = "ID", hidden = true) + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @JSONField(serialize = false) + @ManyToMany(mappedBy = "menus") + @ApiModelProperty(value = "菜单角色") + private Set roles; + + @ApiModelProperty(value = "菜单标题") + private String title; + + @Column(name = "name") + @ApiModelProperty(value = "菜单组件名称") + private String componentName; + + @ApiModelProperty(value = "排序") + private Integer menuSort = 999; + + @ApiModelProperty(value = "组件路径") + private String component; + + @ApiModelProperty(value = "路由地址") + private String path; + + @ApiModelProperty(value = "菜单类型,目录、菜单、按钮") + private Integer type; + + @ApiModelProperty(value = "权限标识") + private String permission; + + @ApiModelProperty(value = "菜单图标") + private String icon; + + @Column(columnDefinition = "bit(1) default 0") + @ApiModelProperty(value = "缓存") + private Boolean cache; + + @Column(columnDefinition = "bit(1) default 0") + @ApiModelProperty(value = "是否隐藏") + private Boolean hidden; + + @ApiModelProperty(value = "上级菜单") + private Long pid; + + @ApiModelProperty(value = "子节点数目", hidden = true) + private Integer subCount = 0; + + @ApiModelProperty(value = "外链菜单") + private Boolean iFrame; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Menu menu = (Menu) o; + return Objects.equals(id, menu.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/Purchase.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/Purchase.java new file mode 100644 index 0000000..61c6255 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/Purchase.java @@ -0,0 +1,115 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package me.zhengjie.modules.system.domain; + +import lombok.Data; +import cn.hutool.core.bean.BeanUtil; +import io.swagger.annotations.ApiModelProperty; +import cn.hutool.core.bean.copier.CopyOptions; +import javax.persistence.*; +import javax.validation.constraints.*; +import javax.persistence.Entity; +import javax.persistence.Table; + +import me.zhengjie.base.BaseEntity; +import org.hibernate.annotations.*; +import java.sql.Timestamp; +import java.math.BigDecimal; +import java.io.Serializable; + +/** +* @website https://eladmin.vip +* @description / +* @author author +* +**/ +@Entity +@Data +@Table(name="purchase") +public class Purchase extends BaseEntity implements Serializable { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "`purchase_id`") + @ApiModelProperty(value = "ID") + private Long purchaseId; + + @ApiModelProperty(value = "申请人") + @ManyToOne + @JoinColumn(name = "user_id") + private User user; + + @Column(name = "`reason`") + @ApiModelProperty(value = "申请事由") + private String reason; + + @Column(name = "`fee`") + @ApiModelProperty(value = "申请金额") + private String fee; + + @Column(name = "`product_name`") + @ApiModelProperty(value = "货物/服务名称") + private String productName; + + @Column(name = "`purchase_type`") + @ApiModelProperty(value = "申请类型") + private String purchaseType; + + @Column(name = "`product_info`") + @ApiModelProperty(value = "品牌/规格/参数") + private String productInfo; + + @Column(name = "`product_count`") + @ApiModelProperty(value = "数量") + private BigDecimal productCount; + + @Column(name = "`product_price`") + @ApiModelProperty(value = "预算单价") + private BigDecimal productPrice; + + @Column(name = "`total_fee`") + @ApiModelProperty(value = "预算总价") + private BigDecimal totalFee; + + @Column(name = "`fee_source`") + @ApiModelProperty(value = "资金来源") + private String feeSource; + + @Column(name = "`project_name`") + @ApiModelProperty(value = "项目名称") + private String projectName; + + @Column(name = "`project_no`") + @ApiModelProperty(value = "项目编号") + private String projectNo; + + @Column(name = "`purchase_time`") + @ApiModelProperty(value = "预计采购时间") + private Timestamp purchaseTime; + + @Column(name = "`apply_time`") + @ApiModelProperty(value = "申请时间") + private Timestamp applyTime; + + @Column(name = "`remark`") + @ApiModelProperty(value = "备注") + private String remark; + + + public void copy(Purchase source){ + BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true)); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/Reimburse.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/Reimburse.java new file mode 100644 index 0000000..5fde815 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/Reimburse.java @@ -0,0 +1,105 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package me.zhengjie.modules.system.domain; + +import lombok.Data; +import cn.hutool.core.bean.BeanUtil; +import io.swagger.annotations.ApiModelProperty; +import cn.hutool.core.bean.copier.CopyOptions; +import me.zhengjie.base.BaseEntity; +import me.zhengjie.exception.BadRequestException; + +import javax.persistence.*; +import javax.validation.constraints.*; +import java.sql.Timestamp; +import java.io.Serializable; + +/** +* @website https://eladmin.vip +* @description / +* @author author +* +**/ +@Entity +@Data +@Table(name="reimburse") +public class Reimburse extends BaseEntity implements Serializable { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "`reimburse_id`") + @ApiModelProperty(value = "ID") + private Long reimburseId; + + @ApiModelProperty(value = "申请人") + @ManyToOne + @JoinColumn(name = "user_id") + private User user; + + @Column(name = "`reason`",nullable = false) + @NotBlank + @ApiModelProperty(value = "申请事由") + private String reason; + + @Column(name = "`fee`",nullable = false) + @NotBlank + @ApiModelProperty(value = "申请金额") + private String fee; + + @Column(name = "`reimburse_type`",nullable = false) + @NotBlank + @ApiModelProperty(value = "报销类型") + private String reimburseType; + + @Column(name = "`fee_source`",nullable = false) + @NotBlank + @ApiModelProperty(value = "资金来源") + private String feeSource; + + @Column(name = "`business_fee_type`",nullable = false) + @ApiModelProperty(value = "业务费类型") + private String businessFeeType; + + @Column(name = "`project_name`") + @ApiModelProperty(value = "项目名称") + private String projectName; + + @Column(name = "`project_no`") + @ApiModelProperty(value = "项目编号") + private String projectNo; + + @Column(name = "`project_fee`") + @ApiModelProperty(value = "项目预算") + private String projectFee; + + @Column(name = "`reimburse_time`",nullable = false) + @NotNull + @ApiModelProperty(value = "花费产生时间") + private Timestamp reimburseTime; + + @Column(name = "`apply_time`",nullable = false) + @NotNull + @ApiModelProperty(value = "申请时间") + private Timestamp applyTime; + + @Column(name = "`remark`") + @ApiModelProperty(value = "备注") + private String remark; + + public void copy(Reimburse source){ + BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true)); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/Role.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/Role.java new file mode 100644 index 0000000..d33d75c --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/Role.java @@ -0,0 +1,99 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.domain; + +import com.alibaba.fastjson.annotation.JSONField; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; +import me.zhengjie.base.BaseEntity; +import me.zhengjie.utils.enums.DataScopeEnum; + +import javax.persistence.*; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.Objects; +import java.util.Set; + +/** + * 角色 + * @author Zheng Jie + * @date 2018-11-22 + */ +@Getter +@Setter +@Entity +@Table(name = "sys_role") +public class Role extends BaseEntity implements Serializable { + + @Id + @Column(name = "role_id") + @NotNull(groups = {Update.class}) + @GeneratedValue(strategy = GenerationType.IDENTITY) + @ApiModelProperty(value = "ID", hidden = true) + private Long id; + + @JSONField(serialize = false) + @ManyToMany(mappedBy = "roles") + @ApiModelProperty(value = "用户", hidden = true) + private Set users; + + @ManyToMany(fetch = FetchType.EAGER) + @JoinTable(name = "sys_roles_menus", + joinColumns = {@JoinColumn(name = "role_id",referencedColumnName = "role_id")}, + inverseJoinColumns = {@JoinColumn(name = "menu_id",referencedColumnName = "menu_id")}) + @ApiModelProperty(value = "菜单", hidden = true) + private Set menus; + + @ManyToMany + @JoinTable(name = "sys_roles_depts", + joinColumns = {@JoinColumn(name = "role_id",referencedColumnName = "role_id")}, + inverseJoinColumns = {@JoinColumn(name = "dept_id",referencedColumnName = "dept_id")}) + @ApiModelProperty(value = "部门", hidden = true) + private Set depts; + + @NotBlank + @ApiModelProperty(value = "名称", hidden = true) + private String name; + + @ApiModelProperty(value = "数据权限,全部 、 本级 、 自定义") + private String dataScope = DataScopeEnum.THIS_LEVEL.getValue(); + + @Column(name = "level") + @ApiModelProperty(value = "级别,数值越小,级别越大") + private Integer level = 3; + + @ApiModelProperty(value = "描述") + private String description; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Role role = (Role) o; + return Objects.equals(id, role.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/User.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/User.java new file mode 100644 index 0000000..357836a --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/User.java @@ -0,0 +1,129 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.domain; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; +import me.zhengjie.base.BaseEntity; +import javax.persistence.*; +import javax.validation.constraints.Email; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.Date; +import java.util.Objects; +import java.util.Set; + +/** + * @author Zheng Jie + * @date 2018-11-22 + */ +@Entity +@Getter +@Setter +@Table(name="sys_user") +public class User extends BaseEntity implements Serializable { + + @Id + @Column(name = "user_id") + @NotNull(groups = Update.class) + @GeneratedValue(strategy = GenerationType.IDENTITY) + @ApiModelProperty(value = "ID", hidden = true) + private Long id; + + @ManyToMany(fetch = FetchType.EAGER) + @ApiModelProperty(value = "用户角色") + @JoinTable(name = "sys_users_roles", + joinColumns = {@JoinColumn(name = "user_id",referencedColumnName = "user_id")}, + inverseJoinColumns = {@JoinColumn(name = "role_id",referencedColumnName = "role_id")}) + private Set roles; + + @ManyToMany(fetch = FetchType.EAGER) + @ApiModelProperty(value = "用户岗位") + @JoinTable(name = "sys_users_jobs", + joinColumns = {@JoinColumn(name = "user_id",referencedColumnName = "user_id")}, + inverseJoinColumns = {@JoinColumn(name = "job_id",referencedColumnName = "job_id")}) + private Set jobs; + + @OneToOne + @JoinColumn(name = "dept_id") + @ApiModelProperty(value = "用户部门") + private Dept dept; + + @NotBlank + @Column(unique = true) + @ApiModelProperty(value = "用户名称") + private String username; + + @NotBlank + @ApiModelProperty(value = "用户昵称") + private String nickName; + + @Email + @NotBlank + @ApiModelProperty(value = "邮箱") + private String email; + + @NotBlank + @ApiModelProperty(value = "电话号码") + private String phone; + +// @NotBlank + @ApiModelProperty(value = "电话号码") + private String idNo; + + @ApiModelProperty(value = "用户性别") + private String gender; + + @ApiModelProperty(value = "头像真实名称",hidden = true) + private String avatarName; + + @ApiModelProperty(value = "头像存储的路径", hidden = true) + private String avatarPath; + + @ApiModelProperty(value = "密码") + private String password; + + @NotNull + @ApiModelProperty(value = "是否启用") + private Boolean enabled; + + @ApiModelProperty(value = "是否为admin账号", hidden = true) + private Boolean isAdmin = false; + + @Column(name = "pwd_reset_time") + @ApiModelProperty(value = "最后修改密码的时间", hidden = true) + private Date pwdResetTime; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + User user = (User) o; + return Objects.equals(id, user.id) && + Objects.equals(username, user.username); + } + + @Override + public int hashCode() { + return Objects.hash(id, username); + } +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/UserBank.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/UserBank.java new file mode 100644 index 0000000..06bf995 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/UserBank.java @@ -0,0 +1,73 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package me.zhengjie.modules.system.domain; + +import lombok.Data; +import cn.hutool.core.bean.BeanUtil; +import io.swagger.annotations.ApiModelProperty; +import cn.hutool.core.bean.copier.CopyOptions; +import me.zhengjie.base.BaseEntity; + +import javax.persistence.*; +import javax.validation.constraints.*; +import java.sql.Timestamp; +import java.io.Serializable; + +/** +* @website https://eladmin.vip +* @description / +* @author author +* @date 2023-10-31 +**/ +@Entity +@Data +@Table(name="user_bank") +public class UserBank extends BaseEntity implements Serializable { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "`bank_id`") + @ApiModelProperty(value = "ID") + private Long bankId; + + @ApiModelProperty(value = "所属用户") + @ManyToOne + @JoinColumn(name = "user_id") + private User user; + + @Column(name = "`bank_no`",nullable = false) + @NotBlank + @ApiModelProperty(value = "银行卡号") + private String bankNo; + + @Column(name = "`bank_name`",nullable = false) + @NotBlank + @ApiModelProperty(value = "银行卡所属支行") + private String bankName; + + @Column(name = "`bank_use`") + @ApiModelProperty(value = "是否常用账号") + private String bankUse; + + @Column(name = "`remark`") + @ApiModelProperty(value = "备注") + private String remark; + + + public void copy(UserBank source){ + BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true)); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/vo/MenuMetaVo.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/vo/MenuMetaVo.java new file mode 100644 index 0000000..647baa8 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/vo/MenuMetaVo.java @@ -0,0 +1,35 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.domain.vo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import java.io.Serializable; + +/** + * @author Zheng Jie + * @date 2018-12-20 + */ +@Data +@AllArgsConstructor +public class MenuMetaVo implements Serializable { + + private String title; + + private String icon; + + private Boolean noCache; +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/vo/MenuVo.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/vo/MenuVo.java new file mode 100644 index 0000000..e1efa5a --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/vo/MenuVo.java @@ -0,0 +1,45 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.domain.vo; + +import lombok.Data; +import java.io.Serializable; +import java.util.List; + +/** + * 构建前端路由时用到 + * @author Zheng Jie + * @date 2018-12-20 + */ +@Data +public class MenuVo implements Serializable { + + private String name; + + private String path; + + private Boolean hidden; + + private String redirect; + + private String component; + + private Boolean alwaysShow; + + private MenuMetaVo meta; + + private List children; +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/vo/UserPassVo.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/vo/UserPassVo.java new file mode 100644 index 0000000..fccb100 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/domain/vo/UserPassVo.java @@ -0,0 +1,31 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.domain.vo; + +import lombok.Data; + +/** + * 修改密码的 Vo 类 + * @author Zheng Jie + * @date 2019年7月11日13:59:49 + */ +@Data +public class UserPassVo { + + private String oldPass; + + private String newPass; +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/repository/DeptRepository.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/repository/DeptRepository.java new file mode 100644 index 0000000..9858322 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/repository/DeptRepository.java @@ -0,0 +1,69 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.repository; + +import me.zhengjie.modules.system.domain.Dept; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import java.util.List; +import java.util.Set; + +/** +* @author Zheng Jie +* @date 2019-03-25 +*/ +public interface DeptRepository extends JpaRepository, JpaSpecificationExecutor { + + /** + * 根据 PID 查询 + * @param id pid + * @return / + */ + List findByPid(Long id); + + /** + * 获取顶级部门 + * @return / + */ + List findByPidIsNull(); + + /** + * 根据角色ID 查询 + * @param roleId 角色ID + * @return / + */ + @Query(value = "select d.* from sys_dept d, sys_roles_depts r where " + + "d.dept_id = r.dept_id and r.role_id = ?1", nativeQuery = true) + Set findByRoleId(Long roleId); + + /** + * 判断是否存在子节点 + * @param pid / + * @return / + */ + int countByPid(Long pid); + + /** + * 根据ID更新sub_count + * @param count / + * @param id / + */ + @Modifying + @Query(value = " update sys_dept set sub_count = ?1 where dept_id = ?2 ",nativeQuery = true) + void updateSubCntById(Integer count, Long id); +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/repository/DictDetailRepository.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/repository/DictDetailRepository.java new file mode 100644 index 0000000..d80b970 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/repository/DictDetailRepository.java @@ -0,0 +1,36 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.repository; + +import me.zhengjie.modules.system.domain.DictDetail; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +import java.util.List; + +/** +* @author Zheng Jie +* @date 2019-04-10 +*/ +public interface DictDetailRepository extends JpaRepository, JpaSpecificationExecutor { + + /** + * 根据字典名称查询 + * @param name / + * @return / + */ + List findByDictName(String name); +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/repository/DictRepository.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/repository/DictRepository.java new file mode 100644 index 0000000..f09b6d1 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/repository/DictRepository.java @@ -0,0 +1,43 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.repository; + +import me.zhengjie.modules.system.domain.Dict; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +import java.util.List; +import java.util.Set; + +/** +* @author Zheng Jie +* @date 2019-04-10 +*/ +public interface DictRepository extends JpaRepository, JpaSpecificationExecutor { + + /** + * 删除 + * @param ids / + */ + void deleteByIdIn(Set ids); + + /** + * 查询 + * @param ids / + * @return / + */ + List findByIdIn(Set ids); +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/repository/JobRepository.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/repository/JobRepository.java new file mode 100644 index 0000000..e39ebf0 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/repository/JobRepository.java @@ -0,0 +1,42 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.repository; + +import me.zhengjie.modules.system.domain.Job; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +import java.util.Set; + +/** +* @author Zheng Jie +* @date 2019-03-29 +*/ +public interface JobRepository extends JpaRepository, JpaSpecificationExecutor { + + /** + * 根据名称查询 + * @param name 名称 + * @return / + */ + Job findByName(String name); + + /** + * 根据Id删除 + * @param ids / + */ + void deleteAllByIdIn(Set ids); +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/repository/MenuRepository.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/repository/MenuRepository.java new file mode 100644 index 0000000..75fd7ff --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/repository/MenuRepository.java @@ -0,0 +1,85 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.repository; + +import me.zhengjie.modules.system.domain.Menu; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +/** + * @author Zheng Jie + * @date 2018-12-17 + */ +public interface MenuRepository extends JpaRepository, JpaSpecificationExecutor { + + /** + * 根据菜单标题查询 + * @param title 菜单标题 + * @return / + */ + Menu findByTitle(String title); + + /** + * 根据组件名称查询 + * @param name 组件名称 + * @return / + */ + Menu findByComponentName(String name); + + /** + * 根据菜单的 PID 查询 + * @param pid / + * @return / + */ + List findByPidOrderByMenuSort(long pid); + + /** + * 查询顶级菜单 + * @return / + */ + List findByPidIsNullOrderByMenuSort(); + + /** + * 根据角色ID与菜单类型查询菜单 + * @param roleIds roleIDs + * @param type 类型 + * @return / + */ + @Query(value = "SELECT m.* FROM sys_menu m, sys_roles_menus r WHERE " + + "m.menu_id = r.menu_id AND r.role_id IN ?1 AND type != ?2 order by m.menu_sort asc",nativeQuery = true) + LinkedHashSet findByRoleIdsAndTypeNot(Set roleIds, int type); + + /** + * 获取节点数量 + * @param id / + * @return / + */ + int countByPid(Long id); + + /** + * 更新节点数目 + * @param count / + * @param menuId / + */ + @Modifying + @Query(value = " update sys_menu set sub_count = ?1 where menu_id = ?2 ",nativeQuery = true) + void updateSubCntById(int count, Long menuId); +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/repository/PurchaseRepository.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/repository/PurchaseRepository.java new file mode 100644 index 0000000..ce33a89 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/repository/PurchaseRepository.java @@ -0,0 +1,28 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package me.zhengjie.modules.system.repository; + +import me.zhengjie.modules.system.domain.Purchase; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +/** +* @website https://eladmin.vip +* @author author +* +**/ +public interface PurchaseRepository extends JpaRepository, JpaSpecificationExecutor { +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/repository/ReimburseRepository.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/repository/ReimburseRepository.java new file mode 100644 index 0000000..81f5151 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/repository/ReimburseRepository.java @@ -0,0 +1,28 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package me.zhengjie.modules.system.repository; + +import me.zhengjie.modules.system.domain.Reimburse; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +/** +* @website https://eladmin.vip +* @author author +* +**/ +public interface ReimburseRepository extends JpaRepository, JpaSpecificationExecutor { +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/repository/RoleRepository.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/repository/RoleRepository.java new file mode 100644 index 0000000..8e76cc5 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/repository/RoleRepository.java @@ -0,0 +1,80 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.repository; + +import me.zhengjie.modules.system.domain.Role; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; + +import java.util.List; +import java.util.Set; + +/** + * @author Zheng Jie + * @date 2018-12-03 + */ +public interface RoleRepository extends JpaRepository, JpaSpecificationExecutor { + + /** + * 根据名称查询 + * @param name / + * @return / + */ + Role findByName(String name); + + /** + * 删除多个角色 + * @param ids / + */ + void deleteAllByIdIn(Set ids); + + /** + * 根据用户ID查询 + * @param id 用户ID + * @return / + */ + @Query(value = "SELECT r.* FROM sys_role r, sys_users_roles u WHERE " + + "r.role_id = u.role_id AND u.user_id = ?1",nativeQuery = true) + Set findByUserId(Long id); + + /** + * 解绑角色菜单 + * @param id 菜单ID + */ + @Modifying + @Query(value = "delete from sys_roles_menus where menu_id = ?1",nativeQuery = true) + void untiedMenu(Long id); + + /** + * 根据部门查询 + * @param deptIds / + * @return / + */ + @Query(value = "select count(1) from sys_role r, sys_roles_depts d where " + + "r.role_id = d.role_id and d.dept_id in ?1",nativeQuery = true) + int countByDepts(Set deptIds); + + /** + * 根据菜单Id查询 + * @param menuIds / + * @return / + */ + @Query(value = "SELECT r.* FROM sys_role r, sys_roles_menus m WHERE " + + "r.role_id = m.role_id AND m.menu_id in ?1",nativeQuery = true) + List findInMenuId(List menuIds); +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/repository/UserBankRepository.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/repository/UserBankRepository.java new file mode 100644 index 0000000..4eb7a17 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/repository/UserBankRepository.java @@ -0,0 +1,33 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package me.zhengjie.modules.system.repository; + +import me.zhengjie.modules.system.domain.User; +import me.zhengjie.modules.system.domain.UserBank; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +import java.util.List; + +/** +* @website https://eladmin.vip +* @author author +* @date 2023-10-31 +**/ +public interface UserBankRepository extends JpaRepository, JpaSpecificationExecutor { + + UserBank findByBankUseAndUser(String bankUse, User user); +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/repository/UserRepository.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/repository/UserRepository.java new file mode 100644 index 0000000..f7583b0 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/repository/UserRepository.java @@ -0,0 +1,139 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.repository; + +import me.zhengjie.modules.system.domain.User; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import java.util.Date; +import java.util.List; +import java.util.Set; + +/** + * @author Zheng Jie + * @date 2018-11-22 + */ +public interface UserRepository extends JpaRepository, JpaSpecificationExecutor { + + /** + * 根据用户名查询 + * @param username 用户名 + * @return / + */ + User findByUsername(String username); + + /** + * 根据邮箱查询 + * @param email 邮箱 + * @return / + */ + User findByEmail(String email); + + /** + * 根据手机号查询 + * @param phone 手机号 + * @return / + */ + User findByPhone(String phone); + + /** + * 修改密码 + * @param username 用户名 + * @param pass 密码 + * @param lastPasswordResetTime / + */ + @Modifying + @Query(value = "update sys_user set password = ?2 , pwd_reset_time = ?3 where username = ?1",nativeQuery = true) + void updatePass(String username, String pass, Date lastPasswordResetTime); + + /** + * 修改邮箱 + * @param username 用户名 + * @param email 邮箱 + */ + @Modifying + @Query(value = "update sys_user set email = ?2 where username = ?1",nativeQuery = true) + void updateEmail(String username, String email); + + /** + * 根据角色查询用户 + * @param roleId / + * @return / + */ + @Query(value = "SELECT u.* FROM sys_user u, sys_users_roles r WHERE" + + " u.user_id = r.user_id AND r.role_id = ?1", nativeQuery = true) + List findByRoleId(Long roleId); + + /** + * 根据角色中的部门查询 + * @param deptId / + * @return / + */ + @Query(value = "SELECT u.* FROM sys_user u, sys_users_roles r, sys_roles_depts d WHERE " + + "u.user_id = r.user_id AND r.role_id = d.role_id AND d.dept_id = ?1 group by u.user_id", nativeQuery = true) + List findByRoleDeptId(Long deptId); + + /** + * 根据菜单查询 + * @param id 菜单ID + * @return / + */ + @Query(value = "SELECT u.* FROM sys_user u, sys_users_roles ur, sys_roles_menus rm WHERE\n" + + "u.user_id = ur.user_id AND ur.role_id = rm.role_id AND rm.menu_id = ?1 group by u.user_id", nativeQuery = true) + List findByMenuId(Long id); + + /** + * 根据Id删除 + * @param ids / + */ + void deleteAllByIdIn(Set ids); + + /** + * 根据岗位查询 + * @param ids / + * @return / + */ + @Query(value = "SELECT count(1) FROM sys_user u, sys_users_jobs j WHERE u.user_id = j.user_id AND j.job_id IN ?1", nativeQuery = true) + int countByJobs(Set ids); + + /** + * 根据部门查询 + * @param deptIds / + * @return / + */ + @Query(value = "SELECT count(1) FROM sys_user u WHERE u.dept_id IN ?1", nativeQuery = true) + int countByDepts(Set deptIds); + + /** + * 根据角色查询 + * @param ids / + * @return / + */ + @Query(value = "SELECT count(1) FROM sys_user u, sys_users_roles r WHERE " + + "u.user_id = r.user_id AND r.role_id in ?1", nativeQuery = true) + int countByRoles(Set ids); + + /** + * 重置密码 + * @param ids 、 + * @param pwd 、 + */ + @Modifying + @Query(value = "update sys_user set password = ?2 where user_id in ?1",nativeQuery = true) + void resetPwd(Set ids, String pwd); +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/DeptController.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/DeptController.java new file mode 100644 index 0000000..ebeb260 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/DeptController.java @@ -0,0 +1,129 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.rest; + +import cn.hutool.core.collection.CollectionUtil; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import me.zhengjie.annotation.Log; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.modules.system.domain.Dept; +import me.zhengjie.modules.system.service.DeptService; +import me.zhengjie.modules.system.service.dto.DeptDto; +import me.zhengjie.modules.system.service.dto.DeptQueryCriteria; +import me.zhengjie.utils.PageResult; +import me.zhengjie.utils.PageUtil; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import javax.servlet.http.HttpServletResponse; +import java.util.*; +import java.util.stream.Collectors; + +/** +* @author Zheng Jie +* @date 2019-03-25 +*/ +@RestController +@RequiredArgsConstructor +@Api(tags = "系统:部门管理") +@RequestMapping("/api/dept") +public class DeptController { + + private final DeptService deptService; + private static final String ENTITY_NAME = "dept"; + + @ApiOperation("导出部门数据") + @GetMapping(value = "/download") + @PreAuthorize("@el.check('dept:list')") + public void exportDept(HttpServletResponse response, DeptQueryCriteria criteria) throws Exception { + deptService.download(deptService.queryAll(criteria, false), response); + } + + @ApiOperation("查询部门") + @GetMapping + @PreAuthorize("@el.check('user:list','dept:list')") + public ResponseEntity> queryDept(DeptQueryCriteria criteria) throws Exception { + List depts = deptService.queryAll(criteria, true); + return new ResponseEntity<>(PageUtil.toPage(depts, depts.size()),HttpStatus.OK); + } + + @ApiOperation("查询部门:根据ID获取同级与上级数据") + @PostMapping("/superior") + @PreAuthorize("@el.check('user:list','dept:list')") + public ResponseEntity getDeptSuperior(@RequestBody List ids, + @RequestParam(defaultValue = "false") Boolean exclude) { + Set deptSet = new LinkedHashSet<>(); + for (Long id : ids) { + DeptDto deptDto = deptService.findById(id); + List depts = deptService.getSuperior(deptDto, new ArrayList<>()); + if(exclude){ + for (DeptDto dept : depts) { + if(dept.getId().equals(deptDto.getPid())) { + dept.setSubCount(dept.getSubCount() - 1); + } + } + // 编辑部门时不显示自己以及自己下级的数据,避免出现PID数据环形问题 + depts = depts.stream().filter(i -> !ids.contains(i.getId())).collect(Collectors.toList()); + } + deptSet.addAll(depts); + } + return new ResponseEntity<>(deptService.buildTree(new ArrayList<>(deptSet)),HttpStatus.OK); + } + + @Log("新增部门") + @ApiOperation("新增部门") + @PostMapping + @PreAuthorize("@el.check('dept:add')") + public ResponseEntity createDept(@Validated @RequestBody Dept resources){ + if (resources.getId() != null) { + throw new BadRequestException("A new "+ ENTITY_NAME +" cannot already have an ID"); + } + deptService.create(resources); + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @Log("修改部门") + @ApiOperation("修改部门") + @PutMapping + @PreAuthorize("@el.check('dept:edit')") + public ResponseEntity updateDept(@Validated(Dept.Update.class) @RequestBody Dept resources){ + deptService.update(resources); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @Log("删除部门") + @ApiOperation("删除部门") + @DeleteMapping + @PreAuthorize("@el.check('dept:del')") + public ResponseEntity deleteDept(@RequestBody Set ids){ + Set deptDtos = new HashSet<>(); + for (Long id : ids) { + List deptList = deptService.findByPid(id); + deptDtos.add(deptService.findById(id)); + if(CollectionUtil.isNotEmpty(deptList)){ + deptDtos = deptService.getDeleteDepts(deptList, deptDtos); + } + } + // 验证是否被角色或用户关联 + deptService.verification(deptDtos); + deptService.delete(deptDtos); + return new ResponseEntity<>(HttpStatus.OK); + } +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/DictController.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/DictController.java new file mode 100644 index 0000000..6161f54 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/DictController.java @@ -0,0 +1,103 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.rest; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import me.zhengjie.annotation.Log; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.modules.system.domain.Dict; +import me.zhengjie.modules.system.service.DictService; +import me.zhengjie.modules.system.service.dto.DictDto; +import me.zhengjie.modules.system.service.dto.DictQueryCriteria; +import me.zhengjie.utils.PageResult; +import me.zhengjie.utils.PageUtil; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; +import java.util.Set; + +/** +* @author Zheng Jie +* @date 2019-04-10 +*/ +@RestController +@RequiredArgsConstructor +@Api(tags = "系统:字典管理") +@RequestMapping("/api/dict") +public class DictController { + + private final DictService dictService; + private static final String ENTITY_NAME = "dict"; + + @ApiOperation("导出字典数据") + @GetMapping(value = "/download") + @PreAuthorize("@el.check('dict:list')") + public void exportDict(HttpServletResponse response, DictQueryCriteria criteria) throws IOException { + dictService.download(dictService.queryAll(criteria), response); + } + + @ApiOperation("查询字典") + @GetMapping(value = "/all") + @PreAuthorize("@el.check('dict:list')") + public ResponseEntity> queryAllDict(){ + return new ResponseEntity<>(dictService.queryAll(new DictQueryCriteria()),HttpStatus.OK); + } + + @ApiOperation("查询字典") + @GetMapping + @PreAuthorize("@el.check('dict:list')") + public ResponseEntity> queryDict(DictQueryCriteria resources, Pageable pageable){ + return new ResponseEntity<>(dictService.queryAll(resources,pageable),HttpStatus.OK); + } + + @Log("新增字典") + @ApiOperation("新增字典") + @PostMapping + @PreAuthorize("@el.check('dict:add')") + public ResponseEntity createDict(@Validated @RequestBody Dict resources){ + if (resources.getId() != null) { + throw new BadRequestException("A new "+ ENTITY_NAME +" cannot already have an ID"); + } + dictService.create(resources); + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @Log("修改字典") + @ApiOperation("修改字典") + @PutMapping + @PreAuthorize("@el.check('dict:edit')") + public ResponseEntity updateDict(@Validated(Dict.Update.class) @RequestBody Dict resources){ + dictService.update(resources); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @Log("删除字典") + @ApiOperation("删除字典") + @DeleteMapping + @PreAuthorize("@el.check('dict:del')") + public ResponseEntity deleteDict(@RequestBody Set ids){ + dictService.delete(ids); + return new ResponseEntity<>(HttpStatus.OK); + } +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/DictDetailController.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/DictDetailController.java new file mode 100644 index 0000000..3cea7a1 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/DictDetailController.java @@ -0,0 +1,100 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.rest; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import me.zhengjie.annotation.Log; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.modules.system.domain.DictDetail; +import me.zhengjie.modules.system.service.DictDetailService; +import me.zhengjie.modules.system.service.dto.DictDetailDto; +import me.zhengjie.modules.system.service.dto.DictDetailQueryCriteria; +import me.zhengjie.utils.PageResult; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.web.PageableDefault; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** +* @author Zheng Jie +* @date 2019-04-10 +*/ +@RestController +@RequiredArgsConstructor +@Api(tags = "系统:字典详情管理") +@RequestMapping("/api/dictDetail") +public class DictDetailController { + + private final DictDetailService dictDetailService; + private static final String ENTITY_NAME = "dictDetail"; + + @ApiOperation("查询字典详情") + @GetMapping + public ResponseEntity> queryDictDetail(DictDetailQueryCriteria criteria, + @PageableDefault(sort = {"dictSort"}, direction = Sort.Direction.ASC) Pageable pageable){ + return new ResponseEntity<>(dictDetailService.queryAll(criteria,pageable),HttpStatus.OK); + } + + @ApiOperation("查询多个字典详情") + @GetMapping(value = "/map") + public ResponseEntity getDictDetailMaps(@RequestParam String dictName){ + String[] names = dictName.split("[,,]"); + Map> dictMap = new HashMap<>(16); + for (String name : names) { + dictMap.put(name, dictDetailService.getDictByName(name)); + } + return new ResponseEntity<>(dictMap, HttpStatus.OK); + } + + @Log("新增字典详情") + @ApiOperation("新增字典详情") + @PostMapping + @PreAuthorize("@el.check('dict:add')") + public ResponseEntity createDictDetail(@Validated @RequestBody DictDetail resources){ + if (resources.getId() != null) { + throw new BadRequestException("A new "+ ENTITY_NAME +" cannot already have an ID"); + } + dictDetailService.create(resources); + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @Log("修改字典详情") + @ApiOperation("修改字典详情") + @PutMapping + @PreAuthorize("@el.check('dict:edit')") + public ResponseEntity updateDictDetail(@Validated(DictDetail.Update.class) @RequestBody DictDetail resources){ + dictDetailService.update(resources); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @Log("删除字典详情") + @ApiOperation("删除字典详情") + @DeleteMapping(value = "/{id}") + @PreAuthorize("@el.check('dict:del')") + public ResponseEntity deleteDictDetail(@PathVariable Long id){ + dictDetailService.delete(id); + return new ResponseEntity<>(HttpStatus.OK); + } +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/JobController.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/JobController.java new file mode 100644 index 0000000..fa10176 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/JobController.java @@ -0,0 +1,96 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.rest; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import me.zhengjie.annotation.Log; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.modules.system.domain.Job; +import me.zhengjie.modules.system.service.JobService; +import me.zhengjie.modules.system.service.dto.JobDto; +import me.zhengjie.modules.system.service.dto.JobQueryCriteria; +import me.zhengjie.utils.PageResult; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Set; + +/** +* @author Zheng Jie +* @date 2019-03-29 +*/ +@RestController +@RequiredArgsConstructor +@Api(tags = "系统:岗位管理") +@RequestMapping("/api/job") +public class JobController { + + private final JobService jobService; + private static final String ENTITY_NAME = "job"; + + @ApiOperation("导出岗位数据") + @GetMapping(value = "/download") + @PreAuthorize("@el.check('job:list')") + public void exportJob(HttpServletResponse response, JobQueryCriteria criteria) throws IOException { + jobService.download(jobService.queryAll(criteria), response); + } + + @ApiOperation("查询岗位") + @GetMapping + @PreAuthorize("@el.check('job:list','user:list')") + public ResponseEntity> queryJob(JobQueryCriteria criteria, Pageable pageable){ + return new ResponseEntity<>(jobService.queryAll(criteria, pageable),HttpStatus.OK); + } + + @Log("新增岗位") + @ApiOperation("新增岗位") + @PostMapping + @PreAuthorize("@el.check('job:add')") + public ResponseEntity createJob(@Validated @RequestBody Job resources){ + if (resources.getId() != null) { + throw new BadRequestException("A new "+ ENTITY_NAME +" cannot already have an ID"); + } + jobService.create(resources); + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @Log("修改岗位") + @ApiOperation("修改岗位") + @PutMapping + @PreAuthorize("@el.check('job:edit')") + public ResponseEntity updateJob(@Validated(Job.Update.class) @RequestBody Job resources){ + jobService.update(resources); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @Log("删除岗位") + @ApiOperation("删除岗位") + @DeleteMapping + @PreAuthorize("@el.check('job:del')") + public ResponseEntity deleteJob(@RequestBody Set ids){ + // 验证是否被用户关联 + jobService.verification(ids); + jobService.delete(ids); + return new ResponseEntity<>(HttpStatus.OK); + } +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/LimitController.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/LimitController.java new file mode 100644 index 0000000..02db642 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/LimitController.java @@ -0,0 +1,47 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.rest; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import me.zhengjie.annotation.Limit; +import me.zhengjie.annotation.rest.AnonymousGetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author / + * 接口限流测试类 + */ +@RestController +@RequestMapping("/api/limit") +@Api(tags = "系统:限流测试管理") +public class LimitController { + + private static final AtomicInteger ATOMIC_INTEGER = new AtomicInteger(); + + /** + * 测试限流注解,下面配置说明该接口 60秒内最多只能访问 10次,保存到redis的键名为 limit_test, + */ + @AnonymousGetMapping + @ApiOperation("测试") + @Limit(key = "test", period = 60, count = 10, name = "testLimit", prefix = "limit") + public int testLimit() { + return ATOMIC_INTEGER.incrementAndGet(); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/MenuController.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/MenuController.java new file mode 100644 index 0000000..18084f1 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/MenuController.java @@ -0,0 +1,157 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.rest; + +import cn.hutool.core.collection.CollectionUtil; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import me.zhengjie.annotation.Log; +import me.zhengjie.modules.system.domain.Menu; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.modules.system.domain.vo.MenuVo; +import me.zhengjie.modules.system.service.MenuService; +import me.zhengjie.modules.system.service.dto.DeptDto; +import me.zhengjie.modules.system.service.dto.MenuDto; +import me.zhengjie.modules.system.service.dto.MenuQueryCriteria; +import me.zhengjie.modules.system.service.mapstruct.MenuMapper; +import me.zhengjie.utils.PageResult; +import me.zhengjie.utils.PageUtil; +import me.zhengjie.utils.SecurityUtils; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import javax.servlet.http.HttpServletResponse; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author Zheng Jie + * @date 2018-12-03 + */ +@RestController +@RequiredArgsConstructor +@Api(tags = "系统:菜单管理") +@RequestMapping("/api/menus") +public class MenuController { + + private final MenuService menuService; + private final MenuMapper menuMapper; + private static final String ENTITY_NAME = "menu"; + + @ApiOperation("导出菜单数据") + @GetMapping(value = "/download") + @PreAuthorize("@el.check('menu:list')") + public void exportMenu(HttpServletResponse response, MenuQueryCriteria criteria) throws Exception { + menuService.download(menuService.queryAll(criteria, false), response); + } + + @GetMapping(value = "/build") + @ApiOperation("获取前端所需菜单") + public ResponseEntity> buildMenus(){ + List menuDtoList = menuService.findByUser(SecurityUtils.getCurrentUserId()); + List menus = menuService.buildTree(menuDtoList); + return new ResponseEntity<>(menuService.buildMenus(menus),HttpStatus.OK); + } + + @ApiOperation("返回全部的菜单") + @GetMapping(value = "/lazy") + @PreAuthorize("@el.check('menu:list','roles:list')") + public ResponseEntity> queryAllMenu(@RequestParam Long pid){ + return new ResponseEntity<>(menuService.getMenus(pid),HttpStatus.OK); + } + + @ApiOperation("根据菜单ID返回所有子节点ID,包含自身ID") + @GetMapping(value = "/child") + @PreAuthorize("@el.check('menu:list','roles:list')") + public ResponseEntity childMenu(@RequestParam Long id){ + Set menuSet = new HashSet<>(); + List menuList = menuService.getMenus(id); + menuSet.add(menuService.findOne(id)); + menuSet = menuService.getChildMenus(menuMapper.toEntity(menuList), menuSet); + Set ids = menuSet.stream().map(Menu::getId).collect(Collectors.toSet()); + return new ResponseEntity<>(ids,HttpStatus.OK); + } + + @GetMapping + @ApiOperation("查询菜单") + @PreAuthorize("@el.check('menu:list')") + public ResponseEntity> queryMenu(MenuQueryCriteria criteria) throws Exception { + List menuDtoList = menuService.queryAll(criteria, true); + return new ResponseEntity<>(PageUtil.toPage(menuDtoList, menuDtoList.size()),HttpStatus.OK); + } + + @ApiOperation("查询菜单:根据ID获取同级与上级数据") + @PostMapping("/superior") + @PreAuthorize("@el.check('menu:list')") + public ResponseEntity> getMenuSuperior(@RequestBody List ids) { + Set menuDtos = new LinkedHashSet<>(); + if(CollectionUtil.isNotEmpty(ids)){ + for (Long id : ids) { + MenuDto menuDto = menuService.findById(id); + List menuDtoList = menuService.getSuperior(menuDto, new ArrayList<>()); + for (MenuDto menu : menuDtoList) { + if(menu.getId().equals(menuDto.getPid())) { + menu.setSubCount(menu.getSubCount() - 1); + } + } + menuDtos.addAll(menuDtoList); + } + // 编辑菜单时不显示自己以及自己下级的数据,避免出现PID数据环形问题 + menuDtos = menuDtos.stream().filter(i -> !ids.contains(i.getId())).collect(Collectors.toSet()); + return new ResponseEntity<>(menuService.buildTree(new ArrayList<>(menuDtos)),HttpStatus.OK); + } + return new ResponseEntity<>(menuService.getMenus(null),HttpStatus.OK); + } + + @Log("新增菜单") + @ApiOperation("新增菜单") + @PostMapping + @PreAuthorize("@el.check('menu:add')") + public ResponseEntity createMenu(@Validated @RequestBody Menu resources){ + if (resources.getId() != null) { + throw new BadRequestException("A new "+ ENTITY_NAME +" cannot already have an ID"); + } + menuService.create(resources); + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @Log("修改菜单") + @ApiOperation("修改菜单") + @PutMapping + @PreAuthorize("@el.check('menu:edit')") + public ResponseEntity updateMenu(@Validated(Menu.Update.class) @RequestBody Menu resources){ + menuService.update(resources); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @Log("删除菜单") + @ApiOperation("删除菜单") + @DeleteMapping + @PreAuthorize("@el.check('menu:del')") + public ResponseEntity deleteMenu(@RequestBody Set ids){ + Set menuSet = new HashSet<>(); + for (Long id : ids) { + List menuList = menuService.getMenus(id); + menuSet.add(menuService.findOne(id)); + menuSet = menuService.getChildMenus(menuMapper.toEntity(menuList), menuSet); + } + menuService.delete(menuSet); + return new ResponseEntity<>(HttpStatus.OK); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/MonitorController.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/MonitorController.java new file mode 100644 index 0000000..bb0e39b --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/MonitorController.java @@ -0,0 +1,45 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.rest; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import me.zhengjie.modules.system.service.MonitorService; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +/** + * @author Zheng Jie + * @date 2020-05-02 + */ +@RestController +@RequiredArgsConstructor +@Api(tags = "系统-服务监控管理") +@RequestMapping("/api/monitor") +public class MonitorController { + + private final MonitorService serverService; + + @GetMapping + @ApiOperation("查询服务监控") + @PreAuthorize("@el.check('monitor:list')") + public ResponseEntity queryMonitor(){ + return new ResponseEntity<>(serverService.getServers(),HttpStatus.OK); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/PurchaseController.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/PurchaseController.java new file mode 100644 index 0000000..3202e0d --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/PurchaseController.java @@ -0,0 +1,90 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package me.zhengjie.modules.system.rest; + +import me.zhengjie.annotation.Log; +import me.zhengjie.modules.system.domain.Purchase; +import me.zhengjie.modules.system.service.PurchaseService; +import me.zhengjie.modules.system.service.dto.PurchaseQueryCriteria; +import org.springframework.data.domain.Pageable; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import io.swagger.annotations.*; +import java.io.IOException; +import javax.servlet.http.HttpServletResponse; +import me.zhengjie.utils.PageResult; +import me.zhengjie.modules.system.service.dto.PurchaseDto; + +/** +* @website https://eladmin.vip +* @author author +* +**/ +@RestController +@RequiredArgsConstructor +@Api(tags = "经费支出管理") +@RequestMapping("/api/purchase") +public class PurchaseController { + + private final PurchaseService purchaseService; + + @Log("导出数据") + @ApiOperation("导出数据") + @GetMapping(value = "/download") + @PreAuthorize("@el.check('purchase:list')") + public void exportPurchase(HttpServletResponse response, PurchaseQueryCriteria criteria) throws IOException { + purchaseService.download(purchaseService.queryAll(criteria), response); + } + + @GetMapping + @Log("查询经费支出") + @ApiOperation("查询经费支出") + @PreAuthorize("@el.check('purchase:list')") + public ResponseEntity> queryPurchase(PurchaseQueryCriteria criteria, Pageable pageable){ + return new ResponseEntity<>(purchaseService.queryAll(criteria,pageable),HttpStatus.OK); + } + + @PostMapping + @Log("新增经费支出") + @ApiOperation("新增经费支出") + @PreAuthorize("@el.check('purchase:add')") + public ResponseEntity createPurchase(@Validated @RequestBody Purchase resources){ + purchaseService.create(resources); + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @PutMapping + @Log("修改经费支出") + @ApiOperation("修改经费支出") + @PreAuthorize("@el.check('purchase:edit')") + public ResponseEntity updatePurchase(@Validated @RequestBody Purchase resources){ + purchaseService.update(resources); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @DeleteMapping + @Log("删除经费支出") + @ApiOperation("删除经费支出") + @PreAuthorize("@el.check('purchase:del')") + public ResponseEntity deletePurchase(@RequestBody Long[] ids) { + purchaseService.deleteAll(ids); + return new ResponseEntity<>(HttpStatus.OK); + } +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/ReimburseController.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/ReimburseController.java new file mode 100644 index 0000000..96ce25d --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/ReimburseController.java @@ -0,0 +1,90 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package me.zhengjie.modules.system.rest; + +import me.zhengjie.annotation.Log; +import me.zhengjie.modules.system.domain.Reimburse; +import me.zhengjie.modules.system.service.ReimburseService; +import me.zhengjie.modules.system.service.dto.ReimburseQueryCriteria; +import org.springframework.data.domain.Pageable; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import io.swagger.annotations.*; +import java.io.IOException; +import javax.servlet.http.HttpServletResponse; +import me.zhengjie.utils.PageResult; +import me.zhengjie.modules.system.service.dto.ReimburseDto; + +/** +* @website https://eladmin.vip +* @author author +* +**/ +@RestController +@RequiredArgsConstructor +@Api(tags = "报销单管理") +@RequestMapping("/api/reimburse") +public class ReimburseController { + + private final ReimburseService reimburseService; + + @Log("导出数据") + @ApiOperation("导出数据") + @GetMapping(value = "/download") + @PreAuthorize("@el.check('reimburse:list')") + public void exportReimburse(HttpServletResponse response, ReimburseQueryCriteria criteria) throws IOException { + reimburseService.download(reimburseService.queryAll(criteria), response); + } + + @GetMapping + @Log("查询报销单") + @ApiOperation("查询报销单") + @PreAuthorize("@el.check('reimburse:list')") + public ResponseEntity> queryReimburse(ReimburseQueryCriteria criteria, Pageable pageable){ + return new ResponseEntity<>(reimburseService.queryAll(criteria,pageable),HttpStatus.OK); + } + + @PostMapping + @Log("新增报销单") + @ApiOperation("新增报销单") + @PreAuthorize("@el.check('reimburse:add')") + public ResponseEntity createReimburse(@Validated @RequestBody Reimburse resources){ + reimburseService.create(resources); + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @PutMapping + @Log("修改报销单") + @ApiOperation("修改报销单") + @PreAuthorize("@el.check('reimburse:edit')") + public ResponseEntity updateReimburse(@Validated @RequestBody Reimburse resources){ + reimburseService.update(resources); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @DeleteMapping + @Log("删除报销单") + @ApiOperation("删除报销单") + @PreAuthorize("@el.check('reimburse:del')") + public ResponseEntity deleteReimburse(@RequestBody Long[] ids) { + reimburseService.deleteAll(ids); + return new ResponseEntity<>(HttpStatus.OK); + } +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/RoleController.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/RoleController.java new file mode 100644 index 0000000..6988432 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/RoleController.java @@ -0,0 +1,155 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.rest; + +import cn.hutool.core.lang.Dict; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import me.zhengjie.annotation.Log; +import me.zhengjie.modules.system.domain.Role; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.modules.system.service.RoleService; +import me.zhengjie.modules.system.service.dto.RoleDto; +import me.zhengjie.modules.system.service.dto.RoleQueryCriteria; +import me.zhengjie.modules.system.service.dto.RoleSmallDto; +import me.zhengjie.utils.PageResult; +import me.zhengjie.utils.SecurityUtils; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * @author Zheng Jie + * @date 2018-12-03 + */ +@RestController +@RequiredArgsConstructor +@Api(tags = "系统:角色管理") +@RequestMapping("/api/roles") +public class RoleController { + + private final RoleService roleService; + + private static final String ENTITY_NAME = "role"; + + @ApiOperation("获取单个role") + @GetMapping(value = "/{id}") + @PreAuthorize("@el.check('roles:list')") + public ResponseEntity findRoleById(@PathVariable Long id){ + return new ResponseEntity<>(roleService.findById(id), HttpStatus.OK); + } + + @ApiOperation("导出角色数据") + @GetMapping(value = "/download") + @PreAuthorize("@el.check('role:list')") + public void exportRole(HttpServletResponse response, RoleQueryCriteria criteria) throws IOException { + roleService.download(roleService.queryAll(criteria), response); + } + + @ApiOperation("返回全部的角色") + @GetMapping(value = "/all") + @PreAuthorize("@el.check('roles:list','user:add','user:edit')") + public ResponseEntity> queryAllRole(){ + return new ResponseEntity<>(roleService.queryAll(),HttpStatus.OK); + } + + @ApiOperation("查询角色") + @GetMapping + @PreAuthorize("@el.check('roles:list')") + public ResponseEntity> queryRole(RoleQueryCriteria criteria, Pageable pageable){ + return new ResponseEntity<>(roleService.queryAll(criteria,pageable),HttpStatus.OK); + } + + @ApiOperation("获取用户级别") + @GetMapping(value = "/level") + public ResponseEntity getRoleLevel(){ + return new ResponseEntity<>(Dict.create().set("level", getLevels(null)),HttpStatus.OK); + } + + @Log("新增角色") + @ApiOperation("新增角色") + @PostMapping + @PreAuthorize("@el.check('roles:add')") + public ResponseEntity createRole(@Validated @RequestBody Role resources){ + if (resources.getId() != null) { + throw new BadRequestException("A new "+ ENTITY_NAME +" cannot already have an ID"); + } + getLevels(resources.getLevel()); + roleService.create(resources); + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @Log("修改角色") + @ApiOperation("修改角色") + @PutMapping + @PreAuthorize("@el.check('roles:edit')") + public ResponseEntity updateRole(@Validated(Role.Update.class) @RequestBody Role resources){ + getLevels(resources.getLevel()); + roleService.update(resources); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @Log("修改角色菜单") + @ApiOperation("修改角色菜单") + @PutMapping(value = "/menu") + @PreAuthorize("@el.check('roles:edit')") + public ResponseEntity updateRoleMenu(@RequestBody Role resources){ + RoleDto role = roleService.findById(resources.getId()); + getLevels(role.getLevel()); + roleService.updateMenu(resources,role); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @Log("删除角色") + @ApiOperation("删除角色") + @DeleteMapping + @PreAuthorize("@el.check('roles:del')") + public ResponseEntity deleteRole(@RequestBody Set ids){ + for (Long id : ids) { + RoleDto role = roleService.findById(id); + getLevels(role.getLevel()); + } + // 验证是否被用户关联 + roleService.verification(ids); + roleService.delete(ids); + return new ResponseEntity<>(HttpStatus.OK); + } + + /** + * 获取用户的角色级别 + * @return / + */ + private int getLevels(Integer level){ + List levels = roleService.findByUsersId(SecurityUtils.getCurrentUserId()).stream().map(RoleSmallDto::getLevel).collect(Collectors.toList()); + int min = Collections.min(levels); + if(level != null){ + if(level < min){ + throw new BadRequestException("权限不足,你的角色级别:" + min + ",低于操作的角色级别:" + level); + } + } + return min; + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/UserBankController.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/UserBankController.java new file mode 100644 index 0000000..229f892 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/UserBankController.java @@ -0,0 +1,90 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package me.zhengjie.modules.system.rest; + +import me.zhengjie.annotation.Log; +import me.zhengjie.modules.system.domain.UserBank; +import me.zhengjie.modules.system.service.UserBankService; +import me.zhengjie.modules.system.service.dto.UserBankQueryCriteria; +import org.springframework.data.domain.Pageable; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import io.swagger.annotations.*; +import java.io.IOException; +import javax.servlet.http.HttpServletResponse; +import me.zhengjie.utils.PageResult; +import me.zhengjie.modules.system.service.dto.UserBankDto; + +/** +* @website https://eladmin.vip +* @author author +* @date 2023-10-31 +**/ +@RestController +@RequiredArgsConstructor +@Api(tags = "银行卡信息管理") +@RequestMapping("/api/userBank") +public class UserBankController { + + private final UserBankService userBankService; + + @Log("导出数据") + @ApiOperation("导出数据") + @GetMapping(value = "/download") + @PreAuthorize("@el.check('userBank:list')") + public void exportUserBank(HttpServletResponse response, UserBankQueryCriteria criteria) throws IOException { + userBankService.download(userBankService.queryAll(criteria), response); + } + + @GetMapping + @Log("查询银行卡信息") + @ApiOperation("查询银行卡信息") + @PreAuthorize("@el.check('userBank:list')") + public ResponseEntity> queryUserBank(UserBankQueryCriteria criteria, Pageable pageable){ + return new ResponseEntity<>(userBankService.queryAll(criteria,pageable),HttpStatus.OK); + } + + @PostMapping + @Log("新增银行卡信息") + @ApiOperation("新增银行卡信息") + @PreAuthorize("@el.check('userBank:add')") + public ResponseEntity createUserBank(@Validated @RequestBody UserBank resources){ + userBankService.create(resources); + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @PutMapping + @Log("修改银行卡信息") + @ApiOperation("修改银行卡信息") + @PreAuthorize("@el.check('userBank:edit')") + public ResponseEntity updateUserBank(@Validated @RequestBody UserBank resources){ + userBankService.update(resources); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @DeleteMapping + @Log("删除银行卡信息") + @ApiOperation("删除银行卡信息") + @PreAuthorize("@el.check('userBank:del')") + public ResponseEntity deleteUserBank(@RequestBody Long[] ids) { + userBankService.deleteAll(ids); + return new ResponseEntity<>(HttpStatus.OK); + } +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/UserController.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/UserController.java new file mode 100644 index 0000000..f75b8bb --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/UserController.java @@ -0,0 +1,210 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.rest; + +import cn.hutool.core.collection.CollectionUtil; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import me.zhengjie.annotation.Log; +import me.zhengjie.utils.PageResult; +import me.zhengjie.config.RsaProperties; +import me.zhengjie.modules.system.domain.Dept; +import me.zhengjie.modules.system.service.DataService; +import me.zhengjie.modules.system.domain.User; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.modules.system.domain.vo.UserPassVo; +import me.zhengjie.modules.system.service.DeptService; +import me.zhengjie.modules.system.service.RoleService; +import me.zhengjie.modules.system.service.dto.RoleSmallDto; +import me.zhengjie.modules.system.service.dto.UserDto; +import me.zhengjie.modules.system.service.dto.UserQueryCriteria; +import me.zhengjie.modules.system.service.VerifyService; +import me.zhengjie.utils.*; +import me.zhengjie.modules.system.service.UserService; +import me.zhengjie.utils.enums.CodeEnum; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author Zheng Jie + * @date 2018-11-23 + */ +@Api(tags = "系统:用户管理") +@RestController +@RequestMapping("/api/users") +@RequiredArgsConstructor +public class UserController { + + private final PasswordEncoder passwordEncoder; + private final UserService userService; + private final DataService dataService; + private final DeptService deptService; + private final RoleService roleService; + private final VerifyService verificationCodeService; + + @ApiOperation("导出用户数据") + @GetMapping(value = "/download") + @PreAuthorize("@el.check('user:list')") + public void exportUser(HttpServletResponse response, UserQueryCriteria criteria) throws IOException { + userService.download(userService.queryAll(criteria), response); + } + + @ApiOperation("查询用户") + @GetMapping + @PreAuthorize("@el.check('user:list')") + public ResponseEntity> queryUser(UserQueryCriteria criteria, Pageable pageable){ + if (!ObjectUtils.isEmpty(criteria.getDeptId())) { + criteria.getDeptIds().add(criteria.getDeptId()); + // 先查找是否存在子节点 + List data = deptService.findByPid(criteria.getDeptId()); + // 然后把子节点的ID都加入到集合中 + criteria.getDeptIds().addAll(deptService.getDeptChildren(data)); + } + // 数据权限 + List dataScopes = dataService.getDeptIds(userService.findByName(SecurityUtils.getCurrentUsername())); + // criteria.getDeptIds() 不为空并且数据权限不为空则取交集 + if (!CollectionUtils.isEmpty(criteria.getDeptIds()) && !CollectionUtils.isEmpty(dataScopes)){ + // 取交集 + criteria.getDeptIds().retainAll(dataScopes); + if(!CollectionUtil.isEmpty(criteria.getDeptIds())){ + return new ResponseEntity<>(userService.queryAll(criteria,pageable),HttpStatus.OK); + } + } else { + // 否则取并集 + criteria.getDeptIds().addAll(dataScopes); + return new ResponseEntity<>(userService.queryAll(criteria,pageable),HttpStatus.OK); + } + return new ResponseEntity<>(PageUtil.noData(),HttpStatus.OK); + } + + @Log("新增用户") + @ApiOperation("新增用户") + @PostMapping + @PreAuthorize("@el.check('user:add')") + public ResponseEntity createUser(@Validated @RequestBody User resources){ + checkLevel(resources); + // 默认密码 123456 + resources.setPassword(passwordEncoder.encode("123456")); + userService.create(resources); + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @Log("修改用户") + @ApiOperation("修改用户") + @PutMapping + @PreAuthorize("@el.check('user:edit')") + public ResponseEntity updateUser(@Validated(User.Update.class) @RequestBody User resources) throws Exception { + checkLevel(resources); + userService.update(resources); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @Log("修改用户:个人中心") + @ApiOperation("修改用户:个人中心") + @PutMapping(value = "center") + public ResponseEntity centerUser(@Validated(User.Update.class) @RequestBody User resources){ + if(!resources.getId().equals(SecurityUtils.getCurrentUserId())){ + throw new BadRequestException("不能修改他人资料"); + } + userService.updateCenter(resources); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @Log("删除用户") + @ApiOperation("删除用户") + @DeleteMapping + @PreAuthorize("@el.check('user:del')") + public ResponseEntity deleteUser(@RequestBody Set ids){ + for (Long id : ids) { + Integer currentLevel = Collections.min(roleService.findByUsersId(SecurityUtils.getCurrentUserId()).stream().map(RoleSmallDto::getLevel).collect(Collectors.toList())); + Integer optLevel = Collections.min(roleService.findByUsersId(id).stream().map(RoleSmallDto::getLevel).collect(Collectors.toList())); + if (currentLevel > optLevel) { + throw new BadRequestException("角色权限不足,不能删除:" + userService.findById(id).getUsername()); + } + } + userService.delete(ids); + return new ResponseEntity<>(HttpStatus.OK); + } + + @ApiOperation("修改密码") + @PostMapping(value = "/updatePass") + public ResponseEntity updateUserPass(@RequestBody UserPassVo passVo) throws Exception { + String oldPass = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey,passVo.getOldPass()); + String newPass = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey,passVo.getNewPass()); + UserDto user = userService.findByName(SecurityUtils.getCurrentUsername()); + if(!passwordEncoder.matches(oldPass, user.getPassword())){ + throw new BadRequestException("修改失败,旧密码错误"); + } + if(passwordEncoder.matches(newPass, user.getPassword())){ + throw new BadRequestException("新密码不能与旧密码相同"); + } + userService.updatePass(user.getUsername(),passwordEncoder.encode(newPass)); + return new ResponseEntity<>(HttpStatus.OK); + } + + @ApiOperation("重置密码") + @PutMapping(value = "/resetPwd") + public ResponseEntity resetPwd(@RequestBody Set ids) { + String pwd = passwordEncoder.encode("123456"); + userService.resetPwd(ids, pwd); + return new ResponseEntity<>(HttpStatus.OK); + } + + @ApiOperation("修改头像") + @PostMapping(value = "/updateAvatar") + public ResponseEntity updateUserAvatar(@RequestParam MultipartFile avatar){ + return new ResponseEntity<>(userService.updateAvatar(avatar), HttpStatus.OK); + } + + @Log("修改邮箱") + @ApiOperation("修改邮箱") + @PostMapping(value = "/updateEmail/{code}") + public ResponseEntity updateUserEmail(@PathVariable String code,@RequestBody User user) throws Exception { + String password = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey,user.getPassword()); + UserDto userDto = userService.findByName(SecurityUtils.getCurrentUsername()); + if(!passwordEncoder.matches(password, userDto.getPassword())){ + throw new BadRequestException("密码错误"); + } + verificationCodeService.validated(CodeEnum.EMAIL_RESET_EMAIL_CODE.getKey() + user.getEmail(), code); + userService.updateEmail(userDto.getUsername(),user.getEmail()); + return new ResponseEntity<>(HttpStatus.OK); + } + + /** + * 如果当前用户的角色级别低于创建用户的角色级别,则抛出权限不足的错误 + * @param resources / + */ + private void checkLevel(User resources) { + Integer currentLevel = Collections.min(roleService.findByUsersId(SecurityUtils.getCurrentUserId()).stream().map(RoleSmallDto::getLevel).collect(Collectors.toList())); + Integer optLevel = roleService.findByRoles(resources.getRoles()); + if (currentLevel > optLevel) { + throw new BadRequestException("角色权限不足"); + } + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/VerifyController.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/VerifyController.java new file mode 100644 index 0000000..09ba44b --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/VerifyController.java @@ -0,0 +1,76 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.rest; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import me.zhengjie.domain.vo.EmailVo; +import me.zhengjie.service.EmailService; +import me.zhengjie.modules.system.service.VerifyService; +import me.zhengjie.utils.enums.CodeBiEnum; +import me.zhengjie.utils.enums.CodeEnum; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import java.util.Objects; + +/** + * @author Zheng Jie + * @date 2018-12-26 + */ +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/code") +@Api(tags = "系统:验证码管理") +public class VerifyController { + + private final VerifyService verificationCodeService; + private final EmailService emailService; + + @PostMapping(value = "/resetEmail") + @ApiOperation("重置邮箱,发送验证码") + public ResponseEntity resetEmail(@RequestParam String email){ + EmailVo emailVo = verificationCodeService.sendEmail(email, CodeEnum.EMAIL_RESET_EMAIL_CODE.getKey()); + emailService.send(emailVo,emailService.find()); + return new ResponseEntity<>(HttpStatus.OK); + } + + @PostMapping(value = "/email/resetPass") + @ApiOperation("重置密码,发送验证码") + public ResponseEntity resetPass(@RequestParam String email){ + EmailVo emailVo = verificationCodeService.sendEmail(email, CodeEnum.EMAIL_RESET_PWD_CODE.getKey()); + emailService.send(emailVo,emailService.find()); + return new ResponseEntity<>(HttpStatus.OK); + } + + @GetMapping(value = "/validated") + @ApiOperation("验证码验证") + public ResponseEntity validated(@RequestParam String email, @RequestParam String code, @RequestParam Integer codeBi){ + CodeBiEnum biEnum = CodeBiEnum.find(codeBi); + switch (Objects.requireNonNull(biEnum)){ + case ONE: + verificationCodeService.validated(CodeEnum.EMAIL_RESET_EMAIL_CODE.getKey() + email ,code); + break; + case TWO: + verificationCodeService.validated(CodeEnum.EMAIL_RESET_PWD_CODE.getKey() + email ,code); + break; + default: + break; + } + return new ResponseEntity<>(HttpStatus.OK); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/DataService.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/DataService.java new file mode 100644 index 0000000..10258d9 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/DataService.java @@ -0,0 +1,34 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service; + +import me.zhengjie.modules.system.service.dto.UserDto; +import java.util.List; + +/** + * 数据权限服务类 + * @author Zheng Jie + * @date 2020-05-07 + */ +public interface DataService { + + /** + * 获取数据权限 + * @param user / + * @return / + */ + List getDeptIds(UserDto user); +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/DeptService.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/DeptService.java new file mode 100644 index 0000000..902f603 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/DeptService.java @@ -0,0 +1,124 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service; + +import me.zhengjie.modules.system.domain.Dept; +import me.zhengjie.modules.system.service.dto.DeptDto; +import me.zhengjie.modules.system.service.dto.DeptQueryCriteria; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; +import java.util.Set; + +/** +* @author Zheng Jie +* @date 2019-03-25 +*/ +public interface DeptService { + + /** + * 查询所有数据 + * @param criteria 条件 + * @param isQuery / + * @throws Exception / + * @return / + */ + List queryAll(DeptQueryCriteria criteria, Boolean isQuery) throws Exception; + + /** + * 根据ID查询 + * @param id / + * @return / + */ + DeptDto findById(Long id); + + /** + * 创建 + * @param resources / + */ + void create(Dept resources); + + /** + * 编辑 + * @param resources / + */ + void update(Dept resources); + + /** + * 删除 + * @param deptDtos / + * + */ + void delete(Set deptDtos); + + /** + * 根据PID查询 + * @param pid / + * @return / + */ + List findByPid(long pid); + + /** + * 根据角色ID查询 + * @param id / + * @return / + */ + Set findByRoleId(Long id); + + /** + * 导出数据 + * @param queryAll 待导出的数据 + * @param response / + * @throws IOException / + */ + void download(List queryAll, HttpServletResponse response) throws IOException; + + /** + * 获取待删除的部门 + * @param deptList / + * @param deptDtos / + * @return / + */ + Set getDeleteDepts(List deptList, Set deptDtos); + + /** + * 根据ID获取同级与上级数据 + * @param deptDto / + * @param depts / + * @return / + */ + List getSuperior(DeptDto deptDto, List depts); + + /** + * 构建树形数据 + * @param deptDtos / + * @return / + */ + Object buildTree(List deptDtos); + + /** + * 获取 + * @param deptList / + * @return / + */ + List getDeptChildren(List deptList); + + /** + * 验证是否被角色或用户关联 + * @param deptDtos / + */ + void verification(Set deptDtos); +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/DictDetailService.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/DictDetailService.java new file mode 100644 index 0000000..7e2efb7 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/DictDetailService.java @@ -0,0 +1,63 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service; + +import me.zhengjie.utils.PageResult; +import me.zhengjie.modules.system.domain.DictDetail; +import me.zhengjie.modules.system.service.dto.DictDetailDto; +import me.zhengjie.modules.system.service.dto.DictDetailQueryCriteria; +import org.springframework.data.domain.Pageable; +import java.util.List; + +/** +* @author Zheng Jie +* @date 2019-04-10 +*/ +public interface DictDetailService { + + /** + * 创建 + * @param resources / + */ + void create(DictDetail resources); + + /** + * 编辑 + * @param resources / + */ + void update(DictDetail resources); + + /** + * 删除 + * @param id / + */ + void delete(Long id); + + /** + * 分页查询 + * @param criteria 条件 + * @param pageable 分页参数 + * @return / + */ + PageResult queryAll(DictDetailQueryCriteria criteria, Pageable pageable); + + /** + * 根据字典名称获取字典详情 + * @param name 字典名称 + * @return / + */ + List getDictByName(String name); +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/DictService.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/DictService.java new file mode 100644 index 0000000..05077b7 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/DictService.java @@ -0,0 +1,75 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service; + +import me.zhengjie.utils.PageResult; +import me.zhengjie.modules.system.domain.Dict; +import me.zhengjie.modules.system.service.dto.DictDto; +import me.zhengjie.modules.system.service.dto.DictQueryCriteria; +import org.springframework.data.domain.Pageable; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; +import java.util.Set; + +/** +* @author Zheng Jie +* @date 2019-04-10 +*/ +public interface DictService { + + /** + * 分页查询 + * @param criteria 条件 + * @param pageable 分页参数 + * @return / + */ + PageResult queryAll(DictQueryCriteria criteria, Pageable pageable); + + /** + * 查询全部数据 + * @param dict / + * @return / + */ + List queryAll(DictQueryCriteria dict); + + /** + * 创建 + * @param resources / + * @return / + */ + void create(Dict resources); + + /** + * 编辑 + * @param resources / + */ + void update(Dict resources); + + /** + * 删除 + * @param ids / + */ + void delete(Set ids); + + /** + * 导出数据 + * @param queryAll 待导出的数据 + * @param response / + * @throws IOException / + */ + void download(List queryAll, HttpServletResponse response) throws IOException; +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/JobService.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/JobService.java new file mode 100644 index 0000000..dab9f58 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/JobService.java @@ -0,0 +1,88 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service; + +import me.zhengjie.utils.PageResult; +import me.zhengjie.modules.system.domain.Job; +import me.zhengjie.modules.system.service.dto.JobDto; +import me.zhengjie.modules.system.service.dto.JobQueryCriteria; +import org.springframework.data.domain.Pageable; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; +import java.util.Set; + +/** +* @author Zheng Jie +* @date 2019-03-29 +*/ +public interface JobService { + + /** + * 根据ID查询 + * @param id / + * @return / + */ + JobDto findById(Long id); + + /** + * 创建 + * @param resources / + * @return / + */ + void create(Job resources); + + /** + * 编辑 + * @param resources / + */ + void update(Job resources); + + /** + * 删除 + * @param ids / + */ + void delete(Set ids); + + /** + * 分页查询 + * @param criteria 条件 + * @param pageable 分页参数 + * @return / + */ + PageResult queryAll(JobQueryCriteria criteria, Pageable pageable); + + /** + * 查询全部数据 + * @param criteria / + * @return / + */ + List queryAll(JobQueryCriteria criteria); + + /** + * 导出数据 + * @param queryAll 待导出的数据 + * @param response / + * @throws IOException / + */ + void download(List queryAll, HttpServletResponse response) throws IOException; + + /** + * 验证是否被用户关联 + * @param ids / + */ + void verification(Set ids); +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/MenuService.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/MenuService.java new file mode 100644 index 0000000..1116d10 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/MenuService.java @@ -0,0 +1,126 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service; + +import me.zhengjie.modules.system.domain.Menu; +import me.zhengjie.modules.system.domain.vo.MenuVo; +import me.zhengjie.modules.system.service.dto.MenuDto; +import me.zhengjie.modules.system.service.dto.MenuQueryCriteria; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; +import java.util.Set; + +/** + * @author Zheng Jie + * @date 2018-12-17 + */ +public interface MenuService { + + /** + * 查询全部数据 + * @param criteria 条件 + * @param isQuery / + * @throws Exception / + * @return / + */ + List queryAll(MenuQueryCriteria criteria, Boolean isQuery) throws Exception; + + /** + * 根据ID查询 + * @param id / + * @return / + */ + MenuDto findById(long id); + + /** + * 创建 + * @param resources / + */ + void create(Menu resources); + + /** + * 编辑 + * @param resources / + */ + void update(Menu resources); + + /** + * 获取所有子节点,包含自身ID + * @param menuList / + * @param menuSet / + * @return / + */ + Set getChildMenus(List menuList, Set menuSet); + + /** + * 构建菜单树 + * @param menuDtos 原始数据 + * @return / + */ + List buildTree(List menuDtos); + + /** + * 构建菜单树 + * @param menuDtos / + * @return / + */ + List buildMenus(List menuDtos); + + /** + * 根据ID查询 + * @param id / + * @return / + */ + Menu findOne(Long id); + + /** + * 删除 + * @param menuSet / + */ + void delete(Set menuSet); + + /** + * 导出 + * @param queryAll 待导出的数据 + * @param response / + * @throws IOException / + */ + void download(List queryAll, HttpServletResponse response) throws IOException; + + /** + * 懒加载菜单数据 + * @param pid / + * @return / + */ + List getMenus(Long pid); + + /** + * 根据ID获取同级与上级数据 + * @param menuDto / + * @param objects / + * @return / + */ + List getSuperior(MenuDto menuDto, List objects); + + /** + * 根据当前用户获取菜单 + * @param currentUserId / + * @return / + */ + List findByUser(Long currentUserId); +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/MonitorService.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/MonitorService.java new file mode 100644 index 0000000..478225a --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/MonitorService.java @@ -0,0 +1,31 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service; + +import java.util.Map; + +/** + * @author Zheng Jie + * @date 2020-05-02 + */ +public interface MonitorService { + + /** + * 查询数据分页 + * @return Map + */ + Map getServers(); +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/PurchaseService.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/PurchaseService.java new file mode 100644 index 0000000..64dfd30 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/PurchaseService.java @@ -0,0 +1,83 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package me.zhengjie.modules.system.service; + +import me.zhengjie.modules.system.domain.Purchase; +import me.zhengjie.modules.system.service.dto.PurchaseDto; +import me.zhengjie.modules.system.service.dto.PurchaseQueryCriteria; +import org.springframework.data.domain.Pageable; +import java.util.Map; +import java.util.List; +import java.io.IOException; +import javax.servlet.http.HttpServletResponse; +import me.zhengjie.utils.PageResult; + +/** +* @website https://eladmin.vip +* @description 服务接口 +* @author author +* +**/ +public interface PurchaseService { + + /** + * 查询数据分页 + * @param criteria 条件 + * @param pageable 分页参数 + * @return Map + */ + PageResult queryAll(PurchaseQueryCriteria criteria, Pageable pageable); + + /** + * 查询所有数据不分页 + * @param criteria 条件参数 + * @return List + */ + List queryAll(PurchaseQueryCriteria criteria); + + /** + * 根据ID查询 + * @param purchaseId ID + * @return PurchaseDto + */ + PurchaseDto findById(Long purchaseId); + + /** + * 创建 + * @param resources / + */ + void create(Purchase resources); + + /** + * 编辑 + * @param resources / + */ + void update(Purchase resources); + + /** + * 多选删除 + * @param ids / + */ + void deleteAll(Long[] ids); + + /** + * 导出数据 + * @param all 待导出的数据 + * @param response / + * @throws IOException / + */ + void download(List all, HttpServletResponse response) throws IOException; +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/ReimburseService.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/ReimburseService.java new file mode 100644 index 0000000..fe7a48e --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/ReimburseService.java @@ -0,0 +1,83 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package me.zhengjie.modules.system.service; + +import me.zhengjie.modules.system.domain.Reimburse; +import me.zhengjie.modules.system.service.dto.ReimburseDto; +import me.zhengjie.modules.system.service.dto.ReimburseQueryCriteria; +import org.springframework.data.domain.Pageable; +import java.util.Map; +import java.util.List; +import java.io.IOException; +import javax.servlet.http.HttpServletResponse; +import me.zhengjie.utils.PageResult; + +/** +* @website https://eladmin.vip +* @description 服务接口 +* @author author +* +**/ +public interface ReimburseService { + + /** + * 查询数据分页 + * @param criteria 条件 + * @param pageable 分页参数 + * @return Map + */ + PageResult queryAll(ReimburseQueryCriteria criteria, Pageable pageable); + + /** + * 查询所有数据不分页 + * @param criteria 条件参数 + * @return List + */ + List queryAll(ReimburseQueryCriteria criteria); + + /** + * 根据ID查询 + * @param reimburseId ID + * @return ReimburseDto + */ + ReimburseDto findById(Long reimburseId); + + /** + * 创建 + * @param resources / + */ + void create(Reimburse resources); + + /** + * 编辑 + * @param resources / + */ + void update(Reimburse resources); + + /** + * 多选删除 + * @param ids / + */ + void deleteAll(Long[] ids); + + /** + * 导出数据 + * @param all 待导出的数据 + * @param response / + * @throws IOException / + */ + void download(List all, HttpServletResponse response) throws IOException; +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/RoleService.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/RoleService.java new file mode 100644 index 0000000..6bc8cce --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/RoleService.java @@ -0,0 +1,137 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service; + +import me.zhengjie.modules.security.service.dto.AuthorityDto; +import me.zhengjie.modules.system.domain.Role; +import me.zhengjie.modules.system.service.dto.RoleDto; +import me.zhengjie.modules.system.service.dto.RoleQueryCriteria; +import me.zhengjie.modules.system.service.dto.RoleSmallDto; +import me.zhengjie.modules.system.service.dto.UserDto; +import me.zhengjie.utils.PageResult; +import org.springframework.data.domain.Pageable; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; +import java.util.Set; + +/** + * @author Zheng Jie + * @date 2018-12-03 + */ +public interface RoleService { + + /** + * 查询全部数据 + * @return / + */ + List queryAll(); + + /** + * 根据ID查询 + * @param id / + * @return / + */ + RoleDto findById(long id); + + /** + * 创建 + * @param resources / + */ + void create(Role resources); + + /** + * 编辑 + * @param resources / + */ + void update(Role resources); + + /** + * 删除 + * @param ids / + */ + void delete(Set ids); + + /** + * 根据用户ID查询 + * @param id 用户ID + * @return / + */ + List findByUsersId(Long id); + + /** + * 根据角色查询角色级别 + * @param roles / + * @return / + */ + Integer findByRoles(Set roles); + + /** + * 修改绑定的菜单 + * @param resources / + * @param roleDTO / + */ + void updateMenu(Role resources, RoleDto roleDTO); + + /** + * 解绑菜单 + * @param id / + */ + void untiedMenu(Long id); + + /** + * 待条件分页查询 + * @param criteria 条件 + * @param pageable 分页参数 + * @return / + */ + PageResult queryAll(RoleQueryCriteria criteria, Pageable pageable); + + /** + * 查询全部 + * @param criteria 条件 + * @return / + */ + List queryAll(RoleQueryCriteria criteria); + + /** + * 导出数据 + * @param queryAll 待导出的数据 + * @param response / + * @throws IOException / + */ + void download(List queryAll, HttpServletResponse response) throws IOException; + + /** + * 获取用户权限信息 + * @param user 用户信息 + * @return 权限信息 + */ + List mapToGrantedAuthorities(UserDto user); + + /** + * 验证是否被用户关联 + * @param ids / + */ + void verification(Set ids); + + /** + * 根据菜单Id查询 + * @param menuIds / + * @return / + */ + List findInMenuId(List menuIds); +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/UserBankService.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/UserBankService.java new file mode 100644 index 0000000..560d55a --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/UserBankService.java @@ -0,0 +1,83 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package me.zhengjie.modules.system.service; + +import me.zhengjie.modules.system.domain.UserBank; +import me.zhengjie.modules.system.service.dto.UserBankDto; +import me.zhengjie.modules.system.service.dto.UserBankQueryCriteria; +import org.springframework.data.domain.Pageable; +import java.util.Map; +import java.util.List; +import java.io.IOException; +import javax.servlet.http.HttpServletResponse; +import me.zhengjie.utils.PageResult; + +/** +* @website https://eladmin.vip +* @description 服务接口 +* @author author +* @date 2023-10-31 +**/ +public interface UserBankService { + + /** + * 查询数据分页 + * @param criteria 条件 + * @param pageable 分页参数 + * @return Map + */ + PageResult queryAll(UserBankQueryCriteria criteria, Pageable pageable); + + /** + * 查询所有数据不分页 + * @param criteria 条件参数 + * @return List + */ + List queryAll(UserBankQueryCriteria criteria); + + /** + * 根据ID查询 + * @param bankId ID + * @return UserBankDto + */ + UserBankDto findById(Long bankId); + + /** + * 创建 + * @param resources / + */ + void create(UserBank resources); + + /** + * 编辑 + * @param resources / + */ + void update(UserBank resources); + + /** + * 多选删除 + * @param ids / + */ + void deleteAll(Long[] ids); + + /** + * 导出数据 + * @param all 待导出的数据 + * @param response / + * @throws IOException / + */ + void download(List all, HttpServletResponse response) throws IOException; +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/UserService.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/UserService.java new file mode 100644 index 0000000..a3cc4f2 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/UserService.java @@ -0,0 +1,133 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service; + +import me.zhengjie.utils.PageResult; +import me.zhengjie.modules.system.domain.User; +import me.zhengjie.modules.system.service.dto.UserDto; +import me.zhengjie.modules.system.service.dto.UserLoginDto; +import me.zhengjie.modules.system.service.dto.UserQueryCriteria; +import org.springframework.data.domain.Pageable; +import org.springframework.web.multipart.MultipartFile; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author Zheng Jie + * @date 2018-11-23 + */ +public interface UserService { + + /** + * 根据ID查询 + * @param id ID + * @return / + */ + UserDto findById(long id); + + /** + * 新增用户 + * @param resources / + */ + void create(User resources); + + /** + * 编辑用户 + * @param resources / + * @throws Exception / + */ + void update(User resources) throws Exception; + + /** + * 删除用户 + * @param ids / + */ + void delete(Set ids); + + /** + * 根据用户名查询 + * @param userName / + * @return / + */ + UserDto findByName(String userName); + + /** + * 根据用户名查询 + * @param userName / + * @return / + */ + UserLoginDto getLoginData(String userName); + + /** + * 修改密码 + * @param username 用户名 + * @param encryptPassword 密码 + */ + void updatePass(String username, String encryptPassword); + + /** + * 修改头像 + * @param file 文件 + * @return / + */ + Map updateAvatar(MultipartFile file); + + /** + * 修改邮箱 + * @param username 用户名 + * @param email 邮箱 + */ + void updateEmail(String username, String email); + + /** + * 查询全部 + * @param criteria 条件 + * @param pageable 分页参数 + * @return / + */ + PageResult queryAll(UserQueryCriteria criteria, Pageable pageable); + + /** + * 查询全部不分页 + * @param criteria 条件 + * @return / + */ + List queryAll(UserQueryCriteria criteria); + + /** + * 导出数据 + * @param queryAll 待导出的数据 + * @param response / + * @throws IOException / + */ + void download(List queryAll, HttpServletResponse response) throws IOException; + + /** + * 用户自助修改资料 + * @param resources / + */ + void updateCenter(User resources); + + /** + * 重置密码 + * @param ids 用户id + * @param pwd 密码 + */ + void resetPwd(Set ids, String pwd); +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/VerifyService.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/VerifyService.java new file mode 100644 index 0000000..4ca39b9 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/VerifyService.java @@ -0,0 +1,41 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service; + +import me.zhengjie.domain.vo.EmailVo; + +/** + * @author Zheng Jie + * @date 2018-12-26 + */ +public interface VerifyService { + + /** + * 发送验证码 + * @param email / + * @param key / + * @return / + */ + EmailVo sendEmail(String email, String key); + + + /** + * 验证 + * @param code / + * @param key / + */ + void validated(String key, String code); +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DeptDto.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DeptDto.java new file mode 100644 index 0000000..bc236a1 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DeptDto.java @@ -0,0 +1,76 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.dto; + +import lombok.Getter; +import lombok.Setter; +import me.zhengjie.base.BaseDTO; +import java.io.Serializable; +import java.util.List; +import java.util.Objects; + +/** +* @author Zheng Jie +* @date 2019-03-25 +*/ +@Getter +@Setter +public class DeptDto extends BaseDTO implements Serializable { + + private Long id; + + private String name; + + private Boolean enabled; + + private Integer deptSort; + + private List children; + + private Long pid; + + private Integer subCount; + + public Boolean getHasChildren() { + return subCount > 0; + } + + public Boolean getLeaf() { + return subCount <= 0; + } + + public String getLabel() { + return name; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DeptDto deptDto = (DeptDto) o; + return Objects.equals(id, deptDto.id) && + Objects.equals(name, deptDto.name); + } + + @Override + public int hashCode() { + return Objects.hash(id, name); + } +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DeptQueryCriteria.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DeptQueryCriteria.java new file mode 100644 index 0000000..4d8cdf2 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DeptQueryCriteria.java @@ -0,0 +1,46 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.dto; + +import lombok.Data; +import me.zhengjie.annotation.DataPermission; +import me.zhengjie.annotation.Query; +import java.sql.Timestamp; +import java.util.List; + +/** +* @author Zheng Jie +* @date 2019-03-25 +*/ +@Data +@DataPermission(fieldName = "id") +public class DeptQueryCriteria{ + + @Query(type = Query.Type.INNER_LIKE) + private String name; + + @Query + private Boolean enabled; + + @Query + private Long pid; + + @Query(type = Query.Type.IS_NULL, propName = "pid") + private Boolean pidIsNull; + + @Query(type = Query.Type.BETWEEN) + private List createTime; +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DeptSmallDto.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DeptSmallDto.java new file mode 100644 index 0000000..4dc64e5 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DeptSmallDto.java @@ -0,0 +1,31 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.dto; + +import lombok.Data; +import java.io.Serializable; + +/** +* @author Zheng Jie +* @date 2019-6-10 16:32:18 +*/ +@Data +public class DeptSmallDto implements Serializable { + + private Long id; + + private String name; +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DictDetailDto.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DictDetailDto.java new file mode 100644 index 0000000..a4b931b --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DictDetailDto.java @@ -0,0 +1,40 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.dto; + +import lombok.Getter; +import lombok.Setter; +import me.zhengjie.base.BaseDTO; +import java.io.Serializable; + +/** +* @author Zheng Jie +* @date 2019-04-10 +*/ +@Getter +@Setter +public class DictDetailDto extends BaseDTO implements Serializable { + + private Long id; + + private DictSmallDto dict; + + private String label; + + private String value; + + private Integer dictSort; +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DictDetailQueryCriteria.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DictDetailQueryCriteria.java new file mode 100644 index 0000000..91a01b1 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DictDetailQueryCriteria.java @@ -0,0 +1,33 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.dto; + +import lombok.Data; +import me.zhengjie.annotation.Query; + +/** +* @author Zheng Jie +* @date 2019-04-10 +*/ +@Data +public class DictDetailQueryCriteria { + + @Query(type = Query.Type.INNER_LIKE) + private String label; + + @Query(propName = "name",joinName = "dict") + private String dictName; +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DictDto.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DictDto.java new file mode 100644 index 0000000..048b4a1 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DictDto.java @@ -0,0 +1,39 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.dto; + +import lombok.Getter; +import lombok.Setter; +import me.zhengjie.base.BaseDTO; +import java.io.Serializable; +import java.util.List; + +/** +* @author Zheng Jie +* @date 2019-04-10 +*/ +@Getter +@Setter +public class DictDto extends BaseDTO implements Serializable { + + private Long id; + + private List dictDetails; + + private String name; + + private String description; +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DictQueryCriteria.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DictQueryCriteria.java new file mode 100644 index 0000000..7207c61 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DictQueryCriteria.java @@ -0,0 +1,30 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.dto; + +import lombok.Data; +import me.zhengjie.annotation.Query; + +/** + * @author Zheng Jie + * 公共查询类 + */ +@Data +public class DictQueryCriteria { + + @Query(blurry = "name,description") + private String blurry; +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DictSmallDto.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DictSmallDto.java new file mode 100644 index 0000000..98fc6ee --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/DictSmallDto.java @@ -0,0 +1,31 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.dto; + +import lombok.Getter; +import lombok.Setter; +import java.io.Serializable; + +/** +* @author Zheng Jie +* @date 2019-04-10 +*/ +@Getter +@Setter +public class DictSmallDto implements Serializable { + + private Long id; +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/JobDto.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/JobDto.java new file mode 100644 index 0000000..8836d55 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/JobDto.java @@ -0,0 +1,46 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.dto; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import me.zhengjie.base.BaseDTO; + +import java.io.Serializable; + +/** +* @author Zheng Jie +* @date 2019-03-29 +*/ +@Getter +@Setter +@NoArgsConstructor +public class JobDto extends BaseDTO implements Serializable { + + private Long id; + + private Integer jobSort; + + private String name; + + private Boolean enabled; + + public JobDto(String name, Boolean enabled) { + this.name = name; + this.enabled = enabled; + } +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/JobQueryCriteria.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/JobQueryCriteria.java new file mode 100644 index 0000000..ee851dc --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/JobQueryCriteria.java @@ -0,0 +1,40 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; +import me.zhengjie.annotation.Query; +import java.sql.Timestamp; +import java.util.List; + +/** +* @author Zheng Jie +* @date 2019-6-4 14:49:34 +*/ +@Data +@NoArgsConstructor +public class JobQueryCriteria { + + @Query(type = Query.Type.INNER_LIKE) + private String name; + + @Query + private Boolean enabled; + + @Query(type = Query.Type.BETWEEN) + private List createTime; +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/JobSmallDto.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/JobSmallDto.java new file mode 100644 index 0000000..09cfa62 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/JobSmallDto.java @@ -0,0 +1,33 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; +import java.io.Serializable; + +/** +* @author Zheng Jie +* @date 2019-6-10 16:32:18 +*/ +@Data +@NoArgsConstructor +public class JobSmallDto implements Serializable { + + private Long id; + + private String name; +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/MenuDto.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/MenuDto.java new file mode 100644 index 0000000..d60dd29 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/MenuDto.java @@ -0,0 +1,91 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.dto; + +import lombok.Getter; +import lombok.Setter; +import me.zhengjie.base.BaseDTO; +import java.io.Serializable; +import java.util.List; +import java.util.Objects; + +/** + * @author Zheng Jie + * @date 2018-12-17 + */ +@Getter +@Setter +public class MenuDto extends BaseDTO implements Serializable { + + private Long id; + + private List children; + + private Integer type; + + private String permission; + + private String title; + + private Integer menuSort; + + private String path; + + private String component; + + private Long pid; + + private Integer subCount; + + private Boolean iFrame; + + private Boolean cache; + + private Boolean hidden; + + private String componentName; + + private String icon; + + public Boolean getHasChildren() { + return subCount > 0; + } + + public Boolean getLeaf() { + return subCount <= 0; + } + + public String getLabel() { + return title; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + MenuDto menuDto = (MenuDto) o; + return Objects.equals(id, menuDto.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/MenuQueryCriteria.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/MenuQueryCriteria.java new file mode 100644 index 0000000..bd43e8d --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/MenuQueryCriteria.java @@ -0,0 +1,41 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.dto; + +import lombok.Data; +import me.zhengjie.annotation.Query; +import java.sql.Timestamp; +import java.util.List; + +/** + * @author Zheng Jie + * 公共查询类 + */ +@Data +public class MenuQueryCriteria { + + @Query(blurry = "title,component,permission") + private String blurry; + + @Query(type = Query.Type.BETWEEN) + private List createTime; + + @Query(type = Query.Type.IS_NULL, propName = "pid") + private Boolean pidIsNull; + + @Query + private Long pid; +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/PurchaseDto.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/PurchaseDto.java new file mode 100644 index 0000000..6910cfb --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/PurchaseDto.java @@ -0,0 +1,93 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package me.zhengjie.modules.system.service.dto; + +import lombok.Data; +import me.zhengjie.base.BaseDTO; + +import java.sql.Timestamp; +import java.math.BigDecimal; +import java.io.Serializable; + +/** +* @website https://eladmin.vip +* @description / +* @author author +* +**/ +@Data +public class PurchaseDto implements Serializable { + + /** ID */ + private Long purchaseId; + + /** 申请人 */ + private UserDto user; + + /** 申请事由 */ + private String reason; + + /** 申请金额 */ + private String fee; + + /** 货物/服务名称 */ + private String productName; + + /** 申请类型 */ + private String purchaseType; + + /** 品牌/规格/参数 */ + private String productInfo; + + /** 数量 */ + private BigDecimal productCount; + + /** 预算单价 */ + private BigDecimal productPrice; + + /** 预算总价 */ + private BigDecimal totalFee; + + /** 资金来源 */ + private String feeSource; + + /** 项目名称 */ + private String projectName; + + /** 项目编号 */ + private String projectNo; + + /** 预计采购时间 */ + private Timestamp purchaseTime; + + /** 申请时间 */ + private Timestamp applyTime; + + /** 备注 */ + private String remark; + + /** 创建者 */ + private String createBy; + + /** 更新者 */ + private String updateBy; + + /** 创建日期 */ + private Timestamp createTime; + + /** 更新时间 */ + private Timestamp updateTime; +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/PurchaseQueryCriteria.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/PurchaseQueryCriteria.java new file mode 100644 index 0000000..6f9c252 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/PurchaseQueryCriteria.java @@ -0,0 +1,33 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package me.zhengjie.modules.system.service.dto; + +import lombok.Data; +import java.util.List; +import me.zhengjie.annotation.Query; + +/** +* @website https://eladmin.vip +* @author author +* +**/ +@Data +public class PurchaseQueryCriteria{ + + /** 精确 */ + @Query + private String purchaseType; +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/ReimburseDto.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/ReimburseDto.java new file mode 100644 index 0000000..b7320a8 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/ReimburseDto.java @@ -0,0 +1,81 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package me.zhengjie.modules.system.service.dto; + +import lombok.Data; +import java.sql.Timestamp; +import java.io.Serializable; +import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson.serializer.ToStringSerializer; + +/** +* @website https://eladmin.vip +* @description / +* @author author +* +**/ +@Data +public class ReimburseDto implements Serializable { + + private Long reimburseId; + + /** 申请人 */ + private UserDto user; + + /** 申请事由 */ + private String reason; + + /** 申请金额 */ + private String fee; + + /** 报销类型 */ + private String reimburseType; + + /** 资金来源 */ + private String feeSource; + + /** 项目名称 */ + private String projectName; + + /** 项目编号 */ + private String projectNo; + + /** 项目预算 */ + private String projectFee; + + /** 花费产生时间 */ + private Timestamp reimburseTime; + + /** 申请时间 */ + private Timestamp applyTime; + + /** 备注 */ + private String remark; + + /** 创建者 */ + private String createBy; + + /** 更新者 */ + private String updateBy; + + /** 创建日期 */ + private Timestamp createTime; + + /** 更新时间 */ + private Timestamp updateTime; + + private String businessFeeType; +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/ReimburseQueryCriteria.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/ReimburseQueryCriteria.java new file mode 100644 index 0000000..4ad20af --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/ReimburseQueryCriteria.java @@ -0,0 +1,33 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package me.zhengjie.modules.system.service.dto; + +import lombok.Data; +import java.util.List; +import me.zhengjie.annotation.Query; + +/** +* @website https://eladmin.vip +* @author author +* +**/ +@Data +public class ReimburseQueryCriteria{ + + /** 精确 */ + @Query + private String reimburseType; +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/RoleDto.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/RoleDto.java new file mode 100644 index 0000000..fc74d84 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/RoleDto.java @@ -0,0 +1,63 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.dto; + +import lombok.Getter; +import lombok.Setter; +import me.zhengjie.base.BaseDTO; +import java.io.Serializable; +import java.util.Objects; +import java.util.Set; + +/** + * @author Zheng Jie + * @date 2018-11-23 + */ +@Getter +@Setter +public class RoleDto extends BaseDTO implements Serializable { + + private Long id; + + private Set menus; + + private Set depts; + + private String name; + + private String dataScope; + + private Integer level; + + private String description; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + RoleDto roleDto = (RoleDto) o; + return Objects.equals(id, roleDto.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/RoleQueryCriteria.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/RoleQueryCriteria.java new file mode 100644 index 0000000..5454fc4 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/RoleQueryCriteria.java @@ -0,0 +1,36 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.dto; + +import lombok.Data; +import me.zhengjie.annotation.Query; + +import java.sql.Timestamp; +import java.util.List; + +/** + * @author Zheng Jie + * 公共查询类 + */ +@Data +public class RoleQueryCriteria { + + @Query(blurry = "name,description") + private String blurry; + + @Query(type = Query.Type.BETWEEN) + private List createTime; +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/RoleSmallDto.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/RoleSmallDto.java new file mode 100644 index 0000000..99215e6 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/RoleSmallDto.java @@ -0,0 +1,35 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.dto; + +import lombok.Data; +import java.io.Serializable; + +/** + * @author Zheng Jie + * @date 2018-11-23 + */ +@Data +public class RoleSmallDto implements Serializable { + + private Long id; + + private String name; + + private Integer level; + + private String dataScope; +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/UserBankDto.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/UserBankDto.java new file mode 100644 index 0000000..ec0d208 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/UserBankDto.java @@ -0,0 +1,61 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package me.zhengjie.modules.system.service.dto; + +import lombok.Data; +import java.sql.Timestamp; +import java.io.Serializable; + +/** +* @website https://eladmin.vip +* @description / +* @author author +* @date 2023-10-31 +**/ +@Data +public class UserBankDto implements Serializable { + + /** ID */ + private Long bankId; + + /** 所属用户 */ + private Long userId; + + /** 银行卡号 */ + private String bankNo; + + /** 银行卡所属支行 */ + private String bankName; + + /** 是否常用账号 */ + private String bankUse; + + /** 备注 */ + private String remark; + + /** 创建者 */ + private String createBy; + + /** 更新者 */ + private String updateBy; + + /** 创建日期 */ + private Timestamp createTime; + + /** 更新时间 */ + private Timestamp updateTime; + private UserDto user; +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/UserBankQueryCriteria.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/UserBankQueryCriteria.java new file mode 100644 index 0000000..410648f --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/UserBankQueryCriteria.java @@ -0,0 +1,29 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package me.zhengjie.modules.system.service.dto; + +import lombok.Data; +import java.util.List; +import me.zhengjie.annotation.Query; + +/** +* @website https://eladmin.vip +* @author author +* @date 2023-10-31 +**/ +@Data +public class UserBankQueryCriteria{ +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/UserDto.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/UserDto.java new file mode 100644 index 0000000..d8a0173 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/UserDto.java @@ -0,0 +1,68 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.dto; + +import com.alibaba.fastjson.annotation.JSONField; +import lombok.Getter; +import lombok.Setter; +import me.zhengjie.base.BaseDTO; +import java.io.Serializable; +import java.util.Date; +import java.util.Set; + +/** + * @author Zheng Jie + * @date 2018-11-23 + */ +@Getter +@Setter +public class UserDto extends BaseDTO implements Serializable { + + private Long id; + + private Set roles; + + private Set jobs; + + private DeptSmallDto dept; + + private Long deptId; + + private String username; + + private String nickName; + + private String email; + + private String phone; + + private String gender; + + private String avatarName; + + private String avatarPath; + + @JSONField(serialize = false) + private String password; + + private Boolean enabled; + + @JSONField(serialize = false) + private Boolean isAdmin = false; + + private Date pwdResetTime; + private String idNo; +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/UserLoginDto.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/UserLoginDto.java new file mode 100644 index 0000000..13a51f7 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/UserLoginDto.java @@ -0,0 +1,28 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.dto; + +/** + * @author Zheng Jie + * @description 用户缓存时使用 + * @date 2022-05-26 + **/ +public class UserLoginDto extends UserDto { + + private String password; + + private Boolean isAdmin; +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/UserQueryCriteria.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/UserQueryCriteria.java new file mode 100644 index 0000000..ad8e775 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/UserQueryCriteria.java @@ -0,0 +1,49 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.dto; + +import lombok.Data; +import me.zhengjie.annotation.Query; +import java.io.Serializable; +import java.sql.Timestamp; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @author Zheng Jie + * @date 2018-11-23 + */ +@Data +public class UserQueryCriteria implements Serializable { + + @Query + private Long id; + + @Query(propName = "id", type = Query.Type.IN, joinName = "dept") + private Set deptIds = new HashSet<>(); + + @Query(blurry = "email,username,nickName") + private String blurry; + + @Query + private Boolean enabled; + + private Long deptId; + + @Query(type = Query.Type.BETWEEN) + private List createTime; +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/DataServiceImpl.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/DataServiceImpl.java new file mode 100644 index 0000000..782fbd8 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/DataServiceImpl.java @@ -0,0 +1,91 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.impl; + +import lombok.RequiredArgsConstructor; +import me.zhengjie.modules.system.domain.Dept; +import me.zhengjie.modules.system.service.DataService; +import me.zhengjie.modules.system.service.DeptService; +import me.zhengjie.modules.system.service.RoleService; +import me.zhengjie.modules.system.service.dto.RoleSmallDto; +import me.zhengjie.modules.system.service.dto.UserDto; +import me.zhengjie.utils.enums.DataScopeEnum; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import java.util.*; + +/** + * @author Zheng Jie + * @website https://eladmin.vip + * @description 数据权限服务实现 + * @date 2020-05-07 + **/ +@Service +@RequiredArgsConstructor +@CacheConfig(cacheNames = "data") +public class DataServiceImpl implements DataService { + + private final RoleService roleService; + private final DeptService deptService; + + /** + * 用户角色和用户部门改变时需清理缓存 + * @param user / + * @return / + */ + @Override + @Cacheable(key = "'user:' + #p0.id") + public List getDeptIds(UserDto user) { + // 用于存储部门id + Set deptIds = new HashSet<>(); + // 查询用户角色 + List roleSet = roleService.findByUsersId(user.getId()); + // 获取对应的部门ID + for (RoleSmallDto role : roleSet) { + DataScopeEnum dataScopeEnum = DataScopeEnum.find(role.getDataScope()); + switch (Objects.requireNonNull(dataScopeEnum)) { + case THIS_LEVEL: + deptIds.add(user.getDept().getId()); + break; + case CUSTOMIZE: + deptIds.addAll(getCustomize(deptIds, role)); + break; + default: + return new ArrayList<>(); + } + } + return new ArrayList<>(deptIds); + } + + /** + * 获取自定义的数据权限 + * @param deptIds 部门ID + * @param role 角色 + * @return 数据权限ID + */ + public Set getCustomize(Set deptIds, RoleSmallDto role){ + Set depts = deptService.findByRoleId(role.getId()); + for (Dept dept : depts) { + deptIds.add(dept.getId()); + List deptChildren = deptService.findByPid(dept.getId()); + if (deptChildren != null && deptChildren.size() != 0) { + deptIds.addAll(deptService.getDeptChildren(deptChildren)); + } + } + return deptIds; + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/DeptServiceImpl.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/DeptServiceImpl.java new file mode 100644 index 0000000..a02bd69 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/DeptServiceImpl.java @@ -0,0 +1,283 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import lombok.RequiredArgsConstructor; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.modules.system.domain.Dept; +import me.zhengjie.modules.system.domain.User; +import me.zhengjie.modules.system.repository.RoleRepository; +import me.zhengjie.modules.system.repository.UserRepository; +import me.zhengjie.modules.system.service.dto.DeptDto; +import me.zhengjie.modules.system.service.dto.DeptQueryCriteria; +import me.zhengjie.utils.*; +import me.zhengjie.modules.system.repository.DeptRepository; +import me.zhengjie.modules.system.service.DeptService; +import me.zhengjie.modules.system.service.mapstruct.DeptMapper; +import me.zhengjie.utils.enums.DataScopeEnum; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.*; +import java.util.stream.Collectors; + +/** +* @author Zheng Jie +* @date 2019-03-25 +*/ +@Service +@RequiredArgsConstructor +@CacheConfig(cacheNames = "dept") +public class DeptServiceImpl implements DeptService { + + private final DeptRepository deptRepository; + private final DeptMapper deptMapper; + private final UserRepository userRepository; + private final RedisUtils redisUtils; + private final RoleRepository roleRepository; + + @Override + public List queryAll(DeptQueryCriteria criteria, Boolean isQuery) throws Exception { + Sort sort = Sort.by(Sort.Direction.ASC, "deptSort"); + String dataScopeType = SecurityUtils.getDataScopeType(); + if (isQuery) { + if(dataScopeType.equals(DataScopeEnum.ALL.getValue())){ + criteria.setPidIsNull(true); + } + List fields = QueryHelp.getAllFields(criteria.getClass(), new ArrayList<>()); + List fieldNames = new ArrayList(){{ add("pidIsNull");add("enabled");}}; + for (Field field : fields) { + //设置对象的访问权限,保证对private的属性的访问 + field.setAccessible(true); + Object val = field.get(criteria); + if(fieldNames.contains(field.getName())){ + continue; + } + if (ObjectUtil.isNotNull(val)) { + criteria.setPidIsNull(null); + break; + } + } + } + List list = deptMapper.toDto(deptRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),sort)); + // 如果为空,就代表为自定义权限或者本级权限,就需要去重,不理解可以注释掉,看查询结果 + if(StringUtils.isBlank(dataScopeType)){ + return deduplication(list); + } + return list; + } + + @Override + @Cacheable(key = "'id:' + #p0") + public DeptDto findById(Long id) { + Dept dept = deptRepository.findById(id).orElseGet(Dept::new); + ValidationUtil.isNull(dept.getId(),"Dept","id",id); + return deptMapper.toDto(dept); + } + + @Override + public List findByPid(long pid) { + return deptRepository.findByPid(pid); + } + + @Override + public Set findByRoleId(Long id) { + return deptRepository.findByRoleId(id); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void create(Dept resources) { + deptRepository.save(resources); + // 计算子节点数目 + resources.setSubCount(0); + // 清理缓存 + updateSubCnt(resources.getPid()); + // 清理自定义角色权限的datascope缓存 + delCaches(resources.getPid()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(Dept resources) { + // 旧的部门 + Long oldPid = findById(resources.getId()).getPid(); + Long newPid = resources.getPid(); + if(resources.getPid() != null && resources.getId().equals(resources.getPid())) { + throw new BadRequestException("上级不能为自己"); + } + Dept dept = deptRepository.findById(resources.getId()).orElseGet(Dept::new); + ValidationUtil.isNull( dept.getId(),"Dept","id",resources.getId()); + resources.setId(dept.getId()); + deptRepository.save(resources); + // 更新父节点中子节点数目 + updateSubCnt(oldPid); + updateSubCnt(newPid); + // 清理缓存 + delCaches(resources.getId()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Set deptDtos) { + for (DeptDto deptDto : deptDtos) { + // 清理缓存 + delCaches(deptDto.getId()); + deptRepository.deleteById(deptDto.getId()); + updateSubCnt(deptDto.getPid()); + } + } + + @Override + public void download(List deptDtos, HttpServletResponse response) throws IOException { + List> list = new ArrayList<>(); + for (DeptDto deptDTO : deptDtos) { + Map map = new LinkedHashMap<>(); + map.put("部门名称", deptDTO.getName()); + map.put("部门状态", deptDTO.getEnabled() ? "启用" : "停用"); + map.put("创建日期", deptDTO.getCreateTime()); + list.add(map); + } + FileUtil.downloadExcel(list, response); + } + + @Override + public Set getDeleteDepts(List menuList, Set deptDtos) { + for (Dept dept : menuList) { + deptDtos.add(deptMapper.toDto(dept)); + List depts = deptRepository.findByPid(dept.getId()); + if(depts!=null && depts.size()!=0){ + getDeleteDepts(depts, deptDtos); + } + } + return deptDtos; + } + + @Override + public List getDeptChildren(List deptList) { + List list = new ArrayList<>(); + deptList.forEach(dept -> { + if (dept!=null && dept.getEnabled()) { + List depts = deptRepository.findByPid(dept.getId()); + if (depts.size() != 0) { + list.addAll(getDeptChildren(depts)); + } + list.add(dept.getId()); + } + } + ); + return list; + } + + @Override + public List getSuperior(DeptDto deptDto, List depts) { + if(deptDto.getPid() == null){ + depts.addAll(deptRepository.findByPidIsNull()); + return deptMapper.toDto(depts); + } + depts.addAll(deptRepository.findByPid(deptDto.getPid())); + return getSuperior(findById(deptDto.getPid()), depts); + } + + @Override + public Object buildTree(List deptDtos) { + Set trees = new LinkedHashSet<>(); + Set depts= new LinkedHashSet<>(); + List deptNames = deptDtos.stream().map(DeptDto::getName).collect(Collectors.toList()); + boolean isChild; + for (DeptDto deptDTO : deptDtos) { + isChild = false; + if (deptDTO.getPid() == null) { + trees.add(deptDTO); + } + for (DeptDto it : deptDtos) { + if (it.getPid() != null && deptDTO.getId().equals(it.getPid())) { + isChild = true; + if (deptDTO.getChildren() == null) { + deptDTO.setChildren(new ArrayList<>()); + } + deptDTO.getChildren().add(it); + } + } + if(isChild) { + depts.add(deptDTO); + } else if(deptDTO.getPid() != null && !deptNames.contains(findById(deptDTO.getPid()).getName())) { + depts.add(deptDTO); + } + } + + if (CollectionUtil.isEmpty(trees)) { + trees = depts; + } + Map map = new HashMap<>(2); + map.put("totalElements",deptDtos.size()); + map.put("content",CollectionUtil.isEmpty(trees)? deptDtos :trees); + return map; + } + + @Override + public void verification(Set deptDtos) { + Set deptIds = deptDtos.stream().map(DeptDto::getId).collect(Collectors.toSet()); + if(userRepository.countByDepts(deptIds) > 0){ + throw new BadRequestException("所选部门存在用户关联,请解除后再试!"); + } + if(roleRepository.countByDepts(deptIds) > 0){ + throw new BadRequestException("所选部门存在角色关联,请解除后再试!"); + } + } + + private void updateSubCnt(Long deptId){ + if(deptId != null){ + int count = deptRepository.countByPid(deptId); + deptRepository.updateSubCntById(count, deptId); + } + } + + private List deduplication(List list) { + List deptDtos = new ArrayList<>(); + for (DeptDto deptDto : list) { + boolean flag = true; + for (DeptDto dto : list) { + if (dto.getId().equals(deptDto.getPid())) { + flag = false; + break; + } + } + if (flag){ + deptDtos.add(deptDto); + } + } + return deptDtos; + } + + /** + * 清理缓存 + * @param id / + */ + public void delCaches(Long id){ + List users = userRepository.findByRoleDeptId(id); + // 删除数据权限 + redisUtils.delByKeys(CacheKey.DATA_USER, users.stream().map(User::getId).collect(Collectors.toSet())); + redisUtils.del(CacheKey.DEPT_ID + id); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/DictDetailServiceImpl.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/DictDetailServiceImpl.java new file mode 100644 index 0000000..5da4d05 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/DictDetailServiceImpl.java @@ -0,0 +1,95 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.impl; + +import lombok.RequiredArgsConstructor; +import me.zhengjie.utils.PageResult; +import me.zhengjie.modules.system.domain.Dict; +import me.zhengjie.modules.system.domain.DictDetail; +import me.zhengjie.modules.system.repository.DictRepository; +import me.zhengjie.modules.system.service.dto.DictDetailQueryCriteria; +import me.zhengjie.utils.*; +import me.zhengjie.modules.system.repository.DictDetailRepository; +import me.zhengjie.modules.system.service.DictDetailService; +import me.zhengjie.modules.system.service.dto.DictDetailDto; +import me.zhengjie.modules.system.service.mapstruct.DictDetailMapper; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import java.util.List; + +/** +* @author Zheng Jie +* @date 2019-04-10 +*/ +@Service +@RequiredArgsConstructor +@CacheConfig(cacheNames = "dict") +public class DictDetailServiceImpl implements DictDetailService { + + private final DictRepository dictRepository; + private final DictDetailRepository dictDetailRepository; + private final DictDetailMapper dictDetailMapper; + private final RedisUtils redisUtils; + + @Override + public PageResult queryAll(DictDetailQueryCriteria criteria, Pageable pageable) { + Page page = dictDetailRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable); + return PageUtil.toPage(page.map(dictDetailMapper::toDto)); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void create(DictDetail resources) { + dictDetailRepository.save(resources); + // 清理缓存 + delCaches(resources); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(DictDetail resources) { + DictDetail dictDetail = dictDetailRepository.findById(resources.getId()).orElseGet(DictDetail::new); + ValidationUtil.isNull( dictDetail.getId(),"DictDetail","id",resources.getId()); + resources.setId(dictDetail.getId()); + dictDetailRepository.save(resources); + // 清理缓存 + delCaches(resources); + } + + @Override + @Cacheable(key = "'name:' + #p0") + public List getDictByName(String name) { + return dictDetailMapper.toDto(dictDetailRepository.findByDictName(name)); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Long id) { + DictDetail dictDetail = dictDetailRepository.findById(id).orElseGet(DictDetail::new); + // 清理缓存 + delCaches(dictDetail); + dictDetailRepository.deleteById(id); + } + + public void delCaches(DictDetail dictDetail){ + Dict dict = dictRepository.findById(dictDetail.getDict().getId()).orElseGet(Dict::new); + redisUtils.del(CacheKey.DICT_NAME + dict.getName()); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/DictServiceImpl.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/DictServiceImpl.java new file mode 100644 index 0000000..6826512 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/DictServiceImpl.java @@ -0,0 +1,122 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import lombok.RequiredArgsConstructor; +import me.zhengjie.utils.PageResult; +import me.zhengjie.modules.system.domain.Dict; +import me.zhengjie.modules.system.service.dto.DictDetailDto; +import me.zhengjie.modules.system.service.dto.DictQueryCriteria; +import me.zhengjie.utils.*; +import me.zhengjie.modules.system.repository.DictRepository; +import me.zhengjie.modules.system.service.DictService; +import me.zhengjie.modules.system.service.dto.DictDto; +import me.zhengjie.modules.system.service.mapstruct.DictMapper; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.*; + +/** +* @author Zheng Jie +* @date 2019-04-10 +*/ +@Service +@RequiredArgsConstructor +@CacheConfig(cacheNames = "dict") +public class DictServiceImpl implements DictService { + + private final DictRepository dictRepository; + private final DictMapper dictMapper; + private final RedisUtils redisUtils; + + @Override + public PageResult queryAll(DictQueryCriteria dict, Pageable pageable){ + Page page = dictRepository.findAll((root, query, cb) -> QueryHelp.getPredicate(root, dict, cb), pageable); + return PageUtil.toPage(page.map(dictMapper::toDto)); + } + + @Override + public List queryAll(DictQueryCriteria dict) { + List list = dictRepository.findAll((root, query, cb) -> QueryHelp.getPredicate(root, dict, cb)); + return dictMapper.toDto(list); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void create(Dict resources) { + dictRepository.save(resources); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(Dict resources) { + // 清理缓存 + delCaches(resources); + Dict dict = dictRepository.findById(resources.getId()).orElseGet(Dict::new); + ValidationUtil.isNull( dict.getId(),"Dict","id",resources.getId()); + dict.setName(resources.getName()); + dict.setDescription(resources.getDescription()); + dictRepository.save(dict); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Set ids) { + // 清理缓存 + List dicts = dictRepository.findByIdIn(ids); + for (Dict dict : dicts) { + delCaches(dict); + } + dictRepository.deleteByIdIn(ids); + } + + @Override + public void download(List dictDtos, HttpServletResponse response) throws IOException { + List> list = new ArrayList<>(); + for (DictDto dictDTO : dictDtos) { + if(CollectionUtil.isNotEmpty(dictDTO.getDictDetails())){ + for (DictDetailDto dictDetail : dictDTO.getDictDetails()) { + Map map = new LinkedHashMap<>(); + map.put("字典名称", dictDTO.getName()); + map.put("字典描述", dictDTO.getDescription()); + map.put("字典标签", dictDetail.getLabel()); + map.put("字典值", dictDetail.getValue()); + map.put("创建日期", dictDetail.getCreateTime()); + list.add(map); + } + } else { + Map map = new LinkedHashMap<>(); + map.put("字典名称", dictDTO.getName()); + map.put("字典描述", dictDTO.getDescription()); + map.put("字典标签", null); + map.put("字典值", null); + map.put("创建日期", dictDTO.getCreateTime()); + list.add(map); + } + } + FileUtil.downloadExcel(list, response); + } + + public void delCaches(Dict dict){ + redisUtils.del(CacheKey.DICT_NAME + dict.getName()); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/JobServiceImpl.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/JobServiceImpl.java new file mode 100644 index 0000000..a608b97 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/JobServiceImpl.java @@ -0,0 +1,126 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.impl; + +import lombok.RequiredArgsConstructor; +import me.zhengjie.utils.PageResult; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.exception.EntityExistException; +import me.zhengjie.modules.system.domain.Job; +import me.zhengjie.modules.system.repository.UserRepository; +import me.zhengjie.modules.system.service.dto.JobQueryCriteria; +import me.zhengjie.utils.*; +import me.zhengjie.modules.system.repository.JobRepository; +import me.zhengjie.modules.system.service.JobService; +import me.zhengjie.modules.system.service.dto.JobDto; +import me.zhengjie.modules.system.service.mapstruct.JobMapper; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.*; + +/** +* @author Zheng Jie +* @date 2019-03-29 +*/ +@Service +@RequiredArgsConstructor +@CacheConfig(cacheNames = "job") +public class JobServiceImpl implements JobService { + + private final JobRepository jobRepository; + private final JobMapper jobMapper; + private final RedisUtils redisUtils; + private final UserRepository userRepository; + + @Override + public PageResult queryAll(JobQueryCriteria criteria, Pageable pageable) { + Page page = jobRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable); + return PageUtil.toPage(page.map(jobMapper::toDto).getContent(),page.getTotalElements()); + } + + @Override + public List queryAll(JobQueryCriteria criteria) { + List list = jobRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder)); + return jobMapper.toDto(list); + } + + @Override + @Cacheable(key = "'id:' + #p0") + public JobDto findById(Long id) { + Job job = jobRepository.findById(id).orElseGet(Job::new); + ValidationUtil.isNull(job.getId(),"Job","id",id); + return jobMapper.toDto(job); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void create(Job resources) { + Job job = jobRepository.findByName(resources.getName()); + if(job != null){ + throw new EntityExistException(Job.class,"name",resources.getName()); + } + jobRepository.save(resources); + } + + @Override + @CacheEvict(key = "'id:' + #p0.id") + @Transactional(rollbackFor = Exception.class) + public void update(Job resources) { + Job job = jobRepository.findById(resources.getId()).orElseGet(Job::new); + Job old = jobRepository.findByName(resources.getName()); + if(old != null && !old.getId().equals(resources.getId())){ + throw new EntityExistException(Job.class,"name",resources.getName()); + } + ValidationUtil.isNull( job.getId(),"Job","id",resources.getId()); + resources.setId(job.getId()); + jobRepository.save(resources); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Set ids) { + jobRepository.deleteAllByIdIn(ids); + // 删除缓存 + redisUtils.delByKeys(CacheKey.JOB_ID, ids); + } + + @Override + public void download(List jobDtos, HttpServletResponse response) throws IOException { + List> list = new ArrayList<>(); + for (JobDto jobDTO : jobDtos) { + Map map = new LinkedHashMap<>(); + map.put("岗位名称", jobDTO.getName()); + map.put("岗位状态", jobDTO.getEnabled() ? "启用" : "停用"); + map.put("创建日期", jobDTO.getCreateTime()); + list.add(map); + } + FileUtil.downloadExcel(list, response); + } + + @Override + public void verification(Set ids) { + if(userRepository.countByJobs(ids) > 0){ + throw new BadRequestException("所选的岗位中存在用户关联,请解除关联再试!"); + } + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/MenuServiceImpl.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/MenuServiceImpl.java new file mode 100644 index 0000000..ca8297e --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/MenuServiceImpl.java @@ -0,0 +1,359 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import lombok.RequiredArgsConstructor; +import me.zhengjie.modules.system.domain.Menu; +import me.zhengjie.modules.system.domain.Role; +import me.zhengjie.modules.system.domain.User; +import me.zhengjie.modules.system.domain.vo.MenuMetaVo; +import me.zhengjie.modules.system.domain.vo.MenuVo; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.exception.EntityExistException; +import me.zhengjie.modules.system.repository.MenuRepository; +import me.zhengjie.modules.system.repository.UserRepository; +import me.zhengjie.modules.system.service.MenuService; +import me.zhengjie.modules.system.service.RoleService; +import me.zhengjie.modules.system.service.dto.MenuDto; +import me.zhengjie.modules.system.service.dto.MenuQueryCriteria; +import me.zhengjie.modules.system.service.dto.RoleSmallDto; +import me.zhengjie.modules.system.service.mapstruct.MenuMapper; +import me.zhengjie.utils.*; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author Zheng Jie + */ +@Service +@RequiredArgsConstructor +@CacheConfig(cacheNames = "menu") +public class MenuServiceImpl implements MenuService { + + private final MenuRepository menuRepository; + private final UserRepository userRepository; + private final MenuMapper menuMapper; + private final RoleService roleService; + private final RedisUtils redisUtils; + + private static final String HTTP_PRE = "http://"; + private static final String HTTPS_PRE = "https://"; + private static final String YES_STR = "是"; + private static final String NO_STR = "否"; + private static final String BAD_REQUEST = "外链必须以http://或者https://开头"; + + @Override + public List queryAll(MenuQueryCriteria criteria, Boolean isQuery) throws Exception { + Sort sort = Sort.by(Sort.Direction.ASC, "menuSort"); + if(Boolean.TRUE.equals(isQuery)){ + criteria.setPidIsNull(true); + List fields = QueryHelp.getAllFields(criteria.getClass(), new ArrayList<>()); + for (Field field : fields) { + //设置对象的访问权限,保证对private的属性的访问 + field.setAccessible(true); + Object val = field.get(criteria); + if("pidIsNull".equals(field.getName())){ + continue; + } + if (ObjectUtil.isNotNull(val)) { + criteria.setPidIsNull(null); + break; + } + } + } + return menuMapper.toDto(menuRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),sort)); + } + + @Override + @Cacheable(key = "'id:' + #p0") + public MenuDto findById(long id) { + Menu menu = menuRepository.findById(id).orElseGet(Menu::new); + ValidationUtil.isNull(menu.getId(),"Menu","id",id); + return menuMapper.toDto(menu); + } + + /** + * 用户角色改变时需清理缓存 + * @param currentUserId / + * @return / + */ + @Override + @Cacheable(key = "'user:' + #p0") + public List findByUser(Long currentUserId) { + List roles = roleService.findByUsersId(currentUserId); + Set roleIds = roles.stream().map(RoleSmallDto::getId).collect(Collectors.toSet()); + LinkedHashSet menus = menuRepository.findByRoleIdsAndTypeNot(roleIds, 2); + return menus.stream().map(menuMapper::toDto).collect(Collectors.toList()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void create(Menu resources) { + if(menuRepository.findByTitle(resources.getTitle()) != null){ + throw new EntityExistException(Menu.class,"title",resources.getTitle()); + } + if(StringUtils.isNotBlank(resources.getComponentName())){ + if(menuRepository.findByComponentName(resources.getComponentName()) != null){ + throw new EntityExistException(Menu.class,"componentName",resources.getComponentName()); + } + } + if (Long.valueOf(0L).equals(resources.getPid())) { + resources.setPid(null); + } + if(resources.getIFrame()){ + if (!(resources.getPath().toLowerCase().startsWith(HTTP_PRE)||resources.getPath().toLowerCase().startsWith(HTTPS_PRE))) { + throw new BadRequestException(BAD_REQUEST); + } + } + menuRepository.save(resources); + // 计算子节点数目 + resources.setSubCount(0); + // 更新父节点菜单数目 + updateSubCnt(resources.getPid()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(Menu resources) { + if(resources.getId().equals(resources.getPid())) { + throw new BadRequestException("上级不能为自己"); + } + Menu menu = menuRepository.findById(resources.getId()).orElseGet(Menu::new); + ValidationUtil.isNull(menu.getId(),"Permission","id",resources.getId()); + + if(resources.getIFrame()){ + if (!(resources.getPath().toLowerCase().startsWith(HTTP_PRE)||resources.getPath().toLowerCase().startsWith(HTTPS_PRE))) { + throw new BadRequestException(BAD_REQUEST); + } + } + Menu menu1 = menuRepository.findByTitle(resources.getTitle()); + + if(menu1 != null && !menu1.getId().equals(menu.getId())){ + throw new EntityExistException(Menu.class,"title",resources.getTitle()); + } + + if(resources.getPid().equals(0L)){ + resources.setPid(null); + } + + // 记录的父节点ID + Long oldPid = menu.getPid(); + Long newPid = resources.getPid(); + + if(StringUtils.isNotBlank(resources.getComponentName())){ + menu1 = menuRepository.findByComponentName(resources.getComponentName()); + if(menu1 != null && !menu1.getId().equals(menu.getId())){ + throw new EntityExistException(Menu.class,"componentName",resources.getComponentName()); + } + } + menu.setTitle(resources.getTitle()); + menu.setComponent(resources.getComponent()); + menu.setPath(resources.getPath()); + menu.setIcon(resources.getIcon()); + menu.setIFrame(resources.getIFrame()); + menu.setPid(resources.getPid()); + menu.setMenuSort(resources.getMenuSort()); + menu.setCache(resources.getCache()); + menu.setHidden(resources.getHidden()); + menu.setComponentName(resources.getComponentName()); + menu.setPermission(resources.getPermission()); + menu.setType(resources.getType()); + menuRepository.save(menu); + // 计算父级菜单节点数目 + updateSubCnt(oldPid); + updateSubCnt(newPid); + // 清理缓存 + delCaches(resources.getId()); + } + + @Override + public Set getChildMenus(List menuList, Set menuSet) { + for (Menu menu : menuList) { + menuSet.add(menu); + List menus = menuRepository.findByPidOrderByMenuSort(menu.getId()); + if(menus!=null && menus.size()!=0){ + getChildMenus(menus, menuSet); + } + } + return menuSet; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Set menuSet) { + for (Menu menu : menuSet) { + // 清理缓存 + delCaches(menu.getId()); + roleService.untiedMenu(menu.getId()); + menuRepository.deleteById(menu.getId()); + updateSubCnt(menu.getPid()); + } + } + + @Override + public List getMenus(Long pid) { + List menus; + if(pid != null && !pid.equals(0L)){ + menus = menuRepository.findByPidOrderByMenuSort(pid); + } else { + menus = menuRepository.findByPidIsNullOrderByMenuSort(); + } + return menuMapper.toDto(menus); + } + + @Override + public List getSuperior(MenuDto menuDto, List menus) { + if(menuDto.getPid() == null){ + menus.addAll(menuRepository.findByPidIsNullOrderByMenuSort()); + return menuMapper.toDto(menus); + } + menus.addAll(menuRepository.findByPidOrderByMenuSort(menuDto.getPid())); + return getSuperior(findById(menuDto.getPid()), menus); + } + + @Override + public List buildTree(List menuDtos) { + List trees = new ArrayList<>(); + Set ids = new HashSet<>(); + for (MenuDto menuDTO : menuDtos) { + if (menuDTO.getPid() == null) { + trees.add(menuDTO); + } + for (MenuDto it : menuDtos) { + if (menuDTO.getId().equals(it.getPid())) { + if (menuDTO.getChildren() == null) { + menuDTO.setChildren(new ArrayList<>()); + } + menuDTO.getChildren().add(it); + ids.add(it.getId()); + } + } + } + if(trees.size() == 0){ + trees = menuDtos.stream().filter(s -> !ids.contains(s.getId())).collect(Collectors.toList()); + } + return trees; + } + + @Override + public List buildMenus(List menuDtos) { + List list = new LinkedList<>(); + menuDtos.forEach(menuDTO -> { + if (menuDTO!=null){ + List menuDtoList = menuDTO.getChildren(); + MenuVo menuVo = new MenuVo(); + menuVo.setName(ObjectUtil.isNotEmpty(menuDTO.getComponentName()) ? menuDTO.getComponentName() : menuDTO.getTitle()); + // 一级目录需要加斜杠,不然会报警告 + menuVo.setPath(menuDTO.getPid() == null ? "/" + menuDTO.getPath() :menuDTO.getPath()); + menuVo.setHidden(menuDTO.getHidden()); + // 如果不是外链 + if(!menuDTO.getIFrame()){ + if(menuDTO.getPid() == null){ + menuVo.setComponent(StringUtils.isEmpty(menuDTO.getComponent())?"Layout":menuDTO.getComponent()); + // 如果不是一级菜单,并且菜单类型为目录,则代表是多级菜单 + }else if(menuDTO.getType() == 0){ + menuVo.setComponent(StringUtils.isEmpty(menuDTO.getComponent())?"ParentView":menuDTO.getComponent()); + }else if(StringUtils.isNoneBlank(menuDTO.getComponent())){ + menuVo.setComponent(menuDTO.getComponent()); + } + } + menuVo.setMeta(new MenuMetaVo(menuDTO.getTitle(),menuDTO.getIcon(),!menuDTO.getCache())); + if(CollectionUtil.isNotEmpty(menuDtoList)){ + menuVo.setAlwaysShow(true); + menuVo.setRedirect("noredirect"); + menuVo.setChildren(buildMenus(menuDtoList)); + // 处理是一级菜单并且没有子菜单的情况 + } else if(menuDTO.getPid() == null){ + MenuVo menuVo1 = new MenuVo(); + menuVo1.setMeta(menuVo.getMeta()); + // 非外链 + if(!menuDTO.getIFrame()){ + menuVo1.setPath("index"); + menuVo1.setName(menuVo.getName()); + menuVo1.setComponent(menuVo.getComponent()); + } else { + menuVo1.setPath(menuDTO.getPath()); + } + menuVo.setName(null); + menuVo.setMeta(null); + menuVo.setComponent("Layout"); + List list1 = new ArrayList<>(); + list1.add(menuVo1); + menuVo.setChildren(list1); + } + list.add(menuVo); + } + } + ); + return list; + } + + @Override + public Menu findOne(Long id) { + Menu menu = menuRepository.findById(id).orElseGet(Menu::new); + ValidationUtil.isNull(menu.getId(),"Menu","id",id); + return menu; + } + + @Override + public void download(List menuDtos, HttpServletResponse response) throws IOException { + List> list = new ArrayList<>(); + for (MenuDto menuDTO : menuDtos) { + Map map = new LinkedHashMap<>(); + map.put("菜单标题", menuDTO.getTitle()); + map.put("菜单类型", menuDTO.getType() == null ? "目录" : menuDTO.getType() == 1 ? "菜单" : "按钮"); + map.put("权限标识", menuDTO.getPermission()); + map.put("外链菜单", menuDTO.getIFrame() ? YES_STR : NO_STR); + map.put("菜单可见", menuDTO.getHidden() ? NO_STR : YES_STR); + map.put("是否缓存", menuDTO.getCache() ? YES_STR : NO_STR); + map.put("创建日期", menuDTO.getCreateTime()); + list.add(map); + } + FileUtil.downloadExcel(list, response); + } + + private void updateSubCnt(Long menuId){ + if(menuId != null){ + int count = menuRepository.countByPid(menuId); + menuRepository.updateSubCntById(count, menuId); + } + } + + /** + * 清理缓存 + * @param id 菜单ID + */ + public void delCaches(Long id){ + List users = userRepository.findByMenuId(id); + redisUtils.del(CacheKey.MENU_ID + id); + redisUtils.delByKeys(CacheKey.MENU_USER, users.stream().map(User::getId).collect(Collectors.toSet())); + // 清除 Role 缓存 + List roles = roleService.findInMenuId(new ArrayList(){{ + add(id); + }}); + redisUtils.delByKeys(CacheKey.ROLE_ID, roles.stream().map(Role::getId).collect(Collectors.toSet())); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/MonitorServiceImpl.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/MonitorServiceImpl.java new file mode 100644 index 0000000..4868bab --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/MonitorServiceImpl.java @@ -0,0 +1,192 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.impl; + +import cn.hutool.core.date.BetweenFormatter.Level; +import cn.hutool.core.date.DateUtil; +import me.zhengjie.modules.system.service.MonitorService; +import me.zhengjie.utils.ElConstant; +import me.zhengjie.utils.FileUtil; +import me.zhengjie.utils.StringUtils; +import org.springframework.stereotype.Service; +import oshi.SystemInfo; +import oshi.hardware.*; +import oshi.software.os.FileSystem; +import oshi.software.os.OSFileStore; +import oshi.software.os.OperatingSystem; +import oshi.util.FormatUtil; +import oshi.util.Util; +import java.lang.management.ManagementFactory; +import java.text.DecimalFormat; +import java.util.*; + +/** +* @author Zheng Jie +* @date 2020-05-02 +*/ +@Service +public class MonitorServiceImpl implements MonitorService { + + private final DecimalFormat df = new DecimalFormat("0.00"); + + @Override + public Map getServers(){ + Map resultMap = new LinkedHashMap<>(8); + try { + SystemInfo si = new SystemInfo(); + OperatingSystem os = si.getOperatingSystem(); + HardwareAbstractionLayer hal = si.getHardware(); + // 系统信息 + resultMap.put("sys", getSystemInfo(os)); + // cpu 信息 + resultMap.put("cpu", getCpuInfo(hal.getProcessor())); + // 内存信息 + resultMap.put("memory", getMemoryInfo(hal.getMemory())); + // 交换区信息 + resultMap.put("swap", getSwapInfo(hal.getMemory())); + // 磁盘 + resultMap.put("disk", getDiskInfo(os)); + resultMap.put("time", DateUtil.format(new Date(), "HH:mm:ss")); + } catch (Exception e) { + e.printStackTrace(); + } + return resultMap; + } + + /** + * 获取磁盘信息 + * @return / + */ + private Map getDiskInfo(OperatingSystem os) { + Map diskInfo = new LinkedHashMap<>(); + FileSystem fileSystem = os.getFileSystem(); + List fsArray = fileSystem.getFileStores(); + String osName = System.getProperty("os.name"); + long available = 0, total = 0; + for (OSFileStore fs : fsArray){ + // windows 需要将所有磁盘分区累加,linux 和 mac 直接累加会出现磁盘重复的问题,待修复 + if(osName.toLowerCase().startsWith(ElConstant.WIN)) { + available += fs.getUsableSpace(); + total += fs.getTotalSpace(); + } else { + available = fs.getUsableSpace(); + total = fs.getTotalSpace(); + break; + } + } + long used = total - available; + diskInfo.put("total", total > 0 ? FileUtil.getSize(total) : "?"); + diskInfo.put("available", FileUtil.getSize(available)); + diskInfo.put("used", FileUtil.getSize(used)); + if(total != 0){ + diskInfo.put("usageRate", df.format(used/(double)total * 100)); + } else { + diskInfo.put("usageRate", 0); + } + return diskInfo; + } + + /** + * 获取交换区信息 + * @param memory / + * @return / + */ + private Map getSwapInfo(GlobalMemory memory) { + Map swapInfo = new LinkedHashMap<>(); + VirtualMemory virtualMemory = memory.getVirtualMemory(); + long total = virtualMemory.getSwapTotal(); + long used = virtualMemory.getSwapUsed(); + swapInfo.put("total", FormatUtil.formatBytes(total)); + swapInfo.put("used", FormatUtil.formatBytes(used)); + swapInfo.put("available", FormatUtil.formatBytes(total - used)); + if(used == 0){ + swapInfo.put("usageRate", 0); + } else { + swapInfo.put("usageRate", df.format(used/(double)total * 100)); + } + return swapInfo; + } + + /** + * 获取内存信息 + * @param memory / + * @return / + */ + private Map getMemoryInfo(GlobalMemory memory) { + Map memoryInfo = new LinkedHashMap<>(); + memoryInfo.put("total", FormatUtil.formatBytes(memory.getTotal())); + memoryInfo.put("available", FormatUtil.formatBytes(memory.getAvailable())); + memoryInfo.put("used", FormatUtil.formatBytes(memory.getTotal() - memory.getAvailable())); + memoryInfo.put("usageRate", df.format((memory.getTotal() - memory.getAvailable())/(double)memory.getTotal() * 100)); + return memoryInfo; + } + + /** + * 获取Cpu相关信息 + * @param processor / + * @return / + */ + private Map getCpuInfo(CentralProcessor processor) { + Map cpuInfo = new LinkedHashMap<>(); + cpuInfo.put("name", processor.getProcessorIdentifier().getName()); + cpuInfo.put("package", processor.getPhysicalPackageCount() + "个物理CPU"); + cpuInfo.put("core", processor.getPhysicalProcessorCount() + "个物理核心"); + cpuInfo.put("coreNumber", processor.getPhysicalProcessorCount()); + cpuInfo.put("logic", processor.getLogicalProcessorCount() + "个逻辑CPU"); + // CPU信息 + long[] prevTicks = processor.getSystemCpuLoadTicks(); + // 默认等待300毫秒... + long time = 300; + Util.sleep(time); + long[] ticks = processor.getSystemCpuLoadTicks(); + while (Arrays.toString(prevTicks).equals(Arrays.toString(ticks)) && time < 1000){ + time += 25; + Util.sleep(25); + ticks = processor.getSystemCpuLoadTicks(); + } + long user = ticks[CentralProcessor.TickType.USER.getIndex()] - prevTicks[CentralProcessor.TickType.USER.getIndex()]; + long nice = ticks[CentralProcessor.TickType.NICE.getIndex()] - prevTicks[CentralProcessor.TickType.NICE.getIndex()]; + long sys = ticks[CentralProcessor.TickType.SYSTEM.getIndex()] - prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()]; + long idle = ticks[CentralProcessor.TickType.IDLE.getIndex()] - prevTicks[CentralProcessor.TickType.IDLE.getIndex()]; + long iowait = ticks[CentralProcessor.TickType.IOWAIT.getIndex()] - prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()]; + long irq = ticks[CentralProcessor.TickType.IRQ.getIndex()] - prevTicks[CentralProcessor.TickType.IRQ.getIndex()]; + long softirq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()] - prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()]; + long steal = ticks[CentralProcessor.TickType.STEAL.getIndex()] - prevTicks[CentralProcessor.TickType.STEAL.getIndex()]; + long totalCpu = user + nice + sys + idle + iowait + irq + softirq + steal; + cpuInfo.put("used", df.format(100d * user / totalCpu + 100d * sys / totalCpu)); + cpuInfo.put("idle", df.format(100d * idle / totalCpu)); + return cpuInfo; + } + + /** + * 获取系统相关信息,系统、运行天数、系统IP + * @param os / + * @return / + */ + private Map getSystemInfo(OperatingSystem os){ + Map systemInfo = new LinkedHashMap<>(); + // jvm 运行时间 + long time = ManagementFactory.getRuntimeMXBean().getStartTime(); + Date date = new Date(time); + // 计算项目运行时间 + String formatBetween = DateUtil.formatBetween(date, new Date(), Level.HOUR); + // 系统信息 + systemInfo.put("os", os.toString()); + systemInfo.put("day", formatBetween); + systemInfo.put("ip", StringUtils.getLocalIp()); + return systemInfo; + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/PurchaseServiceImpl.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/PurchaseServiceImpl.java new file mode 100644 index 0000000..9ca5060 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/PurchaseServiceImpl.java @@ -0,0 +1,127 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package me.zhengjie.modules.system.service.impl; + +import me.zhengjie.modules.system.domain.Purchase; +import me.zhengjie.utils.ValidationUtil; +import me.zhengjie.utils.FileUtil; +import lombok.RequiredArgsConstructor; +import me.zhengjie.modules.system.repository.PurchaseRepository; +import me.zhengjie.modules.system.service.PurchaseService; +import me.zhengjie.modules.system.service.dto.PurchaseDto; +import me.zhengjie.modules.system.service.dto.PurchaseQueryCriteria; +import me.zhengjie.modules.system.service.mapstruct.PurchaseMapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import me.zhengjie.utils.PageUtil; +import me.zhengjie.utils.QueryHelp; + +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import java.io.IOException; +import javax.servlet.http.HttpServletResponse; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import me.zhengjie.utils.PageResult; + +/** +* @website https://eladmin.vip +* @description 服务实现 +* @author author +* +**/ +@Service +@RequiredArgsConstructor +public class PurchaseServiceImpl implements PurchaseService { + + private final PurchaseRepository purchaseRepository; + private final PurchaseMapper purchaseMapper; + + @Override + public PageResult queryAll(PurchaseQueryCriteria criteria, Pageable pageable){ + Page page = purchaseRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable); + return PageUtil.toPage(page.map(purchaseMapper::toDto)); + } + + @Override + public List queryAll(PurchaseQueryCriteria criteria){ + return purchaseMapper.toDto(purchaseRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder))); + } + + @Override + @Transactional + public PurchaseDto findById(Long purchaseId) { + Purchase purchase = purchaseRepository.findById(purchaseId).orElseGet(Purchase::new); + ValidationUtil.isNull(purchase.getPurchaseId(),"Purchase","purchaseId",purchaseId); + return purchaseMapper.toDto(purchase); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void create(Purchase resources) { + resources.setApplyTime(Timestamp.valueOf(LocalDateTime.now())); + purchaseRepository.save(resources); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(Purchase resources) { + Purchase purchase = purchaseRepository.findById(resources.getPurchaseId()).orElseGet(Purchase::new); + ValidationUtil.isNull( purchase.getPurchaseId(),"Purchase","id",resources.getPurchaseId()); + purchase.copy(resources); + purchaseRepository.save(purchase); + } + + @Override + public void deleteAll(Long[] ids) { + for (Long purchaseId : ids) { + purchaseRepository.deleteById(purchaseId); + } + } + + @Override + public void download(List all, HttpServletResponse response) throws IOException { + List> list = new ArrayList<>(); + for (PurchaseDto purchase : all) { + Map map = new LinkedHashMap<>(); + map.put("申请人", purchase.getUser().getNickName()); + map.put("申请事由", purchase.getReason()); + map.put("申请金额", purchase.getFee()); + map.put("货物/服务名称", purchase.getProductName()); + map.put("申请类型", purchase.getPurchaseType()); + map.put("品牌/规格/参数", purchase.getProductInfo()); + map.put("数量", purchase.getProductCount()); + map.put("预算单价", purchase.getProductPrice()); + map.put("预算总价", purchase.getTotalFee()); + map.put("资金来源", purchase.getFeeSource()); + map.put("项目名称", purchase.getProjectName()); + map.put("项目编号", purchase.getProjectNo()); + map.put("预计采购时间", purchase.getPurchaseTime()); + map.put("申请时间", purchase.getApplyTime()); + map.put("备注", purchase.getRemark()); + map.put("创建者", purchase.getCreateBy()); + map.put("更新者", purchase.getUpdateBy()); + map.put("创建日期", purchase.getCreateTime()); + map.put("更新时间", purchase.getUpdateTime()); + list.add(map); + } + FileUtil.downloadExcel(list, response); + } +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/ReimburseServiceImpl.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/ReimburseServiceImpl.java new file mode 100644 index 0000000..43a10a5 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/ReimburseServiceImpl.java @@ -0,0 +1,124 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package me.zhengjie.modules.system.service.impl; + +import me.zhengjie.modules.system.domain.Reimburse; +import me.zhengjie.utils.ValidationUtil; +import me.zhengjie.utils.FileUtil; +import lombok.RequiredArgsConstructor; +import me.zhengjie.modules.system.repository.ReimburseRepository; +import me.zhengjie.modules.system.service.ReimburseService; +import me.zhengjie.modules.system.service.dto.ReimburseDto; +import me.zhengjie.modules.system.service.dto.ReimburseQueryCriteria; +import me.zhengjie.modules.system.service.mapstruct.ReimburseMapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import cn.hutool.core.lang.Snowflake; +import cn.hutool.core.util.IdUtil; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import me.zhengjie.utils.PageUtil; +import me.zhengjie.utils.QueryHelp; + +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import java.io.IOException; +import javax.servlet.http.HttpServletResponse; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import me.zhengjie.utils.PageResult; + +/** +* @website https://eladmin.vip +* @description 服务实现 +* @author author +* +**/ +@Service +@RequiredArgsConstructor +public class ReimburseServiceImpl implements ReimburseService { + + private final ReimburseRepository reimburseRepository; + private final ReimburseMapper reimburseMapper; + + @Override + public PageResult queryAll(ReimburseQueryCriteria criteria, Pageable pageable){ + Page page = reimburseRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable); + return PageUtil.toPage(page.map(reimburseMapper::toDto)); + } + + @Override + public List queryAll(ReimburseQueryCriteria criteria){ + return reimburseMapper.toDto(reimburseRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder))); + } + + @Override + @Transactional + public ReimburseDto findById(Long reimburseId) { + Reimburse reimburse = reimburseRepository.findById(reimburseId).orElseGet(Reimburse::new); + ValidationUtil.isNull(reimburse.getReimburseId(),"Reimburse","reimburseId",reimburseId); + return reimburseMapper.toDto(reimburse); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void create(Reimburse resources) { + resources.setApplyTime(Timestamp.valueOf(LocalDateTime.now())); + reimburseRepository.save(resources); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(Reimburse resources) { + Reimburse reimburse = reimburseRepository.findById(resources.getReimburseId()).orElseGet(Reimburse::new); + ValidationUtil.isNull( reimburse.getReimburseId(),"Reimburse","id",resources.getReimburseId()); + reimburse.copy(resources); + reimburseRepository.save(reimburse); + } + + @Override + public void deleteAll(Long[] ids) { + for (Long reimburseId : ids) { + reimburseRepository.deleteById(reimburseId); + } + } + + @Override + public void download(List all, HttpServletResponse response) throws IOException { + List> list = new ArrayList<>(); + for (ReimburseDto reimburse : all) { + Map map = new LinkedHashMap<>(); + map.put("申请人", reimburse.getUser().getNickName()); + map.put("申请事由", reimburse.getReason()); + map.put("申请金额", reimburse.getFee()); + map.put("报销类型", reimburse.getReimburseType()); + map.put("项目名称", reimburse.getProjectName()); + map.put("项目编号", reimburse.getProjectNo()); + map.put("项目预算", reimburse.getProjectFee()); + map.put("花费产生时间", reimburse.getReimburseTime()); + map.put("申请时间", reimburse.getApplyTime()); + map.put("备注", reimburse.getRemark()); + map.put("创建者", reimburse.getCreateBy()); + map.put("更新者", reimburse.getUpdateBy()); + map.put("创建日期", reimburse.getCreateTime()); + map.put("更新时间", reimburse.getUpdateTime()); + list.add(map); + } + FileUtil.downloadExcel(list, response); + } +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/RoleServiceImpl.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/RoleServiceImpl.java new file mode 100644 index 0000000..e416aa8 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/RoleServiceImpl.java @@ -0,0 +1,224 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import lombok.RequiredArgsConstructor; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.modules.security.service.UserCacheManager; +import me.zhengjie.modules.security.service.dto.AuthorityDto; +import me.zhengjie.modules.system.domain.Menu; +import me.zhengjie.modules.system.domain.Role; +import me.zhengjie.exception.EntityExistException; +import me.zhengjie.modules.system.domain.User; +import me.zhengjie.modules.system.repository.RoleRepository; +import me.zhengjie.modules.system.repository.UserRepository; +import me.zhengjie.modules.system.service.RoleService; +import me.zhengjie.modules.system.service.dto.RoleDto; +import me.zhengjie.modules.system.service.dto.RoleQueryCriteria; +import me.zhengjie.modules.system.service.dto.RoleSmallDto; +import me.zhengjie.modules.system.service.dto.UserDto; +import me.zhengjie.modules.system.service.mapstruct.RoleMapper; +import me.zhengjie.modules.system.service.mapstruct.RoleSmallMapper; +import me.zhengjie.utils.*; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author Zheng Jie + * @date 2018-12-03 + */ +@Service +@RequiredArgsConstructor +@CacheConfig(cacheNames = "role") +public class RoleServiceImpl implements RoleService { + + private final RoleRepository roleRepository; + private final RoleMapper roleMapper; + private final RoleSmallMapper roleSmallMapper; + private final RedisUtils redisUtils; + private final UserRepository userRepository; + private final UserCacheManager userCacheManager; + + @Override + public List queryAll() { + Sort sort = Sort.by(Sort.Direction.ASC, "level"); + return roleMapper.toDto(roleRepository.findAll(sort)); + } + + @Override + public List queryAll(RoleQueryCriteria criteria) { + return roleMapper.toDto(roleRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder))); + } + + @Override + public PageResult queryAll(RoleQueryCriteria criteria, Pageable pageable) { + Page page = roleRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder), pageable); + return PageUtil.toPage(page.map(roleMapper::toDto)); + } + + @Override + @Cacheable(key = "'id:' + #p0") + @Transactional(rollbackFor = Exception.class) + public RoleDto findById(long id) { + Role role = roleRepository.findById(id).orElseGet(Role::new); + ValidationUtil.isNull(role.getId(), "Role", "id", id); + return roleMapper.toDto(role); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void create(Role resources) { + if (roleRepository.findByName(resources.getName()) != null) { + throw new EntityExistException(Role.class, "username", resources.getName()); + } + roleRepository.save(resources); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(Role resources) { + Role role = roleRepository.findById(resources.getId()).orElseGet(Role::new); + ValidationUtil.isNull(role.getId(), "Role", "id", resources.getId()); + + Role role1 = roleRepository.findByName(resources.getName()); + + if (role1 != null && !role1.getId().equals(role.getId())) { + throw new EntityExistException(Role.class, "username", resources.getName()); + } + role.setName(resources.getName()); + role.setDescription(resources.getDescription()); + role.setDataScope(resources.getDataScope()); + role.setDepts(resources.getDepts()); + role.setLevel(resources.getLevel()); + roleRepository.save(role); + // 更新相关缓存 + delCaches(role.getId(), null); + } + + @Override + public void updateMenu(Role resources, RoleDto roleDTO) { + Role role = roleMapper.toEntity(roleDTO); + List users = userRepository.findByRoleId(role.getId()); + // 更新菜单 + role.setMenus(resources.getMenus()); + delCaches(resources.getId(), users); + roleRepository.save(role); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void untiedMenu(Long menuId) { + // 更新菜单 + roleRepository.untiedMenu(menuId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Set ids) { + for (Long id : ids) { + // 更新相关缓存 + delCaches(id, null); + } + roleRepository.deleteAllByIdIn(ids); + } + + @Override + public List findByUsersId(Long id) { + return roleSmallMapper.toDto(new ArrayList<>(roleRepository.findByUserId(id))); + } + + @Override + public Integer findByRoles(Set roles) { + if (roles.size() == 0) { + return Integer.MAX_VALUE; + } + Set roleDtos = new HashSet<>(); + for (Role role : roles) { + roleDtos.add(findById(role.getId())); + } + return Collections.min(roleDtos.stream().map(RoleDto::getLevel).collect(Collectors.toList())); + } + + @Override + @Cacheable(key = "'auth:' + #p0.id") + public List mapToGrantedAuthorities(UserDto user) { + Set permissions = new HashSet<>(); + // 如果是管理员直接返回 + if (user.getIsAdmin()) { + permissions.add("admin"); + return permissions.stream().map(AuthorityDto::new) + .collect(Collectors.toList()); + } + Set roles = roleRepository.findByUserId(user.getId()); + permissions = roles.stream().flatMap(role -> role.getMenus().stream()) + .map(Menu::getPermission) + .filter(StringUtils::isNotBlank).collect(Collectors.toSet()); + return permissions.stream().map(AuthorityDto::new) + .collect(Collectors.toList()); + } + + @Override + public void download(List roles, HttpServletResponse response) throws IOException { + List> list = new ArrayList<>(); + for (RoleDto role : roles) { + Map map = new LinkedHashMap<>(); + map.put("角色名称", role.getName()); + map.put("角色级别", role.getLevel()); + map.put("描述", role.getDescription()); + map.put("创建日期", role.getCreateTime()); + list.add(map); + } + FileUtil.downloadExcel(list, response); + } + + @Override + public void verification(Set ids) { + if (userRepository.countByRoles(ids) > 0) { + throw new BadRequestException("所选角色存在用户关联,请解除关联再试!"); + } + } + + @Override + public List findInMenuId(List menuIds) { + return roleRepository.findInMenuId(menuIds); + } + + /** + * 清理缓存 + * @param id / + */ + public void delCaches(Long id, List users) { + users = CollectionUtil.isEmpty(users) ? userRepository.findByRoleId(id) : users; + if (CollectionUtil.isNotEmpty(users)) { + users.forEach(item -> userCacheManager.cleanUserCache(item.getUsername())); + Set userIds = users.stream().map(User::getId).collect(Collectors.toSet()); + redisUtils.delByKeys(CacheKey.DATA_USER, userIds); + redisUtils.delByKeys(CacheKey.MENU_USER, userIds); + redisUtils.delByKeys(CacheKey.ROLE_AUTH, userIds); + } + redisUtils.del(CacheKey.ROLE_ID + id); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/UserBankServiceImpl.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/UserBankServiceImpl.java new file mode 100644 index 0000000..d20e271 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/UserBankServiceImpl.java @@ -0,0 +1,127 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package me.zhengjie.modules.system.service.impl; + +import me.zhengjie.modules.system.domain.UserBank; +import me.zhengjie.utils.ValidationUtil; +import me.zhengjie.utils.FileUtil; +import lombok.RequiredArgsConstructor; +import me.zhengjie.modules.system.repository.UserBankRepository; +import me.zhengjie.modules.system.service.UserBankService; +import me.zhengjie.modules.system.service.dto.UserBankDto; +import me.zhengjie.modules.system.service.dto.UserBankQueryCriteria; +import me.zhengjie.modules.system.service.mapstruct.UserBankMapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import me.zhengjie.utils.PageUtil; +import me.zhengjie.utils.QueryHelp; + +import java.util.*; +import java.io.IOException; +import javax.servlet.http.HttpServletResponse; + +import me.zhengjie.utils.PageResult; + +/** +* @website https://eladmin.vip +* @description 服务实现 +* @author author +* @date 2023-10-31 +**/ +@Service +@RequiredArgsConstructor +public class UserBankServiceImpl implements UserBankService { + + private final UserBankRepository userBankRepository; + private final UserBankMapper userBankMapper; + + + @Override + public PageResult queryAll(UserBankQueryCriteria criteria, Pageable pageable){ + Page page = userBankRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable); + return PageUtil.toPage(page.map(userBankMapper::toDto)); + } + + @Override + public List queryAll(UserBankQueryCriteria criteria){ + return userBankMapper.toDto(userBankRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder))); + } + + @Override + @Transactional + public UserBankDto findById(Long bankId) { + UserBank userBank = userBankRepository.findById(bankId).orElseGet(UserBank::new); + ValidationUtil.isNull(userBank.getBankId(),"UserBank","bankId",bankId); + return userBankMapper.toDto(userBank); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void create(UserBank resources) { + handleBankUse(resources); + userBankRepository.save(resources); + } + + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(UserBank resources) { + handleBankUse(resources); + UserBank userBank = userBankRepository.findById(resources.getBankId()).orElseGet(UserBank::new); + ValidationUtil.isNull( userBank.getBankId(),"UserBank","id",resources.getBankId()); + userBank.copy(resources); + userBankRepository.save(userBank); + } + + /** + * 常用银行卡处理 + * */ + private void handleBankUse(UserBank resources) { + if (Objects.equals("是", resources.getBankUse())) { + UserBank useUserBank = userBankRepository.findByBankUseAndUser("是",resources.getUser()); + useUserBank.setBankUse("否"); + userBankRepository.save(useUserBank); + } + } + + @Override + public void deleteAll(Long[] ids) { + for (Long bankId : ids) { + userBankRepository.deleteById(bankId); + } + } + + @Override + public void download(List all, HttpServletResponse response) throws IOException { + List> list = new ArrayList<>(); + for (UserBankDto userBank : all) { + Map map = new LinkedHashMap<>(); + map.put("所属用户", userBank.getUser().getNickName()); + map.put("银行卡号", userBank.getBankNo()); + map.put("银行卡所属支行", userBank.getBankName()); + map.put("是否常用账号", userBank.getBankUse()); + map.put("备注", userBank.getRemark()); + map.put("创建者", userBank.getCreateBy()); + map.put("更新者", userBank.getUpdateBy()); + map.put("创建日期", userBank.getCreateTime()); + map.put("更新时间", userBank.getUpdateTime()); + list.add(map); + } + FileUtil.downloadExcel(list, response); + } +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/UserServiceImpl.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..9a6e543 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/UserServiceImpl.java @@ -0,0 +1,277 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.impl; + +import lombok.RequiredArgsConstructor; +import me.zhengjie.utils.PageResult; +import me.zhengjie.config.FileProperties; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.modules.security.service.OnlineUserService; +import me.zhengjie.modules.security.service.UserCacheManager; +import me.zhengjie.modules.system.domain.User; +import me.zhengjie.exception.EntityExistException; +import me.zhengjie.exception.EntityNotFoundException; +import me.zhengjie.modules.system.repository.UserRepository; +import me.zhengjie.modules.system.service.UserService; +import me.zhengjie.modules.system.service.dto.*; +import me.zhengjie.modules.system.service.mapstruct.UserLoginMapper; +import me.zhengjie.modules.system.service.mapstruct.UserMapper; +import me.zhengjie.utils.*; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; +import javax.servlet.http.HttpServletResponse; +import javax.validation.constraints.NotBlank; +import java.io.File; +import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author Zheng Jie + * @date 2018-11-23 + */ +@Service +@RequiredArgsConstructor +@CacheConfig(cacheNames = "user") +public class UserServiceImpl implements UserService { + + private final UserRepository userRepository; + private final UserMapper userMapper; + private final FileProperties properties; + private final RedisUtils redisUtils; + private final UserCacheManager userCacheManager; + private final OnlineUserService onlineUserService; + private final UserLoginMapper userLoginMapper; + + @Override + public PageResult queryAll(UserQueryCriteria criteria, Pageable pageable) { + Page page = userRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder), pageable); + return PageUtil.toPage(page.map(userMapper::toDto)); + } + + @Override + public List queryAll(UserQueryCriteria criteria) { + List users = userRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder)); + return userMapper.toDto(users); + } + + @Override + @Cacheable(key = "'id:' + #p0") + @Transactional(rollbackFor = Exception.class) + public UserDto findById(long id) { + User user = userRepository.findById(id).orElseGet(User::new); + ValidationUtil.isNull(user.getId(), "User", "id", id); + return userMapper.toDto(user); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void create(User resources) { + if (userRepository.findByUsername(resources.getUsername()) != null) { + throw new EntityExistException(User.class, "username", resources.getUsername()); + } + if (userRepository.findByEmail(resources.getEmail()) != null) { + throw new EntityExistException(User.class, "email", resources.getEmail()); + } + if (userRepository.findByPhone(resources.getPhone()) != null) { + throw new EntityExistException(User.class, "phone", resources.getPhone()); + } + userRepository.save(resources); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(User resources) throws Exception { + User user = userRepository.findById(resources.getId()).orElseGet(User::new); + ValidationUtil.isNull(user.getId(), "User", "id", resources.getId()); + User user1 = userRepository.findByUsername(resources.getUsername()); + User user2 = userRepository.findByEmail(resources.getEmail()); + User user3 = userRepository.findByPhone(resources.getPhone()); + if (user1 != null && !user.getId().equals(user1.getId())) { + throw new EntityExistException(User.class, "username", resources.getUsername()); + } + if (user2 != null && !user.getId().equals(user2.getId())) { + throw new EntityExistException(User.class, "email", resources.getEmail()); + } + if (user3 != null && !user.getId().equals(user3.getId())) { + throw new EntityExistException(User.class, "phone", resources.getPhone()); + } + // 如果用户的角色改变 + if (!resources.getRoles().equals(user.getRoles())) { + redisUtils.del(CacheKey.DATA_USER + resources.getId()); + redisUtils.del(CacheKey.MENU_USER + resources.getId()); + redisUtils.del(CacheKey.ROLE_AUTH + resources.getId()); + } + // 修改部门会影响 数据权限 + if (!Objects.equals(resources.getDept(),user.getDept())) { + redisUtils.del(CacheKey.DATA_USER + resources.getId()); + } + // 如果用户被禁用,则清除用户登录信息 + if(!resources.getEnabled()){ + onlineUserService.kickOutForUsername(resources.getUsername()); + } + user.setUsername(resources.getUsername()); + user.setEmail(resources.getEmail()); + user.setEnabled(resources.getEnabled()); + user.setRoles(resources.getRoles()); + user.setDept(resources.getDept()); + user.setJobs(resources.getJobs()); + user.setPhone(resources.getPhone()); + user.setNickName(resources.getNickName()); + user.setGender(resources.getGender()); + userRepository.save(user); + // 清除缓存 + delCaches(user.getId(), user.getUsername()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateCenter(User resources) { + User user = userRepository.findById(resources.getId()).orElseGet(User::new); + User user1 = userRepository.findByPhone(resources.getPhone()); + if (user1 != null && !user.getId().equals(user1.getId())) { + throw new EntityExistException(User.class, "phone", resources.getPhone()); + } + user.setNickName(resources.getNickName()); + user.setPhone(resources.getPhone()); + user.setGender(resources.getGender()); + userRepository.save(user); + // 清理缓存 + delCaches(user.getId(), user.getUsername()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Set ids) { + for (Long id : ids) { + // 清理缓存 + UserDto user = findById(id); + delCaches(user.getId(), user.getUsername()); + } + userRepository.deleteAllByIdIn(ids); + } + + @Override + public UserDto findByName(String userName) { + User user = userRepository.findByUsername(userName); + if (user == null) { + throw new EntityNotFoundException(User.class, "name", userName); + } else { + return userMapper.toDto(user); + } + } + + @Override + public UserLoginDto getLoginData(String userName) { + User user = userRepository.findByUsername(userName); + if (user == null) { + throw new EntityNotFoundException(User.class, "name", userName); + } else { + return userLoginMapper.toDto(user); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updatePass(String username, String pass) { + userRepository.updatePass(username, pass, new Date()); + flushCache(username); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void resetPwd(Set ids, String pwd) { + userRepository.resetPwd(ids, pwd); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Map updateAvatar(MultipartFile multipartFile) { + // 文件大小验证 + FileUtil.checkSize(properties.getAvatarMaxSize(), multipartFile.getSize()); + // 验证文件上传的格式 + String image = "gif jpg png jpeg"; + String fileType = FileUtil.getExtensionName(multipartFile.getOriginalFilename()); + if(fileType != null && !image.contains(fileType)){ + throw new BadRequestException("文件格式错误!, 仅支持 " + image +" 格式"); + } + User user = userRepository.findByUsername(SecurityUtils.getCurrentUsername()); + String oldPath = user.getAvatarPath(); + File file = FileUtil.upload(multipartFile, properties.getPath().getAvatar()); + user.setAvatarPath(Objects.requireNonNull(file).getPath()); + user.setAvatarName(file.getName()); + userRepository.save(user); + if (StringUtils.isNotBlank(oldPath)) { + FileUtil.del(oldPath); + } + @NotBlank String username = user.getUsername(); + flushCache(username); + return new HashMap(1) {{ + put("avatar", file.getName()); + }}; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateEmail(String username, String email) { + userRepository.updateEmail(username, email); + flushCache(username); + } + + @Override + public void download(List queryAll, HttpServletResponse response) throws IOException { + List> list = new ArrayList<>(); + for (UserDto userDTO : queryAll) { + List roles = userDTO.getRoles().stream().map(RoleSmallDto::getName).collect(Collectors.toList()); + Map map = new LinkedHashMap<>(); + map.put("用户名", userDTO.getUsername()); + map.put("角色", roles); + map.put("部门", userDTO.getDept().getName()); + map.put("岗位", userDTO.getJobs().stream().map(JobSmallDto::getName).collect(Collectors.toList())); + map.put("邮箱", userDTO.getEmail()); + map.put("状态", userDTO.getEnabled() ? "启用" : "禁用"); + map.put("手机号码", userDTO.getPhone()); + map.put("修改密码的时间", userDTO.getPwdResetTime()); + map.put("创建日期", userDTO.getCreateTime()); + list.add(map); + } + FileUtil.downloadExcel(list, response); + } + + /** + * 清理缓存 + * + * @param id / + */ + public void delCaches(Long id, String username) { + redisUtils.del(CacheKey.USER_ID + id); + flushCache(username); + } + + /** + * 清理 登陆时 用户缓存信息 + * + * @param username / + */ + private void flushCache(String username) { + userCacheManager.cleanUserCache(username); + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/VerifyServiceImpl.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/VerifyServiceImpl.java new file mode 100644 index 0000000..9f8c8ac --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/VerifyServiceImpl.java @@ -0,0 +1,81 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.impl; + +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.extra.template.Template; +import cn.hutool.extra.template.TemplateConfig; +import cn.hutool.extra.template.TemplateEngine; +import cn.hutool.extra.template.TemplateUtil; +import lombok.RequiredArgsConstructor; +import me.zhengjie.domain.vo.EmailVo; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.modules.system.service.VerifyService; +import me.zhengjie.utils.RedisUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import java.util.Collections; + +/** + * @author Zheng Jie + * @date 2018-12-26 + */ +@Service +@RequiredArgsConstructor +public class VerifyServiceImpl implements VerifyService { + + @Value("${code.expiration}") + private Long expiration; + private final RedisUtils redisUtils; + + @Override + @Transactional(rollbackFor = Exception.class) + public EmailVo sendEmail(String email, String key) { + EmailVo emailVo; + String content; + String redisKey = key + email; + // 如果不存在有效的验证码,就创建一个新的 + TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("template", TemplateConfig.ResourceMode.CLASSPATH)); + Template template = engine.getTemplate("email.ftl"); + Object oldCode = redisUtils.get(redisKey); + if(oldCode == null){ + String code = RandomUtil.randomNumbers (6); + // 存入缓存 + if(!redisUtils.set(redisKey, code, expiration)){ + throw new BadRequestException("服务异常,请联系网站负责人"); + } + content = template.render(Dict.create().set("code",code)); + emailVo = new EmailVo(Collections.singletonList(email),"ELADMIN后台管理系统",content); + // 存在就再次发送原来的验证码 + } else { + content = template.render(Dict.create().set("code",oldCode)); + emailVo = new EmailVo(Collections.singletonList(email),"ELADMIN后台管理系统",content); + } + return emailVo; + } + + @Override + public void validated(String key, String code) { + Object value = redisUtils.get(key); + if(value == null || !value.toString().equals(code)){ + throw new BadRequestException("无效验证码"); + } else { + redisUtils.del(key); + } + } +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/DeptMapper.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/DeptMapper.java new file mode 100644 index 0000000..b0fb904 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/DeptMapper.java @@ -0,0 +1,30 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.mapstruct; + +import me.zhengjie.base.BaseMapper; +import me.zhengjie.modules.system.domain.Dept; +import me.zhengjie.modules.system.service.dto.DeptDto; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** +* @author Zheng Jie +* @date 2019-03-25 +*/ +@Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface DeptMapper extends BaseMapper { +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/DeptSmallMapper.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/DeptSmallMapper.java new file mode 100644 index 0000000..7507600 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/DeptSmallMapper.java @@ -0,0 +1,31 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.mapstruct; + +import me.zhengjie.base.BaseMapper; +import me.zhengjie.modules.system.domain.Dept; +import me.zhengjie.modules.system.service.dto.DeptSmallDto; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** +* @author Zheng Jie +* @date 2019-03-25 +*/ +@Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface DeptSmallMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/DictDetailMapper.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/DictDetailMapper.java new file mode 100644 index 0000000..3f9fe84 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/DictDetailMapper.java @@ -0,0 +1,31 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.mapstruct; + +import me.zhengjie.base.BaseMapper; +import me.zhengjie.modules.system.domain.DictDetail; +import me.zhengjie.modules.system.service.dto.DictDetailDto; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** +* @author Zheng Jie +* @date 2019-04-10 +*/ +@Mapper(componentModel = "spring", uses = {DictSmallMapper.class}, unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface DictDetailMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/DictMapper.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/DictMapper.java new file mode 100644 index 0000000..1047b3b --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/DictMapper.java @@ -0,0 +1,31 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.mapstruct; + +import me.zhengjie.base.BaseMapper; +import me.zhengjie.modules.system.domain.Dict; +import me.zhengjie.modules.system.service.dto.DictDto; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** +* @author Zheng Jie +* @date 2019-04-10 +*/ +@Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface DictMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/DictSmallMapper.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/DictSmallMapper.java new file mode 100644 index 0000000..80ccc4e --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/DictSmallMapper.java @@ -0,0 +1,31 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.mapstruct; + +import me.zhengjie.base.BaseMapper; +import me.zhengjie.modules.system.domain.Dict; +import me.zhengjie.modules.system.service.dto.DictSmallDto; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** +* @author Zheng Jie +* @date 2019-04-10 +*/ +@Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface DictSmallMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/JobMapper.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/JobMapper.java new file mode 100644 index 0000000..3771930 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/JobMapper.java @@ -0,0 +1,30 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.mapstruct; + +import me.zhengjie.base.BaseMapper; +import me.zhengjie.modules.system.domain.Job; +import me.zhengjie.modules.system.service.dto.JobDto; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** +* @author Zheng Jie +* @date 2019-03-29 +*/ +@Mapper(componentModel = "spring",uses = {DeptMapper.class},unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface JobMapper extends BaseMapper { +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/JobSmallMapper.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/JobSmallMapper.java new file mode 100644 index 0000000..36a59cf --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/JobSmallMapper.java @@ -0,0 +1,31 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.mapstruct; + +import me.zhengjie.base.BaseMapper; +import me.zhengjie.modules.system.domain.Job; +import me.zhengjie.modules.system.service.dto.JobSmallDto; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** +* @author Zheng Jie +* @date 2019-03-29 +*/ +@Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface JobSmallMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/MenuMapper.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/MenuMapper.java new file mode 100644 index 0000000..6d8bffa --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/MenuMapper.java @@ -0,0 +1,30 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.mapstruct; + +import me.zhengjie.base.BaseMapper; +import me.zhengjie.modules.system.domain.Menu; +import me.zhengjie.modules.system.service.dto.MenuDto; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** + * @author Zheng Jie + * @date 2018-12-17 + */ +@Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface MenuMapper extends BaseMapper { +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/PurchaseMapper.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/PurchaseMapper.java new file mode 100644 index 0000000..bcedf46 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/PurchaseMapper.java @@ -0,0 +1,32 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package me.zhengjie.modules.system.service.mapstruct; + +import me.zhengjie.base.BaseMapper; +import me.zhengjie.modules.system.domain.Purchase; +import me.zhengjie.modules.system.service.dto.PurchaseDto; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** +* @website https://eladmin.vip +* @author author +* +**/ +@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface PurchaseMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/ReimburseMapper.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/ReimburseMapper.java new file mode 100644 index 0000000..6d97d96 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/ReimburseMapper.java @@ -0,0 +1,32 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package me.zhengjie.modules.system.service.mapstruct; + +import me.zhengjie.base.BaseMapper; +import me.zhengjie.modules.system.domain.Reimburse; +import me.zhengjie.modules.system.service.dto.ReimburseDto; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** +* @website https://eladmin.vip +* @author author +* +**/ +@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface ReimburseMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/RoleMapper.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/RoleMapper.java new file mode 100644 index 0000000..5dc551d --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/RoleMapper.java @@ -0,0 +1,31 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.mapstruct; + +import me.zhengjie.base.BaseMapper; +import me.zhengjie.modules.system.domain.Role; +import me.zhengjie.modules.system.service.dto.RoleDto; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** + * @author Zheng Jie + * @date 2018-11-23 + */ +@Mapper(componentModel = "spring", uses = {MenuMapper.class, DeptMapper.class}, unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface RoleMapper extends BaseMapper { + +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/RoleSmallMapper.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/RoleSmallMapper.java new file mode 100644 index 0000000..db034b5 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/RoleSmallMapper.java @@ -0,0 +1,31 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.mapstruct; + +import me.zhengjie.base.BaseMapper; +import me.zhengjie.modules.system.domain.Role; +import me.zhengjie.modules.system.service.dto.RoleSmallDto; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** + * @author Zheng Jie + * @date 2019-5-23 + */ +@Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface RoleSmallMapper extends BaseMapper { + +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/UserBankMapper.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/UserBankMapper.java new file mode 100644 index 0000000..9894f7b --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/UserBankMapper.java @@ -0,0 +1,32 @@ +/* +* Copyright 2019-2020 Zheng Jie +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package me.zhengjie.modules.system.service.mapstruct; + +import me.zhengjie.base.BaseMapper; +import me.zhengjie.modules.system.domain.UserBank; +import me.zhengjie.modules.system.service.dto.UserBankDto; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** +* @website https://eladmin.vip +* @author author +* @date 2023-10-31 +**/ +@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface UserBankMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/UserLoginMapper.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/UserLoginMapper.java new file mode 100644 index 0000000..fbb8813 --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/UserLoginMapper.java @@ -0,0 +1,30 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.mapstruct; + +import me.zhengjie.base.BaseMapper; +import me.zhengjie.modules.system.domain.User; +import me.zhengjie.modules.system.service.dto.UserLoginDto; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** + * @author Zheng Jie + * @date 2018-11-23 + */ +@Mapper(componentModel = "spring",uses = {RoleMapper.class, DeptMapper.class, JobMapper.class},unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface UserLoginMapper extends BaseMapper { +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/UserMapper.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/UserMapper.java new file mode 100644 index 0000000..510c21b --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/mapstruct/UserMapper.java @@ -0,0 +1,30 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.system.service.mapstruct; + +import me.zhengjie.base.BaseMapper; +import me.zhengjie.modules.system.domain.User; +import me.zhengjie.modules.system.service.dto.UserDto; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** + * @author Zheng Jie + * @date 2018-11-23 + */ +@Mapper(componentModel = "spring",uses = {RoleMapper.class, DeptMapper.class, JobMapper.class},unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface UserMapper extends BaseMapper { +} diff --git a/eladmin-system/src/main/resources/banner.txt b/eladmin-system/src/main/resources/banner.txt new file mode 100644 index 0000000..cc460ce --- /dev/null +++ b/eladmin-system/src/main/resources/banner.txt @@ -0,0 +1,8 @@ + _ _ _ + | | | | (_) + ___| |______ __ _ __| |_ __ ___ _ _ __ + / _ | |______/ _` |/ _` | '_ ` _ \| | '_ \ + | __| | | (_| | (_| | | | | | | | | | | + \___|_| \__,_|\__,_|_| |_| |_|_|_| |_| + + :: Spring Boot :: (v2.6.4) \ No newline at end of file diff --git a/eladmin-system/src/main/resources/config/application-dev.yml b/eladmin-system/src/main/resources/config/application-dev.yml new file mode 100644 index 0000000..8413bd0 --- /dev/null +++ b/eladmin-system/src/main/resources/config/application-dev.yml @@ -0,0 +1,116 @@ +#配置数据源 +spring: + datasource: + druid: + db-type: com.alibaba.druid.pool.DruidDataSource + driverClassName: net.sf.log4jdbc.sql.jdbcapi.DriverSpy + url: jdbc:log4jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:eladmin}?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false + username: ${DB_USER:root} + password: ${DB_PWD:password} + # 初始连接数 + initial-size: 5 + # 最小连接数 + min-idle: 15 + # 最大连接数 + max-active: 30 + # 超时时间(以秒数为单位) + remove-abandoned-timeout: 180 + # 获取连接超时时间 + max-wait: 3000 + # 连接有效性检测时间 + time-between-eviction-runs-millis: 60000 + # 连接在池中最小生存的时间 + min-evictable-idle-time-millis: 300000 + # 连接在池中最大生存的时间 + max-evictable-idle-time-millis: 900000 + # 指明连接是否被空闲连接回收器(如果有)进行检验.如果检测失败,则连接将被从池中去除 + test-while-idle: true + # 指明是否在从池中取出连接前进行检验,如果检验失败, 则从池中去除连接并尝试取出另一个 + test-on-borrow: true + # 是否在归还到池中前进行检验 + test-on-return: false + # 检测连接是否有效 + validation-query: select 1 + # 配置监控统计 + webStatFilter: + enabled: true + stat-view-servlet: + enabled: true + url-pattern: /druid/* + reset-enable: false + filter: + stat: + enabled: true + # 记录慢SQL + log-slow-sql: true + slow-sql-millis: 1000 + merge-sql: true + wall: + config: + multi-statement-allow: true + +# 登录相关配置 +login: + # 是否限制单用户登录 + single-login: false + # Redis用户登录缓存配置 + user-cache: + # 存活时间/秒 + idle-time: 21600 + # 验证码 + login-code: + # 验证码类型配置 查看 LoginProperties 类 + code-type: arithmetic + # 登录图形验证码有效时间/分钟 + expiration: 2 + # 验证码高度 + width: 111 + # 验证码宽度 + height: 36 + # 内容长度 + length: 2 + # 字体名称,为空则使用默认字体 + font-name: + # 字体大小 + font-size: 25 + +#jwt +jwt: + header: Authorization + # 令牌前缀 + token-start-with: Bearer + # 必须使用最少88位的Base64对该令牌进行编码 + base64-secret: ZmQ0ZGI5NjQ0MDQwY2I4MjMxY2Y3ZmI3MjdhN2ZmMjNhODViOTg1ZGE0NTBjMGM4NDA5NzYxMjdjOWMwYWRmZTBlZjlhNGY3ZTg4Y2U3YTE1ODVkZDU5Y2Y3OGYwZWE1NzUzNWQ2YjFjZDc0NGMxZWU2MmQ3MjY1NzJmNTE0MzI= + # 令牌过期时间 此处单位/毫秒 ,默认4小时,可在此网站生成 https://www.convertworld.com/zh-hans/time/milliseconds.html + token-validity-in-seconds: 14400000 + # 在线用户key + online-key: "online-token:" + # 验证码 + code-key: "captcha-code:" + # token 续期检查时间范围(默认30分钟,单位毫秒),在token即将过期的一段时间内用户操作了,则给用户的token续期 + detect: 1800000 + # 续期时间范围,默认1小时,单位毫秒 + renew: 3600000 + +#是否允许生成代码,生产环境设置为false +generator: + enabled: true + +#是否开启 swagger-ui +swagger: + enabled: true + +# 文件存储路径 +file: + mac: + path: ~/file/ + avatar: ~/avatar/ + linux: + path: /home/eladmin/file/ + avatar: /home/eladmin/avatar/ + windows: + path: D:\eladmin\file\ + avatar: D:\eladmin\avatar\ + # 文件大小 /M + maxSize: 100 + avatarMaxSize: 5 diff --git a/eladmin-system/src/main/resources/config/application-prod.yml b/eladmin-system/src/main/resources/config/application-prod.yml new file mode 100644 index 0000000..39d6a11 --- /dev/null +++ b/eladmin-system/src/main/resources/config/application-prod.yml @@ -0,0 +1,125 @@ +#配置数据源 +spring: + datasource: + druid: + db-type: com.alibaba.druid.pool.DruidDataSource + driverClassName: net.sf.log4jdbc.sql.jdbcapi.DriverSpy + url: jdbc:log4jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:eladmin}?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false + username: ${DB_USER:root} + password: ${DB_PWD:123456} + # 初始连接数 + initial-size: 5 + # 最小连接数 + min-idle: 15 + # 最大连接数 + max-active: 30 + # 获取连接超时时间 + max-wait: 5000 + # 连接有效性检测时间 + time-between-eviction-runs-millis: 60000 + # 连接在池中最小生存的时间 + min-evictable-idle-time-millis: 300000 + # 连接在池中最大生存的时间 + max-evictable-idle-time-millis: 900000 + # 指明连接是否被空闲连接回收器(如果有)进行检验.如果检测失败,则连接将被从池中去除 + test-while-idle: true + # 指明是否在从池中取出连接前进行检验,如果检验失败, 则从池中去除连接并尝试取出另一个 + test-on-borrow: true + # 是否在归还到池中前进行检验 + test-on-return: false + # 检测连接是否有效 + validation-query: select 1 + # 配置监控统计 + webStatFilter: + enabled: true + stat-view-servlet: + allow: + enabled: true + # 控制台管理用户名和密码 + url-pattern: /druid/* + reset-enable: false + login-username: admin + login-password: 123456 + filter: + stat: + enabled: true + # 记录慢SQL + log-slow-sql: true + slow-sql-millis: 1000 + merge-sql: true + wall: + config: + multi-statement-allow: true + +# 登录相关配置 +login: + # 是否限制单用户登录 + single-login: false + # Redis用户登录缓存配置 + user-cache: + # 存活时间/秒 + idle-time: 21600 + # 验证码 + login-code: + # 验证码类型配置 查看 LoginProperties 类 + code-type: arithmetic + # 登录图形验证码有效时间/分钟 + expiration: 2 + # 验证码高度 + width: 111 + # 验证码宽度 + height: 36 + # 内容长度 + length: 2 + # 字体名称,为空则使用默认字体,如遇到线上乱码,设置其他字体即可 + font-name: + # 字体大小 + font-size: 25 + +#jwt +jwt: + header: Authorization + # 令牌前缀 + token-start-with: Bearer + # 必须使用最少88位的Base64对该令牌进行编码 + base64-secret: ZmQ0ZGI5NjQ0MDQwY2I4MjMxY2Y3ZmI3MjdhN2ZmMjNhODViOTg1ZGE0NTBjMGM4NDA5NzYxMjdjOWMwYWRmZTBlZjlhNGY3ZTg4Y2U3YTE1ODVkZDU5Y2Y3OGYwZWE1NzUzNWQ2YjFjZDc0NGMxZWU2MmQ3MjY1NzJmNTE0MzI= + # 令牌过期时间 此处单位/毫秒 ,默认2小时,可在此网站生成 https://www.convertworld.com/zh-hans/time/milliseconds.html + token-validity-in-seconds: 7200000 + # 在线用户key + online-key: "online-token:" + # 验证码 + code-key: "captcha-code:" + # token 续期检查时间范围(默认30分钟,单位默认毫秒),在token即将过期的一段时间内用户操作了,则给用户的token续期 + detect: 1800000 + # 续期时间范围,默认 1小时,这里单位毫秒 + renew: 3600000 + +#是否允许生成代码,生产环境设置为false +generator: + enabled: false + +#如果生产环境要开启swagger,需要配置请求地址 +#springfox: +# documentation: +# swagger: +# v2: +# host: # 接口域名或外网ip + +#是否开启 swagger-ui +swagger: + enabled: false + +# 文件存储路径 +file: + mac: + path: ~/file/ + avatar: ~/avatar/ + linux: + path: /home/eladmin/file/ + avatar: /home/eladmin/avatar/ + windows: + path: C:\eladmin\file\ + avatar: C:\eladmin\avatar\ + # 文件大小 /M + maxSize: 100 + avatarMaxSize: 5 diff --git a/eladmin-system/src/main/resources/config/application.yml b/eladmin-system/src/main/resources/config/application.yml new file mode 100644 index 0000000..774fcec --- /dev/null +++ b/eladmin-system/src/main/resources/config/application.yml @@ -0,0 +1,60 @@ +server: + port: 8000 + compression: + enabled: true + mime-types: text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json + +spring: + freemarker: + check-template-location: false + profiles: + active: dev + data: + redis: + repositories: + enabled: false +# pid: +# file: /自行指定位置/eladmin.pid + + #配置 Jpa + jpa: + hibernate: + ddl-auto: none + open-in-view: true + show-sql: true # 默认false,在日志里显示执行的sql语句 + properties: + hibernate: + dialect: org.hibernate.dialect.MySQL5InnoDBDialect + + redis: + #数据库索引 + database: ${REDIS_DB:0} + host: ${REDIS_HOST:127.0.0.1} + port: ${REDIS_PORT:6379} + password: ${REDIS_PWD:} + #连接超时时间 + timeout: 5000 + +task: + pool: + # 核心线程池大小 + core-pool-size: 10 + # 最大线程数 + max-pool-size: 30 + # 活跃时间 + keep-alive-seconds: 60 + # 队列容量 + queue-capacity: 50 + +#七牛云 +qiniu: + # 文件大小 /M + max-size: 15 + +#邮箱验证码有效时间/秒 +code: + expiration: 300 + +#密码加密传输,前端公钥加密,后端私钥解密 +rsa: + private_key: MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEA0vfvyTdGJkdbHkB8mp0f3FE0GYP3AYPaJF7jUd1M0XxFSE2ceK3k2kw20YvQ09NJKk+OMjWQl9WitG9pB6tSCQIDAQABAkA2SimBrWC2/wvauBuYqjCFwLvYiRYqZKThUS3MZlebXJiLB+Ue/gUifAAKIg1avttUZsHBHrop4qfJCwAI0+YRAiEA+W3NK/RaXtnRqmoUUkb59zsZUBLpvZgQPfj1MhyHDz0CIQDYhsAhPJ3mgS64NbUZmGWuuNKp5coY2GIj/zYDMJp6vQIgUueLFXv/eZ1ekgz2Oi67MNCk5jeTF2BurZqNLR3MSmUCIFT3Q6uHMtsB9Eha4u7hS31tj1UWE+D+ADzp59MGnoftAiBeHT7gDMuqeJHPL4b+kC+gzV4FGTfhR9q3tTbklZkD2A== \ No newline at end of file diff --git a/eladmin-system/src/main/resources/log4jdbc.log4j2.properties b/eladmin-system/src/main/resources/log4jdbc.log4j2.properties new file mode 100644 index 0000000..302525f --- /dev/null +++ b/eladmin-system/src/main/resources/log4jdbc.log4j2.properties @@ -0,0 +1,4 @@ +# If you use SLF4J. First, you need to tell log4jdbc-log4j2 that you want to use the SLF4J logger +log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator +log4jdbc.auto.load.popular.drivers=false +log4jdbc.drivers=com.mysql.cj.jdbc.Driver \ No newline at end of file diff --git a/eladmin-system/src/main/resources/logback.xml b/eladmin-system/src/main/resources/logback.xml new file mode 100644 index 0000000..3f8f9d8 --- /dev/null +++ b/eladmin-system/src/main/resources/logback.xml @@ -0,0 +1,45 @@ + + + elAdmin + + + + + + + ${log.pattern} + ${log.charset} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/eladmin-system/src/main/resources/template/email.ftl b/eladmin-system/src/main/resources/template/email.ftl new file mode 100644 index 0000000..606d490 --- /dev/null +++ b/eladmin-system/src/main/resources/template/email.ftl @@ -0,0 +1,48 @@ + + + + + + + +
+
+

尊敬的用户,您好:

+

您正在申请邮箱验证,您的验证码为:

+

${code}

+
+
+
+ Copyright ©${.now?string("yyyy")} ELADMIN 后台管理系统 All Rights Reserved. +
+ +
+
+ + diff --git a/eladmin-system/src/main/resources/template/taskAlarm.ftl b/eladmin-system/src/main/resources/template/taskAlarm.ftl new file mode 100644 index 0000000..a29b078 --- /dev/null +++ b/eladmin-system/src/main/resources/template/taskAlarm.ftl @@ -0,0 +1,69 @@ + + + + + + + +
+
+

任务信息:

+ + + + + + + + + + + + + + + + + +
任务名称Bean名称执行方法参数内容Cron表达式描述内容
${task.jobName}${task.beanName}${task.methodName}${(task.params)!""}${task.cronExpression}${(task.description)!""}
+
+
+

异常信息:

+
+                ${msg}
+            
+
+
+
+
+ Copyright ©${.now?string("yyyy")} ELADMIN 后台管理系统 All Rights Reserved. +
+ +
+ + + diff --git a/eladmin-system/src/test/java/me/zhengjie/EladminSystemApplicationTests.java b/eladmin-system/src/test/java/me/zhengjie/EladminSystemApplicationTests.java new file mode 100644 index 0000000..d3986f0 --- /dev/null +++ b/eladmin-system/src/test/java/me/zhengjie/EladminSystemApplicationTests.java @@ -0,0 +1,16 @@ +package me.zhengjie; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class EladminSystemApplicationTests { + + @Test + public void contextLoads() { + } + + public static void main(String[] args) { + } +} + diff --git a/eladmin-tools/pom.xml b/eladmin-tools/pom.xml new file mode 100644 index 0000000..7c85d8b --- /dev/null +++ b/eladmin-tools/pom.xml @@ -0,0 +1,50 @@ + + + + eladmin + me.zhengjie + 2.7 + + 4.0.0 + + eladmin-tools + 工具模块 + + + 1.4.7 + 7.9.3 + 4.22.57.ALL + + + + + + me.zhengjie + eladmin-logging + 2.7 + + + + + javax.mail + mail + ${mail.version} + + + + + com.qiniu + qiniu-java-sdk + ${qiniu.version} + + + + + com.alipay.sdk + alipay-sdk-java + ${alipay.version} + + + diff --git a/eladmin-tools/src/main/java/me/zhengjie/config/MultipartConfig.java b/eladmin-tools/src/main/java/me/zhengjie/config/MultipartConfig.java new file mode 100644 index 0000000..cf04de5 --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/config/MultipartConfig.java @@ -0,0 +1,47 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.config; + +import org.springframework.boot.web.servlet.MultipartConfigFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import javax.servlet.MultipartConfigElement; +import java.io.File; + +/** + * @date 2018-12-28 + * @author https://blog.csdn.net/llibin1024530411/article/details/79474953 + */ +@Configuration +public class MultipartConfig { + + /** + * 文件上传临时路径 + */ + @Bean + MultipartConfigElement multipartConfigElement() { + MultipartConfigFactory factory = new MultipartConfigFactory(); + String location = System.getProperty("user.home") + "/.eladmin/file/tmp"; + File tmpFile = new File(location); + if (!tmpFile.exists()) { + if (!tmpFile.mkdirs()) { + System.out.println("create was not successful."); + } + } + factory.setLocation(location); + return factory.createMultipartConfig(); + } +} \ No newline at end of file diff --git a/eladmin-tools/src/main/java/me/zhengjie/domain/AlipayConfig.java b/eladmin-tools/src/main/java/me/zhengjie/domain/AlipayConfig.java new file mode 100644 index 0000000..def695d --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/domain/AlipayConfig.java @@ -0,0 +1,76 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.domain; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import javax.persistence.*; +import javax.validation.constraints.NotBlank; +import java.io.Serializable; + +/** + * 支付宝配置类 + * @author Zheng Jie + * @date 2018-12-31 + */ +@Data +@Entity +@Table(name = "tool_alipay_config") +public class AlipayConfig implements Serializable { + + @Id + @Column(name = "config_id") + @ApiModelProperty(value = "ID", hidden = true) + private Long id; + + @NotBlank + @ApiModelProperty(value = "应用ID") + private String appId; + + @NotBlank + @ApiModelProperty(value = "商户私钥") + private String privateKey; + + @NotBlank + @ApiModelProperty(value = "支付宝公钥") + private String publicKey; + + @ApiModelProperty(value = "签名方式") + private String signType="RSA2"; + + @Column(name = "gateway_url") + @ApiModelProperty(value = "支付宝开放安全地址", hidden = true) + private String gatewayUrl = "https://openapi.alipaydev.com/gateway.do"; + + @ApiModelProperty(value = "编码", hidden = true) + private String charset= "utf-8"; + + @NotBlank + @ApiModelProperty(value = "异步通知地址") + private String notifyUrl; + + @NotBlank + @ApiModelProperty(value = "订单完成后返回的页面") + private String returnUrl; + + @ApiModelProperty(value = "类型") + private String format="JSON"; + + @NotBlank + @ApiModelProperty(value = "商户号") + private String sysServiceProviderId; + +} diff --git a/eladmin-tools/src/main/java/me/zhengjie/domain/EmailConfig.java b/eladmin-tools/src/main/java/me/zhengjie/domain/EmailConfig.java new file mode 100644 index 0000000..b4fc1e6 --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/domain/EmailConfig.java @@ -0,0 +1,58 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.domain; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import javax.persistence.*; +import javax.validation.constraints.NotBlank; +import java.io.Serializable; + +/** + * 邮件配置类,数据存覆盖式存入数据存 + * @author Zheng Jie + * @date 2018-12-26 + */ +@Entity +@Data +@Table(name = "tool_email_config") +public class EmailConfig implements Serializable { + + @Id + @Column(name = "config_id") + @ApiModelProperty(value = "ID", hidden = true) + private Long id; + + @NotBlank + @ApiModelProperty(value = "邮件服务器SMTP地址") + private String host; + + @NotBlank + @ApiModelProperty(value = "邮件服务器 SMTP 端口") + private String port; + + @NotBlank + @ApiModelProperty(value = "发件者用户名") + private String user; + + @NotBlank + @ApiModelProperty(value = "密码") + private String pass; + + @NotBlank + @ApiModelProperty(value = "收件人") + private String fromUser; +} diff --git a/eladmin-tools/src/main/java/me/zhengjie/domain/LocalStorage.java b/eladmin-tools/src/main/java/me/zhengjie/domain/LocalStorage.java new file mode 100644 index 0000000..59fd0ab --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/domain/LocalStorage.java @@ -0,0 +1,73 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.domain; + +import io.swagger.annotations.ApiModelProperty; +import lombok.*; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.bean.copier.CopyOptions; +import me.zhengjie.base.BaseEntity; +import javax.persistence.*; +import java.io.Serializable; + +/** +* @author Zheng Jie +* @date 2019-09-05 +*/ +@Getter +@Setter +@Entity +@Table(name="tool_local_storage") +@NoArgsConstructor +public class LocalStorage extends BaseEntity implements Serializable { + + @Id + @Column(name = "storage_id") + @ApiModelProperty(value = "ID", hidden = true) + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ApiModelProperty(value = "真实文件名") + private String realName; + + @ApiModelProperty(value = "文件名") + private String name; + + @ApiModelProperty(value = "后缀") + private String suffix; + + @ApiModelProperty(value = "路径") + private String path; + + @ApiModelProperty(value = "类型") + private String type; + + @ApiModelProperty(value = "大小") + private String size; + + public LocalStorage(String realName,String name, String suffix, String path, String type, String size) { + this.realName = realName; + this.name = name; + this.suffix = suffix; + this.path = path; + this.type = type; + this.size = size; + } + + public void copy(LocalStorage source){ + BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true)); + } +} \ No newline at end of file diff --git a/eladmin-tools/src/main/java/me/zhengjie/domain/QiniuConfig.java b/eladmin-tools/src/main/java/me/zhengjie/domain/QiniuConfig.java new file mode 100644 index 0000000..0247e67 --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/domain/QiniuConfig.java @@ -0,0 +1,69 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.domain; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import javax.persistence.*; +import javax.validation.constraints.NotBlank; +import java.io.Serializable; + +/** + * 七牛云对象存储配置类 + * @author Zheng Jie + * @date 2018-12-31 + */ +@Data +@Entity +@Table(name = "tool_qiniu_config") +public class QiniuConfig implements Serializable { + + @Id + @Column(name = "config_id") + @ApiModelProperty(value = "ID") + private Long id; + + @NotBlank + @ApiModelProperty(value = "accessKey") + private String accessKey; + + @NotBlank + @ApiModelProperty(value = "secretKey") + private String secretKey; + + @NotBlank + @ApiModelProperty(value = "存储空间名称作为唯一的 Bucket 识别符") + private String bucket; + + /** + * Zone表示与机房的对应关系 + * 华东 Zone.zone0() + * 华北 Zone.zone1() + * 华南 Zone.zone2() + * 北美 Zone.zoneNa0() + * 东南亚 Zone.zoneAs0() + */ + @NotBlank + @ApiModelProperty(value = "Zone表示与机房的对应关系") + private String zone; + + @NotBlank + @ApiModelProperty(value = "外链域名,可自定义,需在七牛云绑定") + private String host; + + @ApiModelProperty(value = "空间类型:公开/私有") + private String type = "公开"; +} diff --git a/eladmin-tools/src/main/java/me/zhengjie/domain/QiniuContent.java b/eladmin-tools/src/main/java/me/zhengjie/domain/QiniuContent.java new file mode 100644 index 0000000..db69a6a --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/domain/QiniuContent.java @@ -0,0 +1,64 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.domain; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.hibernate.annotations.UpdateTimestamp; +import javax.persistence.*; +import java.io.Serializable; +import java.sql.Timestamp; + +/** + * 上传成功后,存储结果 + * @author Zheng Jie + * @date 2018-12-31 + */ +@Data +@Entity +@Table(name = "tool_qiniu_content") +public class QiniuContent implements Serializable { + + @Id + @Column(name = "content_id") + @ApiModelProperty(value = "ID", hidden = true) + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "name") + @ApiModelProperty(value = "文件名") + private String key; + + @ApiModelProperty(value = "空间名") + private String bucket; + + @ApiModelProperty(value = "大小") + private String size; + + @ApiModelProperty(value = "文件地址") + private String url; + + @ApiModelProperty(value = "文件类型") + private String suffix; + + @ApiModelProperty(value = "空间类型:公开/私有") + private String type = "公开"; + + @UpdateTimestamp + @ApiModelProperty(value = "创建或更新时间") + @Column(name = "update_time") + private Timestamp updateTime; +} diff --git a/eladmin-tools/src/main/java/me/zhengjie/domain/vo/EmailVo.java b/eladmin-tools/src/main/java/me/zhengjie/domain/vo/EmailVo.java new file mode 100644 index 0000000..1fb759f --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/domain/vo/EmailVo.java @@ -0,0 +1,44 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.domain.vo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import java.util.List; + +/** + * 发送邮件时,接收参数的类 + * @author 郑杰 + * @date 2018/09/28 12:02:14 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class EmailVo { + + /** 收件人,支持多个收件人 */ + @NotEmpty + private List tos; + + @NotBlank + private String subject; + + @NotBlank + private String content; +} diff --git a/eladmin-tools/src/main/java/me/zhengjie/domain/vo/TradeVo.java b/eladmin-tools/src/main/java/me/zhengjie/domain/vo/TradeVo.java new file mode 100644 index 0000000..01c1c2f --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/domain/vo/TradeVo.java @@ -0,0 +1,63 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.domain.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import javax.validation.constraints.NotBlank; +import java.sql.Date; +import java.sql.Timestamp; + +/** + * 交易详情,按需应该存入数据库,这里存入数据库,仅供临时测试 + * @author Zheng Jie + * @date 2018-12-31 + */ +@Data +public class TradeVo { + + /** (必填)商品描述 */ + @NotBlank + private String body; + + /** (必填)商品名称 */ + @NotBlank + private String subject; + + /** (必填)商户订单号,应该由后台生成 */ + @ApiModelProperty(hidden = true) + private String outTradeNo; + + /** (必填)第三方订单号 */ + @ApiModelProperty(hidden = true) + private String tradeNo; + + /** (必填)价格 */ + @NotBlank + private String totalAmount; + + /** 订单状态,已支付,未支付,作废 */ + @ApiModelProperty(hidden = true) + private String state; + + /** 创建时间,存入数据库时需要 */ + @ApiModelProperty(hidden = true) + private Timestamp createTime; + + /** 作废时间,存入数据库时需要 */ + @ApiModelProperty(hidden = true) + private Date cancelTime; +} diff --git a/eladmin-tools/src/main/java/me/zhengjie/repository/AliPayRepository.java b/eladmin-tools/src/main/java/me/zhengjie/repository/AliPayRepository.java new file mode 100644 index 0000000..61183b4 --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/repository/AliPayRepository.java @@ -0,0 +1,26 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.repository; + +import me.zhengjie.domain.AlipayConfig; +import org.springframework.data.jpa.repository.JpaRepository; + +/** + * @author Zheng Jie + * @date 2018-12-31 + */ +public interface AliPayRepository extends JpaRepository { +} diff --git a/eladmin-tools/src/main/java/me/zhengjie/repository/EmailRepository.java b/eladmin-tools/src/main/java/me/zhengjie/repository/EmailRepository.java new file mode 100644 index 0000000..7765602 --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/repository/EmailRepository.java @@ -0,0 +1,26 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.repository; + +import me.zhengjie.domain.EmailConfig; +import org.springframework.data.jpa.repository.JpaRepository; + +/** + * @author Zheng Jie + * @date 2018-12-26 + */ +public interface EmailRepository extends JpaRepository { +} diff --git a/eladmin-tools/src/main/java/me/zhengjie/repository/LocalStorageRepository.java b/eladmin-tools/src/main/java/me/zhengjie/repository/LocalStorageRepository.java new file mode 100644 index 0000000..8c1e85a --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/repository/LocalStorageRepository.java @@ -0,0 +1,27 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.repository; + +import me.zhengjie.domain.LocalStorage; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +/** +* @author Zheng Jie +* @date 2019-09-05 +*/ +public interface LocalStorageRepository extends JpaRepository, JpaSpecificationExecutor { +} \ No newline at end of file diff --git a/eladmin-tools/src/main/java/me/zhengjie/repository/QiNiuConfigRepository.java b/eladmin-tools/src/main/java/me/zhengjie/repository/QiNiuConfigRepository.java new file mode 100644 index 0000000..9379f55 --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/repository/QiNiuConfigRepository.java @@ -0,0 +1,36 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.repository; + +import me.zhengjie.domain.QiniuConfig; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; + +/** + * @author Zheng Jie + * @date 2018-12-31 + */ +public interface QiNiuConfigRepository extends JpaRepository { + + /** + * 编辑类型 + * @param type + */ + @Modifying + @Query(value = "update QiniuConfig set type = ?1") + void update(String type); +} diff --git a/eladmin-tools/src/main/java/me/zhengjie/repository/QiniuContentRepository.java b/eladmin-tools/src/main/java/me/zhengjie/repository/QiniuContentRepository.java new file mode 100644 index 0000000..55f813f --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/repository/QiniuContentRepository.java @@ -0,0 +1,34 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.repository; + +import me.zhengjie.domain.QiniuContent; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +/** + * @author Zheng Jie + * @date 2018-12-31 + */ +public interface QiniuContentRepository extends JpaRepository, JpaSpecificationExecutor { + + /** + * 根据key查询 + * @param key 文件名 + * @return QiniuContent + */ + QiniuContent findByKey(String key); +} diff --git a/eladmin-tools/src/main/java/me/zhengjie/rest/AliPayController.java b/eladmin-tools/src/main/java/me/zhengjie/rest/AliPayController.java new file mode 100644 index 0000000..0c03d0a --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/rest/AliPayController.java @@ -0,0 +1,135 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.rest; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import me.zhengjie.annotation.AnonymousAccess; +import me.zhengjie.annotation.Log; +import me.zhengjie.annotation.rest.AnonymousGetMapping; +import me.zhengjie.domain.vo.TradeVo; +import me.zhengjie.domain.AlipayConfig; +import me.zhengjie.utils.AliPayStatusEnum; +import me.zhengjie.utils.AlipayUtils; +import me.zhengjie.service.AliPayService; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import springfox.documentation.annotations.ApiIgnore; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.nio.charset.StandardCharsets; +import java.util.Map; + +/** + * @author Zheng Jie + * @date 2018-12-31 + */ +@Slf4j +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/aliPay") +@Api(tags = "工具:支付宝管理") +public class AliPayController { + + private final AlipayUtils alipayUtils; + private final AliPayService alipayService; + + @GetMapping + public ResponseEntity queryAliConfig() { + return new ResponseEntity<>(alipayService.find(), HttpStatus.OK); + } + + @Log("配置支付宝") + @ApiOperation("配置支付宝") + @PutMapping + public ResponseEntity updateAliPayConfig(@Validated @RequestBody AlipayConfig alipayConfig) { + alipayService.config(alipayConfig); + return new ResponseEntity<>(HttpStatus.OK); + } + + @Log("支付宝PC网页支付") + @ApiOperation("PC网页支付") + @PostMapping(value = "/toPayAsPC") + public ResponseEntity toPayAsPc(@Validated @RequestBody TradeVo trade) throws Exception { + AlipayConfig aliPay = alipayService.find(); + trade.setOutTradeNo(alipayUtils.getOrderCode()); + String payUrl = alipayService.toPayAsPc(aliPay, trade); + return ResponseEntity.ok(payUrl); + } + + @Log("支付宝手机网页支付") + @ApiOperation("手机网页支付") + @PostMapping(value = "/toPayAsWeb") + public ResponseEntity toPayAsWeb(@Validated @RequestBody TradeVo trade) throws Exception { + AlipayConfig alipay = alipayService.find(); + trade.setOutTradeNo(alipayUtils.getOrderCode()); + String payUrl = alipayService.toPayAsWeb(alipay, trade); + return ResponseEntity.ok(payUrl); + } + + @ApiIgnore + @AnonymousGetMapping("/return") + @ApiOperation("支付之后跳转的链接") + public ResponseEntity returnPage(HttpServletRequest request, HttpServletResponse response) { + AlipayConfig alipay = alipayService.find(); + response.setContentType("text/html;charset=" + alipay.getCharset()); + //内容验签,防止黑客篡改参数 + if (alipayUtils.rsaCheck(request, alipay)) { + //商户订单号 + String outTradeNo = new String(request.getParameter("out_trade_no").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); + //支付宝交易号 + String tradeNo = new String(request.getParameter("trade_no").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); + System.out.println("商户订单号" + outTradeNo + " " + "第三方交易号" + tradeNo); + + // 根据业务需要返回数据,这里统一返回OK + return new ResponseEntity<>("payment successful", HttpStatus.OK); + } else { + // 根据业务需要返回数据 + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + } + + @ApiIgnore + @RequestMapping("/notify") + @AnonymousAccess + @ApiOperation("支付异步通知(要公网访问),接收异步通知,检查通知内容app_id、out_trade_no、total_amount是否与请求中的一致,根据trade_status进行后续业务处理") + public ResponseEntity notify(HttpServletRequest request) { + AlipayConfig alipay = alipayService.find(); + Map parameterMap = request.getParameterMap(); + //内容验签,防止黑客篡改参数 + if (alipayUtils.rsaCheck(request, alipay)) { + //交易状态 + String tradeStatus = new String(request.getParameter("trade_status").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); + // 商户订单号 + String outTradeNo = new String(request.getParameter("out_trade_no").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); + //支付宝交易号 + String tradeNo = new String(request.getParameter("trade_no").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); + //付款金额 + String totalAmount = new String(request.getParameter("total_amount").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); + //验证 + if (tradeStatus.equals(AliPayStatusEnum.SUCCESS.getValue()) || tradeStatus.equals(AliPayStatusEnum.FINISHED.getValue())) { + // 验证通过后应该根据业务需要处理订单 + } + return new ResponseEntity<>(HttpStatus.OK); + } + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } +} diff --git a/eladmin-tools/src/main/java/me/zhengjie/rest/EmailController.java b/eladmin-tools/src/main/java/me/zhengjie/rest/EmailController.java new file mode 100644 index 0000000..eb7832c --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/rest/EmailController.java @@ -0,0 +1,63 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.rest; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import me.zhengjie.annotation.Log; +import me.zhengjie.domain.vo.EmailVo; +import me.zhengjie.domain.EmailConfig; +import me.zhengjie.service.EmailService; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +/** + * 发送邮件 + * @author 郑杰 + * @date 2018/09/28 6:55:53 + */ +@RestController +@RequiredArgsConstructor +@RequestMapping("api/email") +@Api(tags = "工具:邮件管理") +public class EmailController { + + private final EmailService emailService; + + @GetMapping + public ResponseEntity queryEmailConfig(){ + return new ResponseEntity<>(emailService.find(),HttpStatus.OK); + } + + @Log("配置邮件") + @PutMapping + @ApiOperation("配置邮件") + public ResponseEntity updateEmailConfig(@Validated @RequestBody EmailConfig emailConfig) throws Exception { + emailService.config(emailConfig,emailService.find()); + return new ResponseEntity<>(HttpStatus.OK); + } + + @Log("发送邮件") + @PostMapping + @ApiOperation("发送邮件") + public ResponseEntity sendEmail(@Validated @RequestBody EmailVo emailVo){ + emailService.send(emailVo,emailService.find()); + return new ResponseEntity<>(HttpStatus.OK); + } +} diff --git a/eladmin-tools/src/main/java/me/zhengjie/rest/LocalStorageController.java b/eladmin-tools/src/main/java/me/zhengjie/rest/LocalStorageController.java new file mode 100644 index 0000000..2b61260 --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/rest/LocalStorageController.java @@ -0,0 +1,100 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.rest; + +import lombok.RequiredArgsConstructor; +import me.zhengjie.annotation.Log; +import me.zhengjie.domain.LocalStorage; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.service.LocalStorageService; +import me.zhengjie.service.dto.LocalStorageDto; +import me.zhengjie.service.dto.LocalStorageQueryCriteria; +import me.zhengjie.utils.FileUtil; +import me.zhengjie.utils.PageResult; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import io.swagger.annotations.*; +import org.springframework.web.multipart.MultipartFile; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** +* @author Zheng Jie +* @date 2019-09-05 +*/ +@RestController +@RequiredArgsConstructor +@Api(tags = "工具:本地存储管理") +@RequestMapping("/api/localStorage") +public class LocalStorageController { + + private final LocalStorageService localStorageService; + + @GetMapping + @ApiOperation("查询文件") + @PreAuthorize("@el.check('storage:list')") + public ResponseEntity> queryFile(LocalStorageQueryCriteria criteria, Pageable pageable){ + return new ResponseEntity<>(localStorageService.queryAll(criteria,pageable),HttpStatus.OK); + } + + @ApiOperation("导出数据") + @GetMapping(value = "/download") + @PreAuthorize("@el.check('storage:list')") + public void exportFile(HttpServletResponse response, LocalStorageQueryCriteria criteria) throws IOException { + localStorageService.download(localStorageService.queryAll(criteria), response); + } + + @PostMapping + @ApiOperation("上传文件") + @PreAuthorize("@el.check('storage:add')") + public ResponseEntity createFile(@RequestParam String name, @RequestParam("file") MultipartFile file){ + localStorageService.create(name, file); + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @ApiOperation("上传图片") + @PostMapping("/pictures") + public ResponseEntity uploadPicture(@RequestParam MultipartFile file){ + // 判断文件是否为图片 + String suffix = FileUtil.getExtensionName(file.getOriginalFilename()); + if(!FileUtil.IMAGE.equals(FileUtil.getFileType(suffix))){ + throw new BadRequestException("只能上传图片"); + } + LocalStorage localStorage = localStorageService.create(null, file); + return new ResponseEntity<>(localStorage, HttpStatus.OK); + } + + @PutMapping + @Log("修改文件") + @ApiOperation("修改文件") + @PreAuthorize("@el.check('storage:edit')") + public ResponseEntity updateFile(@Validated @RequestBody LocalStorage resources){ + localStorageService.update(resources); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @Log("删除文件") + @DeleteMapping + @ApiOperation("多选删除") + public ResponseEntity deleteFile(@RequestBody Long[] ids) { + localStorageService.deleteAll(ids); + return new ResponseEntity<>(HttpStatus.OK); + } +} \ No newline at end of file diff --git a/eladmin-tools/src/main/java/me/zhengjie/rest/QiniuController.java b/eladmin-tools/src/main/java/me/zhengjie/rest/QiniuController.java new file mode 100644 index 0000000..2988c72 --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/rest/QiniuController.java @@ -0,0 +1,122 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.rest; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import me.zhengjie.annotation.Log; +import me.zhengjie.domain.QiniuConfig; +import me.zhengjie.domain.QiniuContent; +import me.zhengjie.service.dto.QiniuQueryCriteria; +import me.zhengjie.service.QiNiuService; +import me.zhengjie.utils.PageResult; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * 发送邮件 + * @author 郑杰 + * @date 2018/09/28 6:55:53 + */ +@Slf4j +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/qiNiuContent") +@Api(tags = "工具:七牛云存储管理") +public class QiniuController { + + private final QiNiuService qiNiuService; + + @GetMapping(value = "/config") + public ResponseEntity queryQiNiuConfig(){ + return new ResponseEntity<>(qiNiuService.find(), HttpStatus.OK); + } + + @Log("配置七牛云存储") + @ApiOperation("配置七牛云存储") + @PutMapping(value = "/config") + public ResponseEntity updateQiNiuConfig(@Validated @RequestBody QiniuConfig qiniuConfig){ + qiNiuService.config(qiniuConfig); + qiNiuService.update(qiniuConfig.getType()); + return new ResponseEntity<>(HttpStatus.OK); + } + + @ApiOperation("导出数据") + @GetMapping(value = "/download") + public void exportQiNiu(HttpServletResponse response, QiniuQueryCriteria criteria) throws IOException { + qiNiuService.downloadList(qiNiuService.queryAll(criteria), response); + } + + @ApiOperation("查询文件") + @GetMapping + public ResponseEntity> queryQiNiu(QiniuQueryCriteria criteria, Pageable pageable){ + return new ResponseEntity<>(qiNiuService.queryAll(criteria,pageable),HttpStatus.OK); + } + + @ApiOperation("上传文件") + @PostMapping + public ResponseEntity uploadQiNiu(@RequestParam MultipartFile file){ + QiniuContent qiniuContent = qiNiuService.upload(file,qiNiuService.find()); + Map map = new HashMap<>(3); + map.put("id",qiniuContent.getId()); + map.put("errno",0); + map.put("data",new String[]{qiniuContent.getUrl()}); + return new ResponseEntity<>(map,HttpStatus.OK); + } + + @Log("同步七牛云数据") + @ApiOperation("同步七牛云数据") + @PostMapping(value = "/synchronize") + public ResponseEntity synchronizeQiNiu(){ + qiNiuService.synchronize(qiNiuService.find()); + return new ResponseEntity<>(HttpStatus.OK); + } + + @Log("下载文件") + @ApiOperation("下载文件") + @GetMapping(value = "/download/{id}") + public ResponseEntity downloadQiNiu(@PathVariable Long id){ + Map map = new HashMap<>(1); + map.put("url", qiNiuService.download(qiNiuService.findByContentId(id),qiNiuService.find())); + return new ResponseEntity<>(map,HttpStatus.OK); + } + + @Log("删除文件") + @ApiOperation("删除文件") + @DeleteMapping(value = "/{id}") + public ResponseEntity deleteQiNiu(@PathVariable Long id){ + qiNiuService.delete(qiNiuService.findByContentId(id),qiNiuService.find()); + return new ResponseEntity<>(HttpStatus.OK); + } + + @Log("删除多张图片") + @ApiOperation("删除多张图片") + @DeleteMapping + public ResponseEntity deleteAllQiNiu(@RequestBody Long[] ids) { + qiNiuService.deleteAll(ids, qiNiuService.find()); + return new ResponseEntity<>(HttpStatus.OK); + } +} diff --git a/eladmin-tools/src/main/java/me/zhengjie/service/AliPayService.java b/eladmin-tools/src/main/java/me/zhengjie/service/AliPayService.java new file mode 100644 index 0000000..be19c90 --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/service/AliPayService.java @@ -0,0 +1,57 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.service; + +import me.zhengjie.domain.vo.TradeVo; +import me.zhengjie.domain.AlipayConfig; + +/** + * @author Zheng Jie + * @date 2018-12-31 + */ +public interface AliPayService { + + /** + * 查询配置 + * @return AlipayConfig + */ + AlipayConfig find(); + + /** + * 更新配置 + * @param alipayConfig 支付宝配置 + * @return AlipayConfig + */ + AlipayConfig config(AlipayConfig alipayConfig); + + /** + * 处理来自PC的交易请求 + * @param alipay 支付宝配置 + * @param trade 交易详情 + * @return String + * @throws Exception 异常 + */ + String toPayAsPc(AlipayConfig alipay, TradeVo trade) throws Exception; + + /** + * 处理来自手机网页的交易请求 + * @param alipay 支付宝配置 + * @param trade 交易详情 + * @return String + * @throws Exception 异常 + */ + String toPayAsWeb(AlipayConfig alipay, TradeVo trade) throws Exception; +} diff --git a/eladmin-tools/src/main/java/me/zhengjie/service/EmailService.java b/eladmin-tools/src/main/java/me/zhengjie/service/EmailService.java new file mode 100644 index 0000000..d62cadc --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/service/EmailService.java @@ -0,0 +1,48 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.service; + +import me.zhengjie.domain.vo.EmailVo; +import me.zhengjie.domain.EmailConfig; + +/** + * @author Zheng Jie + * @date 2018-12-26 + */ +public interface EmailService { + + /** + * 更新邮件配置 + * @param emailConfig 邮箱配置 + * @param old / + * @return / + * @throws Exception / + */ + EmailConfig config(EmailConfig emailConfig, EmailConfig old) throws Exception; + + /** + * 查询配置 + * @return EmailConfig 邮件配置 + */ + EmailConfig find(); + + /** + * 发送邮件 + * @param emailVo 邮件发送的内容 + * @param emailConfig 邮件配置 + */ + void send(EmailVo emailVo, EmailConfig emailConfig); +} diff --git a/eladmin-tools/src/main/java/me/zhengjie/service/LocalStorageService.java b/eladmin-tools/src/main/java/me/zhengjie/service/LocalStorageService.java new file mode 100644 index 0000000..71c57d2 --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/service/LocalStorageService.java @@ -0,0 +1,83 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.service; + +import me.zhengjie.domain.LocalStorage; +import me.zhengjie.service.dto.LocalStorageDto; +import me.zhengjie.service.dto.LocalStorageQueryCriteria; +import me.zhengjie.utils.PageResult; +import org.springframework.data.domain.Pageable; +import org.springframework.web.multipart.MultipartFile; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; + +/** +* @author Zheng Jie +* @date 2019-09-05 +*/ +public interface LocalStorageService { + + /** + * 分页查询 + * @param criteria 条件 + * @param pageable 分页参数 + * @return / + */ + PageResult queryAll(LocalStorageQueryCriteria criteria, Pageable pageable); + + /** + * 查询全部数据 + * @param criteria 条件 + * @return / + */ + List queryAll(LocalStorageQueryCriteria criteria); + + /** + * 根据ID查询 + * @param id / + * @return / + */ + LocalStorageDto findById(Long id); + + /** + * 上传 + * @param name 文件名称 + * @param file 文件 + * @return + */ + LocalStorage create(String name, MultipartFile file); + + /** + * 编辑 + * @param resources 文件信息 + */ + void update(LocalStorage resources); + + /** + * 多选删除 + * @param ids / + */ + void deleteAll(Long[] ids); + + /** + * 导出数据 + * @param localStorageDtos 待导出的数据 + * @param response / + * @throws IOException / + */ + void download(List localStorageDtos, HttpServletResponse response) throws IOException; +} \ No newline at end of file diff --git a/eladmin-tools/src/main/java/me/zhengjie/service/QiNiuService.java b/eladmin-tools/src/main/java/me/zhengjie/service/QiNiuService.java new file mode 100644 index 0000000..5321208 --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/service/QiNiuService.java @@ -0,0 +1,119 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.service; + +import me.zhengjie.domain.QiniuConfig; +import me.zhengjie.domain.QiniuContent; +import me.zhengjie.service.dto.QiniuQueryCriteria; +import me.zhengjie.utils.PageResult; +import org.springframework.data.domain.Pageable; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; + +/** + * @author Zheng Jie + * @date 2018-12-31 + */ +public interface QiNiuService { + + /** + * 查配置 + * @return QiniuConfig + */ + QiniuConfig find(); + + /** + * 修改配置 + * @param qiniuConfig 配置 + * @return QiniuConfig + */ + QiniuConfig config(QiniuConfig qiniuConfig); + + /** + * 分页查询 + * @param criteria 条件 + * @param pageable 分页参数 + * @return / + */ + PageResult queryAll(QiniuQueryCriteria criteria, Pageable pageable); + + /** + * 查询全部 + * @param criteria 条件 + * @return / + */ + List queryAll(QiniuQueryCriteria criteria); + + /** + * 上传文件 + * @param file 文件 + * @param qiniuConfig 配置 + * @return QiniuContent + */ + QiniuContent upload(MultipartFile file, QiniuConfig qiniuConfig); + + /** + * 查询文件 + * @param id 文件ID + * @return QiniuContent + */ + QiniuContent findByContentId(Long id); + + /** + * 下载文件 + * @param content 文件信息 + * @param config 配置 + * @return String + */ + String download(QiniuContent content, QiniuConfig config); + + /** + * 删除文件 + * @param content 文件 + * @param config 配置 + */ + void delete(QiniuContent content, QiniuConfig config); + + /** + * 同步数据 + * @param config 配置 + */ + void synchronize(QiniuConfig config); + + /** + * 删除文件 + * @param ids 文件ID数组 + * @param config 配置 + */ + void deleteAll(Long[] ids, QiniuConfig config); + + /** + * 更新数据 + * @param type 类型 + */ + void update(String type); + + /** + * 导出数据 + * @param queryAll / + * @param response / + * @throws IOException / + */ + void downloadList(List queryAll, HttpServletResponse response) throws IOException; +} diff --git a/eladmin-tools/src/main/java/me/zhengjie/service/dto/LocalStorageDto.java b/eladmin-tools/src/main/java/me/zhengjie/service/dto/LocalStorageDto.java new file mode 100644 index 0000000..14221c2 --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/service/dto/LocalStorageDto.java @@ -0,0 +1,42 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.service.dto; + +import lombok.Getter; +import lombok.Setter; +import me.zhengjie.base.BaseDTO; +import java.io.Serializable; + +/** +* @author Zheng Jie +* @date 2019-09-05 +*/ +@Getter +@Setter +public class LocalStorageDto extends BaseDTO implements Serializable { + + private Long id; + + private String realName; + + private String name; + + private String suffix; + + private String type; + + private String size; +} \ No newline at end of file diff --git a/eladmin-tools/src/main/java/me/zhengjie/service/dto/LocalStorageQueryCriteria.java b/eladmin-tools/src/main/java/me/zhengjie/service/dto/LocalStorageQueryCriteria.java new file mode 100644 index 0000000..bea1cc7 --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/service/dto/LocalStorageQueryCriteria.java @@ -0,0 +1,36 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.service.dto; + +import lombok.Data; +import java.sql.Timestamp; +import java.util.List; + +import me.zhengjie.annotation.Query; + +/** +* @author Zheng Jie +* @date 2019-09-05 +*/ +@Data +public class LocalStorageQueryCriteria{ + + @Query(blurry = "name,suffix,type,createBy,size") + private String blurry; + + @Query(type = Query.Type.BETWEEN) + private List createTime; +} \ No newline at end of file diff --git a/eladmin-tools/src/main/java/me/zhengjie/service/dto/PictureQueryCriteria.java b/eladmin-tools/src/main/java/me/zhengjie/service/dto/PictureQueryCriteria.java new file mode 100644 index 0000000..e7d4f1b --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/service/dto/PictureQueryCriteria.java @@ -0,0 +1,40 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.service.dto; + +import lombok.Data; +import me.zhengjie.annotation.Query; +import java.sql.Timestamp; +import java.util.List; + +/** + * sm.ms图床 + * + * @author Zheng Jie + * @date 2019-6-4 09:52:09 + */ +@Data +public class PictureQueryCriteria{ + + @Query(type = Query.Type.INNER_LIKE) + private String filename; + + @Query(type = Query.Type.INNER_LIKE) + private String username; + + @Query(type = Query.Type.BETWEEN) + private List createTime; +} diff --git a/eladmin-tools/src/main/java/me/zhengjie/service/dto/QiniuQueryCriteria.java b/eladmin-tools/src/main/java/me/zhengjie/service/dto/QiniuQueryCriteria.java new file mode 100644 index 0000000..f5c2240 --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/service/dto/QiniuQueryCriteria.java @@ -0,0 +1,36 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.service.dto; + +import lombok.Data; +import me.zhengjie.annotation.Query; + +import java.sql.Timestamp; +import java.util.List; + +/** + * @author Zheng Jie + * @date 2019-6-4 09:54:37 + */ +@Data +public class QiniuQueryCriteria{ + + @Query(type = Query.Type.INNER_LIKE) + private String key; + + @Query(type = Query.Type.BETWEEN) + private List createTime; +} diff --git a/eladmin-tools/src/main/java/me/zhengjie/service/impl/AliPayServiceImpl.java b/eladmin-tools/src/main/java/me/zhengjie/service/impl/AliPayServiceImpl.java new file mode 100644 index 0000000..0625ba4 --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/service/impl/AliPayServiceImpl.java @@ -0,0 +1,119 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.service.impl; + +import com.alipay.api.AlipayClient; +import com.alipay.api.DefaultAlipayClient; +import com.alipay.api.request.AlipayTradePagePayRequest; +import com.alipay.api.request.AlipayTradeWapPayRequest; +import lombok.RequiredArgsConstructor; +import me.zhengjie.domain.vo.TradeVo; +import me.zhengjie.domain.AlipayConfig; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.repository.AliPayRepository; +import me.zhengjie.service.AliPayService; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import java.util.Optional; + +/** + * @author Zheng Jie + * @date 2018-12-31 + */ +@Service +@RequiredArgsConstructor +@CacheConfig(cacheNames = "aliPay") +public class AliPayServiceImpl implements AliPayService { + + private final AliPayRepository alipayRepository; + + @Override + @Cacheable(key = "'config'") + public AlipayConfig find() { + Optional alipayConfig = alipayRepository.findById(1L); + return alipayConfig.orElseGet(AlipayConfig::new); + } + + @Override + @CachePut(key = "'config'") + @Transactional(rollbackFor = Exception.class) + public AlipayConfig config(AlipayConfig alipayConfig) { + alipayConfig.setId(1L); + return alipayRepository.save(alipayConfig); + } + + @Override + public String toPayAsPc(AlipayConfig alipay, TradeVo trade) throws Exception { + + if(alipay.getId() == null){ + throw new BadRequestException("请先添加相应配置,再操作"); + } + AlipayClient alipayClient = new DefaultAlipayClient(alipay.getGatewayUrl(), alipay.getAppId(), alipay.getPrivateKey(), alipay.getFormat(), alipay.getCharset(), alipay.getPublicKey(), alipay.getSignType()); + + // 创建API对应的request(电脑网页版) + AlipayTradePagePayRequest request = new AlipayTradePagePayRequest(); + + // 订单完成后返回的页面和异步通知地址 + request.setReturnUrl(alipay.getReturnUrl()); + request.setNotifyUrl(alipay.getNotifyUrl()); + // 填充订单参数 + request.setBizContent("{" + + " \"out_trade_no\":\""+trade.getOutTradeNo()+"\"," + + " \"product_code\":\"FAST_INSTANT_TRADE_PAY\"," + + " \"total_amount\":"+trade.getTotalAmount()+"," + + " \"subject\":\""+trade.getSubject()+"\"," + + " \"body\":\""+trade.getBody()+"\"," + + " \"extend_params\":{" + + " \"sys_service_provider_id\":\""+alipay.getSysServiceProviderId()+"\"" + + " }"+ + " }");//填充业务参数 + // 调用SDK生成表单, 通过GET方式,口可以获取url + return alipayClient.pageExecute(request, "GET").getBody(); + + } + + @Override + public String toPayAsWeb(AlipayConfig alipay, TradeVo trade) throws Exception { + if(alipay.getId() == null){ + throw new BadRequestException("请先添加相应配置,再操作"); + } + AlipayClient alipayClient = new DefaultAlipayClient(alipay.getGatewayUrl(), alipay.getAppId(), alipay.getPrivateKey(), alipay.getFormat(), alipay.getCharset(), alipay.getPublicKey(), alipay.getSignType()); + + double money = Double.parseDouble(trade.getTotalAmount()); + double maxMoney = 5000; + if(money <= 0 || money >= maxMoney){ + throw new BadRequestException("测试金额过大"); + } + // 创建API对应的request(手机网页版) + AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest(); + request.setReturnUrl(alipay.getReturnUrl()); + request.setNotifyUrl(alipay.getNotifyUrl()); + request.setBizContent("{" + + " \"out_trade_no\":\""+trade.getOutTradeNo()+"\"," + + " \"product_code\":\"FAST_INSTANT_TRADE_PAY\"," + + " \"total_amount\":"+trade.getTotalAmount()+"," + + " \"subject\":\""+trade.getSubject()+"\"," + + " \"body\":\""+trade.getBody()+"\"," + + " \"extend_params\":{" + + " \"sys_service_provider_id\":\""+alipay.getSysServiceProviderId()+"\"" + + " }"+ + " }"); + return alipayClient.pageExecute(request, "GET").getBody(); + } +} diff --git a/eladmin-tools/src/main/java/me/zhengjie/service/impl/EmailServiceImpl.java b/eladmin-tools/src/main/java/me/zhengjie/service/impl/EmailServiceImpl.java new file mode 100644 index 0000000..aa56732 --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/service/impl/EmailServiceImpl.java @@ -0,0 +1,107 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.service.impl; + +import cn.hutool.extra.mail.Mail; +import cn.hutool.extra.mail.MailAccount; +import lombok.RequiredArgsConstructor; +import me.zhengjie.domain.EmailConfig; +import me.zhengjie.domain.vo.EmailVo; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.repository.EmailRepository; +import me.zhengjie.service.EmailService; +import me.zhengjie.utils.EncryptUtils; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import java.util.Optional; + +/** + * @author Zheng Jie + * @date 2018-12-26 + */ +@Service +@RequiredArgsConstructor +@CacheConfig(cacheNames = "email") +public class EmailServiceImpl implements EmailService { + + private final EmailRepository emailRepository; + + @Override + @CachePut(key = "'config'") + @Transactional(rollbackFor = Exception.class) + public EmailConfig config(EmailConfig emailConfig, EmailConfig old) throws Exception { + emailConfig.setId(1L); + if(!emailConfig.getPass().equals(old.getPass())){ + // 对称加密 + emailConfig.setPass(EncryptUtils.desEncrypt(emailConfig.getPass())); + } + return emailRepository.save(emailConfig); + } + + @Override + @Cacheable(key = "'config'") + public EmailConfig find() { + Optional emailConfig = emailRepository.findById(1L); + return emailConfig.orElseGet(EmailConfig::new); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void send(EmailVo emailVo, EmailConfig emailConfig){ + if(emailConfig.getId() == null){ + throw new BadRequestException("请先配置,再操作"); + } + // 封装 + MailAccount account = new MailAccount(); + // 设置用户 + String user = emailConfig.getFromUser().split("@")[0]; + account.setUser(user); + account.setHost(emailConfig.getHost()); + account.setPort(Integer.parseInt(emailConfig.getPort())); + account.setAuth(true); + try { + // 对称解密 + account.setPass(EncryptUtils.desDecrypt(emailConfig.getPass())); + } catch (Exception e) { + throw new BadRequestException(e.getMessage()); + } + account.setFrom(emailConfig.getUser()+"<"+emailConfig.getFromUser()+">"); + // ssl方式发送 + account.setSslEnable(true); + // 使用STARTTLS安全连接 + account.setStarttlsEnable(true); + // 解决jdk8之后默认禁用部分tls协议,导致邮件发送失败的问题 + account.setSslProtocols("TLSv1 TLSv1.1 TLSv1.2"); + String content = emailVo.getContent(); + // 发送 + try { + int size = emailVo.getTos().size(); + Mail.create(account) + .setTos(emailVo.getTos().toArray(new String[size])) + .setTitle(emailVo.getSubject()) + .setContent(content) + .setHtml(true) + //关闭session + .setUseGlobalSession(false) + .send(); + }catch (Exception e){ + throw new BadRequestException(e.getMessage()); + } + } +} diff --git a/eladmin-tools/src/main/java/me/zhengjie/service/impl/LocalStorageServiceImpl.java b/eladmin-tools/src/main/java/me/zhengjie/service/impl/LocalStorageServiceImpl.java new file mode 100644 index 0000000..9454853 --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/service/impl/LocalStorageServiceImpl.java @@ -0,0 +1,133 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import lombok.RequiredArgsConstructor; +import me.zhengjie.config.FileProperties; +import me.zhengjie.domain.LocalStorage; +import me.zhengjie.service.dto.LocalStorageDto; +import me.zhengjie.service.dto.LocalStorageQueryCriteria; +import me.zhengjie.service.mapstruct.LocalStorageMapper; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.utils.*; +import me.zhengjie.repository.LocalStorageRepository; +import me.zhengjie.service.LocalStorageService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.web.multipart.MultipartFile; +import javax.servlet.http.HttpServletResponse; + +/** +* @author Zheng Jie +* @date 2019-09-05 +*/ +@Service +@RequiredArgsConstructor +public class LocalStorageServiceImpl implements LocalStorageService { + + private final LocalStorageRepository localStorageRepository; + private final LocalStorageMapper localStorageMapper; + private final FileProperties properties; + + @Override + public PageResult queryAll(LocalStorageQueryCriteria criteria, Pageable pageable){ + Page page = localStorageRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable); + return PageUtil.toPage(page.map(localStorageMapper::toDto)); + } + + @Override + public List queryAll(LocalStorageQueryCriteria criteria){ + return localStorageMapper.toDto(localStorageRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder))); + } + + @Override + public LocalStorageDto findById(Long id){ + LocalStorage localStorage = localStorageRepository.findById(id).orElseGet(LocalStorage::new); + ValidationUtil.isNull(localStorage.getId(),"LocalStorage","id",id); + return localStorageMapper.toDto(localStorage); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public LocalStorage create(String name, MultipartFile multipartFile) { + FileUtil.checkSize(properties.getMaxSize(), multipartFile.getSize()); + String suffix = FileUtil.getExtensionName(multipartFile.getOriginalFilename()); + String type = FileUtil.getFileType(suffix); + File file = FileUtil.upload(multipartFile, properties.getPath().getPath() + type + File.separator); + if(ObjectUtil.isNull(file)){ + throw new BadRequestException("上传失败"); + } + try { + name = StringUtils.isBlank(name) ? FileUtil.getFileNameNoEx(multipartFile.getOriginalFilename()) : name; + LocalStorage localStorage = new LocalStorage( + file.getName(), + name, + suffix, + file.getPath(), + type, + FileUtil.getSize(multipartFile.getSize()) + ); + return localStorageRepository.save(localStorage); + }catch (Exception e){ + FileUtil.del(file); + throw e; + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(LocalStorage resources) { + LocalStorage localStorage = localStorageRepository.findById(resources.getId()).orElseGet(LocalStorage::new); + ValidationUtil.isNull( localStorage.getId(),"LocalStorage","id",resources.getId()); + localStorage.copy(resources); + localStorageRepository.save(localStorage); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteAll(Long[] ids) { + for (Long id : ids) { + LocalStorage storage = localStorageRepository.findById(id).orElseGet(LocalStorage::new); + FileUtil.del(storage.getPath()); + localStorageRepository.delete(storage); + } + } + + @Override + public void download(List queryAll, HttpServletResponse response) throws IOException { + List> list = new ArrayList<>(); + for (LocalStorageDto localStorageDTO : queryAll) { + Map map = new LinkedHashMap<>(); + map.put("文件名", localStorageDTO.getRealName()); + map.put("备注名", localStorageDTO.getName()); + map.put("文件类型", localStorageDTO.getType()); + map.put("文件大小", localStorageDTO.getSize()); + map.put("创建者", localStorageDTO.getCreateBy()); + map.put("创建日期", localStorageDTO.getCreateTime()); + list.add(map); + } + FileUtil.downloadExcel(list, response); + } +} diff --git a/eladmin-tools/src/main/java/me/zhengjie/service/impl/QiNiuServiceImpl.java b/eladmin-tools/src/main/java/me/zhengjie/service/impl/QiNiuServiceImpl.java new file mode 100644 index 0000000..60093d7 --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/service/impl/QiNiuServiceImpl.java @@ -0,0 +1,234 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.service.impl; + +import com.alibaba.fastjson.JSON; +import com.qiniu.common.QiniuException; +import com.qiniu.http.Response; +import com.qiniu.storage.BucketManager; +import com.qiniu.storage.Configuration; +import com.qiniu.storage.UploadManager; +import com.qiniu.storage.model.DefaultPutRet; +import com.qiniu.storage.model.FileInfo; +import com.qiniu.util.Auth; +import lombok.RequiredArgsConstructor; +import me.zhengjie.domain.QiniuConfig; +import me.zhengjie.domain.QiniuContent; +import me.zhengjie.repository.QiniuContentRepository; +import me.zhengjie.service.dto.QiniuQueryCriteria; +import me.zhengjie.utils.*; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.repository.QiNiuConfigRepository; +import me.zhengjie.service.QiNiuService; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.*; + +/** + * @author Zheng Jie + * @date 2018-12-31 + */ +@Service +@RequiredArgsConstructor +@CacheConfig(cacheNames = "qiNiu") +public class QiNiuServiceImpl implements QiNiuService { + + private final QiNiuConfigRepository qiNiuConfigRepository; + private final QiniuContentRepository qiniuContentRepository; + + @Value("${qiniu.max-size}") + private Long maxSize; + + @Override + @Cacheable(key = "'config'") + public QiniuConfig find() { + Optional qiniuConfig = qiNiuConfigRepository.findById(1L); + return qiniuConfig.orElseGet(QiniuConfig::new); + } + + @Override + @CachePut(key = "'config'") + @Transactional(rollbackFor = Exception.class) + public QiniuConfig config(QiniuConfig qiniuConfig) { + qiniuConfig.setId(1L); + String http = "http://", https = "https://"; + if (!(qiniuConfig.getHost().toLowerCase().startsWith(http)||qiniuConfig.getHost().toLowerCase().startsWith(https))) { + throw new BadRequestException("外链域名必须以http://或者https://开头"); + } + return qiNiuConfigRepository.save(qiniuConfig); + } + + @Override + public PageResult queryAll(QiniuQueryCriteria criteria, Pageable pageable){ + return PageUtil.toPage(qiniuContentRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable)); + } + + @Override + public List queryAll(QiniuQueryCriteria criteria) { + return qiniuContentRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder)); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public QiniuContent upload(MultipartFile file, QiniuConfig qiniuConfig) { + FileUtil.checkSize(maxSize, file.getSize()); + if(qiniuConfig.getId() == null){ + throw new BadRequestException("请先添加相应配置,再操作"); + } + // 构造一个带指定Zone对象的配置类 + Configuration cfg = new Configuration(QiNiuUtil.getRegion(qiniuConfig.getZone())); + UploadManager uploadManager = new UploadManager(cfg); + Auth auth = Auth.create(qiniuConfig.getAccessKey(), qiniuConfig.getSecretKey()); + String upToken = auth.uploadToken(qiniuConfig.getBucket()); + try { + String key = file.getOriginalFilename(); + if(qiniuContentRepository.findByKey(key) != null) { + key = QiNiuUtil.getKey(key); + } + Response response = uploadManager.put(file.getBytes(), key, upToken); + //解析上传成功的结果 + + DefaultPutRet putRet = JSON.parseObject(response.bodyString(), DefaultPutRet.class); + QiniuContent content = qiniuContentRepository.findByKey(FileUtil.getFileNameNoEx(putRet.key)); + if(content == null){ + //存入数据库 + QiniuContent qiniuContent = new QiniuContent(); + qiniuContent.setSuffix(FileUtil.getExtensionName(putRet.key)); + qiniuContent.setBucket(qiniuConfig.getBucket()); + qiniuContent.setType(qiniuConfig.getType()); + qiniuContent.setKey(FileUtil.getFileNameNoEx(putRet.key)); + qiniuContent.setUrl(qiniuConfig.getHost()+"/"+putRet.key); + qiniuContent.setSize(FileUtil.getSize(Integer.parseInt(String.valueOf(file.getSize())))); + return qiniuContentRepository.save(qiniuContent); + } + return content; + } catch (Exception e) { + throw new BadRequestException(e.getMessage()); + } + } + + @Override + public QiniuContent findByContentId(Long id) { + QiniuContent qiniuContent = qiniuContentRepository.findById(id).orElseGet(QiniuContent::new); + ValidationUtil.isNull(qiniuContent.getId(),"QiniuContent", "id",id); + return qiniuContent; + } + + @Override + public String download(QiniuContent content,QiniuConfig config){ + String finalUrl; + String type = "公开"; + if(type.equals(content.getType())){ + finalUrl = content.getUrl(); + } else { + Auth auth = Auth.create(config.getAccessKey(), config.getSecretKey()); + // 1小时,可以自定义链接过期时间 + long expireInSeconds = 3600; + finalUrl = auth.privateDownloadUrl(content.getUrl(), expireInSeconds); + } + return finalUrl; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(QiniuContent content, QiniuConfig config) { + //构造一个带指定Zone对象的配置类 + Configuration cfg = new Configuration(QiNiuUtil.getRegion(config.getZone())); + Auth auth = Auth.create(config.getAccessKey(), config.getSecretKey()); + BucketManager bucketManager = new BucketManager(auth, cfg); + try { + bucketManager.delete(content.getBucket(), content.getKey() + "." + content.getSuffix()); + qiniuContentRepository.delete(content); + } catch (QiniuException ex) { + qiniuContentRepository.delete(content); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void synchronize(QiniuConfig config) { + if(config.getId() == null){ + throw new BadRequestException("请先添加相应配置,再操作"); + } + //构造一个带指定Zone对象的配置类 + Configuration cfg = new Configuration(QiNiuUtil.getRegion(config.getZone())); + Auth auth = Auth.create(config.getAccessKey(), config.getSecretKey()); + BucketManager bucketManager = new BucketManager(auth, cfg); + //文件名前缀 + String prefix = ""; + //每次迭代的长度限制,最大1000,推荐值 1000 + int limit = 1000; + //指定目录分隔符,列出所有公共前缀(模拟列出目录效果)。缺省值为空字符串 + String delimiter = ""; + //列举空间文件列表 + BucketManager.FileListIterator fileListIterator = bucketManager.createFileListIterator(config.getBucket(), prefix, limit, delimiter); + while (fileListIterator.hasNext()) { + //处理获取的file list结果 + QiniuContent qiniuContent; + FileInfo[] items = fileListIterator.next(); + for (FileInfo item : items) { + if(qiniuContentRepository.findByKey(FileUtil.getFileNameNoEx(item.key)) == null){ + qiniuContent = new QiniuContent(); + qiniuContent.setSize(FileUtil.getSize(Integer.parseInt(String.valueOf(item.fsize)))); + qiniuContent.setSuffix(FileUtil.getExtensionName(item.key)); + qiniuContent.setKey(FileUtil.getFileNameNoEx(item.key)); + qiniuContent.setType(config.getType()); + qiniuContent.setBucket(config.getBucket()); + qiniuContent.setUrl(config.getHost()+"/"+item.key); + qiniuContentRepository.save(qiniuContent); + } + } + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteAll(Long[] ids, QiniuConfig config) { + for (Long id : ids) { + delete(findByContentId(id), config); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(String type) { + qiNiuConfigRepository.update(type); + } + + @Override + public void downloadList(List queryAll, HttpServletResponse response) throws IOException { + List> list = new ArrayList<>(); + for (QiniuContent content : queryAll) { + Map map = new LinkedHashMap<>(); + map.put("文件名", content.getKey()); + map.put("文件类型", content.getSuffix()); + map.put("空间名称", content.getBucket()); + map.put("文件大小", content.getSize()); + map.put("空间类型", content.getType()); + map.put("创建日期", content.getUpdateTime()); + list.add(map); + } + FileUtil.downloadExcel(list, response); + } +} diff --git a/eladmin-tools/src/main/java/me/zhengjie/service/mapstruct/LocalStorageMapper.java b/eladmin-tools/src/main/java/me/zhengjie/service/mapstruct/LocalStorageMapper.java new file mode 100644 index 0000000..4c6d955 --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/service/mapstruct/LocalStorageMapper.java @@ -0,0 +1,31 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.service.mapstruct; + +import me.zhengjie.base.BaseMapper; +import me.zhengjie.service.dto.LocalStorageDto; +import me.zhengjie.domain.LocalStorage; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; + +/** +* @author Zheng Jie +* @date 2019-09-05 +*/ +@Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface LocalStorageMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/eladmin-tools/src/main/java/me/zhengjie/utils/AliPayStatusEnum.java b/eladmin-tools/src/main/java/me/zhengjie/utils/AliPayStatusEnum.java new file mode 100644 index 0000000..c114fd1 --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/utils/AliPayStatusEnum.java @@ -0,0 +1,46 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.utils; + +/** + * 支付状态 + * @author zhengjie + * @date 2018/08/01 16:45:43 + */ +public enum AliPayStatusEnum { + + /** 交易成功 */ + FINISHED("TRADE_FINISHED"), + + /** 支付成功 */ + SUCCESS("TRADE_SUCCESS"), + + /** 交易创建 */ + BUYER_PAY("WAIT_BUYER_PAY"), + + /** 交易关闭 */ + CLOSED("TRADE_CLOSED"); + + private final String value; + + AliPayStatusEnum(String value) { + this.value = value; + } + + public String getValue() { + return value; + } +} diff --git a/eladmin-tools/src/main/java/me/zhengjie/utils/AlipayUtils.java b/eladmin-tools/src/main/java/me/zhengjie/utils/AlipayUtils.java new file mode 100644 index 0000000..203f7db --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/utils/AlipayUtils.java @@ -0,0 +1,85 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.utils; + +import com.alipay.api.AlipayApiException; +import com.alipay.api.internal.util.AlipaySignature; +import me.zhengjie.domain.AlipayConfig; +import org.springframework.stereotype.Component; +import javax.servlet.http.HttpServletRequest; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 支付宝工具类 + * @author zhengjie + * @date 2018/09/30 14:04:35 + */ +@Component +public class AlipayUtils { + + /** + * 生成订单号 + * @return String + */ + public String getOrderCode() { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + int a = (int)(Math.random() * 9000.0D) + 1000; + System.out.println(a); + Date date = new Date(); + String str = sdf.format(date); + String[] split = str.split("-"); + String s = split[0] + split[1] + split[2]; + String[] split1 = s.split(" "); + String s1 = split1[0] + split1[1]; + String[] split2 = s1.split(":"); + return split2[0] + split2[1] + split2[2] + a; + } + + /** + * 校验签名 + * @param request HttpServletRequest + * @param alipay 阿里云配置 + * @return boolean + */ + public boolean rsaCheck(HttpServletRequest request, AlipayConfig alipay){ + + // 获取支付宝POST过来反馈信息 + Map params = new HashMap<>(1); + Map requestParams = request.getParameterMap(); + for (Object o : requestParams.keySet()) { + String name = (String) o; + String[] values = requestParams.get(name); + String valueStr = ""; + for (int i = 0; i < values.length; i++) { + valueStr = (i == values.length - 1) ? valueStr + values[i] + : valueStr + values[i] + ","; + } + params.put(name, valueStr); + } + + try { + return AlipaySignature.rsaCheckV1(params, + alipay.getPublicKey(), + alipay.getCharset(), + alipay.getSignType()); + } catch (AlipayApiException e) { + return false; + } + } +} diff --git a/eladmin-tools/src/main/java/me/zhengjie/utils/QiNiuUtil.java b/eladmin-tools/src/main/java/me/zhengjie/utils/QiNiuUtil.java new file mode 100644 index 0000000..8091a95 --- /dev/null +++ b/eladmin-tools/src/main/java/me/zhengjie/utils/QiNiuUtil.java @@ -0,0 +1,71 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.utils; + +import com.qiniu.storage.Region; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * 七牛云存储工具类 + * @author Zheng Jie + * @date 2018-12-31 + */ +public class QiNiuUtil { + + private static final String HUAD = "华东"; + + private static final String HUAB = "华北"; + + private static final String HUAN = "华南"; + + private static final String BEIM = "北美"; + + /** + * 得到机房的对应关系 + * @param zone 机房名称 + * @return Region + */ + public static Region getRegion(String zone){ + + if(HUAD.equals(zone)){ + return Region.huadong(); + } else if(HUAB.equals(zone)){ + return Region.huabei(); + } else if(HUAN.equals(zone)){ + return Region.huanan(); + } else if (BEIM.equals(zone)){ + return Region.beimei(); + // 否则就是东南亚 + } else { + return Region.qvmHuadong(); + } + } + + /** + * 默认不指定key的情况下,以文件内容的hash值作为文件名 + * @param file 文件名 + * @return String + */ + public static String getKey(String file){ + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); + Date date = new Date(); + return FileUtil.getFileNameNoEx(file) + "-" + + sdf.format(date) + + "." + + FileUtil.getExtensionName(file); + } +} diff --git a/eladmin-web/.editorconfig b/eladmin-web/.editorconfig new file mode 100644 index 0000000..3454886 --- /dev/null +++ b/eladmin-web/.editorconfig @@ -0,0 +1,14 @@ +# https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +insert_final_newline = false +trim_trailing_whitespace = false diff --git a/eladmin-web/.env.development b/eladmin-web/.env.development new file mode 100644 index 0000000..d9a2132 --- /dev/null +++ b/eladmin-web/.env.development @@ -0,0 +1,8 @@ +ENV = 'development' + +# 接口地址 +VUE_APP_BASE_API = 'http://localhost:8000' +VUE_APP_WS_API = 'ws://localhost:8000' + +# 是否启用 babel-plugin-dynamic-import-node插件 +VUE_CLI_BABEL_TRANSPILE_MODULES = true diff --git a/eladmin-web/.env.production b/eladmin-web/.env.production new file mode 100644 index 0000000..66da123 --- /dev/null +++ b/eladmin-web/.env.production @@ -0,0 +1,7 @@ +ENV = 'production' + +# 如果使用 Nginx 代理后端接口,那么此处需要改为 '/',文件查看 Docker 部署篇,Nginx 配置 +# 接口地址,注意协议,如果你没有配置 ssl,需要将 https 改为 http +VUE_APP_BASE_API = 'https://eladmin.vip' +# 如果接口是 http 形式, wss 需要改为 ws +VUE_APP_WS_API = 'wss://eladmin.vip' diff --git a/eladmin-web/.eslintignore b/eladmin-web/.eslintignore new file mode 100644 index 0000000..e6529fc --- /dev/null +++ b/eladmin-web/.eslintignore @@ -0,0 +1,4 @@ +build/*.js +src/assets +public +dist diff --git a/eladmin-web/.eslintrc.js b/eladmin-web/.eslintrc.js new file mode 100644 index 0000000..c977505 --- /dev/null +++ b/eladmin-web/.eslintrc.js @@ -0,0 +1,198 @@ +module.exports = { + root: true, + parserOptions: { + parser: 'babel-eslint', + sourceType: 'module' + }, + env: { + browser: true, + node: true, + es6: true, + }, + extends: ['plugin:vue/recommended', 'eslint:recommended'], + + // add your custom rules here + //it is base on https://github.com/vuejs/eslint-config-vue + rules: { + "vue/max-attributes-per-line": [2, { + "singleline": 10, + "multiline": { + "max": 1, + "allowFirstLine": false + } + }], + "vue/singleline-html-element-content-newline": "off", + "vue/multiline-html-element-content-newline":"off", + "vue/name-property-casing": ["error", "PascalCase"], + "vue/no-v-html": "off", + 'accessor-pairs': 2, + 'arrow-spacing': [2, { + 'before': true, + 'after': true + }], + 'block-spacing': [2, 'always'], + 'brace-style': [2, '1tbs', { + 'allowSingleLine': true + }], + 'camelcase': [0, { + 'properties': 'always' + }], + 'comma-dangle': [2, 'never'], + 'comma-spacing': [2, { + 'before': false, + 'after': true + }], + 'comma-style': [2, 'last'], + 'constructor-super': 2, + 'curly': [2, 'multi-line'], + 'dot-location': [2, 'property'], + 'eol-last': 2, + 'eqeqeq': ["error", "always", {"null": "ignore"}], + 'generator-star-spacing': [2, { + 'before': true, + 'after': true + }], + 'handle-callback-err': [2, '^(err|error)$'], + 'indent': [2, 2, { + 'SwitchCase': 1 + }], + 'jsx-quotes': [2, 'prefer-single'], + 'key-spacing': [2, { + 'beforeColon': false, + 'afterColon': true + }], + 'keyword-spacing': [2, { + 'before': true, + 'after': true + }], + 'new-cap': [2, { + 'newIsCap': true, + 'capIsNew': false + }], + 'new-parens': 2, + 'no-array-constructor': 2, + 'no-caller': 2, + 'no-console': 'off', + 'no-class-assign': 2, + 'no-cond-assign': 2, + 'no-const-assign': 2, + 'no-control-regex': 0, + 'no-delete-var': 2, + 'no-dupe-args': 2, + 'no-dupe-class-members': 2, + 'no-dupe-keys': 2, + 'no-duplicate-case': 2, + 'no-empty-character-class': 2, + 'no-empty-pattern': 2, + 'no-eval': 2, + 'no-ex-assign': 2, + 'no-extend-native': 2, + 'no-extra-bind': 2, + 'no-extra-boolean-cast': 2, + 'no-extra-parens': [2, 'functions'], + 'no-fallthrough': 2, + 'no-floating-decimal': 2, + 'no-func-assign': 2, + 'no-implied-eval': 2, + 'no-inner-declarations': [2, 'functions'], + 'no-invalid-regexp': 2, + 'no-irregular-whitespace': 2, + 'no-iterator': 2, + 'no-label-var': 2, + 'no-labels': [2, { + 'allowLoop': false, + 'allowSwitch': false + }], + 'no-lone-blocks': 2, + 'no-mixed-spaces-and-tabs': 2, + 'no-multi-spaces': 2, + 'no-multi-str': 2, + 'no-multiple-empty-lines': [2, { + 'max': 1 + }], + 'no-native-reassign': 2, + 'no-negated-in-lhs': 2, + 'no-new-object': 2, + 'no-new-require': 2, + 'no-new-symbol': 2, + 'no-new-wrappers': 2, + 'no-obj-calls': 2, + 'no-octal': 2, + 'no-octal-escape': 2, + 'no-path-concat': 2, + 'no-proto': 2, + 'no-redeclare': 2, + 'no-regex-spaces': 2, + 'no-return-assign': [2, 'except-parens'], + 'no-self-assign': 2, + 'no-self-compare': 2, + 'no-sequences': 2, + 'no-shadow-restricted-names': 2, + 'no-spaced-func': 2, + 'no-sparse-arrays': 2, + 'no-this-before-super': 2, + 'no-throw-literal': 2, + 'no-trailing-spaces': 2, + 'no-undef': 2, + 'no-undef-init': 2, + 'no-unexpected-multiline': 2, + 'no-unmodified-loop-condition': 2, + 'no-unneeded-ternary': [2, { + 'defaultAssignment': false + }], + 'no-unreachable': 2, + 'no-unsafe-finally': 2, + 'no-unused-vars': [2, { + 'vars': 'all', + 'args': 'none' + }], + 'no-useless-call': 2, + 'no-useless-computed-key': 2, + 'no-useless-constructor': 2, + 'no-useless-escape': 0, + 'no-whitespace-before-property': 2, + 'no-with': 2, + 'one-var': [2, { + 'initialized': 'never' + }], + 'operator-linebreak': [2, 'after', { + 'overrides': { + '?': 'before', + ':': 'before' + } + }], + 'padded-blocks': [2, 'never'], + 'quotes': [2, 'single', { + 'avoidEscape': true, + 'allowTemplateLiterals': true + }], + 'semi': [2, 'never'], + 'semi-spacing': [2, { + 'before': false, + 'after': true + }], + 'space-before-blocks': [2, 'always'], + 'space-before-function-paren': [2, 'never'], + 'space-in-parens': [2, 'never'], + 'space-infix-ops': 2, + 'space-unary-ops': [2, { + 'words': true, + 'nonwords': false + }], + 'spaced-comment': [2, 'always', { + 'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ','] + }], + 'template-curly-spacing': [2, 'never'], + 'use-isnan': 2, + 'valid-typeof': 2, + 'wrap-iife': [2, 'any'], + 'yield-star-spacing': [2, 'both'], + 'yoda': [2, 'never'], + 'prefer-const': 2, + 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, + 'object-curly-spacing': [2, 'always', { + objectsInObjects: false + }], + 'array-bracket-spacing': [2, 'never'] + } +} diff --git a/eladmin-web/.gitignore b/eladmin-web/.gitignore new file mode 100644 index 0000000..1978bc2 --- /dev/null +++ b/eladmin-web/.gitignore @@ -0,0 +1,24 @@ +.DS_Store +node_modules/ +dist/ +demo/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +**/*.log + +tests/**/coverage/ +tests/e2e/reports +selenium-debug.log + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.local + +package-lock.json +yarn.lock diff --git a/eladmin-web/.travis.yml b/eladmin-web/.travis.yml new file mode 100644 index 0000000..f4be7a0 --- /dev/null +++ b/eladmin-web/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: 10 +script: npm run test +notifications: + email: false diff --git a/eladmin-web/LICENSE b/eladmin-web/LICENSE new file mode 100644 index 0000000..78a36cb --- /dev/null +++ b/eladmin-web/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "{}" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright 2019 Zheng Jie + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/eladmin-web/README.md b/eladmin-web/README.md new file mode 100644 index 0000000..71d32bf --- /dev/null +++ b/eladmin-web/README.md @@ -0,0 +1,77 @@ +# ELADMIN-WEB + +ELADMIN 前端源码 + +#### 项目源码 + +| | 后端源码 | 前端源码 | +|--- |--- | --- | +| github | https://github.com/elunez/eladmin | https://github.com/elunez/eladmin-web | +| 码云 | https://gitee.com/elunez/eladmin | https://gitee.com/elunez/eladmin-web | + +#### 开发文档 +[https://eladmin.vip](https://eladmin.vip) + +#### 体验地址 +[https://eladmin.vip/demo](https://eladmin.vip/demo) + +#### 前端模板 + +初始模板基于: [https://github.com/PanJiaChen/vue-element-admin](https://github.com/PanJiaChen/vue-element-admin) + +模板文档: [https://panjiachen.github.io/vue-element-admin-site/zh/guide/](https://panjiachen.github.io/vue-element-admin-site/zh/guide/) + +#### VPS推荐 + + + + +使用优惠码: `BWHNCXNVXV`,可获得 6.81% 的折扣, [查看介绍](https://eladmin.vip/pages/040101/) + +#### Build Setup +**推荐 node 版本:12-16** +``` bash +# 配置镜像加速 +https://www.ydyno.com/archives/1219.html + +# 安装依赖 +npm install + +# 启动服务 localhost:8013 +npm run dev + +# 构建生产环境 +npm run build:prod +``` + +#### 常见问题 + +1、linux 系统在安装依赖的时候会出现 node-sass 无法安装的问题 + +解决方案: +``` +1. 单独安装:npm install --unsafe-perm node-sass +2. 直接使用:npm install --unsafe-perm +``` + +2、加速node-sass安装 + +https://www.ydyno.com/archives/1219.html + +#### 特别鸣谢 + +- 感谢 [JetBrains](https://www.jetbrains.com/) 提供的非商业开源软件开发授权 + +- 感谢 [PanJiaChen](https://github.com/PanJiaChen/vue-element-admin) 大佬提供的前端模板 + +- 感谢 [Moxun](https://github.com/moxun1639) 大佬提供的前端 Crud 通用组件 + +- 感谢 [zhy6599](https://gitee.com/zhy6599) 大佬提供的后端运维管理相关功能 + +- 感谢 [j.yao.SUSE](https://github.com/everhopingandwaiting) 大佬提供的匿名接口与Redis限流等功能 + +- 感谢 [d15801543974](https://github.com/d15801543974) 大佬提供的基于注解的通用查询方式 + +#### 反馈交流 + +- QQ交流群:891137268 、947578238、659622532 \ No newline at end of file diff --git a/eladmin-web/babel.config.js b/eladmin-web/babel.config.js new file mode 100644 index 0000000..804632a --- /dev/null +++ b/eladmin-web/babel.config.js @@ -0,0 +1,11 @@ +const plugins = ['@vue/babel-plugin-transform-vue-jsx'] +// 生产环境移除console +if (process.env.NODE_ENV === 'production') { + plugins.push('transform-remove-console') +} +module.exports = { + plugins: plugins, + presets: [ + '@vue/app' + ] +} diff --git a/eladmin-web/jest.config.js b/eladmin-web/jest.config.js new file mode 100644 index 0000000..143cdc8 --- /dev/null +++ b/eladmin-web/jest.config.js @@ -0,0 +1,24 @@ +module.exports = { + moduleFileExtensions: ['js', 'jsx', 'json', 'vue'], + transform: { + '^.+\\.vue$': 'vue-jest', + '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': + 'jest-transform-stub', + '^.+\\.jsx?$': 'babel-jest' + }, + moduleNameMapper: { + '^@/(.*)$': '/src/$1' + }, + snapshotSerializers: ['jest-serializer-vue'], + testMatch: [ + '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)' + ], + collectCoverageFrom: ['src/utils/**/*.{js,vue}', '!src/utils/auth.js', '!src/utils/request.js', 'src/components/**/*.{js,vue}'], + coverageDirectory: '/tests/unit/coverage', + // 'collectCoverage': true, + 'coverageReporters': [ + 'lcov', + 'text-summary' + ], + testURL: 'http://localhost/' +} diff --git a/eladmin-web/package.json b/eladmin-web/package.json new file mode 100644 index 0000000..689e255 --- /dev/null +++ b/eladmin-web/package.json @@ -0,0 +1,115 @@ +{ + "name": "eladmin-web", + "version": "2.7.0", + "description": "ELADMIN 前端源码", + "author": "Zheng Jie", + "license": "Apache-2.0", + "scripts": { + "dev": "vue-cli-service serve", + "build:prod": "vue-cli-service build", + "build:stage": "vue-cli-service build --mode staging", + "preview": "node build/index.js --preview", + "lint": "eslint --ext .js,.vue src", + "test:unit": "jest --clearCache && vue-cli-service test:unit", + "svgo": "svgo -f src/assets/icons/svg --config=src/assets/icons/svgo.yml", + "new": "plop" + }, + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + }, + "lint-staged": { + "src/**/*.{js,vue}": [ + "eslint --fix", + "git add" + ] + }, + "repository": { + "type": "git", + "url": "https://github.com/elunez/eladmin-web.git" + }, + "bugs": { + "url": "https://github.com/elunez/eladmin/issues" + }, + "dependencies": { + "@riophae/vue-treeselect": "^0.4.0", + "@wangeditor/editor": "^5.1.23", + "@wangeditor/editor-for-vue": "^1.0.2", + "axios": "^0.21.1", + "clipboard": "2.0.4", + "codemirror": "^5.49.2", + "core-js": "^2.6.12", + "echarts": "^4.2.1", + "echarts-wordcloud": "^1.1.3", + "element-ui": "^2.15.8", + "file-saver": "1.3.8", + "fuse.js": "3.4.4", + "js-beautify": "^1.10.2", + "js-cookie": "2.2.0", + "jsencrypt": "^3.0.0-rc.1", + "jszip": "^3.7.1", + "mavon-editor": "^2.9.1", + "normalize.css": "7.0.0", + "nprogress": "0.2.0", + "path-to-regexp": "2.4.0", + "qs": "^6.10.1", + "screenfull": "4.2.0", + "sortablejs": "1.8.4", + "vue": "^2.6.14", + "vue-count-to": "^1.0.13", + "vue-cropper": "0.4.9", + "vue-echarts": "^5.0.0-beta.0", + "vue-image-crop-upload": "^2.5.0", + "vue-router": "3.0.2", + "vue-splitpane": "1.0.4", + "vuedraggable": "2.20.0", + "vuex": "3.1.0", + "wangeditor": "^4.7.11", + "webpack": "^4.46.0" + }, + "devDependencies": { + "@babel/parser": "^7.7.4", + "@babel/register": "7.0.0", + "@vue/babel-plugin-transform-vue-jsx": "^1.2.1", + "@vue/cli-plugin-babel": "3.5.3", + "@vue/cli-plugin-eslint": "^3.9.1", + "@vue/cli-plugin-unit-jest": "3.5.3", + "@vue/cli-service": "3.5.3", + "@vue/test-utils": "1.0.0-beta.29", + "autoprefixer": "^9.5.1", + "babel-core": "7.0.0-bridge.0", + "babel-eslint": "10.0.1", + "babel-jest": "23.6.0", + "babel-plugin-dynamic-import-node": "2.3.0", + "babel-plugin-transform-remove-console": "^6.9.4", + "chalk": "2.4.2", + "chokidar": "2.1.5", + "compression-webpack-plugin": "5.0.2", + "connect": "3.6.6", + "eslint": "5.15.3", + "eslint-plugin-vue": "5.2.2", + "html-webpack-plugin": "3.2.0", + "http-proxy-middleware": "^0.19.1", + "husky": "1.3.1", + "lint-staged": "8.1.5", + "plop": "2.3.0", + "sass": "1.32.13", + "sass-loader": "10.2.0", + "script-ext-html-webpack-plugin": "2.1.3", + "script-loader": "0.7.2", + "serve-static": "^1.13.2", + "svg-sprite-loader": "4.1.3", + "svgo": "1.2.0", + "tasksfile": "^5.1.1", + "vue-template-compiler": "2.6.14" + }, + "engines": { + "node": ">=8.9", + "npm": ">= 3.0.0" + }, + "browserslist": [ + "> 1%", + "last 2 versions" + ] +} diff --git a/eladmin-web/plopfile.js b/eladmin-web/plopfile.js new file mode 100644 index 0000000..9f3147e --- /dev/null +++ b/eladmin-web/plopfile.js @@ -0,0 +1,7 @@ +const viewGenerator = require('./plop-templates/view/prompt') +const componentGenerator = require('./plop-templates/component/prompt') + +module.exports = function(plop) { + plop.setGenerator('view', viewGenerator) + plop.setGenerator('component', componentGenerator) +} diff --git a/eladmin-web/postcss.config.js b/eladmin-web/postcss.config.js new file mode 100644 index 0000000..961986e --- /dev/null +++ b/eladmin-web/postcss.config.js @@ -0,0 +1,5 @@ +module.exports = { + plugins: { + autoprefixer: {} + } +} diff --git a/eladmin-web/public/favicon.ico b/eladmin-web/public/favicon.ico new file mode 100644 index 0000000..fcb6999 Binary files /dev/null and b/eladmin-web/public/favicon.ico differ diff --git a/eladmin-web/public/index.html b/eladmin-web/public/index.html new file mode 100644 index 0000000..e918500 --- /dev/null +++ b/eladmin-web/public/index.html @@ -0,0 +1,15 @@ + + + + + + + + + <%= webpackConfig.name %> + + +
+ + + diff --git a/eladmin-web/public/robots.txt b/eladmin-web/public/robots.txt new file mode 100644 index 0000000..77470cb --- /dev/null +++ b/eladmin-web/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / \ No newline at end of file diff --git a/eladmin-web/src/App.vue b/eladmin-web/src/App.vue new file mode 100644 index 0000000..ec9032c --- /dev/null +++ b/eladmin-web/src/App.vue @@ -0,0 +1,11 @@ + + + diff --git a/eladmin-web/src/api/data.js b/eladmin-web/src/api/data.js new file mode 100644 index 0000000..d975d75 --- /dev/null +++ b/eladmin-web/src/api/data.js @@ -0,0 +1,17 @@ +import request from '@/utils/request' +import qs from 'qs' + +export function initData(url, params) { + return request({ + url: url + '?' + qs.stringify(params, { indices: false }), + method: 'get' + }) +} + +export function download(url, params) { + return request({ + url: url + '?' + qs.stringify(params, { indices: false }), + method: 'get', + responseType: 'blob' + }) +} diff --git a/eladmin-web/src/api/generator/genConfig.js b/eladmin-web/src/api/generator/genConfig.js new file mode 100644 index 0000000..e15b200 --- /dev/null +++ b/eladmin-web/src/api/generator/genConfig.js @@ -0,0 +1,16 @@ +import request from '@/utils/request' + +export function get(tableName) { + return request({ + url: 'api/genConfig/' + tableName, + method: 'get' + }) +} + +export function update(data) { + return request({ + url: 'api/genConfig', + data, + method: 'put' + }) +} diff --git a/eladmin-web/src/api/generator/generator.js b/eladmin-web/src/api/generator/generator.js new file mode 100644 index 0000000..0c49718 --- /dev/null +++ b/eladmin-web/src/api/generator/generator.js @@ -0,0 +1,33 @@ +import request from '@/utils/request' + +export function getAllTable() { + return request({ + url: 'api/generator/tables/all', + method: 'get' + }) +} + +export function generator(tableName, type) { + return request({ + url: 'api/generator/' + tableName + '/' + type, + method: 'post', + responseType: type === 2 ? 'blob' : '' + }) +} + +export function save(data) { + return request({ + url: 'api/generator', + data, + method: 'put' + }) +} + +export function sync(tables) { + return request({ + url: 'api/generator/sync', + method: 'post', + data: tables + }) +} + diff --git a/eladmin-web/src/api/login.js b/eladmin-web/src/api/login.js new file mode 100644 index 0000000..fe257d2 --- /dev/null +++ b/eladmin-web/src/api/login.js @@ -0,0 +1,35 @@ +import request from '@/utils/request' + +export function login(username, password, code, uuid) { + return request({ + url: 'auth/login', + method: 'post', + data: { + username, + password, + code, + uuid + } + }) +} + +export function getInfo() { + return request({ + url: 'auth/info', + method: 'get' + }) +} + +export function getCodeImg() { + return request({ + url: 'auth/code', + method: 'get' + }) +} + +export function logout() { + return request({ + url: 'auth/logout', + method: 'delete' + }) +} diff --git a/eladmin-web/src/api/mnt/app.js b/eladmin-web/src/api/mnt/app.js new file mode 100644 index 0000000..2a27054 --- /dev/null +++ b/eladmin-web/src/api/mnt/app.js @@ -0,0 +1,27 @@ +import request from '@/utils/request' + +export function add(data) { + return request({ + url: 'api/app', + method: 'post', + data + }) +} + +export function del(ids) { + return request({ + url: 'api/app', + method: 'delete', + data: ids + }) +} + +export function edit(data) { + return request({ + url: 'api/app', + method: 'put', + data + }) +} + +export default { add, edit, del } diff --git a/eladmin-web/src/api/mnt/connect.js b/eladmin-web/src/api/mnt/connect.js new file mode 100644 index 0000000..1bbe90b --- /dev/null +++ b/eladmin-web/src/api/mnt/connect.js @@ -0,0 +1,17 @@ +import request from '@/utils/request' + +export function testDbConnect(data) { + return request({ + url: 'api/database/testConnect', + method: 'post', + data + }) +} + +export function testServerConnect(data) { + return request({ + url: 'api/serverDeploy/testConnect', + method: 'post', + data + }) +} diff --git a/eladmin-web/src/api/mnt/database.js b/eladmin-web/src/api/mnt/database.js new file mode 100644 index 0000000..91797fb --- /dev/null +++ b/eladmin-web/src/api/mnt/database.js @@ -0,0 +1,35 @@ +import request from '@/utils/request' + +export function add(data) { + return request({ + url: 'api/database', + method: 'post', + data + }) +} + +export function del(ids) { + return request({ + url: 'api/database', + method: 'delete', + data: ids + }) +} + +export function edit(data) { + return request({ + url: 'api/database', + method: 'put', + data + }) +} + +export function testDbConnection(data) { + return request({ + url: 'api/database/testConnect', + method: 'post', + data + }) +} + +export default { add, edit, del, testDbConnection } diff --git a/eladmin-web/src/api/mnt/deploy.js b/eladmin-web/src/api/mnt/deploy.js new file mode 100644 index 0000000..c1475ea --- /dev/null +++ b/eladmin-web/src/api/mnt/deploy.js @@ -0,0 +1,77 @@ +import request from '@/utils/request' + +export function add(data) { + return request({ + url: 'api/deploy', + method: 'post', + data + }) +} + +export function del(ids) { + return request({ + url: 'api/deploy', + method: 'delete', + data: ids + }) +} + +export function edit(data) { + return request({ + url: 'api/deploy', + method: 'put', + data + }) +} + +export function getApps() { + return request({ + url: 'api/app', + method: 'get' + }) +} + +export function getServers() { + return request({ + url: 'api/serverDeploy', + method: 'get' + }) +} + +/** + * 启动服务 + * @param data 选中行 + */ +export function startServer(data) { + return request({ + url: 'api/deploy/startServer', + method: 'post', + data + }) +} + +/** + * 停止服务 + * @param data 选中行 + */ +export function stopServer(data) { + return request({ + url: 'api/deploy/stopServer', + method: 'post', + data + }) +} + +/** + * 停止服务 + * @param data 选中行 + */ +export function serverStatus(data) { + return request({ + url: 'api/deploy/serverStatus', + method: 'post', + data + }) +} + +export default { add, edit, del, stopServer, serverStatus, startServer, getServers, getApps } diff --git a/eladmin-web/src/api/mnt/deployHistory.js b/eladmin-web/src/api/mnt/deployHistory.js new file mode 100644 index 0000000..30335e4 --- /dev/null +++ b/eladmin-web/src/api/mnt/deployHistory.js @@ -0,0 +1,21 @@ +import request from '@/utils/request' + +export function del(ids) { + return request({ + url: 'api/deployHistory', + method: 'delete', + data: ids + }) +} + +/** + * 版本回退 + * @param data 选中行 + */ +export function reducte(data) { + return request({ + url: 'api/deploy/serverReduction', + method: 'post', + data + }) +} diff --git a/eladmin-web/src/api/mnt/serverDeploy.js b/eladmin-web/src/api/mnt/serverDeploy.js new file mode 100644 index 0000000..e796114 --- /dev/null +++ b/eladmin-web/src/api/mnt/serverDeploy.js @@ -0,0 +1,27 @@ +import request from '@/utils/request' + +export function add(data) { + return request({ + url: 'api/serverDeploy', + method: 'post', + data + }) +} + +export function del(ids) { + return request({ + url: 'api/serverDeploy', + method: 'delete', + data: ids + }) +} + +export function edit(data) { + return request({ + url: 'api/serverDeploy', + method: 'put', + data + }) +} + +export default { add, edit, del } diff --git a/eladmin-web/src/api/monitor/log.js b/eladmin-web/src/api/monitor/log.js new file mode 100644 index 0000000..13f0d39 --- /dev/null +++ b/eladmin-web/src/api/monitor/log.js @@ -0,0 +1,22 @@ +import request from '@/utils/request' + +export function getErrDetail(id) { + return request({ + url: 'api/logs/error/' + id, + method: 'get' + }) +} + +export function delAllError() { + return request({ + url: 'api/logs/del/error', + method: 'delete' + }) +} + +export function delAllInfo() { + return request({ + url: 'api/logs/del/info', + method: 'delete' + }) +} diff --git a/eladmin-web/src/api/monitor/online.js b/eladmin-web/src/api/monitor/online.js new file mode 100644 index 0000000..057275b --- /dev/null +++ b/eladmin-web/src/api/monitor/online.js @@ -0,0 +1,9 @@ +import request from '@/utils/request' + +export function del(keys) { + return request({ + url: 'auth/online', + method: 'delete', + data: keys + }) +} diff --git a/eladmin-web/src/api/system/code.js b/eladmin-web/src/api/system/code.js new file mode 100644 index 0000000..0d2e4c2 --- /dev/null +++ b/eladmin-web/src/api/system/code.js @@ -0,0 +1,15 @@ +import request from '@/utils/request' + +export function resetEmail(data) { + return request({ + url: 'api/code/resetEmail?email=' + data, + method: 'post' + }) +} + +export function updatePass(pass) { + return request({ + url: 'api/users/updatePass/' + pass, + method: 'get' + }) +} diff --git a/eladmin-web/src/api/system/dept.js b/eladmin-web/src/api/system/dept.js new file mode 100644 index 0000000..8f920d0 --- /dev/null +++ b/eladmin-web/src/api/system/dept.js @@ -0,0 +1,45 @@ +import request from '@/utils/request' + +export function getDepts(params) { + return request({ + url: 'api/dept', + method: 'get', + params + }) +} + +export function getDeptSuperior(ids, exclude) { + exclude = exclude !== undefined ? exclude : false + const data = ids.length || ids.length === 0 ? ids : Array.of(ids) + return request({ + url: 'api/dept/superior?exclude=' + exclude, + method: 'post', + data + }) +} + +export function add(data) { + return request({ + url: 'api/dept', + method: 'post', + data + }) +} + +export function del(ids) { + return request({ + url: 'api/dept', + method: 'delete', + data: ids + }) +} + +export function edit(data) { + return request({ + url: 'api/dept', + method: 'put', + data + }) +} + +export default { add, edit, del, getDepts, getDeptSuperior } diff --git a/eladmin-web/src/api/system/dict.js b/eladmin-web/src/api/system/dict.js new file mode 100644 index 0000000..99170f7 --- /dev/null +++ b/eladmin-web/src/api/system/dict.js @@ -0,0 +1,34 @@ +import request from '@/utils/request' + +export function getDicts() { + return request({ + url: 'api/dict/all', + method: 'get' + }) +} + +export function add(data) { + return request({ + url: 'api/dict', + method: 'post', + data + }) +} + +export function del(ids) { + return request({ + url: 'api/dict/', + method: 'delete', + data: ids + }) +} + +export function edit(data) { + return request({ + url: 'api/dict', + method: 'put', + data + }) +} + +export default { add, edit, del } diff --git a/eladmin-web/src/api/system/dictDetail.js b/eladmin-web/src/api/system/dictDetail.js new file mode 100644 index 0000000..e8dc512 --- /dev/null +++ b/eladmin-web/src/api/system/dictDetail.js @@ -0,0 +1,52 @@ +import request from '@/utils/request' + +export function get(dictName) { + const params = { + dictName, + page: 0, + size: 9999 + } + return request({ + url: 'api/dictDetail', + method: 'get', + params + }) +} + +export function getDictMap(dictName) { + const params = { + dictName, + page: 0, + size: 9999 + } + return request({ + url: 'api/dictDetail/map', + method: 'get', + params + }) +} + +export function add(data) { + return request({ + url: 'api/dictDetail', + method: 'post', + data + }) +} + +export function del(id) { + return request({ + url: 'api/dictDetail/' + id, + method: 'delete' + }) +} + +export function edit(data) { + return request({ + url: 'api/dictDetail', + method: 'put', + data + }) +} + +export default { add, edit, del } diff --git a/eladmin-web/src/api/system/job.js b/eladmin-web/src/api/system/job.js new file mode 100644 index 0000000..a00630e --- /dev/null +++ b/eladmin-web/src/api/system/job.js @@ -0,0 +1,40 @@ +import request from '@/utils/request' + +export function getAllJob() { + const params = { + page: 0, + size: 9999, + enabled: true + } + return request({ + url: 'api/job', + method: 'get', + params + }) +} + +export function add(data) { + return request({ + url: 'api/job', + method: 'post', + data + }) +} + +export function del(ids) { + return request({ + url: 'api/job', + method: 'delete', + data: ids + }) +} + +export function edit(data) { + return request({ + url: 'api/job', + method: 'put', + data + }) +} + +export default { add, edit, del } diff --git a/eladmin-web/src/api/system/menu.js b/eladmin-web/src/api/system/menu.js new file mode 100644 index 0000000..282dd8d --- /dev/null +++ b/eladmin-web/src/api/system/menu.js @@ -0,0 +1,65 @@ +import request from '@/utils/request' + +export function getMenusTree(pid) { + return request({ + url: 'api/menus/lazy?pid=' + pid, + method: 'get' + }) +} + +export function getMenus(params) { + return request({ + url: 'api/menus', + method: 'get', + params + }) +} + +export function getMenuSuperior(ids) { + const data = Array.isArray(ids) || ids.length === 0 ? ids : Array.of(ids) + return request({ + url: 'api/menus/superior', + method: 'post', + data + }) +} + +export function getChild(id) { + return request({ + url: 'api/menus/child?id=' + id, + method: 'get' + }) +} + +export function buildMenus() { + return request({ + url: 'api/menus/build', + method: 'get' + }) +} + +export function add(data) { + return request({ + url: 'api/menus', + method: 'post', + data + }) +} + +export function del(ids) { + return request({ + url: 'api/menus', + method: 'delete', + data: ids + }) +} + +export function edit(data) { + return request({ + url: 'api/menus', + method: 'put', + data + }) +} + +export default { add, edit, del, getMenusTree, getMenuSuperior, getMenus, getChild } diff --git a/eladmin-web/src/api/system/purchase.js b/eladmin-web/src/api/system/purchase.js new file mode 100644 index 0000000..719bbc7 --- /dev/null +++ b/eladmin-web/src/api/system/purchase.js @@ -0,0 +1,27 @@ +import request from '@/utils/request' + +export function add(data) { + return request({ + url: 'api/purchase', + method: 'post', + data + }) +} + +export function del(ids) { + return request({ + url: 'api/purchase/', + method: 'delete', + data: ids + }) +} + +export function edit(data) { + return request({ + url: 'api/purchase', + method: 'put', + data + }) +} + +export default { add, edit, del } diff --git a/eladmin-web/src/api/system/reimburse.js b/eladmin-web/src/api/system/reimburse.js new file mode 100644 index 0000000..dfadcd9 --- /dev/null +++ b/eladmin-web/src/api/system/reimburse.js @@ -0,0 +1,27 @@ +import request from '@/utils/request' + +export function add(data) { + return request({ + url: 'api/reimburse', + method: 'post', + data + }) +} + +export function del(ids) { + return request({ + url: 'api/reimburse/', + method: 'delete', + data: ids + }) +} + +export function edit(data) { + return request({ + url: 'api/reimburse', + method: 'put', + data + }) +} + +export default { add, edit, del } diff --git a/eladmin-web/src/api/system/role.js b/eladmin-web/src/api/system/role.js new file mode 100644 index 0000000..1f7bc1e --- /dev/null +++ b/eladmin-web/src/api/system/role.js @@ -0,0 +1,57 @@ +import request from '@/utils/request' + +// 获取所有的Role +export function getAll() { + return request({ + url: 'api/roles/all', + method: 'get' + }) +} + +export function add(data) { + return request({ + url: 'api/roles', + method: 'post', + data + }) +} + +export function get(id) { + return request({ + url: 'api/roles/' + id, + method: 'get' + }) +} + +export function getLevel() { + return request({ + url: 'api/roles/level', + method: 'get' + }) +} + +export function del(ids) { + return request({ + url: 'api/roles', + method: 'delete', + data: ids + }) +} + +export function edit(data) { + return request({ + url: 'api/roles', + method: 'put', + data + }) +} + +export function editMenu(data) { + return request({ + url: 'api/roles/menu', + method: 'put', + data + }) +} + +export default { add, edit, del, get, editMenu, getLevel } diff --git a/eladmin-web/src/api/system/timing.js b/eladmin-web/src/api/system/timing.js new file mode 100644 index 0000000..613e15f --- /dev/null +++ b/eladmin-web/src/api/system/timing.js @@ -0,0 +1,41 @@ +import request from '@/utils/request' + +export function add(data) { + return request({ + url: 'api/jobs', + method: 'post', + data + }) +} + +export function del(ids) { + return request({ + url: 'api/jobs', + method: 'delete', + data: ids + }) +} + +export function edit(data) { + return request({ + url: 'api/jobs', + method: 'put', + data + }) +} + +export function updateIsPause(id) { + return request({ + url: 'api/jobs/' + id, + method: 'put' + }) +} + +export function execution(id) { + return request({ + url: 'api/jobs/exec/' + id, + method: 'put' + }) +} + +export default { del, updateIsPause, execution, add, edit } diff --git a/eladmin-web/src/api/system/user.js b/eladmin-web/src/api/system/user.js new file mode 100644 index 0000000..c14df11 --- /dev/null +++ b/eladmin-web/src/api/system/user.js @@ -0,0 +1,69 @@ +import request from '@/utils/request' +import { encrypt } from '@/utils/rsaEncrypt' + +export function add(data) { + return request({ + url: 'api/users', + method: 'post', + data + }) +} + +export function del(ids) { + return request({ + url: 'api/users', + method: 'delete', + data: ids + }) +} + +export function edit(data) { + return request({ + url: 'api/users', + method: 'put', + data + }) +} + +export function editUser(data) { + return request({ + url: 'api/users/center', + method: 'put', + data + }) +} + +export function updatePass(user) { + const data = { + oldPass: encrypt(user.oldPass), + newPass: encrypt(user.newPass) + } + return request({ + url: 'api/users/updatePass/', + method: 'post', + data + }) +} + +export function resetPwd(ids) { + return request({ + url: 'api/users/resetPwd', + method: 'put', + data: ids + }) +} + +export function updateEmail(form) { + const data = { + password: encrypt(form.pass), + email: form.email + } + return request({ + url: 'api/users/updateEmail/' + form.code, + method: 'post', + data + }) +} + +export default { add, edit, del, resetPwd } + diff --git a/eladmin-web/src/api/tools/alipay.js b/eladmin-web/src/api/tools/alipay.js new file mode 100644 index 0000000..54090f5 --- /dev/null +++ b/eladmin-web/src/api/tools/alipay.js @@ -0,0 +1,25 @@ +import request from '@/utils/request' + +export function get() { + return request({ + url: 'api/aliPay', + method: 'get' + }) +} + +export function update(data) { + return request({ + url: 'api/aliPay', + data, + method: 'put' + }) +} + +// 支付 +export function toAliPay(url, data) { + return request({ + url: 'api/' + url, + data, + method: 'post' + }) +} diff --git a/eladmin-web/src/api/tools/email.js b/eladmin-web/src/api/tools/email.js new file mode 100644 index 0000000..af030cb --- /dev/null +++ b/eladmin-web/src/api/tools/email.js @@ -0,0 +1,24 @@ +import request from '@/utils/request' + +export function get() { + return request({ + url: 'api/email', + method: 'get' + }) +} + +export function update(data) { + return request({ + url: 'api/email', + data, + method: 'put' + }) +} + +export function send(data) { + return request({ + url: 'api/email', + data, + method: 'post' + }) +} diff --git a/eladmin-web/src/api/tools/localStorage.js b/eladmin-web/src/api/tools/localStorage.js new file mode 100644 index 0000000..63ebe2b --- /dev/null +++ b/eladmin-web/src/api/tools/localStorage.js @@ -0,0 +1,27 @@ +import request from '@/utils/request' + +export function add(data) { + return request({ + url: 'api/localStorage', + method: 'post', + data + }) +} + +export function del(ids) { + return request({ + url: 'api/localStorage/', + method: 'delete', + data: ids + }) +} + +export function edit(data) { + return request({ + url: 'api/localStorage', + method: 'put', + data + }) +} + +export default { add, edit, del } diff --git a/eladmin-web/src/api/tools/qiniu.js b/eladmin-web/src/api/tools/qiniu.js new file mode 100644 index 0000000..6d56771 --- /dev/null +++ b/eladmin-web/src/api/tools/qiniu.js @@ -0,0 +1,40 @@ +import request from '@/utils/request' + +export function get() { + return request({ + url: 'api/qiNiuContent/config', + method: 'get' + }) +} + +export function update(data) { + return request({ + url: 'api/qiNiuContent/config', + data, + method: 'put' + }) +} + +export function download(id) { + return request({ + url: 'api/qiNiuContent/download/' + id, + method: 'get' + }) +} + +export function sync() { + return request({ + url: 'api/qiNiuContent/synchronize', + method: 'post' + }) +} + +export function del(ids) { + return request({ + url: 'api/qiNiuContent', + method: 'delete', + data: ids + }) +} + +export default { del, download, sync } diff --git a/eladmin-web/src/api/userBank.js b/eladmin-web/src/api/userBank.js new file mode 100644 index 0000000..b88cdd8 --- /dev/null +++ b/eladmin-web/src/api/userBank.js @@ -0,0 +1,27 @@ +import request from '@/utils/request' + +export function add(data) { + return request({ + url: 'api/userBank', + method: 'post', + data + }) +} + +export function del(ids) { + return request({ + url: 'api/userBank/', + method: 'delete', + data: ids + }) +} + +export function edit(data) { + return request({ + url: 'api/userBank', + method: 'put', + data + }) +} + +export default { add, edit, del } diff --git a/eladmin-web/src/assets/401_images/401.gif b/eladmin-web/src/assets/401_images/401.gif new file mode 100644 index 0000000..cd6e0d9 Binary files /dev/null and b/eladmin-web/src/assets/401_images/401.gif differ diff --git a/eladmin-web/src/assets/404_images/404.png b/eladmin-web/src/assets/404_images/404.png new file mode 100644 index 0000000..3d8e230 Binary files /dev/null and b/eladmin-web/src/assets/404_images/404.png differ diff --git a/eladmin-web/src/assets/404_images/404_cloud.png b/eladmin-web/src/assets/404_images/404_cloud.png new file mode 100644 index 0000000..c6281d0 Binary files /dev/null and b/eladmin-web/src/assets/404_images/404_cloud.png differ diff --git a/eladmin-web/src/assets/icons/index.js b/eladmin-web/src/assets/icons/index.js new file mode 100644 index 0000000..2c6b309 --- /dev/null +++ b/eladmin-web/src/assets/icons/index.js @@ -0,0 +1,9 @@ +import Vue from 'vue' +import SvgIcon from '@/components/SvgIcon'// svg component + +// register globally +Vue.component('svg-icon', SvgIcon) + +const req = require.context('./svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys().map(requireContext) +requireAll(req) diff --git a/eladmin-web/src/assets/icons/svg/Steve-Jobs.svg b/eladmin-web/src/assets/icons/svg/Steve-Jobs.svg new file mode 100644 index 0000000..53843e2 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/Steve-Jobs.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/alipay.svg b/eladmin-web/src/assets/icons/svg/alipay.svg new file mode 100644 index 0000000..9138981 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/alipay.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/anq.svg b/eladmin-web/src/assets/icons/svg/anq.svg new file mode 100644 index 0000000..a466608 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/anq.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/app.svg b/eladmin-web/src/assets/icons/svg/app.svg new file mode 100644 index 0000000..0796da3 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/app.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/backup.svg b/eladmin-web/src/assets/icons/svg/backup.svg new file mode 100644 index 0000000..a3272a4 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/backup.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/blog.svg b/eladmin-web/src/assets/icons/svg/blog.svg new file mode 100644 index 0000000..a990eba --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/blog.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/chain.svg b/eladmin-web/src/assets/icons/svg/chain.svg new file mode 100644 index 0000000..ed3317f --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/chain.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/chart.svg b/eladmin-web/src/assets/icons/svg/chart.svg new file mode 100644 index 0000000..27728fb --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/chart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/codeConsole.svg b/eladmin-web/src/assets/icons/svg/codeConsole.svg new file mode 100644 index 0000000..672ec6e --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/codeConsole.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/dashboard.svg b/eladmin-web/src/assets/icons/svg/dashboard.svg new file mode 100644 index 0000000..5317d37 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/dashboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/database.svg b/eladmin-web/src/assets/icons/svg/database.svg new file mode 100644 index 0000000..7fbad9b --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/database.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/date.svg b/eladmin-web/src/assets/icons/svg/date.svg new file mode 100644 index 0000000..0540e99 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/date.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/deploy.svg b/eladmin-web/src/assets/icons/svg/deploy.svg new file mode 100644 index 0000000..f4a1c56 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/deploy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/dept.svg b/eladmin-web/src/assets/icons/svg/dept.svg new file mode 100644 index 0000000..894e4bf --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/dept.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/dev.svg b/eladmin-web/src/assets/icons/svg/dev.svg new file mode 100644 index 0000000..ed4d23c --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/dev.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/develop.svg b/eladmin-web/src/assets/icons/svg/develop.svg new file mode 100644 index 0000000..e189223 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/develop.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/dictionary.svg b/eladmin-web/src/assets/icons/svg/dictionary.svg new file mode 100644 index 0000000..6e83c43 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/dictionary.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/doc.svg b/eladmin-web/src/assets/icons/svg/doc.svg new file mode 100644 index 0000000..9160de8 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/doc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/download.svg b/eladmin-web/src/assets/icons/svg/download.svg new file mode 100644 index 0000000..0243c6a --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/edit.svg b/eladmin-web/src/assets/icons/svg/edit.svg new file mode 100644 index 0000000..d26101f --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/edit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/education.svg b/eladmin-web/src/assets/icons/svg/education.svg new file mode 100644 index 0000000..7bfb01d --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/education.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/email.svg b/eladmin-web/src/assets/icons/svg/email.svg new file mode 100644 index 0000000..f1cf3ae --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/email.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/error.svg b/eladmin-web/src/assets/icons/svg/error.svg new file mode 100644 index 0000000..fd935da --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/error.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/exit-fullscreen.svg b/eladmin-web/src/assets/icons/svg/exit-fullscreen.svg new file mode 100644 index 0000000..485c128 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/exit-fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/fullscreen.svg b/eladmin-web/src/assets/icons/svg/fullscreen.svg new file mode 100644 index 0000000..0e86b6f --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/fwb.svg b/eladmin-web/src/assets/icons/svg/fwb.svg new file mode 100644 index 0000000..59933fc --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/fwb.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/github.svg b/eladmin-web/src/assets/icons/svg/github.svg new file mode 100644 index 0000000..8145e95 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/gonggao.svg b/eladmin-web/src/assets/icons/svg/gonggao.svg new file mode 100644 index 0000000..22aed08 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/gonggao.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/icon.svg b/eladmin-web/src/assets/icons/svg/icon.svg new file mode 100644 index 0000000..82fbdd9 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/image.svg b/eladmin-web/src/assets/icons/svg/image.svg new file mode 100644 index 0000000..16d572f --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/image.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/index.svg b/eladmin-web/src/assets/icons/svg/index.svg new file mode 100644 index 0000000..fdb3826 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/index.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/international.svg b/eladmin-web/src/assets/icons/svg/international.svg new file mode 100644 index 0000000..e9b56ee --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/international.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/ipvisits.svg b/eladmin-web/src/assets/icons/svg/ipvisits.svg new file mode 100644 index 0000000..4ca473d --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/ipvisits.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/java.svg b/eladmin-web/src/assets/icons/svg/java.svg new file mode 100644 index 0000000..e2effbb --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/java.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/link.svg b/eladmin-web/src/assets/icons/svg/link.svg new file mode 100644 index 0000000..48197ba --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/link.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/list.svg b/eladmin-web/src/assets/icons/svg/list.svg new file mode 100644 index 0000000..20259ed --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/list.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/lock.svg b/eladmin-web/src/assets/icons/svg/lock.svg new file mode 100644 index 0000000..0f842ea --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/lock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/log.svg b/eladmin-web/src/assets/icons/svg/log.svg new file mode 100644 index 0000000..4fefe74 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/log.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/login.svg b/eladmin-web/src/assets/icons/svg/login.svg new file mode 100644 index 0000000..cc5a854 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/login.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/markdown.svg b/eladmin-web/src/assets/icons/svg/markdown.svg new file mode 100644 index 0000000..7cd6747 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/markdown.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/menu.svg b/eladmin-web/src/assets/icons/svg/menu.svg new file mode 100644 index 0000000..e4360a0 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/menu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/message.svg b/eladmin-web/src/assets/icons/svg/message.svg new file mode 100644 index 0000000..14ca817 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/message.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/mnt.svg b/eladmin-web/src/assets/icons/svg/mnt.svg new file mode 100644 index 0000000..936ce29 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/mnt.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/money.svg b/eladmin-web/src/assets/icons/svg/money.svg new file mode 100644 index 0000000..c1580de --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/money.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/monitor.svg b/eladmin-web/src/assets/icons/svg/monitor.svg new file mode 100644 index 0000000..339370a --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/monitor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/nested.svg b/eladmin-web/src/assets/icons/svg/nested.svg new file mode 100644 index 0000000..06713a8 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/nested.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/password.svg b/eladmin-web/src/assets/icons/svg/password.svg new file mode 100644 index 0000000..4ab451f --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/password.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/people.svg b/eladmin-web/src/assets/icons/svg/people.svg new file mode 100644 index 0000000..2bd54ae --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/people.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/peoples.svg b/eladmin-web/src/assets/icons/svg/peoples.svg new file mode 100644 index 0000000..2c91161 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/peoples.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/permission.svg b/eladmin-web/src/assets/icons/svg/permission.svg new file mode 100644 index 0000000..c4c7409 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/permission.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/phone.svg b/eladmin-web/src/assets/icons/svg/phone.svg new file mode 100644 index 0000000..da339f9 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/phone.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/qiniu.svg b/eladmin-web/src/assets/icons/svg/qiniu.svg new file mode 100644 index 0000000..c2f9f8b --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/qiniu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/redis.svg b/eladmin-web/src/assets/icons/svg/redis.svg new file mode 100644 index 0000000..bef111b --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/redis.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/role.svg b/eladmin-web/src/assets/icons/svg/role.svg new file mode 100644 index 0000000..76cb18f --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/role.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/search.svg b/eladmin-web/src/assets/icons/svg/search.svg new file mode 100644 index 0000000..84233dd --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/server.svg b/eladmin-web/src/assets/icons/svg/server.svg new file mode 100644 index 0000000..db6dcdf --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/server.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/shopping.svg b/eladmin-web/src/assets/icons/svg/shopping.svg new file mode 100644 index 0000000..87513e7 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/shopping.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/size.svg b/eladmin-web/src/assets/icons/svg/size.svg new file mode 100644 index 0000000..ddb25b8 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/size.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/skill.svg b/eladmin-web/src/assets/icons/svg/skill.svg new file mode 100644 index 0000000..a3b7312 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/skill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/source.svg b/eladmin-web/src/assets/icons/svg/source.svg new file mode 100644 index 0000000..1c3a038 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/source.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/sqlMonitor.svg b/eladmin-web/src/assets/icons/svg/sqlMonitor.svg new file mode 100644 index 0000000..950a430 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/sqlMonitor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/swagger.svg b/eladmin-web/src/assets/icons/svg/swagger.svg new file mode 100644 index 0000000..ded7de8 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/swagger.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/sys-tools.svg b/eladmin-web/src/assets/icons/svg/sys-tools.svg new file mode 100644 index 0000000..8f9055e --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/sys-tools.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/system.svg b/eladmin-web/src/assets/icons/svg/system.svg new file mode 100644 index 0000000..9333c60 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/system.svg @@ -0,0 +1 @@ + diff --git a/eladmin-web/src/assets/icons/svg/system1.svg b/eladmin-web/src/assets/icons/svg/system1.svg new file mode 100644 index 0000000..37b0a0a --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/system1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/tab.svg b/eladmin-web/src/assets/icons/svg/tab.svg new file mode 100644 index 0000000..b4b48e4 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/tab.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/theme.svg b/eladmin-web/src/assets/icons/svg/theme.svg new file mode 100644 index 0000000..5982a2f --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/theme.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/timing.svg b/eladmin-web/src/assets/icons/svg/timing.svg new file mode 100644 index 0000000..f8fdc6d --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/timing.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/tools.svg b/eladmin-web/src/assets/icons/svg/tools.svg new file mode 100644 index 0000000..aba1a40 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/tools.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/tree-table.svg b/eladmin-web/src/assets/icons/svg/tree-table.svg new file mode 100644 index 0000000..8aafdb8 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/tree-table.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/tree.svg b/eladmin-web/src/assets/icons/svg/tree.svg new file mode 100644 index 0000000..dd4b7dd --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/tree.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/unlock.svg b/eladmin-web/src/assets/icons/svg/unlock.svg new file mode 100644 index 0000000..1219e41 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/unlock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/user.svg b/eladmin-web/src/assets/icons/svg/user.svg new file mode 100644 index 0000000..09d7a81 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/user.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/user1.svg b/eladmin-web/src/assets/icons/svg/user1.svg new file mode 100644 index 0000000..14ca51e --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/user1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/validCode.svg b/eladmin-web/src/assets/icons/svg/validCode.svg new file mode 100644 index 0000000..a1feb74 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/validCode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/visits.svg b/eladmin-web/src/assets/icons/svg/visits.svg new file mode 100644 index 0000000..8425662 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/visits.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/web.svg b/eladmin-web/src/assets/icons/svg/web.svg new file mode 100644 index 0000000..9c57415 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/web.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/wechat.svg b/eladmin-web/src/assets/icons/svg/wechat.svg new file mode 100644 index 0000000..c586e55 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/wechat.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/weixin.svg b/eladmin-web/src/assets/icons/svg/weixin.svg new file mode 100644 index 0000000..8dbcfa5 --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/weixin.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svg/zujian.svg b/eladmin-web/src/assets/icons/svg/zujian.svg new file mode 100644 index 0000000..2aba32f --- /dev/null +++ b/eladmin-web/src/assets/icons/svg/zujian.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eladmin-web/src/assets/icons/svgo.yml b/eladmin-web/src/assets/icons/svgo.yml new file mode 100644 index 0000000..d11906a --- /dev/null +++ b/eladmin-web/src/assets/icons/svgo.yml @@ -0,0 +1,22 @@ +# replace default config + +# multipass: true +# full: true + +plugins: + + # - name + # + # or: + # - name: false + # - name: true + # + # or: + # - name: + # param1: 1 + # param2: 2 + +- removeAttrs: + attrs: + - 'fill' + - 'fill-rule' diff --git a/eladmin-web/src/assets/images/avatar.png b/eladmin-web/src/assets/images/avatar.png new file mode 100644 index 0000000..997732a Binary files /dev/null and b/eladmin-web/src/assets/images/avatar.png differ diff --git a/eladmin-web/src/assets/images/background.webp b/eladmin-web/src/assets/images/background.webp new file mode 100644 index 0000000..3ad73c8 Binary files /dev/null and b/eladmin-web/src/assets/images/background.webp differ diff --git a/eladmin-web/src/assets/images/logo.png b/eladmin-web/src/assets/images/logo.png new file mode 100644 index 0000000..f757710 Binary files /dev/null and b/eladmin-web/src/assets/images/logo.png differ diff --git a/eladmin-web/src/assets/styles/btn.scss b/eladmin-web/src/assets/styles/btn.scss new file mode 100644 index 0000000..8f47f2c --- /dev/null +++ b/eladmin-web/src/assets/styles/btn.scss @@ -0,0 +1,99 @@ +@import 'variables'; + +@mixin colorBtn($color) { + background: $color; + + &:hover { + color: $color; + + &:before, + &:after { + background: $color; + } + } +} + +.blue-btn { + @include colorBtn($blue) +} + +.light-blue-btn { + @include colorBtn($light-blue) +} + +.red-btn { + @include colorBtn($red) +} + +.pink-btn { + @include colorBtn($pink) +} + +.green-btn { + @include colorBtn($green) +} + +.tiffany-btn { + @include colorBtn($tiffany) +} + +.yellow-btn { + @include colorBtn($yellow) +} + +.pan-btn { + font-size: 14px; + color: #fff; + padding: 14px 36px; + border-radius: 8px; + border: none; + outline: none; + transition: 600ms ease all; + position: relative; + display: inline-block; + + &:hover { + background: #fff; + + &:before, + &:after { + width: 100%; + transition: 600ms ease all; + } + } + + &:before, + &:after { + content: ''; + position: absolute; + top: 0; + right: 0; + height: 2px; + width: 0; + transition: 400ms ease all; + } + + &::after { + right: inherit; + top: inherit; + left: 0; + bottom: 0; + } +} + +.custom-button { + display: inline-block; + line-height: 1; + white-space: nowrap; + cursor: pointer; + background: #fff; + color: #fff; + -webkit-appearance: none; + text-align: center; + box-sizing: border-box; + outline: 0; + margin: 0; + padding: 10px 15px; + font-size: 14px; + border-radius: 4px; +} diff --git a/eladmin-web/src/assets/styles/eladmin.scss b/eladmin-web/src/assets/styles/eladmin.scss new file mode 100644 index 0000000..e1e0195 --- /dev/null +++ b/eladmin-web/src/assets/styles/eladmin.scss @@ -0,0 +1,117 @@ +.head-container { + padding-bottom: 10px; + .filter-item { + display: inline-block; + vertical-align: middle; + margin: 0 3px 10px 0; + input { + height: 30.5px; + line-height: 30.5px; + } + } + .el-form-item-label { + margin: 0 3px 9px 0; + display: inline-block; + text-align: right; + vertical-align: middle; + font-size: 14px; + color: #606266; + line-height: 30.5px; + padding: 0 7px 0 7px; + } + .el-button+.el-button { + margin-left: 0 !important; + } + .el-select__caret.el-input__icon.el-icon-arrow-up{ + line-height: 30.5px; + } + .date-item { + display: inline-block; + vertical-align: middle; + margin-bottom: 10px; + height: 30.5px !important; + width: 230px !important; + } +} +.el-avatar { + display: inline-block; + text-align: center; + background: #ccc; + color: #fff; + white-space: nowrap; + position: relative; + overflow: hidden; + vertical-align: middle; + width: 32px; + height: 32px; + line-height: 32px; + border-radius: 16px; +} + +.logo-con{ + height: 60px; + padding: 13px 0 0; + img{ + height: 32px; + width: 135px; + display: block; + //margin: 0 auto; + } +} + +#el-login-footer { + height: 40px; + line-height: 40px; + position: fixed; + bottom: 0; + width: 100%; + text-align: center; + color: #fff; + font-family: Arial, serif; + font-size: 12px; + letter-spacing: 1px; +} + +#el-main-footer { + background: none repeat scroll 0 0 white; + border-top: 1px solid #e7eaec; + overflow: hidden; + padding: 10px 6px 0 6px; + height: 33px; + font-size: 0.7rem !important; + color: #7a8b9a; + letter-spacing: 0.8px; + font-family: Arial, sans-serif !important; + position: fixed; + bottom: 0; + z-index: 99; + width: 100%; +} +.eladmin-upload { + border: 1px dashed #c0ccda; + border-radius: 5px; + height: 45px; + line-height: 45px; + width: 368px; +} +.my-blockquote{ + margin: 0 0 10px; + padding: 15px; + line-height: 22px; + border-left: 5px solid #00437B; + border-radius: 0 2px 2px 0; + background-color: #f2f2f2; +} +.my-code{ + position: relative; + padding: 15px; + line-height: 20px; + border-left: 5px solid #ddd; + color: #333; + font-family: Courier New, serif; + font-size: 12px +} + +.el-tabs{ + margin-bottom: 25px; +} diff --git a/eladmin-web/src/assets/styles/element-ui.scss b/eladmin-web/src/assets/styles/element-ui.scss new file mode 100644 index 0000000..8f7881c --- /dev/null +++ b/eladmin-web/src/assets/styles/element-ui.scss @@ -0,0 +1,79 @@ +// cover some element-ui styles + +.el-breadcrumb__inner, +.el-breadcrumb__inner a { + font-weight: 400 !important; +} + +.el-upload { + input[type="file"] { + display: none !important; + } +} + +.el-upload__input { + display: none; +} + +.cell { + .el-tag { + margin-right: 0; + } +} + +.small-padding { + .cell { + padding-left: 5px; + padding-right: 5px; + } +} + +.fixed-width { + .el-button--mini { + padding: 7px 10px; + width: 60px; + } +} + +.status-col { + .cell { + padding: 0 10px; + text-align: center; + + .el-tag { + margin-right: 0; + } + } +} + +// to fixed https://github.com/ElemeFE/element/issues/2461 +.el-dialog { + transform: none; + left: 0; + position: relative; + margin: 0 auto; +} + +// refine element ui upload +.upload-container { + .el-upload { + width: 100%; + + .el-upload-dragger { + width: 100%; + height: 200px; + } + } +} + +// dropdown +.el-dropdown-menu { + a { + display: block + } +} + +// fix date-picker ui bug in filter-item +.el-range-editor.el-input__inner { + display: inline-flex !important; +} diff --git a/eladmin-web/src/assets/styles/element-variables.scss b/eladmin-web/src/assets/styles/element-variables.scss new file mode 100644 index 0000000..a4f8c4a --- /dev/null +++ b/eladmin-web/src/assets/styles/element-variables.scss @@ -0,0 +1,31 @@ +/** +* I think element-ui's default theme color is too light for long-term use. +* So I modified the default color and you can modify it to your liking. +**/ + +/* theme color */ +$--color-primary: #1890ff; +$--color-success: #13ce66; +$--color-warning: #FFBA00; +$--color-danger: #ff4949; +// $--color-info: #1E1E1E; + +$--button-font-weight: 400; + +// $--color-text-regular: #1f2d3d; + +$--border-color-light: #dfe4ed; +$--border-color-lighter: #e6ebf5; + +$--table-border:1px solid#dfe6ec; + +/* icon font path, required */ +$--font-path: '~element-ui/lib/theme-chalk/fonts'; + +@import "../../../node_modules/element-ui/packages/theme-chalk/src/index"; + +// the :export directive is the magic sauce for webpack +// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass +:export { + theme: $--color-primary; +} diff --git a/eladmin-web/src/assets/styles/index.scss b/eladmin-web/src/assets/styles/index.scss new file mode 100644 index 0000000..21fbda1 --- /dev/null +++ b/eladmin-web/src/assets/styles/index.scss @@ -0,0 +1,182 @@ +@import 'variables'; +@import 'mixin'; +@import 'transition'; +@import 'element-ui'; +@import 'sidebar'; +@import 'btn'; +@import 'eladmin'; + +body { + height: 100%; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; +} + +label { + font-weight: 700; +} + +html { + height: 100%; + box-sizing: border-box; +} + +#app { + height: 100%; +} + +*, +*:before, +*:after { + box-sizing: inherit; +} + +.no-padding { + padding: 0 !important; +} + +.padding-content { + padding: 4px 0; +} + +a:focus, +a:active { + outline: none; +} + +a, +a:focus, +a:hover { + cursor: pointer; + color: inherit; + text-decoration: none; +} + +div:focus { + outline: none; +} + +.fr { + float: right; +} + +.fl { + float: left; +} + +.pr-5 { + padding-right: 5px; +} + +.pl-5 { + padding-left: 5px; +} + +.block { + display: block; +} + +.pointer { + cursor: pointer; +} + +.inlineBlock { + display: block; +} + +.clearfix { + &:after { + visibility: hidden; + display: block; + font-size: 0; + content: " "; + clear: both; + height: 0; + } +} + +aside { + background: #eef1f6; + padding: 8px 24px; + margin-bottom: 20px; + border-radius: 2px; + display: block; + line-height: 32px; + font-size: 16px; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; + color: #2c3e50; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + a { + color: #337ab7; + cursor: pointer; + + &:hover { + color: rgb(32, 160, 255); + } + } +} + +//main-container全局样式 +.app-container { + padding: 20px 20px 45px 20px; +} + +.components-container { + margin: 30px 50px; + position: relative; +} + +.pagination-container { + margin-top: 30px; +} + +.text-center { + text-align: center +} + +.sub-navbar { + height: 50px; + line-height: 50px; + position: relative; + width: 100%; + text-align: right; + padding-right: 20px; + transition: 600ms ease position; + background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%); + + .subtitle { + font-size: 20px; + color: #fff; + } + + &.draft { + background: #d0d0d0; + } + + &.deleted { + background: #d0d0d0; + } +} + +.link-type, +.link-type:focus { + color: #337ab7; + cursor: pointer; + + &:hover { + color: rgb(32, 160, 255); + } +} + +//refine vue-multiselect plugin +.multiselect { + line-height: 16px; +} + +.multiselect--active { + z-index: 1000 !important; +} diff --git a/eladmin-web/src/assets/styles/mixin.scss b/eladmin-web/src/assets/styles/mixin.scss new file mode 100644 index 0000000..06fa061 --- /dev/null +++ b/eladmin-web/src/assets/styles/mixin.scss @@ -0,0 +1,66 @@ +@mixin clearfix { + &:after { + content: ""; + display: table; + clear: both; + } +} + +@mixin scrollBar { + &::-webkit-scrollbar-track-piece { + background: #d3dce6; + } + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: #99a9bf; + border-radius: 20px; + } +} + +@mixin relative { + position: relative; + width: 100%; + height: 100%; +} + +@mixin pct($pct) { + width: #{$pct}; + position: relative; + margin: 0 auto; +} + +@mixin triangle($width, $height, $color, $direction) { + $width: $width/2; + $color-border-style: $height solid $color; + $transparent-border-style: $width solid transparent; + height: 0; + width: 0; + + @if $direction==up { + border-bottom: $color-border-style; + border-left: $transparent-border-style; + border-right: $transparent-border-style; + } + + @else if $direction==right { + border-left: $color-border-style; + border-top: $transparent-border-style; + border-bottom: $transparent-border-style; + } + + @else if $direction==down { + border-top: $color-border-style; + border-left: $transparent-border-style; + border-right: $transparent-border-style; + } + + @else if $direction==left { + border-right: $color-border-style; + border-top: $transparent-border-style; + border-bottom: $transparent-border-style; + } +} diff --git a/eladmin-web/src/assets/styles/sidebar.scss b/eladmin-web/src/assets/styles/sidebar.scss new file mode 100644 index 0000000..17381fc --- /dev/null +++ b/eladmin-web/src/assets/styles/sidebar.scss @@ -0,0 +1,209 @@ +#app { + + .main-container { + min-height: 100%; + transition: margin-left .28s; + margin-left: $sideBarWidth; + position: relative; + } + + .sidebar-container { + transition: width 0.28s; + width: $sideBarWidth !important; + background-color: $menuBg; + height: 100%; + position: fixed; + font-size: 0; + top: 0; + bottom: 0; + left: 0; + z-index: 1001; + overflow: hidden; + + // reset element-ui css + .horizontal-collapse-transition { + transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out; + } + + .scrollbar-wrapper { + overflow-x: hidden !important; + } + + .el-scrollbar__bar.is-vertical { + right: 0; + } + + .el-scrollbar { + height: 100%; + } + + &.has-logo { + .el-scrollbar { + height: calc(100% - 50px); + } + } + + .is-horizontal { + display: none; + } + + a { + display: inline-block; + width: 100%; + overflow: hidden; + } + + .svg-icon { + margin-right: 16px; + } + + .el-menu { + border: none; + height: 100%; + width: 100% !important; + } + + // menu hover + .submenu-title-noDropdown, + .el-submenu__title { + &:hover { + background-color: $menuHover !important; + } + } + + .is-active>.el-submenu__title { + color: $subMenuActiveText !important; + } + + & .nest-menu .el-submenu>.el-submenu__title, + & .el-submenu .el-menu-item { + min-width: $sideBarWidth !important; + background-color: $subMenuBg !important; + + &:hover { + background-color: $subMenuHover !important; + } + } + } + + .hideSidebar { + .sidebar-container { + width: 54px !important; + } + + .main-container { + margin-left: 54px; + } + + .submenu-title-noDropdown { + padding: 0 !important; + position: relative; + + .el-tooltip { + padding: 0 !important; + + .svg-icon { + margin-left: 20px; + } + } + } + + .el-submenu { + overflow: hidden; + + &>.el-submenu__title { + padding: 0 !important; + + .svg-icon { + margin-left: 20px; + } + + .el-submenu__icon-arrow { + display: none; + } + } + } + + .el-menu--collapse { + .el-submenu { + &>.el-submenu__title { + &>span { + height: 0; + width: 0; + overflow: hidden; + visibility: hidden; + display: inline-block; + } + } + } + } + } + + .el-menu--collapse .el-menu .el-submenu { + min-width: $sideBarWidth !important; + } + + // mobile responsive + .mobile { + .main-container { + margin-left: 0; + } + + .sidebar-container { + transition: transform .28s; + width: $sideBarWidth !important; + } + + &.hideSidebar { + .sidebar-container { + pointer-events: none; + transition-duration: 0.3s; + transform: translate3d(-$sideBarWidth, 0, 0); + } + } + } + + .withoutAnimation { + + .main-container, + .sidebar-container { + transition: none; + } + } +} + +// when menu collapsed +.el-menu--vertical { + &>.el-menu { + .svg-icon { + margin-right: 16px; + } + } + + .nest-menu .el-submenu>.el-submenu__title, + .el-menu-item { + &:hover { + // you can use $subMenuHover + background-color: $menuHover !important; + } + } + + // the scroll bar appears when the subMenu is too long + >.el-menu--popup { + max-height: 100vh; + overflow-y: auto; + + &::-webkit-scrollbar-track-piece { + background: #d3dce6; + } + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: #99a9bf; + border-radius: 20px; + } + } +} diff --git a/eladmin-web/src/assets/styles/transition.scss b/eladmin-web/src/assets/styles/transition.scss new file mode 100644 index 0000000..4cb27cc --- /dev/null +++ b/eladmin-web/src/assets/styles/transition.scss @@ -0,0 +1,48 @@ +// global transition css + +/* fade */ +.fade-enter-active, +.fade-leave-active { + transition: opacity 0.28s; +} + +.fade-enter, +.fade-leave-active { + opacity: 0; +} + +/* fade-transform */ +.fade-transform-leave-active, +.fade-transform-enter-active { + transition: all .5s; +} + +.fade-transform-enter { + opacity: 0; + transform: translateX(-30px); +} + +.fade-transform-leave-to { + opacity: 0; + transform: translateX(30px); +} + +/* breadcrumb transition */ +.breadcrumb-enter-active, +.breadcrumb-leave-active { + transition: all .5s; +} + +.breadcrumb-enter, +.breadcrumb-leave-active { + opacity: 0; + transform: translateX(20px); +} + +.breadcrumb-move { + transition: all .5s; +} + +.breadcrumb-leave-active { + position: absolute; +} diff --git a/eladmin-web/src/assets/styles/variables.scss b/eladmin-web/src/assets/styles/variables.scss new file mode 100644 index 0000000..41de79c --- /dev/null +++ b/eladmin-web/src/assets/styles/variables.scss @@ -0,0 +1,35 @@ +// base color +$blue:#324157; +$light-blue:#3A71A8; +$red:#C03639; +$pink: #E65D6E; +$green: #30B08F; +$tiffany: #4AB7BD; +$yellow:#FEC171; +$panGreen: #30B08F; + +// sidebar +$menuText:#bfcbd9; +$menuActiveText:#409EFF; +$subMenuActiveText:#f4f4f5; // https://github.com/ElemeFE/element/issues/12951 + +$menuBg:#304156; +$menuHover:#263445; + +$subMenuBg:#1f2d3d; +$subMenuHover:#001528; + +$sideBarWidth: 205px; + +// the :export directive is the magic sauce for webpack +// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass +:export { + menuText: $menuText; + menuActiveText: $menuActiveText; + subMenuActiveText: $subMenuActiveText; + menuBg: $menuBg; + menuHover: $menuHover; + subMenuBg: $subMenuBg; + subMenuHover: $subMenuHover; + sideBarWidth: $sideBarWidth; +} diff --git a/eladmin-web/src/components/Breadcrumb/index.vue b/eladmin-web/src/components/Breadcrumb/index.vue new file mode 100644 index 0000000..204ea59 --- /dev/null +++ b/eladmin-web/src/components/Breadcrumb/index.vue @@ -0,0 +1,81 @@ + + + + + diff --git a/eladmin-web/src/components/Crud/CRUD.operation.vue b/eladmin-web/src/components/Crud/CRUD.operation.vue new file mode 100644 index 0000000..33d2077 --- /dev/null +++ b/eladmin-web/src/components/Crud/CRUD.operation.vue @@ -0,0 +1,268 @@ + + + + diff --git a/eladmin-web/src/components/Crud/Pagination.vue b/eladmin-web/src/components/Crud/Pagination.vue new file mode 100644 index 0000000..d4482fb --- /dev/null +++ b/eladmin-web/src/components/Crud/Pagination.vue @@ -0,0 +1,18 @@ + + + diff --git a/eladmin-web/src/components/Crud/RR.operation.vue b/eladmin-web/src/components/Crud/RR.operation.vue new file mode 100644 index 0000000..df2c138 --- /dev/null +++ b/eladmin-web/src/components/Crud/RR.operation.vue @@ -0,0 +1,20 @@ + + + diff --git a/eladmin-web/src/components/Crud/UD.operation.vue b/eladmin-web/src/components/Crud/UD.operation.vue new file mode 100644 index 0000000..221a4ef --- /dev/null +++ b/eladmin-web/src/components/Crud/UD.operation.vue @@ -0,0 +1,71 @@ + + diff --git a/eladmin-web/src/components/Crud/crud.js b/eladmin-web/src/components/Crud/crud.js new file mode 100644 index 0000000..b5bd398 --- /dev/null +++ b/eladmin-web/src/components/Crud/crud.js @@ -0,0 +1,864 @@ +import { initData, download } from '@/api/data' +import { parseTime, downloadFile } from '@/utils/index' +import Vue from 'vue' + +/** + * CRUD配置 + * @author moxun + * @param {*} options
+ * @return crud instance. + * @example + * 要使用多crud时,请在关联crud的组件处使用crud-tag进行标记,如: + */ +function CRUD(options) { + const defaultOptions = { + tag: 'default', + // id字段名 + idField: 'id', + // 标题 + title: '', + // 请求数据的url + url: '', + // 表格数据 + data: [], + // 选择项 + selections: [], + // 待查询的对象 + query: {}, + // 查询数据的参数 + params: {}, + // Form 表单 + form: {}, + // 重置表单 + defaultForm: () => {}, + // 排序规则,默认 id 降序, 支持多字段排序 ['id,desc', 'createTime,asc'] + sort: ['id,desc'], + // 等待时间 + time: 50, + // CRUD Method + crudMethod: { + add: (form) => {}, + del: (id) => {}, + edit: (form) => {}, + get: (id) => {} + }, + // 主页操作栏显示哪些按钮 + optShow: { + add: true, + edit: true, + del: true, + download: true, + reset: true + }, + // 自定义一些扩展属性 + props: {}, + // 在主页准备 + queryOnPresenterCreated: true, + // 调试开关 + debug: false + } + options = mergeOptions(defaultOptions, options) + const data = { + ...options, + // 记录数据状态 + dataStatus: {}, + status: { + add: CRUD.STATUS.NORMAL, + edit: CRUD.STATUS.NORMAL, + // 添加或编辑状态 + get cu() { + if (this.add === CRUD.STATUS.NORMAL && this.edit === CRUD.STATUS.NORMAL) { + return CRUD.STATUS.NORMAL + } else if (this.add === CRUD.STATUS.PREPARED || this.edit === CRUD.STATUS.PREPARED) { + return CRUD.STATUS.PREPARED + } else if (this.add === CRUD.STATUS.PROCESSING || this.edit === CRUD.STATUS.PROCESSING) { + return CRUD.STATUS.PROCESSING + } + throw new Error('wrong crud\'s cu status') + }, + // 标题 + get title() { + return this.add > CRUD.STATUS.NORMAL ? `新增${crud.title}` : this.edit > CRUD.STATUS.NORMAL ? `编辑${crud.title}` : crud.title + } + }, + msg: { + submit: '提交成功', + add: '新增成功', + edit: '编辑成功', + del: '删除成功' + }, + page: { + // 页码 + page: 0, + // 每页数据条数 + size: 10, + // 总数据条数 + total: 0 + }, + // 整体loading + loading: false, + // 导出的 Loading + downloadLoading: false, + // 删除的 Loading + delAllLoading: false + } + const methods = { + /** + * 通用的提示 + */ + submitSuccessNotify() { + crud.notify(crud.msg.submit, CRUD.NOTIFICATION_TYPE.SUCCESS) + }, + addSuccessNotify() { + crud.notify(crud.msg.add, CRUD.NOTIFICATION_TYPE.SUCCESS) + }, + editSuccessNotify() { + crud.notify(crud.msg.edit, CRUD.NOTIFICATION_TYPE.SUCCESS) + }, + delSuccessNotify() { + crud.notify(crud.msg.del, CRUD.NOTIFICATION_TYPE.SUCCESS) + }, + // 搜索 + toQuery() { + crud.page.page = 1 + crud.refresh() + }, + // 刷新 + refresh() { + if (!callVmHook(crud, CRUD.HOOK.beforeRefresh)) { + return + } + return new Promise((resolve, reject) => { + crud.loading = true + // 请求数据 + initData(crud.url, crud.getQueryParams()).then(data => { + const table = crud.getTable() + if (table && table.lazy) { // 懒加载子节点数据,清掉已加载的数据 + table.store.states.treeData = {} + table.store.states.lazyTreeNodeMap = {} + } + crud.page.total = data.totalElements + crud.data = data.content + crud.resetDataStatus() + // time 毫秒后显示表格 + setTimeout(() => { + crud.loading = false + callVmHook(crud, CRUD.HOOK.afterRefresh) + }, crud.time) + resolve(data) + }).catch(err => { + crud.loading = false + reject(err) + }) + }) + }, + /** + * 启动添加 + */ + toAdd() { + crud.resetForm() + if (!(callVmHook(crud, CRUD.HOOK.beforeToAdd, crud.form) && callVmHook(crud, CRUD.HOOK.beforeToCU, crud.form))) { + return + } + crud.status.add = CRUD.STATUS.PREPARED + callVmHook(crud, CRUD.HOOK.afterToAdd, crud.form) + callVmHook(crud, CRUD.HOOK.afterToCU, crud.form) + }, + /** + * 启动编辑 + * @param {*} data 数据项 + */ + toEdit(data) { + crud.resetForm(JSON.parse(JSON.stringify(data))) + if (!(callVmHook(crud, CRUD.HOOK.beforeToEdit, crud.form) && callVmHook(crud, CRUD.HOOK.beforeToCU, crud.form))) { + return + } + crud.status.edit = CRUD.STATUS.PREPARED + crud.getDataStatus(crud.getDataId(data)).edit = CRUD.STATUS.PREPARED + callVmHook(crud, CRUD.HOOK.afterToEdit, crud.form) + callVmHook(crud, CRUD.HOOK.afterToCU, crud.form) + }, + /** + * 启动删除 + * @param {*} data 数据项 + */ + toDelete(data) { + crud.getDataStatus(crud.getDataId(data)).delete = CRUD.STATUS.PREPARED + }, + /** + * 取消删除 + * @param {*} data 数据项 + */ + cancelDelete(data) { + if (!callVmHook(crud, CRUD.HOOK.beforeDeleteCancel, data)) { + return + } + crud.getDataStatus(crud.getDataId(data)).delete = CRUD.STATUS.NORMAL + callVmHook(crud, CRUD.HOOK.afterDeleteCancel, data) + }, + /** + * 取消新增/编辑 + */ + cancelCU() { + const addStatus = crud.status.add + const editStatus = crud.status.edit + if (addStatus === CRUD.STATUS.PREPARED) { + if (!callVmHook(crud, CRUD.HOOK.beforeAddCancel, crud.form)) { + return + } + crud.status.add = CRUD.STATUS.NORMAL + } + if (editStatus === CRUD.STATUS.PREPARED) { + if (!callVmHook(crud, CRUD.HOOK.beforeEditCancel, crud.form)) { + return + } + crud.status.edit = CRUD.STATUS.NORMAL + crud.getDataStatus(crud.getDataId(crud.form)).edit = CRUD.STATUS.NORMAL + } + crud.resetForm() + if (addStatus === CRUD.STATUS.PREPARED) { + callVmHook(crud, CRUD.HOOK.afterAddCancel, crud.form) + } + if (editStatus === CRUD.STATUS.PREPARED) { + callVmHook(crud, CRUD.HOOK.afterEditCancel, crud.form) + } + // 清除表单验证 + if (crud.findVM('form').$refs['form']) { + crud.findVM('form').$refs['form'].clearValidate() + } + }, + /** + * 提交新增/编辑 + */ + submitCU() { + if (!callVmHook(crud, CRUD.HOOK.beforeValidateCU)) { + return + } + crud.findVM('form').$refs['form'].validate(valid => { + if (!valid) { + return + } + if (!callVmHook(crud, CRUD.HOOK.afterValidateCU)) { + return + } + if (crud.status.add === CRUD.STATUS.PREPARED) { + crud.doAdd() + } else if (crud.status.edit === CRUD.STATUS.PREPARED) { + crud.doEdit() + } + }) + }, + /** + * 执行添加 + */ + doAdd() { + if (!callVmHook(crud, CRUD.HOOK.beforeSubmit)) { + return + } + crud.status.add = CRUD.STATUS.PROCESSING + crud.crudMethod.add(crud.form).then(() => { + crud.status.add = CRUD.STATUS.NORMAL + crud.resetForm() + crud.addSuccessNotify() + callVmHook(crud, CRUD.HOOK.afterSubmit) + crud.toQuery() + }).catch(() => { + crud.status.add = CRUD.STATUS.PREPARED + callVmHook(crud, CRUD.HOOK.afterAddError) + }) + }, + /** + * 执行编辑 + */ + doEdit() { + if (!callVmHook(crud, CRUD.HOOK.beforeSubmit)) { + return + } + crud.status.edit = CRUD.STATUS.PROCESSING + crud.crudMethod.edit(crud.form).then(() => { + crud.status.edit = CRUD.STATUS.NORMAL + crud.getDataStatus(crud.getDataId(crud.form)).edit = CRUD.STATUS.NORMAL + crud.editSuccessNotify() + crud.resetForm() + callVmHook(crud, CRUD.HOOK.afterSubmit) + crud.refresh() + }).catch(() => { + crud.status.edit = CRUD.STATUS.PREPARED + callVmHook(crud, CRUD.HOOK.afterEditError) + }) + }, + /** + * 执行删除 + * @param {*} data 数据项 + */ + doDelete(data) { + let delAll = false + let dataStatus + const ids = [] + if (data instanceof Array) { + delAll = true + data.forEach(val => { + ids.push(this.getDataId(val)) + }) + } else { + ids.push(this.getDataId(data)) + dataStatus = crud.getDataStatus(this.getDataId(data)) + } + if (!callVmHook(crud, CRUD.HOOK.beforeDelete, data)) { + return + } + if (!delAll) { + dataStatus.delete = CRUD.STATUS.PROCESSING + } + return crud.crudMethod.del(ids).then(() => { + if (delAll) { + crud.delAllLoading = false + } else dataStatus.delete = CRUD.STATUS.PREPARED + crud.dleChangePage(1) + crud.delSuccessNotify() + callVmHook(crud, CRUD.HOOK.afterDelete, data) + crud.refresh() + }).catch(() => { + if (delAll) { + crud.delAllLoading = false + } else dataStatus.delete = CRUD.STATUS.PREPARED + }) + }, + /** + * 通用导出 + */ + doExport() { + crud.downloadLoading = true + download(crud.url + '/download', crud.getQueryParams()).then(result => { + downloadFile(result, crud.title + '数据', 'xlsx') + crud.downloadLoading = false + }).catch(() => { + crud.downloadLoading = false + }) + }, + /** + * 获取查询参数 + */ + getQueryParams: function() { + // 清除参数无值的情况 + Object.keys(crud.query).length !== 0 && Object.keys(crud.query).forEach(item => { + if (crud.query[item] === null || crud.query[item] === '') crud.query[item] = undefined + }) + Object.keys(crud.params).length !== 0 && Object.keys(crud.params).forEach(item => { + if (crud.params[item] === null || crud.params[item] === '') crud.params[item] = undefined + }) + return { + page: crud.page.page - 1, + size: crud.page.size, + sort: crud.sort, + ...crud.query, + ...crud.params + } + }, + // 当前页改变 + pageChangeHandler(e) { + crud.page.page = e + crud.refresh() + }, + // 每页条数改变 + sizeChangeHandler(e) { + crud.page.size = e + crud.page.page = 1 + crud.refresh() + }, + // 预防删除第二页最后一条数据时,或者多选删除第二页的数据时,页码错误导致请求无数据 + dleChangePage(size) { + if (crud.data.length === size && crud.page.page !== 1) { + crud.page.page -= 1 + } + }, + // 选择改变 + selectionChangeHandler(val) { + crud.selections = val + }, + /** + * 重置查询参数 + * @param {Boolean} toQuery 重置后进行查询操作 + */ + resetQuery(toQuery = true) { + const defaultQuery = JSON.parse(JSON.stringify(crud.defaultQuery)) + const query = crud.query + Object.keys(query).forEach(key => { + query[key] = defaultQuery[key] + }) + // 重置参数 + this.params = {} + if (toQuery) { + crud.toQuery() + } + }, + /** + * 重置表单 + * @param {Array} data 数据 + */ + resetForm(data) { + const form = data || (typeof crud.defaultForm === 'object' ? JSON.parse(JSON.stringify(crud.defaultForm)) : crud.defaultForm.apply(crud.findVM('form'))) + const crudFrom = crud.form + for (const key in form) { + if (crudFrom.hasOwnProperty(key)) { + crudFrom[key] = form[key] + } else { + Vue.set(crudFrom, key, form[key]) + } + } + // add by ghl 2020-10-04 页面重复添加信息时,下拉框的校验会存在,需要找工取消 + if (crud.findVM('form').$refs['form']) { + crud.findVM('form').$refs['form'].clearValidate() + } + }, + /** + * 重置数据状态 + */ + resetDataStatus() { + const dataStatus = {} + function resetStatus(datas) { + datas.forEach(e => { + dataStatus[crud.getDataId(e)] = { + delete: 0, + edit: 0 + } + if (e.children) { + resetStatus(e.children) + } + }) + } + resetStatus(crud.data) + crud.dataStatus = dataStatus + }, + /** + * 获取数据状态 + * @param {Number | String} id 数据项id + */ + getDataStatus(id) { + return crud.dataStatus[id] + }, + /** + * 用于树形表格多选, 选中所有 + * @param selection + */ + selectAllChange(selection) { + // 如果选中的数目与请求到的数目相同就选中子节点,否则就清空选中 + if (selection && selection.length === crud.data.length) { + selection.forEach(val => { + crud.selectChange(selection, val) + }) + } else { + crud.getTable().clearSelection() + } + }, + /** + * 用于树形表格多选,单选的封装 + * @param selection + * @param row + */ + selectChange(selection, row) { + // 如果selection中存在row代表是选中,否则是取消选中 + if (selection.find(val => { return crud.getDataId(val) === crud.getDataId(row) })) { + if (row.children) { + row.children.forEach(val => { + crud.getTable().toggleRowSelection(val, true) + selection.push(val) + if (val.children) { + crud.selectChange(selection, val) + } + }) + } + } else { + crud.toggleRowSelection(selection, row) + } + }, + /** + * 切换选中状态 + * @param selection + * @param data + */ + toggleRowSelection(selection, data) { + if (data.children) { + data.children.forEach(val => { + selection.splice(selection.findIndex(item => this.getDataId(item) === this.getDataId(val)), 1) + crud.getTable().toggleRowSelection(val, false) + if (val.children) { + crud.toggleRowSelection(selection, val) + } + }) + } + }, + findVM(type) { + return crud.vms.find(vm => vm && vm.type === type).vm + }, + notify(title, type = CRUD.NOTIFICATION_TYPE.INFO) { + crud.vms[0].vm.$notify({ + title, + type, + duration: 2500 + }) + }, + updateProp(name, value) { + Vue.set(crud.props, name, value) + }, + getDataId(data) { + return data[this.idField] + }, + getTable() { + return this.findVM('presenter').$refs.table + }, + attchTable() { + const table = this.getTable() + this.updateProp('table', table) + const that = this + table.$on('expand-change', (row, expanded) => { + if (!expanded) { + return + } + const lazyTreeNodeMap = table.store.states.lazyTreeNodeMap + row.children = lazyTreeNodeMap[crud.getDataId(row)] + if (row.children) { + row.children.forEach(ele => { + const id = crud.getDataId(ele) + if (that.dataStatus[id] === undefined) { + that.dataStatus[id] = { + delete: 0, + edit: 0 + } + } + }) + } + }) + } + } + const crud = Object.assign({}, data) + // 可观测化 + Vue.observable(crud) + // 附加方法 + Object.assign(crud, methods) + // 记录初始默认的查询参数,后续重置查询时使用 + Object.assign(crud, { + defaultQuery: JSON.parse(JSON.stringify(data.query)), + // 预留4位存储:组件 主页、头部、分页、表单,调试查看也方便找 + vms: Array(4), + /** + * 注册组件实例 + * @param {String} type 类型 + * @param {*} vm 组件实例 + * @param {Number} index 该参数内部使用 + */ + registerVM(type, vm, index = -1) { + const vmObj = { + type, + vm: vm + } + if (index < 0) { + this.vms.push(vmObj) + return + } + if (index < 4) { // 内置预留vm数 + this.vms[index] = vmObj + return + } + this.vms.length = Math.max(this.vms.length, index) + this.vms.splice(index, 1, vmObj) + }, + /** + * 取消注册组件实例 + * @param {*} vm 组件实例 + */ + unregisterVM(type, vm) { + for (let i = this.vms.length - 1; i >= 0; i--) { + if (this.vms[i] === undefined) { + continue + } + if (this.vms[i].type === type && this.vms[i].vm === vm) { + if (i < 4) { // 内置预留vm数 + this.vms[i] = undefined + } else { + this.vms.splice(i, 1) + } + break + } + } + } + }) + // 冻结处理,需要扩展数据的话,使用crud.updateProp(name, value),以crud.props.name形式访问,这个是响应式的,可以做数据绑定 + Object.freeze(crud) + return crud +} + +// hook VM +function callVmHook(crud, hook) { + if (crud.debug) { + console.log('callVmHook: ' + hook) + } + const tagHook = crud.tag ? hook + '$' + crud.tag : null + let ret = true + const nargs = [crud] + for (let i = 2; i < arguments.length; ++i) { + nargs.push(arguments[i]) + } + // 有些组件扮演了多个角色,调用钩子时,需要去重 + const vmSet = new Set() + crud.vms.forEach(vm => vm && vmSet.add(vm.vm)) + vmSet.forEach(vm => { + if (vm[hook]) { + ret = vm[hook].apply(vm, nargs) !== false && ret + } + if (tagHook && vm[tagHook]) { + ret = vm[tagHook].apply(vm, nargs) !== false && ret + } + }) + return ret +} + +function mergeOptions(src, opts) { + const optsRet = { + ...src + } + for (const key in src) { + if (opts.hasOwnProperty(key)) { + optsRet[key] = opts[key] + } + } + return optsRet +} + +/** + * 查找crud + * @param {*} vm + * @param {string} tag + */ +function lookupCrud(vm, tag) { + tag = tag || vm.$attrs['crud-tag'] || 'default' + // function lookupCrud(vm, tag) { + if (vm.$crud) { + const ret = vm.$crud[tag] + if (ret) { + return ret + } + } + return vm.$parent ? lookupCrud(vm.$parent, tag) : undefined +} + +/** + * crud主页 + */ +function presenter(crud) { + if (crud) { + console.warn('[CRUD warn]: ' + 'please use $options.cruds() { return CRUD(...) or [CRUD(...), ...] }') + } + return { + data() { + // 在data中返回crud,是为了将crud与当前实例关联,组件观测crud相关属性变化 + return { + crud: this.crud + } + }, + beforeCreate() { + this.$crud = this.$crud || {} + let cruds = this.$options.cruds instanceof Function ? this.$options.cruds() : crud + if (!(cruds instanceof Array)) { + cruds = [cruds] + } + cruds.forEach(ele => { + if (this.$crud[ele.tag]) { + console.error('[CRUD error]: ' + 'crud with tag [' + ele.tag + ' is already exist') + } + this.$crud[ele.tag] = ele + ele.registerVM('presenter', this, 0) + }) + this.crud = this.$crud['defalut'] || cruds[0] + }, + methods: { + parseTime + }, + created() { + for (const k in this.$crud) { + if (this.$crud[k].queryOnPresenterCreated) { + this.$crud[k].toQuery() + } + } + }, + destroyed() { + for (const k in this.$crud) { + this.$crud[k].unregisterVM('presenter', this) + } + }, + mounted() { + // 如果table未实例化(例如使用了v-if),请稍后在适当时机crud.attchTable刷新table信息 + if (this.$refs.table !== undefined) { + this.crud.attchTable() + } + } + } +} + +/** + * 头部 + */ +function header() { + return { + data() { + return { + crud: this.crud, + query: this.crud.query + } + }, + beforeCreate() { + this.crud = lookupCrud(this) + this.crud.registerVM('header', this, 1) + }, + destroyed() { + this.crud.unregisterVM('header', this) + } + } +} + +/** + * 分页 + */ +function pagination() { + return { + data() { + return { + crud: this.crud, + page: this.crud.page + } + }, + beforeCreate() { + this.crud = lookupCrud(this) + this.crud.registerVM('pagination', this, 2) + }, + destroyed() { + this.crud.unregisterVM('pagination', this) + } + } +} + +/** + * 表单 + */ +function form(defaultForm) { + return { + data() { + return { + crud: this.crud, + form: this.crud.form + } + }, + beforeCreate() { + this.crud = lookupCrud(this) + this.crud.registerVM('form', this, 3) + }, + created() { + this.crud.defaultForm = defaultForm + this.crud.resetForm() + }, + destroyed() { + this.crud.unregisterVM('form', this) + } + } +} + +/** + * crud + */ +function crud(options = {}) { + const defaultOptions = { + type: undefined + } + options = mergeOptions(defaultOptions, options) + return { + data() { + return { + crud: this.crud + } + }, + beforeCreate() { + this.crud = lookupCrud(this) + this.crud.registerVM(options.type, this) + }, + destroyed() { + this.crud.unregisterVM(options.type, this) + } + } +} + +/** + * CRUD钩子 + */ +CRUD.HOOK = { + /** 刷新 - 之前 */ + beforeRefresh: 'beforeCrudRefresh', + /** 刷新 - 之后 */ + afterRefresh: 'afterCrudRefresh', + /** 删除 - 之前 */ + beforeDelete: 'beforeCrudDelete', + /** 删除 - 之后 */ + afterDelete: 'afterCrudDelete', + /** 删除取消 - 之前 */ + beforeDeleteCancel: 'beforeCrudDeleteCancel', + /** 删除取消 - 之后 */ + afterDeleteCancel: 'afterCrudDeleteCancel', + /** 新建 - 之前 */ + beforeToAdd: 'beforeCrudToAdd', + /** 新建 - 之后 */ + afterToAdd: 'afterCrudToAdd', + /** 编辑 - 之前 */ + beforeToEdit: 'beforeCrudToEdit', + /** 编辑 - 之后 */ + afterToEdit: 'afterCrudToEdit', + /** 开始 "新建/编辑" - 之前 */ + beforeToCU: 'beforeCrudToCU', + /** 开始 "新建/编辑" - 之后 */ + afterToCU: 'afterCrudToCU', + /** "新建/编辑" 验证 - 之前 */ + beforeValidateCU: 'beforeCrudValidateCU', + /** "新建/编辑" 验证 - 之后 */ + afterValidateCU: 'afterCrudValidateCU', + /** 添加取消 - 之前 */ + beforeAddCancel: 'beforeCrudAddCancel', + /** 添加取消 - 之后 */ + afterAddCancel: 'afterCrudAddCancel', + /** 编辑取消 - 之前 */ + beforeEditCancel: 'beforeCrudEditCancel', + /** 编辑取消 - 之后 */ + afterEditCancel: 'afterCrudEditCancel', + /** 提交 - 之前 */ + beforeSubmit: 'beforeCrudSubmitCU', + /** 提交 - 之后 */ + afterSubmit: 'afterCrudSubmitCU', + afterAddError: 'afterCrudAddError', + afterEditError: 'afterCrudEditError' +} + +/** + * CRUD状态 + */ +CRUD.STATUS = { + NORMAL: 0, + PREPARED: 1, + PROCESSING: 2 +} + +/** + * CRUD通知类型 + */ +CRUD.NOTIFICATION_TYPE = { + SUCCESS: 'success', + WARNING: 'warning', + INFO: 'info', + ERROR: 'error' +} + +export default CRUD + +export { + presenter, + header, + form, + pagination, + crud +} diff --git a/eladmin-web/src/components/DateRangePicker/index.vue b/eladmin-web/src/components/DateRangePicker/index.vue new file mode 100644 index 0000000..8feea56 --- /dev/null +++ b/eladmin-web/src/components/DateRangePicker/index.vue @@ -0,0 +1,45 @@ + diff --git a/eladmin-web/src/components/Dict/Dict.js b/eladmin-web/src/components/Dict/Dict.js new file mode 100644 index 0000000..48554de --- /dev/null +++ b/eladmin-web/src/components/Dict/Dict.js @@ -0,0 +1,29 @@ +import Vue from 'vue' +import { get as getDictDetail } from '@/api/system/dictDetail' + +export default class Dict { + constructor(dict) { + this.dict = dict + } + + async init(names, completeCallback) { + if (names === undefined || name === null) { + throw new Error('need Dict names') + } + const ps = [] + names.forEach(n => { + Vue.set(this.dict.dict, n, {}) + Vue.set(this.dict.label, n, {}) + Vue.set(this.dict, n, []) + ps.push(getDictDetail(n).then(data => { + this.dict[n].splice(0, 0, ...data.content) + data.content.forEach(d => { + Vue.set(this.dict.dict[n], d.value, d) + Vue.set(this.dict.label[n], d.value, d.label) + }) + })) + }) + await Promise.all(ps) + completeCallback() + } +} diff --git a/eladmin-web/src/components/Dict/index.js b/eladmin-web/src/components/Dict/index.js new file mode 100644 index 0000000..0952f43 --- /dev/null +++ b/eladmin-web/src/components/Dict/index.js @@ -0,0 +1,29 @@ +import Dict from './Dict' + +const install = function(Vue) { + Vue.mixin({ + data() { + if (this.$options.dicts instanceof Array) { + const dict = { + dict: {}, + label: {} + } + return { + dict + } + } + return {} + }, + created() { + if (this.$options.dicts instanceof Array) { + new Dict(this.dict).init(this.$options.dicts, () => { + this.$nextTick(() => { + this.$emit('dictReady') + }) + }) + } + } + }) +} + +export default { install } diff --git a/eladmin-web/src/components/Doc/index.vue b/eladmin-web/src/components/Doc/index.vue new file mode 100644 index 0000000..8c59930 --- /dev/null +++ b/eladmin-web/src/components/Doc/index.vue @@ -0,0 +1,16 @@ + + + diff --git a/eladmin-web/src/components/Echarts/BarChart.vue b/eladmin-web/src/components/Echarts/BarChart.vue new file mode 100644 index 0000000..fa265ef --- /dev/null +++ b/eladmin-web/src/components/Echarts/BarChart.vue @@ -0,0 +1,106 @@ + + + diff --git a/eladmin-web/src/components/Echarts/Category.vue b/eladmin-web/src/components/Echarts/Category.vue new file mode 100644 index 0000000..5859114 --- /dev/null +++ b/eladmin-web/src/components/Echarts/Category.vue @@ -0,0 +1,438 @@ + + + diff --git a/eladmin-web/src/components/Echarts/Funnel.vue b/eladmin-web/src/components/Echarts/Funnel.vue new file mode 100644 index 0000000..380b373 --- /dev/null +++ b/eladmin-web/src/components/Echarts/Funnel.vue @@ -0,0 +1,120 @@ + + + diff --git a/eladmin-web/src/components/Echarts/Gauge.vue b/eladmin-web/src/components/Echarts/Gauge.vue new file mode 100644 index 0000000..40ce775 --- /dev/null +++ b/eladmin-web/src/components/Echarts/Gauge.vue @@ -0,0 +1,74 @@ + + + diff --git a/eladmin-web/src/components/Echarts/Graph.vue b/eladmin-web/src/components/Echarts/Graph.vue new file mode 100644 index 0000000..a5a88b3 --- /dev/null +++ b/eladmin-web/src/components/Echarts/Graph.vue @@ -0,0 +1,101 @@ + + + diff --git a/eladmin-web/src/components/Echarts/HeatMap.vue b/eladmin-web/src/components/Echarts/HeatMap.vue new file mode 100644 index 0000000..6606278 --- /dev/null +++ b/eladmin-web/src/components/Echarts/HeatMap.vue @@ -0,0 +1,105 @@ + + + diff --git a/eladmin-web/src/components/Echarts/PieChart.vue b/eladmin-web/src/components/Echarts/PieChart.vue new file mode 100644 index 0000000..ff1bc52 --- /dev/null +++ b/eladmin-web/src/components/Echarts/PieChart.vue @@ -0,0 +1,84 @@ + + + diff --git a/eladmin-web/src/components/Echarts/Point.vue b/eladmin-web/src/components/Echarts/Point.vue new file mode 100644 index 0000000..5a6c777 --- /dev/null +++ b/eladmin-web/src/components/Echarts/Point.vue @@ -0,0 +1,149 @@ + + + diff --git a/eladmin-web/src/components/Echarts/RadarChart.vue b/eladmin-web/src/components/Echarts/RadarChart.vue new file mode 100644 index 0000000..de70e52 --- /dev/null +++ b/eladmin-web/src/components/Echarts/RadarChart.vue @@ -0,0 +1,120 @@ + + + diff --git a/eladmin-web/src/components/Echarts/Rich.vue b/eladmin-web/src/components/Echarts/Rich.vue new file mode 100644 index 0000000..1cf6bf2 --- /dev/null +++ b/eladmin-web/src/components/Echarts/Rich.vue @@ -0,0 +1,149 @@ + + + diff --git a/eladmin-web/src/components/Echarts/Sankey.vue b/eladmin-web/src/components/Echarts/Sankey.vue new file mode 100644 index 0000000..49968c4 --- /dev/null +++ b/eladmin-web/src/components/Echarts/Sankey.vue @@ -0,0 +1,100 @@ + + + diff --git a/eladmin-web/src/components/Echarts/Scatter.vue b/eladmin-web/src/components/Echarts/Scatter.vue new file mode 100644 index 0000000..b776efb --- /dev/null +++ b/eladmin-web/src/components/Echarts/Scatter.vue @@ -0,0 +1,143 @@ + + + diff --git a/eladmin-web/src/components/Echarts/Sunburst.vue b/eladmin-web/src/components/Echarts/Sunburst.vue new file mode 100644 index 0000000..fafc1a8 --- /dev/null +++ b/eladmin-web/src/components/Echarts/Sunburst.vue @@ -0,0 +1,107 @@ + + + diff --git a/eladmin-web/src/components/Echarts/ThemeRiver.vue b/eladmin-web/src/components/Echarts/ThemeRiver.vue new file mode 100644 index 0000000..967fc05 --- /dev/null +++ b/eladmin-web/src/components/Echarts/ThemeRiver.vue @@ -0,0 +1,148 @@ + + + diff --git a/eladmin-web/src/components/GithubCorner/index.vue b/eladmin-web/src/components/GithubCorner/index.vue new file mode 100644 index 0000000..fd77324 --- /dev/null +++ b/eladmin-web/src/components/GithubCorner/index.vue @@ -0,0 +1,54 @@ + + + diff --git a/eladmin-web/src/components/Hamburger/index.vue b/eladmin-web/src/components/Hamburger/index.vue new file mode 100644 index 0000000..368b002 --- /dev/null +++ b/eladmin-web/src/components/Hamburger/index.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/eladmin-web/src/components/HeaderSearch/index.vue b/eladmin-web/src/components/HeaderSearch/index.vue new file mode 100644 index 0000000..c713efc --- /dev/null +++ b/eladmin-web/src/components/HeaderSearch/index.vue @@ -0,0 +1,188 @@ + + + + + diff --git a/eladmin-web/src/components/IconSelect/index.vue b/eladmin-web/src/components/IconSelect/index.vue new file mode 100644 index 0000000..b0ec9fa --- /dev/null +++ b/eladmin-web/src/components/IconSelect/index.vue @@ -0,0 +1,68 @@ + + + + + + diff --git a/eladmin-web/src/components/IconSelect/requireIcons.js b/eladmin-web/src/components/IconSelect/requireIcons.js new file mode 100644 index 0000000..99e5c54 --- /dev/null +++ b/eladmin-web/src/components/IconSelect/requireIcons.js @@ -0,0 +1,11 @@ + +const req = require.context('../../assets/icons/svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys() + +const re = /\.\/(.*)\.svg/ + +const icons = requireAll(req).map(i => { + return i.match(re)[1] +}) + +export default icons diff --git a/eladmin-web/src/components/Iframe/index.vue b/eladmin-web/src/components/Iframe/index.vue new file mode 100644 index 0000000..cdd612b --- /dev/null +++ b/eladmin-web/src/components/Iframe/index.vue @@ -0,0 +1,30 @@ +