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 extends Comparable>) fieldType),val));
+ break;
+ case GREATER_THAN:
+ list.add(cb.greaterThanOrEqualTo(getExpression(attributeName,join,root)
+ .as((Class extends Comparable>) fieldType), (Comparable) val));
+ break;
+ case LESS_THAN:
+ list.add(cb.lessThanOrEqualTo(getExpression(attributeName,join,root)
+ .as((Class extends Comparable>) fieldType), (Comparable) val));
+ break;
+ case LESS_THAN_NQ:
+ list.add(cb.lessThan(getExpression(attributeName,join,root)
+ .as((Class extends Comparable>) 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 extends Comparable>) 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>
+<#if hasBigDecimal>
+import java.math.BigDecimal;
+#if>
+import java.io.Serializable;
+<#if !auto && pkColumnType = 'Long'>
+import com.alibaba.fastjson.annotation.JSONField;
+import com.alibaba.fastjson.serializer.ToStringSerializer;
+#if>
+
+/**
+* @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>
+ <#if column.columnKey = 'PRI'>
+ <#if !auto && pkColumnType = 'Long'>
+ /** 防止精度丢失 */
+ @JSONField(serializeUsing = ToStringSerializer.class)
+ #if>
+ #if>
+ private ${column.columnType} ${column.changeColumnName};
+ #list>
+#if>
+}
\ 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>
+<#if hasDateAnnotation>
+import javax.persistence.Entity;
+import javax.persistence.Table;
+import org.hibernate.annotations.*;
+#if>
+<#if hasTimestamp>
+import java.sql.Timestamp;
+#if>
+<#if hasBigDecimal>
+import java.math.BigDecimal;
+#if>
+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)
+ #if>
+ #if>
+ @Column(name = "`${column.columnName}`"<#if column.columnKey = 'UNI'>,unique = true#if><#if column.istNotNull && column.columnKey != 'PRI'>,nullable = false#if>)
+ <#if column.istNotNull && column.columnKey != 'PRI'>
+ <#if column.columnType = 'String'>
+ @NotBlank
+ <#else>
+ @NotNull
+ #if>
+ #if>
+ <#if (column.dateAnnotation)?? && column.dateAnnotation != ''>
+ <#if column.dateAnnotation = 'CreationTimestamp'>
+ @CreationTimestamp
+ <#else>
+ @UpdateTimestamp
+ #if>
+ #if>
+ <#if column.remark != ''>
+ @ApiModelProperty(value = "${column.remark}")
+ <#else>
+ @ApiModelProperty(value = "${column.changeColumnName}")
+ #if>
+ private ${column.columnType} ${column.changeColumnName};
+ #list>
+#if>
+
+ 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>
+<#if queryHasBigDecimal>
+import java.math.BigDecimal;
+#if>
+<#if betweens??>
+import java.util.List;
+#if>
+<#if queryColumns??>
+import me.zhengjie.annotation.Query;
+#if>
+
+/**
+* @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>
+<#if column.queryType = 'Like'>
+ /** 模糊 */
+ @Query(type = Query.Type.INNER_LIKE)
+ private ${column.columnType} ${column.changeColumnName};
+#if>
+<#if column.queryType = '!='>
+ /** 不等于 */
+ @Query(type = Query.Type.NOT_EQUAL)
+ private ${column.columnType} ${column.changeColumnName};
+#if>
+<#if column.queryType = 'NotNull'>
+ /** 不为空 */
+ @Query(type = Query.Type.NOT_NULL)
+ private ${column.columnType} ${column.changeColumnName};
+#if>
+<#if column.queryType = '>='>
+ /** 大于等于 */
+ @Query(type = Query.Type.GREATER_THAN)
+ private ${column.columnType} ${column.changeColumnName};
+#if>
+<#if column.queryType = '<='>
+ /** 小于等于 */
+ @Query(type = Query.Type.LESS_THAN)
+ private ${column.columnType} ${column.changeColumnName};
+#if>
+ #list>
+#if>
+<#if betweens??>
+ <#list betweens as column>
+ /** BETWEEN */
+ @Query(type = Query.Type.BETWEEN)
+ private List<${column.columnType}> ${column.changeColumnName};
+ #list>
+#if>
+}
\ 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});
+ #if>
+ #list>
+#if>
+}
\ 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;
+ #if>
+ #if>
+ #list>
+#if>
+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>
+<#if !auto && pkColumnType = 'String'>
+import cn.hutool.core.util.IdUtil;
+#if>
+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>
+<#if !auto && pkColumnType = 'String'>
+ resources.set${pkCapitalColName}(IdUtil.simpleUUID());
+#if>
+<#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}());
+ }
+ #if>
+ #list>
+#if>
+ ${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;
+ #if>
+ ${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}());
+ }
+ #if>
+ #list>
+#if>
+ ${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}());
+ #if>
+ #if>
+ #list>
+ 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-->
+
+
+
+
+ <#if hasQuery>
+
+
+ <#if queryColumns??>
+ <#list queryColumns as column>
+ <#if column.queryType != 'BetWeen'>
+ <#if column.remark != ''>${column.remark}<#else>${column.changeColumnName}#if>
+
+ #if>
+ #list>
+ #if>
+ <#if betweens??>
+ <#list betweens as column>
+ <#if column.queryType = 'BetWeen'>
+
+ #if>
+ #list>
+ #if>
+
+
+ #if>
+
+
+
+
+ :rules="rules"#if> size="small" label-width="150px">
+ <#if columns??>
+ <#list columns as column>
+ <#if column.formShow>
+ prop="${column.changeColumnName}"#if>>
+ <#if column.formType = 'Input'>
+
+ <#elseif column.formType = 'Textarea'>
+
+ <#elseif column.formType = 'Radio'>
+ <#if (column.dictName)?? && (column.dictName)!="">
+ {{ item.label }}
+ <#else>
+ 未设置字典,请手动设置 Radio
+ #if>
+ <#elseif column.formType = 'Select'>
+ <#if (column.dictName)?? && (column.dictName)!="">
+
+
+
+ <#else>
+ 未设置字典,请手动设置 Select
+ #if>
+ <#else>
+
+ #if>
+
+ #if>
+ #list>
+ #if>
+
+
+
+
+
+
+ <#if columns??>
+ <#list columns as column>
+ <#if column.columnShow>
+ <#if (column.dictName)?? && (column.dictName)!="">
+
+
+ {{ dict.label.${column.dictName}[scope.row.${column.changeColumnName}] }}
+
+
+ <#else>
+
+ #if>
+ #if>
+ #list>
+ #if>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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