diff --git a/cutejava-springboot4/.gitignore b/cutejava-springboot4/.gitignore
new file mode 100644
index 00000000..a4dad206
--- /dev/null
+++ b/cutejava-springboot4/.gitignore
@@ -0,0 +1,9 @@
+### IDEA ###
+.DS_Store
+~/*
+.idea/*
+*.iml
+*/target/*
+*/*.iml
+/.gradle/
+/application.pid
\ No newline at end of file
diff --git a/cutejava-springboot4/LICENSE b/cutejava-springboot4/LICENSE
new file mode 100644
index 00000000..78a36cb2
--- /dev/null
+++ b/cutejava-springboot4/LICENSE
@@ -0,0 +1,191 @@
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and
+distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright
+owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities
+that control, are controlled by, or are under common control with that entity.
+For the purposes of this definition, "control" means (i) the power, direct or
+indirect, to cause the direction or management of such entity, whether by
+contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
+outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising
+permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including
+but not limited to software source code, documentation source, and configuration
+files.
+
+"Object" form shall mean any form resulting from mechanical transformation or
+translation of a Source form, including but not limited to compiled object code,
+generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made
+available under the License, as indicated by a copyright notice that is included
+in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that
+is based on (or derived from) the Work and for which the editorial revisions,
+annotations, elaborations, or other modifications represent, as a whole, an
+original work of authorship. For the purposes of this License, Derivative Works
+shall not include works that remain separable from, or merely link (or bind by
+name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version
+of the Work and any modifications or additions to that Work or Derivative Works
+thereof, that is intentionally submitted to Licensor for inclusion in the Work
+by the copyright owner or by an individual or Legal Entity authorized to submit
+on behalf of the copyright owner. For the purposes of this definition,
+"submitted" means any form of electronic, verbal, or written communication sent
+to the Licensor or its representatives, including but not limited to
+communication on electronic mailing lists, source code control systems, and
+issue tracking systems that are managed by, or on behalf of, the Licensor for
+the purpose of discussing and improving the Work, but excluding communication
+that is conspicuously marked or otherwise designated in writing by the copyright
+owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
+of whom a Contribution has been received by Licensor and subsequently
+incorporated within the Work.
+
+2. Grant of Copyright License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable copyright license to reproduce, prepare Derivative Works of,
+publicly display, publicly perform, sublicense, and distribute the Work and such
+Derivative Works in Source or Object form.
+
+3. Grant of Patent License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable (except as stated in this section) patent license to make, have
+made, use, offer to sell, sell, import, and otherwise transfer the Work, where
+such license applies only to those patent claims licensable by such Contributor
+that are necessarily infringed by their Contribution(s) alone or by combination
+of their Contribution(s) with the Work to which such Contribution(s) was
+submitted. If You institute patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Work or a
+Contribution incorporated within the Work constitutes direct or contributory
+patent infringement, then any patent licenses granted to You under this License
+for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution.
+
+You may reproduce and distribute copies of the Work or Derivative Works thereof
+in any medium, with or without modifications, and in Source or Object form,
+provided that You meet the following conditions:
+
+You must give any other recipients of the Work or Derivative Works a copy of
+this License; and
+You must cause any modified files to carry prominent notices stating that You
+changed the files; and
+You must retain, in the Source form of any Derivative Works that You distribute,
+all copyright, patent, trademark, and attribution notices from the Source form
+of the Work, excluding those notices that do not pertain to any part of the
+Derivative Works; and
+If the Work includes a "NOTICE" text file as part of its distribution, then any
+Derivative Works that You distribute must include a readable copy of the
+attribution notices contained within such NOTICE file, excluding those notices
+that do not pertain to any part of the Derivative Works, in at least one of the
+following places: within a NOTICE text file distributed as part of the
+Derivative Works; within the Source form or documentation, if provided along
+with the Derivative Works; or, within a display generated by the Derivative
+Works, if and wherever such third-party notices normally appear. The contents of
+the NOTICE file are for informational purposes only and do not modify the
+License. You may add Your own attribution notices within Derivative Works that
+You distribute, alongside or as an addendum to the NOTICE text from the Work,
+provided that such additional attribution notices cannot be construed as
+modifying the License.
+You may add Your own copyright statement to Your modifications and may provide
+additional or different license terms and conditions for use, reproduction, or
+distribution of Your modifications, or for any such Derivative Works as a whole,
+provided Your use, reproduction, and distribution of the Work otherwise complies
+with the conditions stated in this License.
+
+5. Submission of Contributions.
+
+Unless You explicitly state otherwise, any Contribution intentionally submitted
+for inclusion in the Work by You to the Licensor shall be under the terms and
+conditions of this License, without any additional terms or conditions.
+Notwithstanding the above, nothing herein shall supersede or modify the terms of
+any separate license agreement you may have executed with Licensor regarding
+such Contributions.
+
+6. Trademarks.
+
+This License does not grant permission to use the trade names, trademarks,
+service marks, or product names of the Licensor, except as required for
+reasonable and customary use in describing the origin of the Work and
+reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty.
+
+Unless required by applicable law or agreed to in writing, Licensor provides the
+Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
+including, without limitation, any warranties or conditions of TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
+solely responsible for determining the appropriateness of using or
+redistributing the Work and assume any risks associated with Your exercise of
+permissions under this License.
+
+8. Limitation of Liability.
+
+In no event and under no legal theory, whether in tort (including negligence),
+contract, or otherwise, unless required by applicable law (such as deliberate
+and grossly negligent acts) or agreed to in writing, shall any Contributor be
+liable to You for damages, including any direct, indirect, special, incidental,
+or consequential damages of any character arising as a result of this License or
+out of the use or inability to use the Work (including but not limited to
+damages for loss of goodwill, work stoppage, computer failure or malfunction, or
+any and all other commercial damages or losses), even if such Contributor has
+been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability.
+
+While redistributing the Work or Derivative Works thereof, You may choose to
+offer, and charge a fee for, acceptance of support, warranty, indemnity, or
+other liability obligations and/or rights consistent with this License. However,
+in accepting such obligations, You may act only on Your own behalf and on Your
+sole responsibility, not on behalf of any other Contributor, and only if You
+agree to indemnify, defend, and hold each Contributor harmless for any liability
+incurred by, or claims asserted against, such Contributor by reason of your
+accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work
+
+To apply the Apache License to your work, attach the following boilerplate
+notice, with the fields enclosed by brackets "{}" replaced with your own
+identifying information. (Don't include the brackets!) The text should be
+enclosed in the appropriate comment syntax for the file format. We also
+recommend that a file or class name and description of purpose be included on
+the same "printed page" as the copyright notice for easier identification within
+third-party archives.
+
+ Copyright 2019 Zheng Jie
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/cutejava-springboot4/cutejava-framework/pom.xml b/cutejava-springboot4/cutejava-framework/pom.xml
new file mode 100644
index 00000000..3fe35a9c
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/pom.xml
@@ -0,0 +1,12 @@
+
+
+
+ cutejava-springboot4
+ cn.odboy
+ 1.5.2
+
+ 4.0.0
+ cutejava-framework
+ 公共模块
+
\ No newline at end of file
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/annotation/AnonymousAccess.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/annotation/AnonymousAccess.java
new file mode 100644
index 00000000..a85c6e53
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/annotation/AnonymousAccess.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 用于标记匿名访问方法
+ */
+@Inherited
+@Documented
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface AnonymousAccess {
+
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/annotation/AnonymousDeleteMapping.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/annotation/AnonymousDeleteMapping.java
new file mode 100644
index 00000000..cb9d7ce8
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/annotation/AnonymousDeleteMapping.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.annotation;
+
+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 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
+ *
+ * @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/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/annotation/AnonymousGetMapping.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/annotation/AnonymousGetMapping.java
new file mode 100644
index 00000000..8e10707c
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/annotation/AnonymousGetMapping.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.annotation;
+
+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 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
+ *
+ * @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/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/annotation/AnonymousPatchMapping.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/annotation/AnonymousPatchMapping.java
new file mode 100644
index 00000000..c3e6ed0f
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/annotation/AnonymousPatchMapping.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.annotation;
+
+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 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
+ *
+ * @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/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/annotation/AnonymousPostMapping.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/annotation/AnonymousPostMapping.java
new file mode 100644
index 00000000..7a43f104
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/annotation/AnonymousPostMapping.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.annotation;
+
+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 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
+ *
+ * @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/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/annotation/AnonymousPutMapping.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/annotation/AnonymousPutMapping.java
new file mode 100644
index 00000000..b5f185ca
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/annotation/AnonymousPutMapping.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.annotation;
+
+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 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
+ *
+ * @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/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitArgs.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitArgs.java
new file mode 100644
index 00000000..c51cf1c3
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitArgs.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.base;
+
+import jakarta.validation.constraints.NotNull;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 通用参数
+ *
+ * @author odboy
+ * @date 2025-07-23
+ */
+public class KitArgs {
+
+ @Getter
+ @Setter
+ public static class FindByLongId extends KitObject {
+
+ @NotNull(message = "id必填")
+ private Long id;
+ }
+
+ @Getter
+ @Setter
+ public static class DeleteById extends KitObject {
+
+ @NotNull(message = "id必填")
+ private Long id;
+ }
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitBaseCreateTimeTb.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitBaseCreateTimeTb.java
new file mode 100644
index 00000000..e1a1bed9
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitBaseCreateTimeTb.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.base;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.ApiModelProperty;
+import java.util.Date;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@EqualsAndHashCode(callSuper = false)
+public class KitBaseCreateTimeTb extends KitObject {
+
+ @TableField(fill = FieldFill.INSERT)
+ @ApiModelProperty(value = "创建时间: yyyy-MM-dd HH:mm:ss", hidden = true)
+ private Date createTime;
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitBaseTimeTb.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitBaseTimeTb.java
new file mode 100644
index 00000000..7d5d428d
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitBaseTimeTb.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.base;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.ApiModelProperty;
+import java.util.Date;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@EqualsAndHashCode(callSuper = false)
+public class KitBaseTimeTb extends KitObject {
+
+ @TableField(fill = FieldFill.INSERT)
+ @ApiModelProperty(value = "创建时间: yyyy-MM-dd HH:mm:ss", hidden = true)
+ private Date createTime;
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ @ApiModelProperty(value = "更新时间: yyyy-MM-dd HH:mm:ss", hidden = true)
+ private Date updateTime;
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitBaseUpdateTimeTb.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitBaseUpdateTimeTb.java
new file mode 100644
index 00000000..0ba57fa1
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitBaseUpdateTimeTb.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.base;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.ApiModelProperty;
+import java.util.Date;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@EqualsAndHashCode(callSuper = false)
+public class KitBaseUpdateTimeTb extends KitObject {
+
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ @ApiModelProperty(value = "更新时间: yyyy-MM-dd HH:mm:ss", hidden = true)
+ private Date updateTime;
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitBaseUserCreateTimeTb.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitBaseUserCreateTimeTb.java
new file mode 100644
index 00000000..c03a1e33
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitBaseUserCreateTimeTb.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.base;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.ApiModelProperty;
+import java.lang.reflect.Field;
+import java.util.Date;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.springframework.data.annotation.CreatedBy;
+
+@Getter
+@Setter
+@EqualsAndHashCode(callSuper = false)
+public class KitBaseUserCreateTimeTb extends KitObject {
+
+ @CreatedBy
+ @TableField(fill = FieldFill.INSERT)
+ @ApiModelProperty(value = "创建人", hidden = true)
+ private String
+ createBy;
+ @TableField(fill = FieldFill.INSERT)
+ @ApiModelProperty(value = "创建时间: yyyy-MM-dd HH:mm:ss", hidden = true)
+ private Date createTime;
+
+ @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();
+ }
+
+ /* 分组校验 */
+ public @interface Create {
+
+ }
+
+ /* 分组校验 */
+ public @interface Update {
+
+ }
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitBaseUserTimeLogicTb.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitBaseUserTimeLogicTb.java
new file mode 100644
index 00000000..9d1c99de
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitBaseUserTimeLogicTb.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.base;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import io.swagger.annotations.ApiModelProperty;
+import java.util.Date;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.data.annotation.CreatedBy;
+import org.springframework.data.annotation.LastModifiedBy;
+
+/**
+ * 逻辑删除通用模型
+ *
+ * @author odboy
+ * @date 2025-01-12
+ */
+@Getter
+@Setter
+public class KitBaseUserTimeLogicTb extends KitObject {
+
+ @CreatedBy
+ @TableField(value = "create_by", fill = FieldFill.INSERT)
+ @ApiModelProperty(value = "创建人", hidden = true)
+ private String createBy;
+ @LastModifiedBy
+ @TableField(value = "update_by", fill = FieldFill.INSERT_UPDATE)
+ @ApiModelProperty(value = "更新人", hidden = true)
+ private String updateBy;
+ @TableField(value = "create_time", fill = FieldFill.INSERT)
+ @ApiModelProperty(value = "创建时间", hidden = true)
+ private Date createTime;
+ @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
+ @ApiModelProperty(value = "更新时间", hidden = true)
+ private Date updateTime;
+ @ApiModelProperty(value = "数据有效性", hidden = true)
+ @TableLogic
+ @TableField("available")
+ private Integer
+ available;
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitBaseUserTimeTb.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitBaseUserTimeTb.java
new file mode 100644
index 00000000..96652bc3
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitBaseUserTimeTb.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.base;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.ApiModelProperty;
+import java.lang.reflect.Field;
+import java.util.Date;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.springframework.data.annotation.CreatedBy;
+import org.springframework.data.annotation.LastModifiedBy;
+
+@Getter
+@Setter
+@EqualsAndHashCode(callSuper = false)
+public class KitBaseUserTimeTb extends KitObject {
+
+ @CreatedBy
+ @TableField(fill = FieldFill.INSERT)
+ @ApiModelProperty(value = "创建人", hidden = true)
+ private String
+ createBy;
+ @LastModifiedBy
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ @ApiModelProperty(value = "更新人", hidden = true)
+ private String updateBy;
+ @TableField(fill = FieldFill.INSERT)
+ @ApiModelProperty(value = "创建时间: yyyy-MM-dd HH:mm:ss", hidden = true)
+ private Date createTime;
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ @ApiModelProperty(value = "更新时间: yyyy-MM-dd HH:mm:ss", hidden = true)
+ private Date 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();
+ }
+
+ /* 分组校验 */
+ public @interface Create {
+
+ }
+
+ /* 分组校验 */
+ public @interface Update {
+
+ }
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitObject.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitObject.java
new file mode 100644
index 00000000..615cf630
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitObject.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.base;
+
+import java.io.Serializable;
+
+/**
+ * 基础对象
+ *
+ * @author odboy
+ * @date 2022-01-12
+ */
+public class KitObject implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitPageArgs.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitPageArgs.java
new file mode 100644
index 00000000..572b5189
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitPageArgs.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.base;
+
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import io.swagger.annotations.ApiModelProperty;
+
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotNull;
+import lombok.*;
+
+/**
+ * 数据分页参数
+ *
+ * @author odboy
+ * @date 2025-01-20
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@EqualsAndHashCode(callSuper = false)
+public class KitPageArgs extends KitObject {
+
+ @NotNull(message = "参数page不能为空")
+ @Min(value = 1, message = "参数page最小值为1")
+ @ApiModelProperty(value = "页码", example = "1")
+ private Integer page;
+ @NotNull(message = "参数size不能为空")
+ @Min(value = 1, message = "参数size最小值为1")
+ @ApiModelProperty(value = "每页数据量", example = "10")
+ private Integer size;
+ private T args;
+ @ApiModelProperty(value = "排序参数")
+ private OrderBy orderBy;
+
+ @Setter
+ public static class OrderBy extends KitObject {
+
+ /**
+ * 排序列名称
+ */
+ private String column;
+ /**
+ * 排序方式
+ */
+ private String orderType;
+
+ /**
+ * 字段属性转下划线
+ *
+ * @return /
+ */
+ public String getColumn() {
+ return StrUtil.toUnderlineCase(this.column);
+ }
+
+ /**
+ * 是否顺序排序
+ *
+ * @return /
+ */
+ public boolean isAsc() {
+ return "asc".equals(this.orderType);
+ }
+
+ /**
+ * 绑定 QueryWrapper,应用排序条件
+ *
+ * @param wrapper QueryWrapper
+ */
+ public void bindWrapper(QueryWrapper wrapper) {
+ wrapper.orderBy(true, this.isAsc(), this.getColumn());
+ }
+ }
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitPageResult.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitPageResult.java
new file mode 100644
index 00000000..e40fda1c
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitPageResult.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.base;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import java.util.ArrayList;
+import java.util.List;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+/**
+ * 结果封装类
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@EqualsAndHashCode(callSuper = false)
+public class KitPageResult extends KitObject {
+
+ private List content;
+ private long totalElements;
+
+ public static KitPageResult emptyListData() {
+ KitPageResult result = new KitPageResult<>();
+ result.setTotalElements(0);
+ result.setContent(new ArrayList<>());
+ return result;
+ }
+
+ public static KitPageResult listData(IPage page) {
+ KitPageResult result = new KitPageResult<>();
+ result.setTotalElements(page.getTotal());
+ result.setContent(page.getRecords());
+ return result;
+ }
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitSelectOptionVo.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitSelectOptionVo.java
new file mode 100644
index 00000000..2b712e9a
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/base/KitSelectOptionVo.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.base;
+
+import java.util.Map;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+/**
+ * 元数据选项
+ *
+ * @author odboy
+ * @date 2025-01-13
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@EqualsAndHashCode(callSuper = false)
+public class KitSelectOptionVo extends KitObject {
+
+ private String label;
+ private String value;
+ private Map ext;
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/constant/CaptchaCodeEnum.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/constant/CaptchaCodeEnum.java
new file mode 100644
index 00000000..2e9e1e51
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/constant/CaptchaCodeEnum.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.constant;
+
+/**
+ * 验证码配置枚举
+ */
+public enum CaptchaCodeEnum {
+ /**
+ * 算数
+ */
+ ARITHMETIC,
+ /**
+ * 中文
+ */
+ CHINESE,
+ /**
+ * 中文闪图
+ */
+ CHINESE_GIF,
+ /**
+ * 闪图
+ */
+ GIF,
+ /**
+ * 静态
+ */
+ SPEC
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/constant/FileTypeEnum.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/constant/FileTypeEnum.java
new file mode 100644
index 00000000..fd95343e
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/constant/FileTypeEnum.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.constant;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public enum FileTypeEnum {
+ /* 文件类型 */
+ IMAGE("image", "图片"), DOC("doc", "文档"), VOICE("voice", "音频"), VIDEO("video", "视频"), OTHER("other", "其他");
+ private final String code;
+ private final String description;
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/constant/RequestMethodEnum.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/constant/RequestMethodEnum.java
new file mode 100644
index 00000000..e4bf5b10
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/constant/RequestMethodEnum.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.constant;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@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/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/constant/SystemConst.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/constant/SystemConst.java
new file mode 100644
index 00000000..8e79d47c
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/constant/SystemConst.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.constant;
+
+/**
+ * 常用静态常量
+ */
+public interface SystemConst {
+
+ /**
+ * win 系统
+ */
+ String WIN = "win";
+ /**
+ * mac 系统
+ */
+ String MAC = "mac";
+ /**
+ * 请求头名称
+ */
+ String HEADER_NAME = "Authorization";
+ /**
+ * Token前缀
+ */
+ String TOKEN_PREFIX = "Bearer";
+ /**
+ * 小数点
+ */
+ String SYMBOL_DOT = ".";
+ String SYMBOL_EQUAL = "=";
+ String SYMBOL_ADD = "+";
+ String SYMBOL_SUBTRACT = "-";
+ String SYMBOL_AT = "@";
+ String PROPERTY_OS_NAME = "os.name";
+ String OS_NAME_WINDOWS = "Windows";
+ String CURRENT_APP_NAME = "cutejava";
+ String CURRENT_APP_TITLE = "CuteJava";
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/feature/KitDynamicTableColumn.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/feature/KitDynamicTableColumn.java
new file mode 100644
index 00000000..5f4e506d
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/feature/KitDynamicTableColumn.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.feature;
+
+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;
+
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
+public @interface KitDynamicTableColumn {
+
+ /**
+ * 标题
+ */
+ String value() default "";
+
+ /**
+ * 宽度
+ */
+ int width() default 0;
+
+ /**
+ * 是否排序字段
+ */
+ boolean sortable() default false;
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/feature/KitDynamicTableResponse.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/feature/KitDynamicTableResponse.java
new file mode 100644
index 00000000..75081f8d
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/feature/KitDynamicTableResponse.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.feature;
+
+import cn.hutool.core.util.ReflectUtil;
+import cn.odboy.base.KitObject;
+import cn.odboy.base.KitPageArgs;
+import cn.odboy.util.KitBeanUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.Getter;
+import lombok.Setter;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 动态表格
+ *
+ * @param 数据表对象
+ * @param 渲染对象
+ * @author odboy
+ * @date 2026-02-05
+ */
+@Getter
+public class KitDynamicTableResponse extends KitObject {
+
+ /**
+ * 主键
+ */
+ @Setter
+ private String primaryKey = "id";
+ /**
+ * 表格列定义
+ */
+ private List columns = new ArrayList<>();
+ /**
+ * 数据总数
+ */
+ private Long totalElements;
+ /**
+ * 数据内容
+ */
+ private List content;
+
+ /**
+ * @param clazz 渲染类型
+ * @param pageResult 渲染类型分页参数
+ */
+ public KitDynamicTableResponse(Class clazz, IPage pageResult) {
+ this.renderColumns(clazz);
+ this.setContent(clazz, pageResult);
+ }
+
+ /**
+ * @param clazz 渲染类型
+ * @param pageResult 渲染类型分页参数
+ * @param primaryKey 主键
+ */
+ public KitDynamicTableResponse(Class clazz, IPage pageResult, String primaryKey) {
+ this.primaryKey = primaryKey;
+ this.renderColumns(clazz);
+ this.setContent(clazz, pageResult);
+ }
+
+ /**
+ * @param clazz 渲染类型
+ * @param pageArgs 分页参数
+ * @param baseMapper 数据表Mapper
+ * @param wrapper 查询条件
+ */
+ public KitDynamicTableResponse(Class clazz, KitPageArgs> pageArgs, BaseMapper baseMapper, LambdaQueryWrapper wrapper) {
+ this.renderColumns(clazz);
+ Page> pageResult = baseMapper.selectPage(new Page<>(pageArgs.getPage(), pageArgs.getSize()), wrapper);
+ this.setContent(clazz, pageResult);
+ }
+
+ /**
+ * @param clazz 渲染类型
+ * @param pageArgs 分页参数
+ * @param primaryKey 主键
+ * @param baseMapper 数据表Mapper
+ * @param wrapper 查询条件
+ */
+ public KitDynamicTableResponse(Class clazz, KitPageArgs> pageArgs, String primaryKey, BaseMapper baseMapper, LambdaQueryWrapper wrapper) {
+ this.primaryKey = primaryKey;
+ this.renderColumns(clazz);
+ Page> pageResult = baseMapper.selectPage(new Page<>(pageArgs.getPage(), pageArgs.getSize()), wrapper);
+ this.setContent(clazz, pageResult);
+ }
+
+ /**
+ * 渲染表格列
+ *
+ * @param clazz 渲染类型
+ */
+ private void renderColumns(Class clazz) {
+ List columns = new ArrayList<>();
+ if (clazz == null) {
+ this.columns = columns;
+ return;
+ }
+ Field[] fields = ReflectUtil.getFields(clazz);
+ for (Field field : fields) {
+ if ("serialVersionUID".equals(field.getName())) {
+ continue;
+ }
+ Column column = new Column();
+ column.setName(field.getName());
+ KitDynamicTableColumn annotation = field.getAnnotation(KitDynamicTableColumn.class);
+ if (annotation == null) {
+ column.setTitle(field.getName());
+ column.setWidth(null);
+ } else {
+ column.setTitle(annotation.value());
+ column.setWidth(annotation.width() == 0 ? null : annotation.width());
+ column.setSortable(annotation.sortable());
+ }
+ columns.add(column);
+ }
+ this.columns = columns;
+ }
+
+ /**
+ * @param clazz 渲染类型
+ * @param pageResult 渲染结果
+ */
+ public void setContent(Class clazz, IPage> pageResult) {
+ if (pageResult == null) {
+ return;
+ }
+ this.totalElements = pageResult.getTotal();
+ this.content = KitBeanUtil.copyToList(pageResult.getRecords(), clazz);
+ }
+
+ @Getter
+ @Setter
+ public static class Column {
+
+ /**
+ * 索引名称
+ */
+ private String name;
+ /**
+ * 索引标题
+ */
+ private String title;
+ /**
+ * 表格宽度
+ */
+ private Integer width;
+ /**
+ * 是否排序列
+ */
+ private Boolean sortable = false;
+ }
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/feature/KitDynamicTableUtil.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/feature/KitDynamicTableUtil.java
new file mode 100644
index 00000000..9f33d1d4
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/feature/KitDynamicTableUtil.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.feature;
+
+import cn.odboy.base.KitPageArgs;
+import cn.odboy.framework.mybatisplus.core.KitMpQUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import lombok.experimental.UtilityClass;
+
+/**
+ * 动态表格辅助工具
+ *
+ * @author odboy
+ */
+@UtilityClass
+public class KitDynamicTableUtil {
+
+ /**
+ * 渲染排序规则
+ *
+ * @param pageArgs 分页参数
+ * @param 数据表对象
+ * @param 渲染对象
+ * @return /
+ */
+ public static QueryWrapper renderOrder(KitPageArgs pageArgs) {
+ QueryWrapper wrapper = new QueryWrapper<>();
+ KitPageArgs.OrderBy orderBy = pageArgs.getOrderBy();
+ if (orderBy != null) {
+ wrapper.orderBy(true, orderBy.isAsc(), orderBy.getColumn());
+ }
+ return wrapper;
+ }
+
+ /**
+ * 渲染所有规则(需要配合 @KitMpQuery 注解使用)
+ *
+ * @param pageArgs 分页参数
+ * @param 数据表对象
+ * @param 渲染对象
+ * @return /
+ */
+ public static LambdaQueryWrapper renderAll(KitPageArgs pageArgs) {
+ QueryWrapper wrapper = KitMpQUtil.build(pageArgs.getArgs());
+ KitPageArgs.OrderBy orderBy = pageArgs.getOrderBy();
+ if (orderBy != null) {
+ wrapper.orderBy(true, orderBy.isAsc(), orderBy.getColumn());
+ }
+ return wrapper.lambda();
+ }
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/context/BeanLoadFinishListener.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/context/BeanLoadFinishListener.java
new file mode 100644
index 00000000..969a19e3
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/context/BeanLoadFinishListener.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.odboy.framework.context;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.stereotype.Component;
+
+/**
+ * Bean加载完毕 监听
+ *
+ * @author odboy
+ * @date 2025-01-16
+ */
+@Slf4j
+@Component
+public class BeanLoadFinishListener implements ApplicationListener {
+
+ @Override
+ public void onApplicationEvent(ContextRefreshedEvent event) {
+ log.info("所有的Bean都加载完毕了");
+ }
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/context/BootApplication.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/context/BootApplication.java
new file mode 100644
index 00000000..91c75738
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/context/BootApplication.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.odboy.framework.context;
+
+import cn.hutool.core.util.StrUtil;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.core.env.Environment;
+
+/**
+ * 主要目的是输出应用的服务地址
+ *
+ * @author odboy
+ * @date 2024-04-20
+ */
+@Slf4j
+public class BootApplication {
+
+ protected static void inited(ConfigurableApplicationContext application) throws UnknownHostException {
+ Environment env = application.getEnvironment();
+ String ip = InetAddress.getLocalHost().getHostAddress();
+ String port = env.getProperty("server.port");
+ String path = StrUtil.isEmpty(env.getProperty("config.servlet.context-path")) ? ""
+ : env.getProperty("config.servlet.context-path");
+ log.info("""
+ ----------------------------------------------------------
+ \tApplication is running! Access URLs:
+ \tLocal: \t\thttp://localhost:{}{}
+ \tExternal: \thttp://{}:{}{}
+ \tSwagger文档: \thttp://{}:{}{}/doc.html
+ ----------------------------------------------------------""", port, path, ip, port, path, ip, port, path);
+ }
+
+ @Bean
+ public KitSpringBeanHolder springBeanHolder() {
+ return new KitSpringBeanHolder();
+ }
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/context/KitRequestHolder.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/context/KitRequestHolder.java
new file mode 100644
index 00000000..503586d9
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/context/KitRequestHolder.java
@@ -0,0 +1,34 @@
+
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.odboy.framework.context;
+
+import jakarta.servlet.http.HttpServletRequest;
+import java.util.Objects;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+/**
+ * 获取 HttpServletRequest
+ */
+public class KitRequestHolder {
+
+ public static HttpServletRequest getHttpServletRequest() {
+ return ((ServletRequestAttributes) Objects.requireNonNull(
+ RequestContextHolder.getRequestAttributes())).getRequest();
+ }
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/context/KitScriptHelper.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/context/KitScriptHelper.java
new file mode 100644
index 00000000..9bd822eb
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/context/KitScriptHelper.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.framework.context;
+
+import com.ezylang.evalex.EvaluationException;
+import com.ezylang.evalex.Expression;
+import com.ezylang.evalex.data.EvaluationValue;
+import com.ezylang.evalex.parser.ParseException;
+import java.util.Map;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+/**
+ * 脚本工具
+ *
+ * @author odboy
+ * @date 2025-09-23
+ */
+@Slf4j
+@Component
+public class KitScriptHelper {
+
+ public Object evalObj(String script, Map args) throws EvaluationException, ParseException {
+ Expression expression = new Expression(script);
+ expression.withValues(args);
+ EvaluationValue result = expression.evaluate();
+ return result.getValue();
+ }
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/context/KitSpringBeanHolder.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/context/KitSpringBeanHolder.java
new file mode 100644
index 00000000..a0ccef14
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/context/KitSpringBeanHolder.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.framework.context;
+
+import jakarta.annotation.Nonnull;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+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;
+
+@Slf4j
+public class KitSpringBeanHolder implements ApplicationContextAware, DisposableBean {
+
+ private static final List CALL_BACKS = new ArrayList<>();
+ private static ApplicationContext applicationContext = null;
+ private static boolean addCallback = true;
+
+ /**
+ * 针对 某些初始化方法, 在SpringContextHolder 未初始化时 提交回调方法。 在SpringContextHolder 初始化后, 进行回调使用
+ *
+ * @param callBack 回调函数
+ */
+ public synchronized static void addCallBacks(CallBack callBack) {
+ if (addCallback) {
+ CALL_BACKS.add(callBack);
+ } else {
+ log.warn("CallBack:{} 已无法添加!立即执行", callBack.getCallBackName());
+ callBack.executor();
+ }
+ }
+
+ /**
+ * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
+ */
+ @SuppressWarnings("all")
+ 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;
+ }
+
+ /**
+ * 获取 @Service 的所有 bean 名称
+ *
+ * @return /
+ */
+ public static List getAllServiceBeanName() {
+ return new ArrayList<>(Arrays.asList(applicationContext.getBeanNamesForAnnotation(Service.class)));
+ }
+
+ @Override
+ public void destroy() {
+ KitSpringBeanHolder.clearHolder();
+ }
+
+ @Override
+ public void setApplicationContext(@Nonnull ApplicationContext applicationContext) throws BeansException {
+ if (KitSpringBeanHolder.applicationContext != null) {
+ log.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:{}", KitSpringBeanHolder.applicationContext);
+ }
+ KitSpringBeanHolder.applicationContext = applicationContext;
+ if (addCallback) {
+ for (CallBack callBack : KitSpringBeanHolder.CALL_BACKS) {
+ callBack.executor();
+ }
+ CALL_BACKS.clear();
+ }
+ KitSpringBeanHolder.addCallback = false;
+ }
+
+ public interface CallBack {
+
+ /**
+ * 回调执行方法
+ */
+ void executor();
+
+ /**
+ * 本回调任务名称
+ *
+ * @return /
+ */
+ default String getCallBackName() {
+ return Thread.currentThread().threadId() + ":" + this.getClass().getName();
+ }
+ }
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/exception/BadRequestException.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/exception/BadRequestException.java
new file mode 100644
index 00000000..186d739e
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/exception/BadRequestException.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.framework.exception;
+
+import lombok.Getter;
+import org.springframework.http.HttpStatus;
+import static org.springframework.http.HttpStatus.BAD_REQUEST;
+
+/**
+ * 统一异常处理
+ */
+@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();
+ }
+
+ public BadRequestException(Throwable cause) {
+ super(cause.getMessage());
+ this.status = BAD_REQUEST.value();
+ }
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/exception/ServerException.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/exception/ServerException.java
new file mode 100644
index 00000000..d7304d59
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/exception/ServerException.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.framework.exception;
+
+import lombok.Getter;
+import org.springframework.http.HttpStatus;
+import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
+
+@Getter
+public class ServerException extends RuntimeException {
+
+ private Integer status = INTERNAL_SERVER_ERROR.value();
+
+ public ServerException(String msg) {
+ super(msg);
+ }
+
+ public ServerException(HttpStatus status, String msg) {
+ super(msg);
+ this.status = status.value();
+ }
+
+ public ServerException(Throwable cause) {
+ super(cause.getMessage());
+ this.status = INTERNAL_SERVER_ERROR.value();
+ }
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/exception/handler/ApiError.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/exception/handler/ApiError.java
new file mode 100644
index 00000000..e043bbcf
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/exception/handler/ApiError.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.framework.exception.handler;
+
+import lombok.Data;
+
+@Data
+public final class ApiError {
+
+ private Integer status = 400;
+ private Long timestamp;
+ private String message;
+
+ private ApiError() {
+ timestamp = System.currentTimeMillis();
+ }
+
+ public static ApiError error(final String message) {
+ ApiError apiError = new ApiError();
+ apiError.setMessage(message);
+ return apiError;
+ }
+
+ public static ApiError error(final Integer status, final String message) {
+ ApiError apiError = new ApiError();
+ apiError.setStatus(status);
+ apiError.setMessage(message);
+ return apiError;
+ }
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/exception/handler/GlobalExceptionHandler.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/exception/handler/GlobalExceptionHandler.java
new file mode 100644
index 00000000..0326c129
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/exception/handler/GlobalExceptionHandler.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.framework.exception.handler;
+
+import cn.odboy.framework.exception.BadRequestException;
+import io.swagger.v3.oas.annotations.Hidden;
+import lombok.extern.slf4j.Slf4j;
+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;
+
+/**
+ * 全局异常处理
+ *
+ * @author odboy
+ */
+@Slf4j
+@Hidden
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+
+ /**
+ * 处理所有不可知的异常
+ */
+ @ExceptionHandler(Throwable.class)
+ public ResponseEntity handleThrowable(Throwable e) {
+ // 打印堆栈信息
+ log.error("handleThrowable", e);
+ return this.buildResponseEntity(ApiError.error(e.getMessage()));
+ }
+
+ /**
+ * 处理所有不可知的异常
+ */
+ @ExceptionHandler(Exception.class)
+ public ResponseEntity handleException(Exception e) {
+ // 打印堆栈信息
+ log.error("handleException", e);
+ return this.buildResponseEntity(ApiError.error(e.getMessage()));
+ }
+
+ /**
+ * BadCredentialsException
+ */
+ @ExceptionHandler(BadCredentialsException.class)
+ public ResponseEntity handleBadCredentialsException(BadCredentialsException e) {
+ // 打印堆栈信息
+ String message = "坏的凭证".equals(e.getMessage()) ? "用户名或密码不正确" : e.getMessage();
+ log.error(message);
+ return this.buildResponseEntity(ApiError.error(message));
+ }
+
+ /**
+ * 处理自定义异常
+ */
+ @ExceptionHandler(value = BadRequestException.class)
+ public ResponseEntity handleBadRequestException(BadRequestException e) {
+ // 打印堆栈信息
+ log.error("handleBadRequestException", e);
+ return this.buildResponseEntity(ApiError.error(e.getStatus(), e.getMessage()));
+ }
+
+ /**
+ * 处理所有接口数据验证异常
+ */
+ @ExceptionHandler(MethodArgumentNotValidException.class)
+ public ResponseEntity handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
+ // 打印堆栈信息
+ log.error("handleMethodArgumentNotValidException", e);
+ ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
+ String message = objectError.getDefaultMessage();
+ if (objectError instanceof FieldError) {
+ message = ((FieldError) objectError).getField() + ": " + message;
+ }
+ return this.buildResponseEntity(ApiError.error(message));
+ }
+
+ /**
+ * 统一返回
+ */
+ private ResponseEntity buildResponseEntity(ApiError apiError) {
+ return new ResponseEntity<>(apiError, HttpStatus.valueOf(apiError.getStatus()));
+ }
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/logging/ExecutionTimeLog.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/logging/ExecutionTimeLog.java
new file mode 100644
index 00000000..159e962f
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/logging/ExecutionTimeLog.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.framework.logging;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 统计执行时长(仅对代理方法有效)
+ *
+ * @author odboy
+ */
+@Inherited
+@Documented
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ExecutionTimeLog {
+
+ /**
+ * 接口描述
+ */
+ String description();
+
+ /**
+ * 是否展示异常日志
+ */
+ boolean showErrLog() default true;
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/logging/ExecutionTimeLogAspect.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/logging/ExecutionTimeLogAspect.java
new file mode 100644
index 00000000..eb21c8fb
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/logging/ExecutionTimeLogAspect.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.odboy.framework.logging;
+
+import cn.hutool.core.date.TimeInterval;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.springframework.stereotype.Component;
+
+/**
+ * 执行时间统计
+ *
+ * @author odboy
+ * @date 2025-05-07
+ */
+@Slf4j
+@Aspect
+@Component
+public class ExecutionTimeLogAspect {
+
+ @Around("@annotation(executionTimeLog)")
+ public Object aroundFace(ProceedingJoinPoint joinPoint, ExecutionTimeLog executionTimeLog)
+ throws Throwable {
+ TimeInterval timeInterval = new TimeInterval();
+ try {
+ Object proceed = joinPoint.proceed();
+ log.info("[{}] 接口执行成功,耗时: {} ms", executionTimeLog.description(),
+ timeInterval.intervalMs());
+ return proceed;
+ } catch (Exception e) {
+ if (executionTimeLog.showErrLog()) {
+ log.error("[{}] 接口执行失败,耗时: {} ms", executionTimeLog.description(),
+ timeInterval.intervalMs(), e);
+ }
+ throw e;
+ }
+ }
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/monitor/controller/HealthCheckController.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/monitor/controller/HealthCheckController.java
new file mode 100644
index 00000000..979db0e0
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/monitor/controller/HealthCheckController.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.odboy.framework.monitor.controller;
+
+import cn.odboy.annotation.AnonymousGetMapping;
+import cn.odboy.framework.monitor.service.KitHealthCheckPointService;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 健康检查接口
+ */
+@RestController
+@RequestMapping("/healthCheck")
+@Tag(name = "系统:健康检查接口")
+public class HealthCheckController {
+
+ @Autowired
+ private KitHealthCheckPointService kitHealthCheckPointService;
+
+ /**
+ * 就绪检查
+ */
+ @AnonymousGetMapping(value = "/readiness")
+ public ResponseEntity> doReadiness() {
+ return kitHealthCheckPointService.doReadiness();
+ }
+
+ /**
+ * 存活检查
+ */
+ @AnonymousGetMapping(value = "/liveness")
+ public ResponseEntity> doLiveness() {
+ return kitHealthCheckPointService.doLiveness();
+ }
+
+ // /**
+ // * 访问首页提示
+ // */
+ // @AnonymousGetMapping("/")
+ // public String index() {
+ // return "success";
+ // }
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/monitor/service/KitHealthCheckPointService.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/monitor/service/KitHealthCheckPointService.java
new file mode 100644
index 00000000..1cd421f6
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/monitor/service/KitHealthCheckPointService.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.odboy.framework.monitor.service;
+
+import org.springframework.http.ResponseEntity;
+
+public interface KitHealthCheckPointService {
+
+ /**
+ * 就绪检查
+ */
+ ResponseEntity> doReadiness();
+
+ /**
+ * 存活检查
+ */
+ ResponseEntity> doLiveness();
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/mybatisplus/config/DataTypeEnum.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/mybatisplus/config/DataTypeEnum.java
new file mode 100644
index 00000000..ea5f4855
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/mybatisplus/config/DataTypeEnum.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.framework.mybatisplus.config;
+
+import cn.odboy.framework.exception.BadRequestException;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+@Getter
+@Slf4j
+public enum DataTypeEnum {
+ /**
+ * mysql
+ */
+ MYSQL("mysql", "mysql", "com.mysql.jdbc.Driver", "`", "`", "'", "'"),
+ /**
+ * oracle
+ */
+ ORACLE("oracle", "oracle", "oracle.jdbc.driver.OracleDriver", "\"", "\"", "\"", "\""),
+ /**
+ * sql config
+ */
+ SQLSERVER("sqlserver", "sqlserver", "com.microsoft.sqlserver.jdbc.SQLServerDriver", "\"", "\"", "\"", "\""),
+ /**
+ * h2
+ */
+ H2("h2", "h2", "org.h2.Driver", "`", "`", "\"", "\""),
+ /**
+ * phoenix
+ */
+ PHOENIX("phoenix", "hbase phoenix", "org.apache.phoenix.jdbc.PhoenixDriver", "", "", "\"", "\""),
+ /**
+ * mongo
+ */
+ MONGODB("mongo", "mongodb", "mongodb.jdbc.MongoDriver", "`", "`", "\"", "\""),
+ /**
+ * sql4es
+ */
+ ELASTICSEARCH("sql4es", "elasticsearch", "nl.anchormen.sql4es.jdbc.ESDriver", "", "", "'", "'"),
+ /**
+ * presto
+ */
+ PRESTO("presto", "presto", "com.facebook.presto.jdbc.PrestoDriver", "", "", "\"", "\""),
+ /**
+ * moonbox
+ */
+ MOONBOX("moonbox", "moonbox", "moonbox.jdbc.MbDriver", "`", "`", "`", "`"),
+ /**
+ * cassandra
+ */
+ CASSANDRA("cassandra", "cassandra", "com.github.adejanovski.cassandra.jdbc.CassandraDriver", "", "", "'", "'"),
+ /**
+ * click house
+ */
+ CLICKHOUSE("clickhouse", "clickhouse", "ru.yandex.clickhouse.ClickHouseDriver", "", "", "\"", "\""),
+ /**
+ * kylin
+ */
+ KYLIN("kylin", "kylin", "org.apache.kylin.jdbc.Driver", "\"", "\"", "\"", "\""),
+ /**
+ * vertica
+ */
+ VERTICA("vertica", "vertica", "com.vertica.jdbc.Driver", "", "", "'", "'"),
+ /**
+ * sap
+ */
+ HANA("sap", "sap hana", "com.sap.db.jdbc.Driver", "", "", "'", "'"),
+ /**
+ * impala
+ */
+ IMPALA("impala", "impala", "com.cloudera.impala.jdbc41.Driver", "", "", "'", "'");
+ private static final String JDBC_URL_PREFIX = "jdbc:";
+ private final String feature;
+ private final String desc;
+ private final String driver;
+ private final String keywordPrefix;
+ private final String keywordSuffix;
+ private final String aliasPrefix;
+ private final String aliasSuffix;
+
+ DataTypeEnum(String feature, String desc, String driver, String keywordPrefix, String keywordSuffix,
+ String aliasPrefix, String aliasSuffix) {
+ this.feature = feature;
+ this.desc = desc;
+ this.driver = driver;
+ this.keywordPrefix = keywordPrefix;
+ this.keywordSuffix = keywordSuffix;
+ this.aliasPrefix = aliasPrefix;
+ this.aliasSuffix = aliasSuffix;
+ }
+
+ public static DataTypeEnum urlOf(String jdbcUrl) {
+ String url = jdbcUrl.toLowerCase().trim();
+ for (DataTypeEnum dataTypeEnum : values()) {
+ if (url.startsWith(JDBC_URL_PREFIX + dataTypeEnum.feature)) {
+ try {
+ Class.forName(dataTypeEnum.getDriver());
+ } catch (ClassNotFoundException e) {
+ throw new BadRequestException("Unable to get driver instance: " + jdbcUrl);
+ }
+ return dataTypeEnum;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/mybatisplus/config/KitMpQTypeEnum.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/mybatisplus/config/KitMpQTypeEnum.java
new file mode 100644
index 00000000..f015c01e
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/mybatisplus/config/KitMpQTypeEnum.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.odboy.framework.mybatisplus.config;
+
+/**
+ * 动态查询 枚举
+ */
+public enum KitMpQTypeEnum {
+ /**
+ * 相等
+ */
+ EQUAL
+ /**
+ * 大于等于
+ */
+ , GREATER_THAN
+ /**
+ * 小于等于
+ */
+ , LESS_THAN
+ /**
+ * 中模糊查询
+ */
+ , INNER_LIKE
+ /**
+ * 左模糊查询
+ */
+ , LEFT_LIKE
+ /**
+ * 右模糊查询
+ */
+ , RIGHT_LIKE
+ /**
+ * 小于
+ */
+ , LESS_THAN_NQ
+ /**
+ * 包含
+ */
+ , IN
+ /**
+ * 不包含
+ */
+ , NOT_IN
+ /**
+ * 不等于
+ */
+ , NOT_EQUAL
+ /**
+ * between(注:被注解的属性一定得是数组或者集合, 长度大于等于2)
+ */
+ , BETWEEN
+ /**
+ * 不为空
+ */
+ , NOT_NULL
+ /**
+ * 为空
+ */
+ , IS_NULL
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/mybatisplus/config/KitMpQuery.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/mybatisplus/config/KitMpQuery.java
new file mode 100644
index 00000000..e56ad5d3
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/mybatisplus/config/KitMpQuery.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.odboy.framework.mybatisplus.config;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 查询描述
+ *
+ * @date 2023-11-25
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface KitMpQuery {
+
+ /**
+ * 基本对象的属性名
+ */
+ String propName() default "";
+
+ /**
+ * 查询方式
+ */
+ KitMpQTypeEnum type() default KitMpQTypeEnum.EQUAL;
+
+ /**
+ * 多字段模糊搜索, 仅支持String类型字段, 多个用逗号隔开, 如@MpQuery(blurry = "email,username")
+ */
+ String blurry() default "";
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/mybatisplus/config/MpCustomP6SpyLogger.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/mybatisplus/config/MpCustomP6SpyLogger.java
new file mode 100644
index 00000000..7374b47a
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/mybatisplus/config/MpCustomP6SpyLogger.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.odboy.framework.mybatisplus.config;
+
+import cn.hutool.core.util.StrUtil;
+import com.p6spy.engine.spy.appender.MessageFormattingStrategy;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 自定义 p6spy sql输出格式
+ */
+@Slf4j
+public class MpCustomP6SpyLogger implements MessageFormattingStrategy {
+
+ /**
+ * 重置颜色
+ */
+ private static final String RESET = "\u001B[0m";
+ /**
+ * 红色
+ */
+ private static final String RED = "\u001B[31m";
+ /**
+ * 绿色
+ */
+ private static final String GREEN = "\u001B[32m";
+ /**
+ * 蓝色
+ */
+ private static final String BLUE = "\u001B[34m";
+
+ /**
+ * 格式化 sql
+ *
+ * @param connectionId 连接id
+ * @param now 当前时间
+ * @param elapsed 执行时长
+ * @param category sql分类
+ * @param prepared 预编译sql
+ * @param sql 执行sql
+ * @param url 数据库连接url
+ * @return 格式化后的sql
+ */
+ @Override
+ public String formatMessage(int connectionId, String now, long elapsed, String category, String prepared,
+ String sql, String url) {
+ // 去掉换行和多余空格
+ if (StrUtil.isNotBlank(sql)) {
+ sql = sql.replaceAll("\\s+", " ").trim();
+ }
+ // 格式化并加上颜色
+ return String.format("CuteJava - %s%s%s %s[%s] %sREVIEW_SQL [Interval: %dms]%s -> %s;", RED, now, RESET, GREEN,
+ Thread.currentThread().getName(), BLUE, elapsed, RESET, sql);
+ }
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/mybatisplus/config/MpInsertBatchSqlInjector.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/mybatisplus/config/MpInsertBatchSqlInjector.java
new file mode 100644
index 00000000..3c5fe44d
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/mybatisplus/config/MpInsertBatchSqlInjector.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.odboy.framework.mybatisplus.config;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.core.injector.AbstractMethod;
+import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
+import com.baomidou.mybatisplus.core.metadata.TableInfo;
+import com.baomidou.mybatisplus.extension.injector.methods.AlwaysUpdateSomeColumnById;
+import com.baomidou.mybatisplus.extension.injector.methods.InsertBatchSomeColumn;
+import java.util.List;
+import org.apache.ibatis.session.Configuration;
+
+/**
+ * 批量插入SQL注入器
+ *
+ * @author odboy
+ * @date 2025-01-11
+ */
+public class MpInsertBatchSqlInjector extends DefaultSqlInjector {
+
+ @Override
+ public List getMethodList(Configuration configuration, Class> mapperClass, TableInfo tableInfo) {
+ List methodList = super.getMethodList(configuration, mapperClass, tableInfo);
+ // 新增自定义方法, InsertBatchSomeColumn这个方法Mp没有开放出来
+ methodList.add(new InsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.UPDATE));
+ // 全量更新, 包括null字段
+ methodList.add(new AlwaysUpdateSomeColumnById(i -> i.getFieldFill() != FieldFill.INSERT));
+ return methodList;
+ }
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/mybatisplus/config/MybatisPlusConfig.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/mybatisplus/config/MybatisPlusConfig.java
new file mode 100644
index 00000000..8a43e558
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/mybatisplus/config/MybatisPlusConfig.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.odboy.framework.mybatisplus.config;
+
+import com.baomidou.mybatisplus.annotation.DbType;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+@Configuration
+@EnableTransactionManagement
+public class MybatisPlusConfig {
+
+ @Bean
+ public MybatisPlusInterceptor paginationInterceptor() {
+ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
+ // MySQL的分页拦截器
+ PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
+ paginationInnerInterceptor.setMaxLimit(500L);
+ /// 这个配置会导致:当分页没数据时, 游标从0开始,会导致无论page为什么值,就算分页超出最大页数,依然能够查询到
+ /// paginationInnerInterceptor.setOverflow(true);
+ paginationInnerInterceptor.setOptimizeJoin(true);
+ interceptor.addInnerInterceptor(paginationInnerInterceptor);
+ // 乐观锁
+ interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
+ // 防全表更新与删除
+ interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
+ return interceptor;
+ }
+
+ /**
+ * Mapper支持批量插入
+ */
+ @Bean
+ public MpInsertBatchSqlInjector insertBatchSqlInjector() {
+ return new MpInsertBatchSqlInjector();
+ }
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/mybatisplus/core/KitMpCmdGenUtil.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/mybatisplus/core/KitMpCmdGenUtil.java
new file mode 100644
index 00000000..3e219e93
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/mybatisplus/core/KitMpCmdGenUtil.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.framework.mybatisplus.core;
+
+import cn.hutool.core.util.StrUtil;
+import cn.odboy.base.KitBaseUserTimeTb;
+import cn.odboy.framework.exception.BadRequestException;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.generator.FastAutoGenerator;
+import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
+import com.baomidou.mybatisplus.generator.config.GlobalConfig;
+import com.baomidou.mybatisplus.generator.config.IDbQuery;
+import com.baomidou.mybatisplus.generator.config.OutputFile;
+import com.baomidou.mybatisplus.generator.config.PackageConfig;
+import com.baomidou.mybatisplus.generator.config.StrategyConfig;
+import com.baomidou.mybatisplus.generator.config.querys.MySqlQuery;
+import com.baomidou.mybatisplus.generator.config.rules.DateType;
+import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
+import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
+import com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine;
+import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
+import com.baomidou.mybatisplus.generator.keywords.MySqlKeyWordsHandler;
+import java.sql.Types;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Consumer;
+import lombok.Data;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 代码生成器入口
+ *
+ * @author odboy
+ * @date 2025-01-11
+ */
+@Data
+public class KitMpCmdGenUtil {
+
+ // 项目根路径。生成结果 C:\Users\Administrator\Documents\GitHub\cutejava\cutejava
+ private static final String projectRootPath = System.getProperty("user.dir") + "/";
+ /**
+ * 数据库类型 MySQL
+ */
+ private static final IDbQuery DB_QUERY = new MySqlQuery();
+ /**
+ * 类型转换
+ */
+ private static final String TYPE_TINY_INT = "tinyint";
+ private static final String TYPE_DATETIME = "datetime";
+ private static final String TYPE_DATE = "date";
+ private String databaseUrl;
+ private String databaseUsername;
+ private String databasePassword;
+ private String driverClassName;
+ private String parentPackageName = "cn.odboy";
+ private String parentModuleName;
+
+ private static Consumer getEntityConfigConsumer() {
+ return strategyConfigBuilder -> strategyConfigBuilder.entityBuilder().enableLombok()
+ // 如果不需要生成注解, 去掉.enableTableFieldAnnotation()
+ .enableTableFieldAnnotation().enableFileOverride().naming(NamingStrategy.underline_to_camel)
+ .columnNaming(NamingStrategy.underline_to_camel).idType(IdType.AUTO);
+ }
+
+ private static Consumer getStrategyConfigConsumer(List tableNames) {
+ return strategyConfigBuilder -> strategyConfigBuilder.enableCapitalMode().enableSkipView().disableSqlFilter()
+ .addInclude(tableNames)
+ // 实体类生成策略
+ .entityBuilder().superClass(KitBaseUserTimeTb.class)
+ .addIgnoreColumns("create_time", "update_time", "create_by", "update_by")
+ // 开启生成实体时生成字段注解。
+ // 会在实体类的属性前,添加[@TableField("nickname")]
+ .enableTableFieldAnnotation().disableSerialVersionUID()
+ // 阶段2:Mapper策略配置
+ .mapperBuilder()
+ // 开启 @Mapper 注解。
+ // 会在mapper接口上添加注解[@Mapper]
+ // .enableMapperAnnotation()
+ // 启用 BaseResultMap 生成。
+ // 会在mapper.xml文件生成[通用查询映射结果]配置。
+ // .enableBaseResultMap()
+ // 启用 BaseColumnList。
+ // 会在mapper.xml文件生成[通用查询结果列 ]配置
+ // .enableBaseColumnList()
+ // 阶段4:Controller策略配置
+ .controllerBuilder()
+ // 会在控制类中加[@RestController]注解。
+ .enableRestStyle()
+ // 开启驼峰转连字符
+ .enableHyphenStyle().build();
+ }
+
+ private static Consumer getGlobalConfigConsumer() {
+ String outputDir = projectRootPath + "CodeGen";
+ return globalConfigBuilder -> globalConfigBuilder.enableSwagger().outputDir(outputDir).author("codegen")
+ .commentDate("yyyy-MM-dd").dateType(DateType.TIME_PACK);
+ }
+
+ private static Consumer getStrategyConfigConsumer() {
+ return strategyConfigBuilder -> strategyConfigBuilder.mapperBuilder().mapperAnnotation(Mapper.class)
+ .formatMapperFileName("%sMapper").formatXmlFileName("%sMapper").entityBuilder().formatFileName("%sTb");
+ }
+
+ private static Consumer getDataSourceConfigConsumer() {
+ return builder -> builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
+ int typeCode = metaInfo.getJdbcType().TYPE_CODE;
+ if (typeCode == Types.SMALLINT) {
+ return DbColumnType.INTEGER;
+ }
+ if (typeCode == Types.DATE) {
+ return DbColumnType.DATE;
+ }
+ if (typeCode == Types.TIME) {
+ return DbColumnType.DATE;
+ }
+ if (typeCode == Types.TIMESTAMP) {
+ return DbColumnType.DATE;
+ }
+ return typeRegistry.getColumnType(metaInfo);
+ });
+ }
+
+ /**
+ * 代码生成函数
+ *
+ * @param moduleName 模块名称
+ * @param tablePrefix 表前缀(可空)
+ * @param tableNames 表名称, 多个用逗号分隔
+ */
+ public void gen(String moduleName, String tablePrefix, List tableNames) {
+ this.parentModuleName = moduleName;
+ // 1.数据库配置
+ DataSourceConfig.Builder dataSourceConfigBuilder =
+ new DataSourceConfig.Builder(this.databaseUrl, this.databaseUsername,
+ this.databasePassword).driverClassName(this.driverClassName);
+ dataSourceConfigBuilder.dbQuery(DB_QUERY);
+ dataSourceConfigBuilder.keyWordsHandler(new MySqlKeyWordsHandler());
+ // 1.1.快速生成器
+ FastAutoGenerator fastAutoGenerator = FastAutoGenerator.create(dataSourceConfigBuilder);
+ // 2.全局配置
+ // 自定义类型转换
+ fastAutoGenerator.dataSourceConfig(getDataSourceConfigConsumer());
+ // 覆盖已生成文件
+ // 添加swagger注解
+ // 设置注释的作者
+ // 设置注释的日期格式
+ // 使用java8新的时间类型
+ fastAutoGenerator.globalConfig(getGlobalConfigConsumer());
+ // 3.包配置
+ // 设置父包名
+ // 设置父包模块名
+ // 设置MVC下各个模块的包名
+ // 设置XML资源文件的目录
+ fastAutoGenerator.packageConfig(getPackageConfigConsumer());
+ // 4.模板配置
+ // 使用Freemarker引擎模板, 默认的是Velocity引擎模板
+ AbstractTemplateEngine templateEngine = new FreemarkerTemplateEngine();
+ fastAutoGenerator.templateEngine(templateEngine);
+ // 5.策略配置
+ // 设置需要生成的表名
+ // 设置过滤表前缀
+ fastAutoGenerator.strategyConfig(getStrategyConfigConsumer(tableNames));
+ if (tablePrefix != null) {
+ // 表前缀, 可多个
+ fastAutoGenerator.strategyConfig(
+ strategyConfigBuilder -> strategyConfigBuilder.addTablePrefix(tablePrefix));
+ }
+ // 5.1.Entity策略配置
+ // 生成实体时生成字段的注解, 包括@TableId注解等
+ // 数据库表和字段映射到实体的命名策略, 为下划线转驼峰
+ // 全局主键类型为None
+ // 实体名称格式化为XXXEntity
+ fastAutoGenerator.strategyConfig(getEntityConfigConsumer());
+ // 5.2.Controller策略配置
+ // 开启生成@RestController控制器
+ fastAutoGenerator.strategyConfig(
+ strategyConfigBuilder -> strategyConfigBuilder.controllerBuilder().enableRestStyle());
+ // 如果不需要生成Controller, fastAutoGenerator.templateConfig(templateConfig -> templateConfig.controller(""))。5.3
+ // .Service策略配置:格式化service接口和实现类的文件名称, 去掉默认的ServiceName前面的I
+ fastAutoGenerator.strategyConfig(
+ strategyConfigBuilder -> strategyConfigBuilder.serviceBuilder().formatServiceFileName("%sService")
+ .formatServiceImplFileName("%sServiceImpl"));
+ // 5.4.Mapper策略配置
+ // 格式化 mapper文件名,格式化xml实现类文件名称
+ fastAutoGenerator.strategyConfig(getStrategyConfigConsumer());
+ // 6.生成代码
+ fastAutoGenerator.execute();
+ }
+
+ private Consumer getPackageConfigConsumer() {
+ String mapperPath = projectRootPath + "CodeGen/resources/mapper";
+ if (StrUtil.isBlank(this.parentModuleName)) {
+ throw new BadRequestException("模块名称必填");
+ }
+ return packageConfigBuilder -> packageConfigBuilder.parent(this.parentPackageName)
+ .moduleName(this.parentModuleName).entity("dal.dataobject").mapper("dal.mysql").service("service")
+ .serviceImpl("service.impl").controller("controller")
+ .pathInfo(Collections.singletonMap(OutputFile.xml, mapperPath));
+ }
+}
diff --git a/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/mybatisplus/core/KitMpQUtil.java b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/mybatisplus/core/KitMpQUtil.java
new file mode 100644
index 00000000..96dbcc5b
--- /dev/null
+++ b/cutejava-springboot4/cutejava-framework/src/main/java/cn/odboy/framework/mybatisplus/core/KitMpQUtil.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2021-2026 Odboy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cn.odboy.framework.mybatisplus.core;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.odboy.base.KitBaseUserTimeTb;
+import cn.odboy.framework.exception.BadRequestException;
+import cn.odboy.framework.mybatisplus.config.KitMpQuery;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 查询条件创建工具
+ */
+@Slf4j
+public class KitMpQUtil {
+
+ public static QueryWrapper build(Q query) {
+ QueryWrapper queryWrapper = new QueryWrapper<>();
+ if (query == null) {
+ return queryWrapper;
+ }
+ Class> clazz = query.getClass();
+ try {
+ List fields = getAllFields(clazz, new ArrayList<>());
+ for (Field field : fields) {
+ if (!field.canAccess(query)) {
+ field.setAccessible(true);
+ KitMpQuery q = field.getAnnotation(KitMpQuery.class);
+ if (q != null) {
+ String attributeName = getAttributeName(q, field);
+ Object fieldVal = field.get(query);
+ if (ObjectUtil.isNull(fieldVal) || "".equals(fieldVal)) {
+ continue;
+ }
+ if (StrUtil.isNotBlank(q.blurry())) {
+ handleBlurryQuery(queryWrapper, q.blurry(), fieldVal);
+ continue;
+ }
+ handleWrapper(q, queryWrapper, attributeName, fieldVal);
+ }
+ field.setAccessible(false);
+ }
+ }
+ } catch (Exception e) {
+ log.error("Failed to build query wrapper for class: {}", clazz.getName(), e);
+ }
+ return queryWrapper;
+ }
+
+ private static String getAttributeName(KitMpQuery q, Field field) {
+ String attributeName = StrUtil.isBlank(q.propName()) ? field.getName() : q.propName();
+ return StrUtil.toUnderlineCase(attributeName);
+ }
+
+ private static void handleWrapper(KitMpQuery q, QueryWrapper queryWrapper, String attributeName,
+ Object fieldVal) {
+ switch (q.type()) {
+ case EQUAL:
+ queryWrapper.eq(attributeName, fieldVal);
+ break;
+ case GREATER_THAN:
+ queryWrapper.ge(attributeName, fieldVal);
+ break;
+ case LESS_THAN:
+ queryWrapper.le(attributeName, fieldVal);
+ break;
+ case LESS_THAN_NQ:
+ queryWrapper.lt(attributeName, fieldVal);
+ break;
+ case INNER_LIKE:
+ queryWrapper.like(attributeName, fieldVal);
+ break;
+ case LEFT_LIKE:
+ queryWrapper.likeLeft(attributeName, fieldVal);
+ break;
+ case RIGHT_LIKE:
+ queryWrapper.likeRight(attributeName, fieldVal);
+ break;
+ case IN:
+ handleInOrNotQuery(true, queryWrapper, attributeName, fieldVal);
+ break;
+ case NOT_IN:
+ handleInOrNotQuery(false, queryWrapper, attributeName, fieldVal);
+ break;
+ case NOT_EQUAL:
+ queryWrapper.ne(attributeName, fieldVal);
+ break;
+ case NOT_NULL:
+ queryWrapper.isNotNull(attributeName);
+ break;
+ case IS_NULL:
+ queryWrapper.isNull(attributeName);
+ break;
+ case BETWEEN:
+ handleBetweenQuery(queryWrapper, fieldVal, attributeName);
+ break;
+ default:
+ break;
+ }
+ }
+
+ private static void handleInOrNotQuery(boolean b, QueryWrapper queryWrapper, String attributeName,
+ Object fieldVal) {
+ Collection> wrapNotInVal = (Collection>) fieldVal;
+ if (CollUtil.isNotEmpty(wrapNotInVal)) {
+ Optional> anyValOptional = wrapNotInVal.stream().findAny();
+ if (anyValOptional.isPresent()) {
+ Object o = anyValOptional.get();
+ if (o instanceof Long) {
+ if (b) {
+ queryWrapper.in(attributeName, fieldVal);
+ } else {
+ queryWrapper.notIn(attributeName, fieldVal);
+ }
+ } else if (o instanceof Integer) {
+ if (b) {
+ queryWrapper.in(attributeName, fieldVal);
+ } else {
+ queryWrapper.notIn(attributeName, fieldVal);
+ }
+ } else {
+ throw new BadRequestException("InOrNotIn查询参数类型的对象属性值必须是Long/Integer类型集合");
+ }
+ }
+ }
+ }
+
+ private static void handleBetweenQuery(QueryWrapper queryWrapper, Object fieldVal,
+ String finalAttributeName) {
+ if (fieldVal instanceof List) {
+ List