diff --git a/.style/Java开发手册(黄山版).pdf b/.style/Java开发手册(黄山版).pdf new file mode 100644 index 0000000..6ff47d6 Binary files /dev/null and b/.style/Java开发手册(黄山版).pdf differ diff --git a/.style/p3c-codestyle.xml b/.style/p3c-codestyle.xml new file mode 100644 index 0000000..fb5f47f --- /dev/null +++ b/.style/p3c-codestyle.xml @@ -0,0 +1,380 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f50c997 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + 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: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) 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 + + (d) 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 (c) 2022-present Charles7c Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT 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/continew-admin-business/pom.xml b/continew-admin-business/pom.xml new file mode 100644 index 0000000..42d6e68 --- /dev/null +++ b/continew-admin-business/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + + top.continew + continew-admin + ${revision} + + + continew-admin-business + 业务管理模块(存放业务管理相关业务功能,例如:模型、作业等) + + + + + top.continew + continew-admin-common + + + + top.continew + continew-admin-system + + + top.continew + continew-admin-system + + + diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/enums/AssignmentsTypeEnum.java b/continew-admin-business/src/main/java/top/continew/admin/business/enums/AssignmentsTypeEnum.java new file mode 100644 index 0000000..83d00be --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/enums/AssignmentsTypeEnum.java @@ -0,0 +1,29 @@ +package top.continew.admin.business.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import top.continew.starter.core.enums.BaseEnum; + +/** + * 消息类型枚举 + * + * @author Charles7c + * @since 2023/11/2 20:08 + */ +@Getter +@RequiredArgsConstructor +public enum AssignmentsTypeEnum implements BaseEnum { + + /** + * 作业 + */ + ASSIGNMENTS(1, "作业"), + + /** + * 考试 + */ + EXAMINATION(2, "考试"),; + + private final Integer value; + private final String description; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/enums/BillingRuleEnum.java b/continew-admin-business/src/main/java/top/continew/admin/business/enums/BillingRuleEnum.java new file mode 100644 index 0000000..16c2517 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/enums/BillingRuleEnum.java @@ -0,0 +1,123 @@ +package top.continew.admin.business.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.math.BigDecimal; + +/** + * 计费规则枚举 + * + * @author jiay + * @since 2025/09/22 + */ +@Getter +@AllArgsConstructor +public enum BillingRuleEnum { + + // 生成平台-艺术类 + AI_PAINTING(10000L, "生成平台-艺术类", "AI绘画", new BigDecimal("0.13")), + AI_MUSIC(10002L, "生成平台-艺术类", "AI音乐", new BigDecimal("0.4")), + AI_VIDEO_TEXT(10003L, "生成平台-艺术类", "AI视频-文生视频", new BigDecimal("1.5")), + AI_VIDEO_IMAGE(10004L, "生成平台-艺术类", "AI视频-图生视频", new BigDecimal("1.5")), + AI_3D(10005L, "生成平台-艺术类", "AI3D", new BigDecimal("0.5")), + + // 效率工具 - AI绘画 + AI_PAINTING_GENERAL(10006L, "效率工具", "AI绘画-通用", new BigDecimal("0.3")), + AI_PAINTING_PORTRAIT(10007L, "效率工具", "AI绘画-人物肖像", new BigDecimal("0.3")), + AI_PAINTING_FLAT_DESIGN(10008L, "效率工具", "AI绘画-平面设计", new BigDecimal("0.3")), + AI_PAINTING_ART_DESIGN(10009L, "效率工具", "AI绘画-环艺设计", new BigDecimal("0.3")), + AI_PAINTING_ECOMMERCE(10010L, "效率工具", "AI绘画-电商产品", new BigDecimal("0.3")), + + // 效率工具 - AI视频 + AI_AUDIO(10011L, "效率工具", "AI音频", new BigDecimal("0.3")), + AI_VIDEO_PREPRODUCTION(10012L, "效率工具", "AI视频-前期制作", new BigDecimal("0.5")), + AI_VIDEO_SPECIAL_EFFECTS(10013L, "效率工具", "AI视频-视频特效", new BigDecimal("1")), + AI_VIDEO_PRODUCTION(10014L, "效率工具", "AI视频-视频生成", new BigDecimal("1")), + + // 效率工具 - AI3D + AI_3D_TOOL(10015L, "效率工具", "AI3D", new BigDecimal("0.5")), + + // 大语言模型 + LANGUAGE_MODEL_MULTIMODAL(10016L, "大语言模型", "多模态", new BigDecimal("0.01")), // 1000 token 扣0.01元 + + // AI写作 + AI_WRITING_IMAGE(10001L, "AI写作", "图片", new BigDecimal("1")), // KEXUE_MULTIMODAL + AI_WRITING_TEXT(10018L, "AI写作", "文本", new BigDecimal("0.01")), // 1000 token 扣0.01元 + + // deep research + DEEP_RESEARCH(10019L, "deep research", "深度研究", new BigDecimal("1")), + + // AI编程级 + AI_PROGRAMMING_MONKEY_OUTLOOK(10020L, "AI编程级", "monkey outlook", new BigDecimal("0.2")), + + // AI编程入门 + AI_PROGRAMMING_CANVAS(10021L, "AI编程入门", "canvas", new BigDecimal("0.03")), + + // 智能体开发 + AGENT_DEVELOPMENT_ADVANCED(10022L, "智能体开发", "包含高级", new BigDecimal("0.1")), + + // 教学智能体 + TEACHING_AGENT(10023L, "教学智能体", "基础", new BigDecimal("0.1")), + + // genie research + GENIE_RESEARCH(10024L, "GENIE research", "京东智能体", new BigDecimal("1")) + + ; + + /** + * 平台编码 + */ + private final Long platformId; + + /** + * 大类 + */ + private final String majorCategory; + + /** + * 分类 + */ + private final String subCategory; + + /** + * 计费金额(元) + */ + private final BigDecimal price; + + /** + * 根据平台编码获取计费规则 + * + * @param platformId 平台编码 + * @return 计费规则 + */ + public static BillingRuleEnum getByPlatformId(Long platformId) { + for (BillingRuleEnum rule : values()) { + if (rule.getPlatformId().equals(platformId)) { + return rule; + } + } + return null; + } + + /** + * 根据平台编码获取计费金额 + * + * @param platformId 平台编码 + * @return 计费金额(元) + */ + public static BigDecimal getPriceByPlatformId(Long platformId) { + BillingRuleEnum rule = getByPlatformId(platformId); + return rule != null ? rule.getPrice() : BigDecimal.ZERO; + } + + /** + * 检查平台编码是否为token计费类型 + * + * @param platformId 平台编码 + * @return 是否为token计费 + */ + public static boolean isTokenBilling(Long platformId) { + return 10016L == platformId || 10018L == platformId; + } +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/enums/ChargeTypeEnum.java b/continew-admin-business/src/main/java/top/continew/admin/business/enums/ChargeTypeEnum.java new file mode 100644 index 0000000..d6430c2 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/enums/ChargeTypeEnum.java @@ -0,0 +1,48 @@ +package top.continew.admin.business.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 平台子账号类型枚举 + * + * @author jiay + */ +@Getter +@AllArgsConstructor +public enum ChargeTypeEnum { + + CHAT(0, "TOKEN"), IMAGE(1, "图"), AUDIO(3, "音频"), VIDEO(4, "视频"), D3D(5, "3D"), TEACHER_AGENT(6, "教学智能体"), + DEEP_RESEARCH(7, "DEEP_RESEARCH"), MULTIMODAL(8, "多模态"), AI_CODE(9, "AI编程"), AGENT_DEVELOPMENT(10, "智能体开发") + + ; + + /** + * 模型平台类型 + */ + private final Integer typeId; + /** + * 描述 + */ + private final String desc; + + public static String convertDesc(Long typeId) { + ChargeTypeEnum[] values = ChargeTypeEnum.values(); + for (ChargeTypeEnum va : values) { + if (va.getTypeId().equals(typeId)) { + return va.getDesc(); + } + } + return "--"; + } + + public static Integer getType(Integer index) { + ChargeTypeEnum[] values = ChargeTypeEnum.values(); + if (index != null && index >= 0 && index < values.length) { + return values[index].getTypeId(); + } + return -1; + + } + +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/enums/PlantformBalanceEnum.java b/continew-admin-business/src/main/java/top/continew/admin/business/enums/PlantformBalanceEnum.java new file mode 100644 index 0000000..540722d --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/enums/PlantformBalanceEnum.java @@ -0,0 +1,57 @@ +package top.continew.admin.business.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 平台子账号类型枚举 + * + * @author jiay + */ +@Getter +@AllArgsConstructor +public enum PlantformBalanceEnum { + + CHAT(10000L, "大模型对话", 0), IMAGE(10001L, "图片大模型", 1), MEDIA(10002L, "视频大模型", 4),; + + /** + * 模型平台类型 + */ + private final Long plantformId; + /** + * 描述 + */ + private final String desc; + + /** + * 类型 + */ + private final Integer type; + + public static String convertDesc(Integer plantformId) { + PlantformBalanceEnum[] values = PlantformBalanceEnum.values(); + for (PlantformBalanceEnum va : values) { + if (va.getPlantformId().equals(plantformId)) { + return va.getDesc(); + } + } + return "--"; + } + + public static Integer getType(Integer index) { + PlantformBalanceEnum[] values = PlantformBalanceEnum.values(); + if (index != null && index >= 0 && index < values.length) { + return values[index].getType(); + } + return -1; + + } + + public static Long getPlantform(Integer index) { + PlantformBalanceEnum[] values = PlantformBalanceEnum.values(); + if (index != null && index >= 0 && index < values.length) { + return values[index].getPlantformId(); + } + return -1L; + } +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/enums/RoleEnum.java b/continew-admin-business/src/main/java/top/continew/admin/business/enums/RoleEnum.java new file mode 100644 index 0000000..b09b300 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/enums/RoleEnum.java @@ -0,0 +1,13 @@ +package top.continew.admin.business.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * @author 张波 + * @since 2024/10/22 14:27 + */ +@Getter +@RequiredArgsConstructor +public enum RoleEnum { +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/file/CommonMultipartFile.java b/continew-admin-business/src/main/java/top/continew/admin/business/file/CommonMultipartFile.java new file mode 100644 index 0000000..c305998 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/file/CommonMultipartFile.java @@ -0,0 +1,129 @@ +package top.continew.admin.business.file; + +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; +import org.springframework.util.FileCopyUtils; +import org.springframework.web.multipart.MultipartFile; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +/** + * @Author: jia + * @Date: 2025/05/21/15:42 + * @Description: + */ +public class CommonMultipartFile implements MultipartFile { + + private final String name; + + private final String originalFilename; + + @Nullable + private final String contentType; + + private final byte[] content; + + /** + * Create a new CommonMultipartFile with the given content. + * + * @param name the name of the file + * @param content the content of the file + */ + public CommonMultipartFile(String name, @Nullable byte[] content) { + this(name, "", null, content); + } + + /** + * Create a new CommonMultipartFile with the given content. + * + * @param name the name of the file + * @param contentStream the content of the file as stream + * @throws IOException if reading from the stream failed + */ + public CommonMultipartFile(String name, InputStream contentStream) throws IOException { + this(name, "", null, FileCopyUtils.copyToByteArray(contentStream)); + } + + /** + * Create a new CommonMultipartFile with the given content. + * + * @param name the name of the file + * @param originalFilename the original filename (as on the client's machine) + * @param contentType the content type (if known) + * @param content the content of the file + */ + public CommonMultipartFile(String name, + @Nullable String originalFilename, + @Nullable String contentType, + @Nullable byte[] content) { + + Assert.hasLength(name, "Name must not be empty"); + this.name = name; + this.originalFilename = (originalFilename != null ? originalFilename : ""); + this.contentType = contentType; + this.content = (content != null ? content : new byte[0]); + } + + /** + * Create a new CommonMultipartFile with the given content. + * + * @param name the name of the file + * @param originalFilename the original filename (as on the client's machine) + * @param contentType the content type (if known) + * @param contentStream the content of the file as stream + * @throws IOException if reading from the stream failed + */ + public CommonMultipartFile(String name, + @Nullable String originalFilename, + @Nullable String contentType, + InputStream contentStream) throws IOException { + + this(name, originalFilename, contentType, FileCopyUtils.copyToByteArray(contentStream)); + } + + @Override + public String getName() { + return this.name; + } + + @Override + @NonNull + public String getOriginalFilename() { + return this.originalFilename; + } + + @Override + @Nullable + public String getContentType() { + return this.contentType; + } + + @Override + public boolean isEmpty() { + return (this.content.length == 0); + } + + @Override + public long getSize() { + return this.content.length; + } + + @Override + public byte[] getBytes() throws IOException { + return this.content; + } + + @Override + public InputStream getInputStream() throws IOException { + return new ByteArrayInputStream(this.content); + } + + @Override + public void transferTo(File dest) throws IOException, IllegalStateException { + FileCopyUtils.copy(this.content, dest); + } +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/mapper/AssignmentSubmissionsMapper.java b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/AssignmentSubmissionsMapper.java new file mode 100644 index 0000000..7d5e35c --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/AssignmentSubmissionsMapper.java @@ -0,0 +1,13 @@ +package top.continew.admin.business.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import top.continew.admin.business.model.entity.AssignmentSubmissionsDO; + +/** + * 作业提交 Mapper + * + * @author 张波 + * @since 2024/10/22 12:02 + */ +public interface AssignmentSubmissionsMapper extends BaseMapper { +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/mapper/AssignmentsMapper.java b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/AssignmentsMapper.java new file mode 100644 index 0000000..b7c1c33 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/AssignmentsMapper.java @@ -0,0 +1,12 @@ +package top.continew.admin.business.mapper; + +import top.continew.starter.data.mp.base.BaseMapper; +import top.continew.admin.business.model.entity.AssignmentsDO; + +/** + * 作业表 Mapper + * + * @author 张波 + * @since 2024/10/22 11:45 + */ +public interface AssignmentsMapper extends BaseMapper {} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/mapper/BillingRuleMapper.java b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/BillingRuleMapper.java new file mode 100644 index 0000000..94f2045 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/BillingRuleMapper.java @@ -0,0 +1,13 @@ +package top.continew.admin.business.mapper; + +import top.continew.admin.business.model.entity.BillingRuleDO; +import top.continew.starter.data.mp.base.BaseMapper; + +/** + * 计费规则 Mapper + * + * @author jiay + * @since 2025/11/14 + */ +public interface BillingRuleMapper extends BaseMapper { +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/mapper/ConsumptionHistoryMapper.java b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/ConsumptionHistoryMapper.java new file mode 100644 index 0000000..137af90 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/ConsumptionHistoryMapper.java @@ -0,0 +1,12 @@ +package top.continew.admin.business.mapper; + +import top.continew.starter.data.mp.base.BaseMapper; +import top.continew.admin.business.model.entity.ConsumptionHistoryDO; + +/** + * 用户充值消费历史记录 Mapper + * + * @author jiay + * @since 2025/05/23 07:51 + */ +public interface ConsumptionHistoryMapper extends BaseMapper {} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/mapper/CopyrightMapper.java b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/CopyrightMapper.java new file mode 100644 index 0000000..63858b9 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/CopyrightMapper.java @@ -0,0 +1,16 @@ +package top.continew.admin.business.mapper; + +import org.apache.ibatis.annotations.Mapper; +import top.continew.admin.business.model.entity.Copyright; +import top.continew.starter.data.mp.base.BaseMapper; + +/** + * 数字人-文案 Mapper 接口 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Mapper +public interface CopyrightMapper extends BaseMapper { + // 可根据需要添加额外的查询方法 +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/mapper/CourseUserMapper.java b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/CourseUserMapper.java new file mode 100644 index 0000000..0446914 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/CourseUserMapper.java @@ -0,0 +1,11 @@ +package top.continew.admin.business.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import top.continew.admin.business.model.entity.CourseUserDO; + +/** + * @author 张波 + * @since 2024/10/22 12:19 + */ +public interface CourseUserMapper extends BaseMapper { +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/mapper/CoursesMapper.java b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/CoursesMapper.java new file mode 100644 index 0000000..9cccf44 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/CoursesMapper.java @@ -0,0 +1,12 @@ +package top.continew.admin.business.mapper; + +import top.continew.starter.data.mp.base.BaseMapper; +import top.continew.admin.business.model.entity.CoursesDO; + +/** + * 课程 Mapper + * + * @author 张波 + * @since 2024/10/22 11:58 + */ +public interface CoursesMapper extends BaseMapper {} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/mapper/DeptModelMapper.java b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/DeptModelMapper.java new file mode 100644 index 0000000..1d4e7f9 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/DeptModelMapper.java @@ -0,0 +1,13 @@ +package top.continew.admin.business.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import top.continew.admin.business.model.entity.DeptModelDO; + +/** + * 部门与模型绑定 Mapper + * + * @author 张波 + * @since 2024/10/20 14:26 + */ +public interface DeptModelMapper extends BaseMapper { +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/mapper/DeptPlatformMapper.java b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/DeptPlatformMapper.java new file mode 100644 index 0000000..c091617 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/DeptPlatformMapper.java @@ -0,0 +1,13 @@ +package top.continew.admin.business.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import top.continew.admin.business.model.entity.DeptPlatformDO; + +/** + * 部门与操作平台绑定 Mapper + * + * @author 张波 + * @since 2024/10/20 14:42 + */ +public interface DeptPlatformMapper extends BaseMapper { +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/mapper/DigitalHumanMapper.java b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/DigitalHumanMapper.java new file mode 100644 index 0000000..c6d8a09 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/DigitalHumanMapper.java @@ -0,0 +1,16 @@ +package top.continew.admin.business.mapper; + +import top.continew.starter.data.mp.base.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import top.continew.admin.business.model.entity.DigitalHuman; + +/** + * 数字人Mapper接口 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Mapper +public interface DigitalHumanMapper extends BaseMapper { + // 可根据需要添加额外的数据库操作方法 +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/mapper/EffectMapper.java b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/EffectMapper.java new file mode 100644 index 0000000..598a684 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/EffectMapper.java @@ -0,0 +1,16 @@ +package top.continew.admin.business.mapper; + +import top.continew.starter.data.mp.base.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import top.continew.admin.business.model.entity.Effect; + +/** + * 数字人-特效Mapper接口 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Mapper +public interface EffectMapper extends BaseMapper { + // 可根据需要添加额外的数据库操作方法 +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/mapper/EffectTemplateMapper.java b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/EffectTemplateMapper.java new file mode 100644 index 0000000..af838e1 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/EffectTemplateMapper.java @@ -0,0 +1,16 @@ +package top.continew.admin.business.mapper; + +import top.continew.starter.data.mp.base.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import top.continew.admin.business.model.entity.EffectTemplate; + +/** + * 数字人-特效模板Mapper接口 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Mapper +public interface EffectTemplateMapper extends BaseMapper { + // 可根据需要添加额外的数据库操作方法 +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/mapper/FreeQuotaConfigMapper.java b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/FreeQuotaConfigMapper.java new file mode 100644 index 0000000..92f3be6 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/FreeQuotaConfigMapper.java @@ -0,0 +1,13 @@ +package top.continew.admin.business.mapper; + +import top.continew.admin.business.model.entity.FreeQuotaConfigDO; +import top.continew.starter.data.mp.base.BaseMapper; + +/** + * 免费额度配置 Mapper + * + * @author jiay + * @since 2025/11/17 + */ +public interface FreeQuotaConfigMapper extends BaseMapper { +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/mapper/HomeworkMapper.java b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/HomeworkMapper.java new file mode 100644 index 0000000..0ec538f --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/HomeworkMapper.java @@ -0,0 +1,12 @@ +package top.continew.admin.business.mapper; + +import top.continew.starter.data.mp.base.BaseMapper; +import top.continew.admin.business.model.entity.HomeworkDO; + +/** + * business Mapper + * + * @author jiay + * @since 2025/06/26 10:24 + */ +public interface HomeworkMapper extends BaseMapper {} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/mapper/HuandaResourceMapper.java b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/HuandaResourceMapper.java new file mode 100644 index 0000000..e2e43c2 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/HuandaResourceMapper.java @@ -0,0 +1,14 @@ +package top.continew.admin.business.mapper; + +import org.apache.ibatis.annotations.Mapper; +import top.continew.admin.business.model.entity.HuandaResourceDO; +import top.continew.starter.data.mp.base.BaseMapper; + +/** + * 换哒-文件资源 Mapper + * + * @author 王志维 + * @since 2025/12/23 16:53 + */ +@Mapper +public interface HuandaResourceMapper extends BaseMapper {} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/mapper/ModelCategoryMapper.java b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/ModelCategoryMapper.java new file mode 100644 index 0000000..871f988 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/ModelCategoryMapper.java @@ -0,0 +1,13 @@ +package top.continew.admin.business.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import top.continew.admin.business.model.entity.ModelCategoryDO; + +/** + * 模型分类Mapper + * + * @author 张波 + * @since 2024/10/20 19:50 + */ +public interface ModelCategoryMapper extends BaseMapper { +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/mapper/ModelMapper.java b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/ModelMapper.java new file mode 100644 index 0000000..5c31386 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/ModelMapper.java @@ -0,0 +1,13 @@ +package top.continew.admin.business.mapper; + +import top.continew.admin.business.model.entity.ModelDO; +import top.continew.starter.data.mp.base.BaseMapper; + +/** + * 模型 Mapper + * + * @author 张波 + * @since 2024/10/19 15:51 + */ +public interface ModelMapper extends BaseMapper { +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/mapper/OrderAudioMapper.java b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/OrderAudioMapper.java new file mode 100644 index 0000000..28df33a --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/OrderAudioMapper.java @@ -0,0 +1,13 @@ +package top.continew.admin.business.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import top.continew.admin.business.model.entity.OrderAudioDo; + +/** + * 音频 Mapper + * + * @author hzy + * @since 2024/10/22 11:58 + */ +public interface OrderAudioMapper extends BaseMapper { +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/mapper/OrderHistoryMapper.java b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/OrderHistoryMapper.java new file mode 100644 index 0000000..35d2f31 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/OrderHistoryMapper.java @@ -0,0 +1,13 @@ +package top.continew.admin.business.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import top.continew.admin.business.model.entity.OrderHistoryDo; + +/** + * 点数历史记录 Mapper + * + * @author hzy + * @since 2024/10/22 11:58 + */ +public interface OrderHistoryMapper extends BaseMapper { +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/mapper/OrderThreeMapper.java b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/OrderThreeMapper.java new file mode 100644 index 0000000..04fc20c --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/OrderThreeMapper.java @@ -0,0 +1,13 @@ +package top.continew.admin.business.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import top.continew.admin.business.model.entity.OrderThreeDo; + +/** + * 3D平台 Mapper + * + * @author hzy + * @since 2024/10/22 11:58 + */ +public interface OrderThreeMapper extends BaseMapper { +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/mapper/OrderVideoMapper.java b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/OrderVideoMapper.java new file mode 100644 index 0000000..7de0e27 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/OrderVideoMapper.java @@ -0,0 +1,15 @@ +package top.continew.admin.business.mapper; + +import top.continew.starter.data.mp.base.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import top.continew.admin.business.model.entity.OrderVideoDo; + +/** + * 视频 Mapper + * + * @author hzy + * @since 2024/10/22 11:58 + */ +@Mapper +public interface OrderVideoMapper extends BaseMapper { +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/mapper/PlantformBalanceMapper.java b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/PlantformBalanceMapper.java new file mode 100644 index 0000000..fa1d4db --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/PlantformBalanceMapper.java @@ -0,0 +1,12 @@ +package top.continew.admin.business.mapper; + +import top.continew.starter.data.mp.base.BaseMapper; +import top.continew.admin.business.model.entity.PlantformBalanceDO; + +/** + * business Mapper + * + * @author jiay + * @since 2025/06/24 16:47 + */ +public interface PlantformBalanceMapper extends BaseMapper {} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/mapper/ScoreMapper.java b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/ScoreMapper.java new file mode 100644 index 0000000..c59193a --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/ScoreMapper.java @@ -0,0 +1,12 @@ +package top.continew.admin.business.mapper; + +import top.continew.starter.data.mp.base.BaseMapper; +import top.continew.admin.business.model.entity.ScoreDO; + +/** + * business Mapper + * + * @author jiay + * @since 2025/06/26 10:24 + */ +public interface ScoreMapper extends BaseMapper {} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/mapper/SignInMapper.java b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/SignInMapper.java new file mode 100644 index 0000000..ac2f464 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/SignInMapper.java @@ -0,0 +1,12 @@ +package top.continew.admin.business.mapper; + +import top.continew.starter.data.mp.base.BaseMapper; +import top.continew.admin.business.model.entity.SignInDO; + +/** + * business Mapper + * + * @author jiay + * @since 2025/06/26 10:24 + */ +public interface SignInMapper extends BaseMapper {} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/mapper/SubtitleItemMapper.java b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/SubtitleItemMapper.java new file mode 100644 index 0000000..055ed7d --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/SubtitleItemMapper.java @@ -0,0 +1,16 @@ +package top.continew.admin.business.mapper; + +import top.continew.starter.data.mp.base.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import top.continew.admin.business.model.entity.SubtitleItem; + +/** + * 数字人-字幕项Mapper接口 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Mapper +public interface SubtitleItemMapper extends BaseMapper { + // 可根据需要添加额外的数据库操作方法 +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/mapper/SubtitleStyleMapper.java b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/SubtitleStyleMapper.java new file mode 100644 index 0000000..1998181 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/SubtitleStyleMapper.java @@ -0,0 +1,16 @@ +package top.continew.admin.business.mapper; + +import top.continew.starter.data.mp.base.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import top.continew.admin.business.model.entity.SubtitleStyle; + +/** + * 数字人-字幕样式Mapper接口 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Mapper +public interface SubtitleStyleMapper extends BaseMapper { + // 可根据需要添加额外的数据库操作方法 +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/mapper/TaskRecordHistoryMapper.java b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/TaskRecordHistoryMapper.java new file mode 100644 index 0000000..c12d23c --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/mapper/TaskRecordHistoryMapper.java @@ -0,0 +1,12 @@ +package top.continew.admin.business.mapper; + +import top.continew.starter.data.mp.base.BaseMapper; +import top.continew.admin.business.model.entity.TaskRecordHistoryDO; + +/** + * 音视图文生成记录 Mapper + * + * @author jiay + * @since 2025/05/23 07:51 + */ +public interface TaskRecordHistoryMapper extends BaseMapper {} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/AssignmentSubmissionsDO.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/AssignmentSubmissionsDO.java new file mode 100644 index 0000000..c0db9b3 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/AssignmentSubmissionsDO.java @@ -0,0 +1,71 @@ +package top.continew.admin.business.model.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import top.continew.starter.extension.crud.model.entity.BaseIdDO; + +import java.io.Serial; +import java.time.LocalDateTime; + +/** + * 作业提交实体 + * + * @author 张波 + * @since 2024/10/22 12:01 + */ +@Data +@TableName("assignment_submissions") +public class AssignmentSubmissionsDO extends BaseIdDO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 作业ID + */ + private Long assignmentId; + + /** + * 学生ID + */ + private Long userId; + + /** + * 图片标题 + */ + private String imageTitle; + + /** + * 图片标签 + */ + private String imageTag; + + /** + * 图片地址 + */ + private String imageUrl; + + /** + * 分数 + */ + private Integer score; + + /** + * 评语 + */ + private String feedback; + + /** + * 创建人 + */ + @TableField(fill = FieldFill.INSERT) + private Long createUser; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/AssignmentsDO.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/AssignmentsDO.java new file mode 100644 index 0000000..8aa485a --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/AssignmentsDO.java @@ -0,0 +1,77 @@ +package top.continew.admin.business.model.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import top.continew.admin.business.enums.AssignmentsTypeEnum; +import top.continew.starter.extension.crud.model.entity.BaseIdDO; + +import java.io.Serial; +import java.time.LocalDateTime; + +/** + * 作业表实体 + * + * @author 张波 + * @since 2024/10/22 11:44 + */ +@Data +@TableName("assignments") +public class AssignmentsDO extends BaseIdDO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 课程表ID + */ + private Long courseId; + + /** + * 分类(1:作业,2:考试) + */ + private AssignmentsTypeEnum type; + + /** + * 作业名称 + */ + private String name; + + /** + * 作业要求 + */ + private String requirements; + + /** + * 满分 + */ + private Integer fullScore; + + /** + * 开始时间 + */ + private LocalDateTime startTime; + + /** + * 结束时间 + */ + private LocalDateTime endTime; + + /** + * 示例图片 + */ + private String exampleImages; + + /** + * 创建人 + */ + @TableField(fill = FieldFill.INSERT) + private Long createUser; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/BillingRuleDO.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/BillingRuleDO.java new file mode 100644 index 0000000..7d33b07 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/BillingRuleDO.java @@ -0,0 +1,81 @@ +package top.continew.admin.business.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import top.continew.starter.extension.crud.model.entity.BaseDO; + +import java.io.Serial; + +/** + * 计费规则实体 + * + * @author jiay + * @since 2025/11/14 + */ +@Data +@TableName("t_billing_rule") +public class BillingRuleDO extends BaseDO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 平台名称 + */ + private String platformName; + + /** + * 账户类型 + */ + private String type; + + /** + * 平台ID + */ + private Long platformId; + + /** + * 平台编码 + */ + private String platformCode; + + /** + * 扣费编码 + */ + private String chargeCode; + + /** + * 计费类型 + */ + private Integer chargeType; + + /** + * 模型名称 + */ + private String modelName; + + /** + * 主账户扣减单价(分/次/整数量) + */ + private Long price; + + /** + * 子账户单价(次/个/数量) + */ + private Long subAccountPrice; + + /** + * 计费单位 + */ + private String unit; + + /** + * 备注 + */ + private String remark; + + /** + * 是否启用(0:禁用 1:启用) + */ + private Integer enabled; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/ConsumptionHistoryDO.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/ConsumptionHistoryDO.java new file mode 100644 index 0000000..19d8114 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/ConsumptionHistoryDO.java @@ -0,0 +1,58 @@ +package top.continew.admin.business.model.entity; + +import lombok.Data; + +import com.baomidou.mybatisplus.annotation.TableName; +import top.continew.starter.extension.crud.model.entity.BaseDO; + +import java.io.Serial; + +/** + * 用户充值消费历史记录实体 + * + * @author jiay + * @since 2025/05/23 07:51 + */ +@Data +@TableName("t_consumption_history") +public class ConsumptionHistoryDO extends BaseDO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + private Long userId; + + /** + * 生成平台 + */ + private Long platformId; + + /** + * 任务类型(token:0, 文生图:1,图生图:2,音效生成:3,参考生视频:4, 白膜贴图:5) + */ + private Integer taskType; + + /** + * 点数量 + */ + private Long chargePoints; + + /** + * 剩余的点数 + */ + private Long remainingPoints; + + /** + * 记录类型(0充点 1 次数 2 点数) + */ + private Integer type; + + /** + * 子账号类型(0主账号,1子账号) + */ + private Integer accountType; + +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/Copyright.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/Copyright.java new file mode 100644 index 0000000..b3b672a --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/Copyright.java @@ -0,0 +1,36 @@ +package top.continew.admin.business.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.starter.extension.crud.model.entity.BaseDO; + +import java.time.LocalDateTime; + +/** + * 数字人-文案实体类 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("dh_copyright") +public class Copyright extends BaseDO { + + /** + * 文案 ID + */ + private Long id; + + /** + * 用户 ID + */ + private String userId; + + /** + * 文案内容 + */ + private String text; + +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/CourseUserDO.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/CourseUserDO.java new file mode 100644 index 0000000..5a7ab9b --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/CourseUserDO.java @@ -0,0 +1,45 @@ +package top.continew.admin.business.model.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import top.continew.starter.extension.crud.model.entity.BaseIdDO; + +import java.io.Serial; +import java.time.LocalDateTime; + +/** + * 课程关联学生实体 + * + * @author 张波 + * @since 2024/10/22 12:14 + */ +@Data +@TableName("course_user") +public class CourseUserDO extends BaseIdDO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 课程ID + */ + private Long courseId; + + /** + * 学生ID + */ + private Long userId; + + /** + * 学生姓名 + */ + private String nickname; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/CoursesDO.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/CoursesDO.java new file mode 100644 index 0000000..c0c91dc --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/CoursesDO.java @@ -0,0 +1,66 @@ +package top.continew.admin.business.model.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import top.continew.starter.extension.crud.model.entity.BaseIdDO; + +import java.io.Serial; +import java.time.LocalDateTime; + +/** + * 课程实体 + * + * @author 张波 + * @since 2024/10/22 11:58 + */ +@Data +@TableName("courses") +public class CoursesDO extends BaseIdDO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 课程名称 + */ + private String name; + + /** + * 描述 + */ + private String description; + + /** + * 班级 + */ + private String className; + + /** + * 封面 + */ + private String coverImage; + + /** + * 开始时间 + */ + private LocalDateTime startTime; + + /** + * 结束时间 + */ + private LocalDateTime endTime; + + /** + * 创建人 + */ + @TableField(fill = FieldFill.INSERT) + private Long createUser; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/DeptModelDO.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/DeptModelDO.java new file mode 100644 index 0000000..9b452b3 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/DeptModelDO.java @@ -0,0 +1,31 @@ +package top.continew.admin.business.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import top.continew.starter.extension.crud.model.entity.BaseIdDO; + +import java.io.Serial; + +/** + * 部门与模型绑定实体类 + * + * @author 张波 + * @since 2024/10/20 14:24 + */ +@Data +@TableName("dept_model") +public class DeptModelDO extends BaseIdDO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 模型ID + */ + private Integer modelId; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/DeptPlatformDO.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/DeptPlatformDO.java new file mode 100644 index 0000000..114adc6 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/DeptPlatformDO.java @@ -0,0 +1,26 @@ +package top.continew.admin.business.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import top.continew.starter.extension.crud.model.entity.BaseIdDO; + +/** + * 部门与操作平台绑定实体类 + * + * @author 张波 + * @since 2024/10/20 14:34 + */ +@Data +@TableName("dept_platform") +public class DeptPlatformDO extends BaseIdDO { + + /** + * 部门ID + */ + private Long deptId; + + /** + * 操作平台 + */ + private String platform; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/DigitalHuman.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/DigitalHuman.java new file mode 100644 index 0000000..40895ed --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/DigitalHuman.java @@ -0,0 +1,74 @@ +package top.continew.admin.business.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.starter.extension.crud.model.entity.BaseDO; + +/** + * 数字人实体类 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("dh_digital_human") +public class DigitalHuman extends BaseDO { + + /** + * 数字人唯一标识符 + */ + private Long id; + + /** + * 创建数字人的用户ID + */ + private String userId; + + /** + * 用户输入的文案提示 + */ + private String copywriting; + + /** + * 生成语音文件的URL + */ + private String generatedVoiceUrl; + + /** + * 生成帧图像URL(或空字符串) + */ + private String generatedFrameImageUrl; + + /** + * 最终合成视频URL + */ + private String generatedVideoUrl; + + /** + * 字幕项列表(冗余存储,用于快速回显) + */ + private String subtitle; + + /** + * 字幕样式配置(冗余或快照) + */ + private String subtitleStyle; + + /** + * 特效列表(冗余存储) + */ + private String effects; + + /** + * 创建时间 + */ + private java.time.LocalDateTime createTime; + + /** + * 更新时间 + */ + private java.time.LocalDateTime updateTime; + +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/Effect.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/Effect.java new file mode 100644 index 0000000..1063a5b --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/Effect.java @@ -0,0 +1,113 @@ +package top.continew.admin.business.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.starter.extension.crud.model.entity.BaseDO; + +/** + * 数字人-特效实体类 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("dh_effect") +public class Effect extends BaseDO { + + /** + * 特效ID(通常来自模板或任务生成) + */ + private Long id; + + /** + * 特效名称 + */ + private String name; + + /** + * 特效模板ID + */ + private Long templateId; + + /** + * 关联的数字人任务ID + */ + private Long digitalHumanId; + + /** + * 特效类型('text','image','music','audio') + */ + private String type; + + /** + * 特效资源URL + */ + private String sourceUrl; + + /** + * 特效显示位置X坐标(像素) + */ + private Integer positionX; + + /** + * 特效显示位置Y坐标(像素) + */ + private Integer positionY; + + /** + * 特效宽度(像素) + */ + private Integer sizeWidth; + + /** + * 特效高度(像素) + */ + private Integer sizeHeight; + + /** + * 特效最大持续时间(秒) + */ + private Double length; + + /** + * 特效文本内容(仅文本类型有效) + */ + private String subtitleText; + + /** + * 字幕样式配置(JSON格式) + */ + private String subtitleStyle; + + /** + * 字幕中心位置({x, y}) + */ + private String subtitlePosition; + + /** + * 特效开始偏移时间(秒) + */ + private Double offset; + + /** + * 特效播放时间段({"start": x, "end": y}) + */ + private String duration; + + /** + * 渲染层级(Z轴顺序) + */ + private Integer zIndex; + + /** + * 创建时间 + */ + private java.time.LocalDateTime createTime; + + /** + * 更新时间 + */ + private java.time.LocalDateTime updateTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/EffectTemplate.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/EffectTemplate.java new file mode 100644 index 0000000..21451e4 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/EffectTemplate.java @@ -0,0 +1,95 @@ +package top.continew.admin.business.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.starter.extension.crud.model.entity.BaseDO; + +/** + * 数字人-特效模板实体类 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("dh_effect_template") +public class EffectTemplate extends BaseDO { + + /** + * 特效模板ID + */ + private Long id; + + /** + * 模板名称 + */ + private String name; + + /** + * 是否为用户自定义特效(true/false) + */ + private String isCustom; + + /** + * 上传自定义特效的用户ID(目前仅支持音乐) + */ + private String userId; + + /** + * 特效类型 + */ + private String type; + + /** + * 默认资源URL + */ + private String sourceUrl; + + /** + * 默认尺寸({"width": w, "height": h}) + */ + private String size_width; + + private String size_height; + + /** + * 默认持续时间(秒),0表示无限 + */ + private Double length; + + /** + * 默认字幕文本(仅文本类型) + */ + private String subtitleText; + + /** + * 关联的字幕样式ID + */ + private Long subtitleStyleId; + + /** + * 字幕默认X位置(可为固定值或表达式) + */ + private String subtitlePositionX; + + /** + * 字幕默认Y位置 + */ + private String subtitlePositionY; + + /** + * 封面图URL + */ + private String coverUrl; + + /** + * 创建时间 + */ + private java.time.LocalDateTime createTime; + + /** + * 更新时间 + */ + private java.time.LocalDateTime updateTime; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/FreeQuotaConfigDO.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/FreeQuotaConfigDO.java new file mode 100644 index 0000000..00b942a --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/FreeQuotaConfigDO.java @@ -0,0 +1,67 @@ +package top.continew.admin.business.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import top.continew.starter.extension.crud.model.entity.BaseDO; + +import java.io.Serial; +import java.math.BigDecimal; + +/** + * 免费额度配置实体 + * + * @author jiay + * @since 2025/11/17 + */ +@Data +@TableName("t_free_quota_config") +public class FreeQuotaConfigDO extends BaseDO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 类别编码 + */ + private String categoryCode; + + /** + * 类别名称 + */ + private String categoryName; + + /** + * 单位 + */ + private String unit; + + /** + * 额度 + */ + private BigDecimal quota; + + /** + * 计费类型(对应ChargeTypeEnum) + */ + private Integer chargeType; + + /** + * 账户类型 1:次数 2:点数 + */ + private Integer type; + + /** + * 描述 + */ + private String description; + + /** + * 排序 + */ + private Integer sort; + + /** + * 是否启用 0:禁用 1:启用 + */ + private Boolean isEnabled; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/HomeworkDO.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/HomeworkDO.java new file mode 100644 index 0000000..ee8209c --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/HomeworkDO.java @@ -0,0 +1,41 @@ +package top.continew.admin.business.model.entity; + +import lombok.Data; + +import com.baomidou.mybatisplus.annotation.TableName; +import top.continew.starter.extension.crud.model.entity.BaseDO; +import java.io.Serial; + +/** + * 作业实体 + * + * @author jiay + * @since 2025/06/26 10:24 + */ +@Data +@TableName("t_homework") +public class HomeworkDO extends BaseDO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + private Long userId; + + /** + * 课程id + */ + private Long courseId; + + /** + * 未交原因 + */ + private String cause; + + /** + * 1、已交 2、未交 + */ + private Integer handedIn; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/HuandaResourceDO.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/HuandaResourceDO.java new file mode 100644 index 0000000..bf7c786 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/HuandaResourceDO.java @@ -0,0 +1,61 @@ +package top.continew.admin.business.model.entity; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.annotation.TableField; +import top.continew.starter.extension.crud.model.entity.BaseDO; + +/** + * 换哒-文件资源实体 + * + * @author 王志维 + * @since 2025/12/23 16:53 + */ +@Data +@TableName("t_huanda_resource") +public class HuandaResourceDO extends BaseDO { + + @TableId(type = IdType.AUTO) + private Long id; + + /** + * 用户ID + */ + private String userId; + + /** + * 资源地址 + */ + private String url; + + /** + * 资源类型:1.服装,2模特,3自定义服装,4.自定义背景 + */ + private Integer resourceType; + + @Schema(description = "人物图片年龄") + private String age; + + @Schema(description = "人物图片性别:男,女") + private String gender; + + @Schema(description = "人物图片区域") + private String region; + + // 显式忽略审计字段,防止插入时出错 + @TableField(exist = false) + private Long createUser; + + @TableField(exist = false) + private java.time.LocalDateTime createTime; + + @TableField(exist = false) + private Long updateUser; + + @TableField(exist = false) + private java.time.LocalDateTime updateTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/ModelCategoryDO.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/ModelCategoryDO.java new file mode 100644 index 0000000..4ba1f6a --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/ModelCategoryDO.java @@ -0,0 +1,36 @@ +package top.continew.admin.business.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import top.continew.starter.extension.crud.model.entity.BaseIdDO; + +import java.io.Serial; + +/** + * 模型分类表 + * + * @author 张波 + * @since 2024/10/20 19:42 + */ +@Data +@TableName("model_category") +public class ModelCategoryDO extends BaseIdDO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 名称 + */ + private String name; + + /** + * 上级ID + */ + private Long parentId; + + /** + * 类型 + */ + private String type; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/ModelDO.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/ModelDO.java new file mode 100644 index 0000000..73cd706 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/ModelDO.java @@ -0,0 +1,93 @@ +package top.continew.admin.business.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.starter.extension.crud.model.entity.BaseIdDO; + +import java.io.Serial; + +/** + * 模型实体类 + * + * @author 张波 + * @since 2024/10/19 15:11 + */ +@Data +@TableName(value = "model") +public class ModelDO extends BaseIdDO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 模型名称 + */ + private String name; + + /** + * 平台分类 + */ + private String platformCategory; + + /** + * 类型 + */ + private String type; + + /** + * 版本 + */ + private String version; + + /** + * 分类 + */ + private String category; + + /** + * 专业分类 + */ + private String professionalCategory; + + /** + * 系列 + */ + private String series; + + /** + * 轮播图信息 + */ + private String images; + + /** + * 推荐分辨率 + */ + private String recommendedResolution; + + /** + * 提示词是否开启 + */ + private DisEnableStatusEnum promptEnabled; + + /** + * 采样方法 + */ + private String samplingMethod; + + /** + * CFG + */ + private Integer cfg; + + /** + * VAE + */ + private String vae; + + /** + * 采样步数 + */ + private Integer samplingSteps; + +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/OrderAudioDo.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/OrderAudioDo.java new file mode 100644 index 0000000..0bb713e --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/OrderAudioDo.java @@ -0,0 +1,49 @@ +package top.continew.admin.business.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import top.continew.starter.extension.crud.model.entity.BaseDO; + +import java.io.Serial; + +/** + * 音频平台任务订单表 + * + * @author hzy + * @since 2024/10/22 11:58 + */ +@Data +@TableName("order_audio") +public class OrderAudioDo extends BaseDO { + + @Serial + private static final long serialVersionUID = 1L; + /** + * 状态 + */ + private String state; + /** + * 删除标示 + */ + private String deleteFlag; + /** + * 任务类型 + */ + private String type; + /** + * 使用的模型 + */ + private String model; + /** + * 消耗点数 + */ + private Integer inviteBeans; + /** + * 存储结果文件路径 + */ + private String fileUrl; + /** + * 订单 类型 + */ + private Integer orderType; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/OrderHistoryDo.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/OrderHistoryDo.java new file mode 100644 index 0000000..1e81177 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/OrderHistoryDo.java @@ -0,0 +1,53 @@ +package top.continew.admin.business.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import top.continew.starter.extension.crud.model.entity.BaseDO; + +import java.io.Serial; + +/** + * 平台任务订单历史表 + * + * @author hzy + * @since 2024/10/22 11:58 + */ +@Data +@TableName("order_history") +public class OrderHistoryDo extends BaseDO { + + @Serial + private static final long serialVersionUID = 1L; + /** + * 状态 + */ + private String state; + /** + * 删除标示 + */ + private String deleteFlag; + /** + * 任务类型 + */ + private String type; + /** + * 使用的模型 + */ + private String model; + /** + * 消耗点数 + */ + private Integer inviteBeans; + /** + * 剩余的点数 + */ + private Integer remainder; + /** + * 记录类型:支出;增加 + */ + private String formType; + /** + * 生成平台 + */ + private String platform; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/OrderThreeDo.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/OrderThreeDo.java new file mode 100644 index 0000000..c2580bf --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/OrderThreeDo.java @@ -0,0 +1,49 @@ +package top.continew.admin.business.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import top.continew.starter.extension.crud.model.entity.BaseDO; + +import java.io.Serial; + +/** + * 3D平台任务订单表 + * + * @author hzy + * @since 2024/10/22 11:58 + */ +@Data +@TableName("order_three") +public class OrderThreeDo extends BaseDO { + + @Serial + private static final long serialVersionUID = 1L; + /** + * 状态 + */ + private String state; + /** + * 删除标示 + */ + private String deleteFlag; + /** + * 任务类型 + */ + private String type; + /** + * 使用的模型 + */ + private String model; + /** + * 消耗点数 + */ + private Integer inviteBeans; + /** + * 存储结果文件路径 + */ + private String fileUrl; + /** + * 订单 类型 + */ + private Integer orderType; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/OrderVideoDo.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/OrderVideoDo.java new file mode 100644 index 0000000..b74ef50 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/OrderVideoDo.java @@ -0,0 +1,48 @@ +package top.continew.admin.business.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serial; + +/** + * 视频平台任务订单表 + * + * @author hzy + * @since 2024/10/22 11:58 + */ +@Data +@TableName("order_video") +public class OrderVideoDo { + + @Serial + private static final long serialVersionUID = 1L; + /** + * 状态 + */ + private String state; + /** + * 删除标示 + */ + private String deleteFlag; + /** + * 任务类型 + */ + private String type; + /** + * 使用的模型 + */ + private String model; + /** + * 消耗点数 + */ + private Integer inviteBeans; + /** + * 存储结果文件路径 + */ + private String fileUrl; + /** + * 订单 类型 + */ + private Integer orderType; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/PlantformBalanceDO.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/PlantformBalanceDO.java new file mode 100644 index 0000000..242e487 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/PlantformBalanceDO.java @@ -0,0 +1,58 @@ +package top.continew.admin.business.model.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import lombok.Data; + +import com.baomidou.mybatisplus.annotation.TableName; +import top.continew.starter.extension.crud.model.entity.BaseIdDO; + +import java.time.LocalDateTime; + +/** + * 用户子账号余额表实体 + * + * @author jiay + * @since 2025/06/24 16:47 + */ +@Data +@TableName("t_plantform_balance") +public class PlantformBalanceDO extends BaseIdDO { + + /** + * 用户id + */ + private Long userId; + + /** + * 账户类型 1 次数 2 点数 + */ + private Integer type; + + /** + * 余额 + */ + private Long balance; + + /** + * 计费类型(token:0, 图:1, 音频:3, 视频:4, 白膜贴图/3D:5) + */ + private Integer chargeType; + + /** + * 修改人 + */ + @TableField(fill = FieldFill.UPDATE) + private Long updateUser; + + /** + * 修改时间 + */ + @TableField(fill = FieldFill.UPDATE) + private LocalDateTime updateTime; + + /** + * 预扣减金额(正在进行的任务消耗) + */ + private Long preDeductBalance = 0L; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/ScoreDO.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/ScoreDO.java new file mode 100644 index 0000000..9b08728 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/ScoreDO.java @@ -0,0 +1,61 @@ +package top.continew.admin.business.model.entity; + +import lombok.Data; + +import com.baomidou.mybatisplus.annotation.TableName; +import top.continew.starter.extension.crud.model.entity.BaseDO; +import java.io.Serial; + +/** + * 分数实体 + * + * @author jiay + * @since 2025/06/26 10:24 + */ +@Data +@TableName("t_score") +public class ScoreDO extends BaseDO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + private Long userId; + + /** + * 课程id + */ + private Long courseId; + + /** + * 考试分数 + */ + private Integer examScore; + + /** + * 作业分数 + */ + private Integer homeworkScore; + + /** + * 签到分数 + */ + private Integer score; + + /** + * 最终总分数 + */ + private Integer signInScore; + + /** + * 1、已考 2、未考 + */ + private Integer takeExam; + + /** + * 未考试原因 + */ + private String cause; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/SignInDO.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/SignInDO.java new file mode 100644 index 0000000..34f608c --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/SignInDO.java @@ -0,0 +1,59 @@ +package top.continew.admin.business.model.entity; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import com.baomidou.mybatisplus.annotation.TableName; +import top.continew.starter.extension.crud.model.entity.BaseDO; +import java.io.Serial; + +/** + * 签到实体 + * + * @author jiay + * @since 2025/06/26 10:24 + */ +@Data +@TableName("t_sign_in") +public class SignInDO extends BaseDO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + private Long userId; + private String userName; + + /** + * 学号 + */ + private String stuNo; + + /** + * 手机 + */ + @Schema(description = "手机") + private String phone; + + /** + * 课程id + */ + private Long courseId; + private String courseName; + /** + * 请假原因 + */ + private String leaveCause; + + /** + * 1、签到 2、请假 3、旷课 + */ + private Integer signIn; + + /** + * 经纬度 + */ + private String location; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/SubtitleItem.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/SubtitleItem.java new file mode 100644 index 0000000..1370859 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/SubtitleItem.java @@ -0,0 +1,53 @@ +package top.continew.admin.business.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.starter.extension.crud.model.entity.BaseDO; + +/** + * 数字人-字幕项实体类 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("dh_subtitle_item") +public class SubtitleItem extends BaseDO { + + /** + * 字幕项ID + */ + private Long id; + + /** + * 关联的数字人ID + */ + private Long digitalHumanId; + + /** + * 字幕文本内容 + */ + private String text; + + /** + * 字幕开始时间(秒) + */ + private Double startTime; + + /** + * 字幕结束时间(秒) + */ + private Double endTime; + + /** + * 创建时间 + */ + private java.time.LocalDateTime createTime; + + /** + * 更新时间 + */ + private java.time.LocalDateTime updateTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/SubtitleStyle.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/SubtitleStyle.java new file mode 100644 index 0000000..f902977 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/SubtitleStyle.java @@ -0,0 +1,83 @@ +package top.continew.admin.business.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.starter.extension.crud.model.entity.BaseDO; + +/** + * 数字人-字幕样式实体类 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("dh_subtitle_style") +public class SubtitleStyle extends BaseDO { + + /** + * 字幕样式ID + */ + private Long id; + + /** + * 关联的数字人ID(可为空,用于全局/默认样式) + */ + private Long digitalHumanId; + + /** + * 字体大小(像素) + */ + private Integer fontSize; + + /** + * 字体族(如微软雅黑、Arial等) + */ + private String fontFamily; + + /** + * 文字颜色(CSS格式,如 #FFFFFF) + */ + private String color; + + /** + * 描边颜色 + */ + private String strokeColor; + + /** + * 描边宽度(像素) + */ + private Integer strokeWidth; + + /** + * 阴影颜色 + */ + private String shadowColor; + + /** + * 阴影模糊半径(像素) + */ + private Integer shadowBlur; + + /** + * 底部内边距(像素) + */ + private Integer paddingBottom; + + /** + * 行高(像素或倍数) + */ + private Integer lineHeight; + + /** + * 创建时间 + */ + private java.time.LocalDateTime createTime; + + /** + * 更新时间 + */ + private java.time.LocalDateTime updateTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/TaskRecordHistoryDO.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/TaskRecordHistoryDO.java new file mode 100644 index 0000000..4a1bbc9 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/entity/TaskRecordHistoryDO.java @@ -0,0 +1,114 @@ +package top.continew.admin.business.model.entity; + +import lombok.Data; + +import com.baomidou.mybatisplus.annotation.TableName; + +import top.continew.starter.extension.crud.model.entity.BaseDO; + +import java.io.Serial; +import java.time.*; + +/** + * 换哒-任务生成记录实体 + * + * @author 王志维 + * @since 2025/12/19 15:37 + */ +@Data +@TableName("t_task_record_history") +public class TaskRecordHistoryDO extends BaseDO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + private Long userId; + + /** + * 文件名 + */ + private String title; + + /** + * 根任务ID + */ + private String taskRootId; + + /** + * 任务订单号 + */ + private String taskId; + + /** + * 记录父任务 ID + */ + private String parentTaskId; + + /** + * 生成平台 + */ + private Long platformId; + + /** + * 内容来源的平台 + */ + private String platformCode; + + /** + * 任务类型:1 - 初始模特穿衣生成(仅根任务可用),2 - 对话修改,3 - 生成视频 + */ + private Integer taskType; + + /** + * 使用的模型 + */ + private String modelName; + + /** + * 消耗点数 + */ + private Long consumptionPoints; + + /** + * 存储结果文件路径 + */ + private String fileUrl; + + /** + * 消费的token数 + */ + private long tokens; + + /** + * 生成的文字 + */ + private String result; + + /** + * 父任务创建时间 + */ + private LocalDateTime parentCreateTime; + + /** + * 任务类型(文字内容:0, 图:2,音效:3,视频:4, 白膜贴图:5) + */ + private Integer chargeType; + + /** + * 文件类型 + */ + private String fileType; + + /** + * 消费类型(0充点,1按次数,2按点数) + */ + private Integer type; + + /** + * 从哪个账户进行的扣费 + */ + private String accountType; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/query/AssignmentsQuery.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/AssignmentsQuery.java new file mode 100644 index 0000000..b0ce084 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/AssignmentsQuery.java @@ -0,0 +1,37 @@ +package top.continew.admin.business.model.query; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.starter.data.core.annotation.Query; +import top.continew.starter.data.core.enums.QueryType; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 作业表查询条件 + * + * @author 张波 + * @since 2024/10/22 11:45 + */ +@Data +@Schema(description = "作业表查询条件") +public class AssignmentsQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 课程表ID + */ + @Schema(description = "课程表ID") + @Query(type = QueryType.EQ) + private Long courseId; + + /** + * 作业名称 + */ + @Schema(description = "作业名称") + @Query(type = QueryType.EQ) + private String name; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/query/BillingRuleQuery.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/BillingRuleQuery.java new file mode 100644 index 0000000..7a29689 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/BillingRuleQuery.java @@ -0,0 +1,65 @@ +package top.continew.admin.business.model.query; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.starter.data.core.annotation.Query; +import top.continew.starter.data.core.enums.QueryType; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 计费规则查询条件 + * + * @author jiay + * @since 2025/11/14 + */ +@Data +@Schema(description = "计费规则查询条件") +public class BillingRuleQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 平台名称 + */ + @Schema(description = "平台名称", example = "AI绘画") + @Query(columns = {"platform_name"}, type = QueryType.LIKE) + private String platformName; + + /** + * 平台ID + */ + @Schema(description = "平台ID", example = "10000") + @Query(columns = {"platform_id"}, type = QueryType.EQ) + private Long platformId; + + /** + * 平台编码 + */ + @Schema(description = "平台编码", example = "KEXUE_CHAT") + @Query(columns = {"platform_code"}, type = QueryType.EQ) + private String platformCode; + + /** + * 扣费编码 + */ + @Schema(description = "扣费编码", example = "C0001") + @Query(columns = {"charge_code"}, type = QueryType.EQ) + private String chargeCode; + + /** + * 模型名称 + */ + @Schema(description = "模型名称", example = "flux") + @Query(columns = {"model_name"}, type = QueryType.LIKE) + private String modelName; + + /** + * 是否启用 + */ + @Schema(description = "是否启用", example = "1") + @Query(columns = {"enabled"}, type = QueryType.EQ) + private Integer enabled; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/query/ConsumptionHistoryQuery.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/ConsumptionHistoryQuery.java new file mode 100644 index 0000000..ab13720 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/ConsumptionHistoryQuery.java @@ -0,0 +1,81 @@ +package top.continew.admin.business.model.query; + +import lombok.Data; + +import io.swagger.v3.oas.annotations.media.Schema; + +import top.continew.starter.data.core.annotation.Query; +import top.continew.starter.data.core.enums.QueryType; +import java.io.Serial; +import java.io.Serializable; +import java.time.*; + +/** + * 用户充值消费历史记录查询条件 + * + * @author jiay + * @since 2025/05/23 07:51 + */ +@Data +@Schema(description = "用户充值消费历史记录查询条件") +public class ConsumptionHistoryQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + @Schema(description = "用户id") + @Query(type = QueryType.EQ) + private Long userId; + + /** + * 生成平台 + */ + @Schema(description = "生成平台") + @Query(type = QueryType.EQ) + private Long platformId; + + /** + * 任务类型(token:0, 文生图:1,图生图:2,音效生成:3,参考生视频:4, 白膜贴图:5) + */ + @Schema(description = "任务类型(token:0, 文生图:1,图生图:2,音效生成:3,参考生视频:4, 白膜贴图:5)") + @Query(type = QueryType.EQ) + private Integer taskType; + + /** + * 点数量 + */ + @Schema(description = "点数量") + @Query(type = QueryType.EQ) + private Long chargePoints; + + /** + * 剩余的点数 + */ + @Schema(description = "剩余的点数") + @Query(type = QueryType.EQ) + private Long remainingPoints; + + /** + * 记录类型(0充点 1 次数 2 点数) + */ + @Schema(description = "记录类型(0充点 1 次数 2 点数)") + @Query(type = QueryType.EQ) + private Integer chargeType; + + /** + * 创建人 + */ + @Schema(description = "创建人") + @Query(type = QueryType.EQ) + private Long createUser; + + /** + * 创建时间 + */ + @Schema(description = "创建时间") + @Query(type = QueryType.EQ) + private LocalDateTime createTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/query/CopyrightPageQuery.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/CopyrightPageQuery.java new file mode 100644 index 0000000..b998042 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/CopyrightPageQuery.java @@ -0,0 +1,17 @@ +package top.continew.admin.business.model.query; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.starter.extension.crud.model.query.PageQuery; + +/** + * 文案分页查询对象 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class CopyrightPageQuery extends PageQuery { + // 可根据需要添加额外的分页查询条件 +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/query/CopyrightQuery.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/CopyrightQuery.java new file mode 100644 index 0000000..ca0ff23 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/CopyrightQuery.java @@ -0,0 +1,17 @@ +package top.continew.admin.business.model.query; + +import lombok.Data; +import java.io.Serializable; + +/** + * 文案查询条件 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +public class CopyrightQuery implements Serializable { + private String userId; + private String text; + private String createTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/query/DigitalHumanPageQuery.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/DigitalHumanPageQuery.java new file mode 100644 index 0000000..0c3d30a --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/DigitalHumanPageQuery.java @@ -0,0 +1,17 @@ +package top.continew.admin.business.model.query; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.starter.extension.crud.model.query.PageQuery; + +/** + * 数字人分页查询对象 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class DigitalHumanPageQuery extends PageQuery { + // 可根据需要添加额外的分页查询条件 +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/query/DigitalHumanQuery.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/DigitalHumanQuery.java new file mode 100644 index 0000000..1df0d32 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/DigitalHumanQuery.java @@ -0,0 +1,22 @@ +package top.continew.admin.business.model.query; + +import lombok.Data; +import java.io.Serializable; + +/** + * 数字人查询条件 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +public class DigitalHumanQuery implements Serializable { + private String userId; + private String copywriting; + private String generatedVoiceUrl; + private String generatedFrameImageUrl; + private String generatedVideoUrl; + private String subtitle; + private String subtitleStyle; + private String effects; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/query/EffectPageQuery.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/EffectPageQuery.java new file mode 100644 index 0000000..3dff23b --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/EffectPageQuery.java @@ -0,0 +1,17 @@ +package top.continew.admin.business.model.query; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.starter.extension.crud.model.query.PageQuery; + +/** + * 特效分页查询对象 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class EffectPageQuery extends PageQuery { + // 可根据需要添加额外的分页查询条件 +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/query/EffectQuery.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/EffectQuery.java new file mode 100644 index 0000000..55a8b76 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/EffectQuery.java @@ -0,0 +1,30 @@ +package top.continew.admin.business.model.query; + +import lombok.Data; +import java.io.Serializable; + +/** + * 特效查询条件 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +public class EffectQuery implements Serializable { + private String name; + private Long templateId; + private Long digitalHumanId; + private String type; + private String sourceUrl; + private Integer positionX; + private Integer positionY; + private Integer sizeWidth; + private Integer sizeHeight; + private Double length; + private String subtitleText; + private String subtitleStyle; + private String subtitlePosition; + private Double offset; + private String duration; + private Integer zIndex; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/query/EffectTemplatePageQuery.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/EffectTemplatePageQuery.java new file mode 100644 index 0000000..710e1a1 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/EffectTemplatePageQuery.java @@ -0,0 +1,17 @@ +package top.continew.admin.business.model.query; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.starter.extension.crud.model.query.PageQuery; + +/** + * 特效模板分页查询对象 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class EffectTemplatePageQuery extends PageQuery { + // 可根据需要添加额外的分页查询条件 +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/query/EffectTemplateQuery.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/EffectTemplateQuery.java new file mode 100644 index 0000000..841f020 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/EffectTemplateQuery.java @@ -0,0 +1,26 @@ +package top.continew.admin.business.model.query; + +import lombok.Data; +import java.io.Serializable; + +/** + * 特效模板查询条件 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +public class EffectTemplateQuery implements Serializable { + private String name; + private String isCustom; + private String userId; + private String type; + private String sourceUrl; + private String size; + private Double length; + private String subtitleText; + private Long subtitleStyleId; + private String subtitlePositionX; + private String subtitlePositionY; + private String coverUrl; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/query/FreeQuotaConfigQuery.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/FreeQuotaConfigQuery.java new file mode 100644 index 0000000..7f689cd --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/FreeQuotaConfigQuery.java @@ -0,0 +1,58 @@ +package top.continew.admin.business.model.query; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.starter.data.core.annotation.Query; +import top.continew.starter.data.core.enums.QueryType; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 免费额度配置查询条件 + * + * @author jiay + * @since 2025/11/17 + */ +@Data +@Schema(description = "免费额度配置查询条件") +public class FreeQuotaConfigQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 类别编码 + */ + @Schema(description = "类别编码", example = "TOKEN") + @Query(columns = {"category_code"}, type = QueryType.EQ) + private String categoryCode; + + /** + * 类别名称 + */ + @Schema(description = "类别名称", example = "大模型") + @Query(columns = {"category_name"}, type = QueryType.LIKE) + private String categoryName; + + /** + * 计费类型 + */ + @Schema(description = "计费类型", example = "0") + @Query(columns = {"charge_type"}, type = QueryType.EQ) + private Integer chargeType; + + /** + * 账户类型 + */ + @Schema(description = "账户类型", example = "1") + @Query(columns = {"type"}, type = QueryType.EQ) + private Integer type; + + /** + * 是否启用 + */ + @Schema(description = "是否启用", example = "true") + @Query(columns = {"is_enabled"}, type = QueryType.EQ) + private Boolean isEnabled; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/query/HomeworkQuery.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/HomeworkQuery.java new file mode 100644 index 0000000..006ac31 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/HomeworkQuery.java @@ -0,0 +1,65 @@ +package top.continew.admin.business.model.query; + +import lombok.Data; + +import io.swagger.v3.oas.annotations.media.Schema; + +import top.continew.starter.data.core.annotation.Query; +import top.continew.starter.data.core.enums.QueryType; +import java.io.Serial; +import java.io.Serializable; +import java.time.*; + +/** + * 作业查询条件 + * + * @author jiay + * @since 2025/06/26 10:24 + */ +@Data +@Schema(description = "作业查询条件") +public class HomeworkQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + private Long userId; + + /** + * 课程id + */ + @Schema(description = "课程id") + @Query(type = QueryType.EQ) + private Long courseId; + + /** + * 未交原因 + */ + @Schema(description = "未交原因") + @Query(type = QueryType.LIKE) + private String cause; + + /** + * 1、已交 2、未交 + */ + @Schema(description = "已交/未交") + @Query(type = QueryType.EQ) + private Integer handedIn; + + /** + * 创建人 + */ + @Schema(description = "创建人") + @Query(type = QueryType.EQ) + private Long createUser; + + /** + * 创建时间 + */ + @Schema(description = "创建时间") + @Query(type = QueryType.EQ) + private LocalDateTime createTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/query/HuandaResourcePageQuery.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/HuandaResourcePageQuery.java new file mode 100644 index 0000000..321b691 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/HuandaResourcePageQuery.java @@ -0,0 +1,40 @@ +package top.continew.admin.business.model.query; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.starter.extension.crud.model.query.PageQuery; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 换哒-文件资源查询条件 + * + * @author 王志维 + * @since 2025/12/23 16:53 + */ +@Data +@Schema(description = "换哒-文件资源查询条件") +public class HuandaResourcePageQuery extends PageQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "资源ID") + private Long id; + + @Schema(description = "用户ID") + private String userId; + + @Schema(description = "资源类型:1.服装,2模特,3自定义服装,4.自定义背景") + private Integer resourceType; + + @Schema(description = "人物图片年龄") + private String age; + + @Schema(description = "人物图片性别:男,女") + private String gender; + + @Schema(description = "人物图片区域") + private String region; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/query/HuandaResourceQuery.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/HuandaResourceQuery.java new file mode 100644 index 0000000..170b885 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/HuandaResourceQuery.java @@ -0,0 +1,39 @@ +package top.continew.admin.business.model.query; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 换哒-文件资源查询条件 + * + * @author 王志维 + * @since 2025/12/23 16:53 + */ +@Data +@Schema(description = "换哒-文件资源查询条件") +public class HuandaResourceQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "资源ID") + private Long id; + + @Schema(description = "用户ID") + private String userId; + + @Schema(description = "资源类型:1.服装,2模特,3自定义服装,4.自定义背景") + private Integer resourceType; + + @Schema(description = "人物图片年龄") + private String age; + + @Schema(description = "人物图片性别:男,女") + private String gender; + + @Schema(description = "人物图片区域") + private String region; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/query/ModelQuery.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/ModelQuery.java new file mode 100644 index 0000000..dffebba --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/ModelQuery.java @@ -0,0 +1,77 @@ +package top.continew.admin.business.model.query; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.starter.data.core.annotation.Query; +import top.continew.starter.data.core.enums.QueryType; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 模型查询条件 + * + * @author 张波 + * @since 2024/10/19 15:38 + */ +@Data +@Schema(description = "模型查询条件") +public class ModelQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "模型ID", example = "1") + @Query(columns = {"id"}, type = QueryType.IN) + private List ids; + + /** + * 模型名称 + */ + @Schema(description = "模型名称", example = "测试模型") + @Query(columns = {"name"}, type = QueryType.LIKE) + private String name; + + /** + * 平台分类 + */ + @Schema(description = "平台分类", example = "绘画") + @Query(columns = {"platform_category"}, type = QueryType.EQ) + private String platformCategory; + + /** + * 类型 + */ + @Schema(description = "类型", example = "Lora") + @Query(columns = {"type"}, type = QueryType.EQ) + private String type; + + /** + * 版本 + */ + @Schema(description = "版本", example = "SDXL") + @Query(columns = {"version"}, type = QueryType.EQ) + private String version; + + /** + * 分类 + */ + @Schema(description = "分类", example = "动漫游戏") + @Query(columns = {"category"}, type = QueryType.EQ) + private String category; + + /** + * 专业分类 + */ + @Schema(description = "专业分类", example = "环境艺术") + @Query(columns = "professional_category", type = QueryType.EQ) + private String professionalCategory; + + /** + * 系列 + */ + @Schema(description = "系列", example = "室内系列") + @Query(columns = {"series"}, type = QueryType.EQ) + private String series; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/query/PlantformBalanceQuery.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/PlantformBalanceQuery.java new file mode 100644 index 0000000..6d93f34 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/PlantformBalanceQuery.java @@ -0,0 +1,46 @@ +package top.continew.admin.business.model.query; + +import lombok.Data; + +import io.swagger.v3.oas.annotations.media.Schema; + +import top.continew.starter.data.core.annotation.Query; +import top.continew.starter.data.core.enums.QueryType; +import java.io.Serial; +import java.io.Serializable; +import java.time.*; + +/** + * 用户子账号余额表 查询条件 + * + * @author jiay + * @since 2025/06/24 16:47 + */ +@Data +@Schema(description = "business查询条件") +public class PlantformBalanceQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + @Schema(description = "用户id") + @Query(type = QueryType.EQ) + private Long userId; + + /** + * 账户类型 1 次数 2 点数 + */ + @Schema(description = "账户类型") + @Query(type = QueryType.EQ) + private Integer type; + + /** + * 平台 + */ + @Schema(description = "平台") + @Query(type = QueryType.EQ) + private Long platformId; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/query/ScoreQuery.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/ScoreQuery.java new file mode 100644 index 0000000..afff08d --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/ScoreQuery.java @@ -0,0 +1,53 @@ +package top.continew.admin.business.model.query; + +import lombok.Data; + +import io.swagger.v3.oas.annotations.media.Schema; + +import top.continew.starter.data.core.annotation.Query; +import top.continew.starter.data.core.enums.QueryType; +import java.io.Serial; +import java.io.Serializable; +import java.time.*; + +/** + * 分数查询条件 + * + * @author jiay + * @since 2025/06/26 10:24 + */ +@Data +@Schema(description = "分数查询条件") +public class ScoreQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + @Schema(description = "用户id") + @Query(type = QueryType.EQ) + private Long userId; + + /** + * 课程id + */ + @Schema(description = "课程id") + @Query(type = QueryType.EQ) + private Long courseId; + + /** + * 创建人 + */ + @Schema(description = "创建人") + @Query(type = QueryType.EQ) + private Long createUser; + + /** + * 创建时间 + */ + @Schema(description = "创建时间") + @Query(type = QueryType.EQ) + private LocalDateTime createTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/query/SignInQuery.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/SignInQuery.java new file mode 100644 index 0000000..55c1f7d --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/SignInQuery.java @@ -0,0 +1,77 @@ +package top.continew.admin.business.model.query; + +import lombok.Data; + +import io.swagger.v3.oas.annotations.media.Schema; + +import top.continew.starter.data.core.annotation.Query; +import top.continew.starter.data.core.enums.QueryType; +import java.io.Serial; +import java.io.Serializable; +import java.time.*; + +/** + * 签到查询条件 + * + * @author jiay + * @since 2025/06/26 10:24 + */ +@Data +@Schema(description = "签到查询条件") +public class SignInQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + /** + * 用户id + */ + @Schema(description = "用户id") + @Query(type = QueryType.EQ) + private Long userId; + private String userName; + + /** + * 学号 + */ + private String stuNo; + + /** + * 手机 + */ + @Schema(description = "手机") + private String phone; + /** + * 课程id + */ + @Schema(description = "课程id") + @Query(type = QueryType.EQ) + private Long courseId; + private String courseName; + /** + * 请假原因 + */ + @Schema(description = "请假原因") + @Query(type = QueryType.LIKE) + private String leaveCause; + + /** + * 1、签到 2、请假 3、旷课 + */ + @Schema(description = "签到") + @Query(type = QueryType.EQ) + private Integer signIn; + + /** + * 创建人 + */ + @Schema(description = "创建人") + @Query(type = QueryType.EQ) + private Long createUser; + + /** + * 创建时间 + */ + @Schema(description = "创建时间") + @Query(type = QueryType.EQ) + private LocalDateTime createTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/query/SubtitleItemPageQuery.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/SubtitleItemPageQuery.java new file mode 100644 index 0000000..a5bfee6 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/SubtitleItemPageQuery.java @@ -0,0 +1,17 @@ +package top.continew.admin.business.model.query; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.starter.extension.crud.model.query.PageQuery; + +/** + * 字幕项分页查询对象 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class SubtitleItemPageQuery extends PageQuery { + // 可根据需要添加额外的分页查询条件 +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/query/SubtitleItemQuery.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/SubtitleItemQuery.java new file mode 100644 index 0000000..72e69d2 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/SubtitleItemQuery.java @@ -0,0 +1,18 @@ +package top.continew.admin.business.model.query; + +import lombok.Data; +import java.io.Serializable; + +/** + * 字幕项查询条件 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +public class SubtitleItemQuery implements Serializable { + private Long digitalHumanId; + private String text; + private Double startTime; + private Double endTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/query/SubtitleStylePageQuery.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/SubtitleStylePageQuery.java new file mode 100644 index 0000000..a48a45c --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/SubtitleStylePageQuery.java @@ -0,0 +1,17 @@ +package top.continew.admin.business.model.query; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.starter.extension.crud.model.query.PageQuery; + +/** + * 字幕样式分页查询对象 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class SubtitleStylePageQuery extends PageQuery { + // 可根据需要添加额外的分页查询条件 +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/query/SubtitleStyleQuery.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/SubtitleStyleQuery.java new file mode 100644 index 0000000..865e3cb --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/SubtitleStyleQuery.java @@ -0,0 +1,24 @@ +package top.continew.admin.business.model.query; + +import lombok.Data; +import java.io.Serializable; + +/** + * 字幕样式查询条件 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +public class SubtitleStyleQuery implements Serializable { + private Long digitalHumanId; + private Integer fontSize; + private String fontFamily; + private String color; + private String strokeColor; + private Integer strokeWidth; + private String shadowColor; + private Integer shadowBlur; + private Integer paddingBottom; + private Integer lineHeight; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/query/TaskRecordHistoryQuery.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/TaskRecordHistoryQuery.java new file mode 100644 index 0000000..9afa4fa --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/query/TaskRecordHistoryQuery.java @@ -0,0 +1,88 @@ +package top.continew.admin.business.model.query; + +import lombok.Data; + +import io.swagger.v3.oas.annotations.media.Schema; + +import top.continew.starter.data.core.annotation.Query; +import top.continew.starter.data.core.enums.QueryType; +import java.io.Serial; +import java.io.Serializable; +import java.time.*; + +/** + * 音视图文生成记录查询条件 + * + * @author jiay + * @since 2025/05/23 07:51 + */ +@Data +@Schema(description = "音视图文生成记录查询条件") +public class TaskRecordHistoryQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + @Schema(description = "用户id") + @Query(type = QueryType.EQ) + private Long userId; + + /** + * 任务订单号 + */ + @Schema(description = "任务订单号") + @Query(type = QueryType.EQ) + private String taskId; + + /** + * 文件名 + */ + @Schema(description = "文件名") + @Query(type = QueryType.EQ) + private String title; + + /** + * 生成平台 + */ + @Schema(description = "生成平台") + @Query(type = QueryType.EQ) + private Long platformId; + + /** + * 任务类型(token:0, 文生图:1,图生图:2,音效生成:3,参考生视频:4, 白膜贴图:5) + */ + @Schema(description = "任务类型(token:0, 文生图:1,图生图:2,音效生成:3,参考生视频:4, 白膜贴图:5)") + @Query(type = QueryType.EQ) + private Integer taskType; + + /** + * 计费类型(token:0, 图:1,音效生成:3,参考生视频:4, 白膜贴图:5) + */ + @Schema(description = "计费类型(token:0, 图:1, 音效:3, 视频:4, 白膜贴图:5)") + @Query(type = QueryType.EQ) + private Integer chargeType; + + /** + * 存储结果文件路径 + */ + @Schema(description = "存储结果文件路径") + @Query(type = QueryType.EQ) + private String fileUrl; + + /** + * 创建人 + */ + @Schema(description = "创建人") + @Query(type = QueryType.EQ) + private Long createUser; + + /** + * 创建时间 + */ + @Schema(description = "创建时间") + @Query(type = QueryType.EQ) + private LocalDateTime createTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/AssignmentSubmissionsReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/AssignmentSubmissionsReq.java new file mode 100644 index 0000000..ff1ccf2 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/AssignmentSubmissionsReq.java @@ -0,0 +1,45 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.starter.extension.crud.model.req.BaseReq; + +import java.io.Serial; + +/** + * 作业提交请求参数 + * + * @author 张波 + * @since 2024/10/22 12:01 + */ +@Data +@Schema(description = "作业提交请求参数") +public class AssignmentSubmissionsReq extends BaseReq { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 作业ID + */ + @Schema(description = "作业ID") + private Long assignmentId; + + /** + * 图片标题 + */ + @Schema(description = "图片标题") + private String imageTitle; + + /** + * 图片标签 + */ + @Schema(description = "图片标签") + private String imageTag; + + /** + * 图片地址 + */ + @Schema(description = "图片地址") + private String imageUrl; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/AssignmentsCorrectReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/AssignmentsCorrectReq.java new file mode 100644 index 0000000..1048ca2 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/AssignmentsCorrectReq.java @@ -0,0 +1,46 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import top.continew.starter.extension.crud.model.req.BaseReq; + +import java.io.Serial; + +/** + * 作业批改请求参数 + * + * @author 张波 + * @since 2024/10/23 19:56 + */ +@Data +@Schema(description = "作业批改请求参数") +public class AssignmentsCorrectReq extends BaseReq { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 作业ID + */ + @Schema(description = "作业提交记录ID") + @NotNull(message = "作业提交记录ID") + private Long id; + + /** + * 分数 + */ + @Schema(description = "分数") + @NotNull(message = "分数不能为空") + @Max(value = 200, message = "满分不能大于200") + @Min(value = 0, message = "分数不能小于0") + private Integer score; + + /** + * 评语 + */ + @Schema(description = "评语") + private String feedback; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/AssignmentsReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/AssignmentsReq.java new file mode 100644 index 0000000..b755e01 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/AssignmentsReq.java @@ -0,0 +1,82 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.hibernate.validator.constraints.Length; +import top.continew.admin.business.enums.AssignmentsTypeEnum; +import top.continew.starter.extension.crud.model.req.BaseReq; + +import java.io.Serial; +import java.time.LocalDateTime; + +/** + * 创建或修改作业表信息 + * + * @author 张波 + * @since 2024/10/22 11:45 + */ +@Data +@Schema(description = "创建或修改作业表信息") +public class AssignmentsReq extends BaseReq { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 课程表ID + */ + @Schema(description = "课程表ID") + @NotNull(message = "课程表ID不能为空") + private Long courseId; + + /** + * 分类(1:作业,2:考试) + */ + @Schema(description = "类型", example = "1") + @NotNull(message = "类型不能为空") + private AssignmentsTypeEnum type; + + /** + * 作业名称 + */ + @Schema(description = "作业名称") + @NotBlank(message = "作业名称不能为空") + @Length(max = 255, message = "作业名称长度不能超过 {max} 个字符") + private String name; + + /** + * 作业要求 + */ + @Schema(description = "作业要求") + @Length(max = 255, message = "作业名称长度不能超过 {max} 个字符") + private String requirements; + + /** + * 满分 + */ + @Schema(description = "满分", example = "100") + @Max(value = 200, message = "满分不能超过200") + private Integer fullScore; + + /** + * 开始时间 + */ + @Schema(description = "开始时间") + @NotNull(message = "开始时间不能为空") + private LocalDateTime startTime; + + /** + * 结束时间 + */ + @Schema(description = "结束时间") + @NotNull(message = "结束时间不能为空") + private LocalDateTime endTime; + + /** + * 示例图片 + */ + private String exampleImages; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/BatchReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/BatchReq.java new file mode 100644 index 0000000..434ef23 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/BatchReq.java @@ -0,0 +1,27 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 批量换哒资源操作请求 + * + * @author your name + * @since 2025/12/25 15:00 + */ +@Data +@Schema(description = "批量换哒资源操作请求") +public class BatchReq implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @NotEmpty(message = "资源ID列表不能为空") + @Schema(description = "资源ID列表", example = "[1,2,3]") + private List ids; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/BillingJudgeReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/BillingJudgeReq.java new file mode 100644 index 0000000..931e764 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/BillingJudgeReq.java @@ -0,0 +1,78 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotNull; + +/** + * 计费判断请求参数 + * + * @author jiay + * @since 2025/08/28 16:23 + */ +@Data +public class BillingJudgeReq { + + /** + * 平台编码 + */ + @Schema(description = "平台编码") + private String platformCode; + + /** + * 生成平台 + */ + @Schema(description = "生成平台id") + @NotNull(message = "生成平台不能为空") + private Long platformId; + + /** + * 使用的模型 + */ + @Schema(description = "使用的模型") + private String modelName; + + /** + * 扣费编码 + */ + @Schema(description = "扣费编码", example = "C0001") + @NotNull(message = "扣费编码不能为空") + private String chargeCode; + + /** + * 本次生成数量 + */ + @Schema(description = "本次生成数量", example = "1") + @NotNull(message = "本次生成数量不能为空") + private Integer quantity; + + /** + * 本次生成数量 + */ + @Schema(description = "本次生成数量", example = "1") + @NotNull(message = "本次生成数量不能为空") + private Long tokens; + + /** + * 计费类型(token:0, 图:1,音效生成:3,参考生视频:4, 白膜贴图:5) + */ + @Schema(description = "计费类型(token:0, 图:1, 音效:3, 视频:4, 白膜贴图:5)") + @NotNull(message = "计费类型不能为空") + private Integer chargeType; + + /** + * * 任务类型(token:0, 文生图:1,图生图:2,音效生成:3,参考生视频:4, 白膜贴图:5) + */ + @Schema(description = "任务类型(token:0, 文生图:1,图生图:2,音效生成:3,参考生视频:4, 白膜贴图:5)") + @NotNull(message = "任务类型不能为空") + private Integer taskType; + + /** + * 计费类型(0充点,1按次数,2按点数) + */ + @Schema(description = "消费类型(0充点,1按次数,2按点数)") + @NotNull(message = "消费类型不能为空") + private Integer type; + +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/BillingJudgeWithAmountReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/BillingJudgeWithAmountReq.java new file mode 100644 index 0000000..5e03d9f --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/BillingJudgeWithAmountReq.java @@ -0,0 +1,22 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 计费判断请求参数(带第三方平台传入的金额) + * + * @author jiay + * @since 2025/11/14 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class BillingJudgeWithAmountReq extends BillingJudgeReq { + + /** + * 预扣减金额(分)- 由第三方平台传入 + */ + @Schema(description = "预扣减金额(分)", example = "100") + private Long preDeductAmount; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/BillingRuleReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/BillingRuleReq.java new file mode 100644 index 0000000..5fa03fc --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/BillingRuleReq.java @@ -0,0 +1,98 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import top.continew.starter.extension.crud.model.req.BaseReq; + +import java.io.Serial; + +/** + * 创建或修改计费规则信息 + * + * @author jiay + * @since 2025/11/14 + */ +@Data +@Schema(description = "创建或修改计费规则信息") +public class BillingRuleReq extends BaseReq { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 平台名称 + */ + @Schema(description = "平台名称", example = "AI绘画") + @NotBlank(message = "平台名称不能为空") + private String platformName; + + /** + * 账户类型 + */ + @Schema(description = "账户类型", example = "图片") + private String type; + + /** + * 平台ID + */ + @Schema(description = "平台ID", example = "10000") + @NotNull(message = "平台ID不能为空") + private Long platformId; + + /** + * 平台编码 + */ + @Schema(description = "平台编码", example = "KEXUE_CHAT") + private String platformCode; + + /** + * 扣费编码 + */ + @Schema(description = "扣费编码", example = "C0001") + @NotBlank(message = "扣费编码不能为空") + private String chargeCode; + + /** + * 计费类型 + */ + @Schema(description = "计费类型", example = "1") + private Integer chargeType; + + /** + * 模型名称 + */ + @Schema(description = "模型名称", example = "flux") + private String modelName; + + /** + * 主账户单价(分/次/整数量) + */ + @Schema(description = "主账户单价(分/次/整数量)", example = "13") + private Long price; + + /** + * 子账户单价(次/个/数量) + */ + @Schema(description = "子账户单价(次/个/数量)", example = "1") + private Long subAccountPrice; + + /** + * 计费单位 + */ + @Schema(description = "计费单位", example = "张") + private String unit; + + /** + * 备注 + */ + @Schema(description = "备注", example = "") + private String remark; + + /** + * 是否启用(0:禁用 1:启用) + */ + @Schema(description = "是否启用", example = "1") + private Integer enabled; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/CheckPlantBalanceReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/CheckPlantBalanceReq.java new file mode 100644 index 0000000..2b9c519 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/CheckPlantBalanceReq.java @@ -0,0 +1,45 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * @Author: jia + * @Date: 2025/05/26/9:04 + * @Description: + */ + +@Data +@Schema(description = "用户消耗点数余额") +public class CheckPlantBalanceReq { + /** + * 用户id + */ + @Schema(description = "用户id") + private Long userId; + + /** + * 平台 + */ + @Schema(description = "平台") + private Long platformId; + + /** + * 计费类型 + */ + @Schema(description = "计费类型") + private Integer chargeType; + + /** + * 账号类型 + */ + @Schema(description = "类型") + private Integer type; + + /** + * 点数 + */ + @Schema(description = "点数") + private Long chargePoints; + +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/ConsumptionHistoryAddReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/ConsumptionHistoryAddReq.java new file mode 100644 index 0000000..98b821c --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/ConsumptionHistoryAddReq.java @@ -0,0 +1,70 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 用户充值消费历史记录创建或修改参数 + * + * @author jiay + * @since 2025/05/23 07:51 + */ +@Data +@Schema(description = "用户充值消费历史记录创建或修改参数") +public class ConsumptionHistoryAddReq implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + @Schema(description = "用户id") + private String userId; + + /** + * 生成平台 + */ + @Schema(description = "生成平台") + private Long platformId; + + /** + * 任务类型(token:0, 图:1,音效生成:3,参考生视频:4, 白膜贴图:5) + */ + @Schema(description = "任务类型(文生:1,图生:2,音效生成:3,参考生视频:4 token:5)") + private Integer taskType; + + /** + * 点数量 + */ + @Schema(description = "点数量") + private Long chargePoints; + + /** + * 剩余的点数 + */ + @Schema(description = "剩余的点数") + private Long remainingPoints; + + /** + * 记录类型(0充点 1 次数 2 点数) + */ + @Schema(description = "记录类型(0充点 1 次数 2 点数)") + private Integer chargeType; + + /** + * 创建人 + */ + @Schema(description = "创建人") + private Long createUser; + + /** + * 创建时间 + */ + @Schema(description = "创建时间") + private LocalDateTime createTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/ConsumptionHistoryEditReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/ConsumptionHistoryEditReq.java new file mode 100644 index 0000000..fedc89e --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/ConsumptionHistoryEditReq.java @@ -0,0 +1,72 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.starter.extension.crud.model.req.BaseReq; + +import java.time.LocalDateTime; + +/** + * 用户充值消费历史记录创建或修改参数 + * + * @author jiay + * @since 2025/05/23 07:51 + */ +@Data +@Schema(description = "用户充值消费历史记录创建或修改参数") +public class ConsumptionHistoryEditReq extends BaseReq { + + /** + * id + */ + @Schema(description = "id") + private Long id; + + /** + * 用户id + */ + @Schema(description = "用户id") + private Long userId; + + /** + * 生成平台 + */ + @Schema(description = "生成平台") + private Long platformId; + + /** + * 任务类型(token:0, 文生图:1,图生图:2,音效生成:3,参考生视频:4, 白膜贴图:5) + */ + @Schema(description = "任务类型(token:0, 文生图:1,图生图:2,音效生成:3,参考生视频:4, 白膜贴图:5)") + private Integer taskType; + + /** + * 点数量 + */ + @Schema(description = "点数量") + private Long chargePoints; + + /** + * 剩余的点数 + */ + @Schema(description = "剩余的点数") + private Long remainingPoints; + + /** + * 记录类型(0充点 1 次数 2 点数) + */ + @Schema(description = "记录类型(0充点 1 次数 2 点数)") + private Integer chargeType; + + /** + * 创建人 + */ + @Schema(description = "创建人") + private Long createUser; + + /** + * 创建时间 + */ + @Schema(description = "创建时间") + private LocalDateTime createTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/ConsumptionReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/ConsumptionReq.java new file mode 100644 index 0000000..a20a0b6 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/ConsumptionReq.java @@ -0,0 +1,26 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * @Author: jia + * @Date: 2025/05/26/9:04 + * @Description: + */ + +@Data +@Schema(description = "用户消耗点数余额判断参数") +public class ConsumptionReq { + /** + * 用户id + */ + @Schema(description = "用户id") + private Long userId; + + /** + * 消耗点数量 + */ + @Schema(description = "消耗点数量") + private Long chargePoints; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/CopyrightReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/CopyrightReq.java new file mode 100644 index 0000000..20b039b --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/CopyrightReq.java @@ -0,0 +1,31 @@ +package top.continew.admin.business.model.req; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.starter.extension.crud.model.req.BaseReq; + +/** + * 数字人-文案请求参数对象 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class CopyrightReq extends BaseReq { + + /** + * 用户 ID + */ + private String userId; + + /** + * 文案内容 + */ + private String text; + + /** + * 文案创建日期 + */ + private String createTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/CourseUserReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/CourseUserReq.java new file mode 100644 index 0000000..9d1cac9 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/CourseUserReq.java @@ -0,0 +1,25 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.starter.extension.crud.model.req.BaseReq; + +import java.io.Serial; + +/** + * @author 张波 + * @since 2024/10/23 21:15 + */ +@Data +@Schema(description = "课程关联学生请求") +public class CourseUserReq extends BaseReq { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "学生ID") + private Long userId; + + @Schema(description = "学生姓名") + private String nickname; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/CoursesReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/CoursesReq.java new file mode 100644 index 0000000..8c51aa9 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/CoursesReq.java @@ -0,0 +1,69 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import top.continew.starter.extension.crud.model.req.BaseReq; + +import java.io.Serial; +import java.time.LocalDateTime; +import java.util.List; + +/** + * 创建课程信息 + * + * @author 张波 + * @since 2024/10/22 11:58 + */ +@Data +public class CoursesReq extends BaseReq { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 课程名称 + */ + @Schema(description = "课程名称") + @NotNull(message = "课程名称不能为空") + private String name; + + /** + * 描述 + */ + @Schema(description = "课程简介") + @NotNull(message = "课程简介不能为空") + private String description; + + /** + * 班级 + */ + @Schema(description = "班级") + @NotNull(message = "班级不能为空") + private String className; + + /** + * 封面 + */ + @Schema(description = "封面Url") + @NotNull(message = "封面Url不能为空") + private String coverImage; + + /** + * 开始时间 + */ + @Schema(description = "开始时间") + @NotNull(message = "开始时间不能为空") + private LocalDateTime startTime; + + /** + * 结束时间 + */ + @Schema(description = "结束时间") + @NotNull(message = "结束时间不能为空") + private LocalDateTime endTime; + + @Schema(description = "关联学生列表") + @NotNull(message = "关联学生不能为空") + private List users; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/DeptModelReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/DeptModelReq.java new file mode 100644 index 0000000..5952930 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/DeptModelReq.java @@ -0,0 +1,31 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +/** + * 修改部门与模型绑定关系 + * + * @author 张波 + * @since 2024/10/20 14:28 + */ +@Data +@Schema(description = "修改部门与模型绑定关系") +public class DeptModelReq { + + /** + * 部门ID + */ + @Schema(description = "部门ID", example = "1") + @NotNull(message = "部门ID不能为空") + private Long deptId; + + /** + * 模型ID列表 + */ + @Schema(description = "模型ID列表", example = "1,2") + private List modelIds; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/DeptPlatformReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/DeptPlatformReq.java new file mode 100644 index 0000000..f8d682a --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/DeptPlatformReq.java @@ -0,0 +1,30 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +/** + * 修改部门与操作平台绑定关系 + * + * @author 张波 + * @since 2024/10/20 14:37 + */ +@Data +public class DeptPlatformReq { + + /** + * 部门ID + */ + @Schema(description = "部门ID", example = "1") + @NotNull(message = "部门ID不能为空") + private Long deptId; + + /** + * 操作平台列表 + */ + @Schema(description = "操作平台列表", example = "AI绘画,comfyUI") + private List platforms; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/DigitalHumanReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/DigitalHumanReq.java new file mode 100644 index 0000000..8f828dc --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/DigitalHumanReq.java @@ -0,0 +1,56 @@ +package top.continew.admin.business.model.req; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.starter.extension.crud.model.req.BaseReq; + +/** + * 数字人请求参数对象 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class DigitalHumanReq extends BaseReq { + + /** + * 创建数字人的用户ID + */ + private String userId; + + /** + * 用户输入的文案提示 + */ + private String copywriting; + + /** + * 生成语音文件的URL + */ + private String generatedVoiceUrl; + + /** + * 生成帧图像URL(或空字符串) + */ + private String generatedFrameImageUrl; + + /** + * 最终合成视频URL + */ + private String generatedVideoUrl; + + /** + * 字幕项列表(冗余存储,用于快速回显) + */ + private String subtitle; + + /** + * 字幕样式配置(冗余或快照) + */ + private String subtitleStyle; + + /** + * 特效列表(冗余存储) + */ + private String effects; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/EffectReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/EffectReq.java new file mode 100644 index 0000000..706ffc3 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/EffectReq.java @@ -0,0 +1,96 @@ +package top.continew.admin.business.model.req; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.starter.extension.crud.model.req.BaseReq; + +/** + * 数字人-特效请求参数对象 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class EffectReq extends BaseReq { + + /** + * 特效名称 + */ + private String name; + + /** + * 特效模板ID + */ + private Long templateId; + + /** + * 关联的数字人任务ID + */ + private Long digitalHumanId; + + /** + * 特效类型('text','image','music','audio') + */ + private String type; + + /** + * 特效资源URL + */ + private String sourceUrl; + + /** + * 特效显示位置X坐标(像素) + */ + private Integer positionX; + + /** + * 特效显示位置Y坐标(像素) + */ + private Integer positionY; + + /** + * 特效宽度(像素) + */ + private Integer sizeWidth; + + /** + * 特效高度(像素) + */ + private Integer sizeHeight; + + /** + * 特效最大持续时间(秒) + */ + private Double length; + + /** + * 特效文本内容(仅文本类型有效) + */ + private String subtitleText; + + /** + * 字幕样式配置(JSON格式) + */ + private String subtitleStyle; + + /** + * 字幕中心位置({x, y}) + */ + private String subtitlePosition; + + /** + * 特效开始偏移时间(秒) + */ + private Double offset; + + /** + * 特效播放时间段({"start": x, "end": y}) + */ + private String duration; + + /** + * 渲染层级(Z轴顺序) + */ + private Integer zIndex; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/EffectTemplateReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/EffectTemplateReq.java new file mode 100644 index 0000000..dd5b134 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/EffectTemplateReq.java @@ -0,0 +1,76 @@ +package top.continew.admin.business.model.req; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.starter.extension.crud.model.req.BaseReq; + +/** + * 数字人-特效模板请求参数对象 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class EffectTemplateReq extends BaseReq { + + /** + * 模板名称 + */ + private String name; + + /** + * 是否为用户自定义特效(true/false) + */ + private String isCustom; + + /** + * 上传自定义特效的用户ID(目前仅支持音乐) + */ + private String userId; + + /** + * 特效类型 + */ + private String type; + + /** + * 默认资源URL + */ + private String sourceUrl; + + /** + * 默认尺寸({"width": w, "height": h}) + */ + private String size; + + /** + * 默认持续时间(秒),0表示无限 + */ + private Double length; + + /** + * 默认字幕文本(仅文本类型) + */ + private String subtitleText; + + /** + * 关联的字幕样式ID + */ + private Long subtitleStyleId; + + /** + * 字幕默认X位置(可为固定值或表达式) + */ + private String subtitlePositionX; + + /** + * 字幕默认Y位置 + */ + private String subtitlePositionY; + + /** + * 封面图URL + */ + private String coverUrl; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/FreeQuotaConfigReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/FreeQuotaConfigReq.java new file mode 100644 index 0000000..80b1dce --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/FreeQuotaConfigReq.java @@ -0,0 +1,82 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import top.continew.starter.extension.crud.model.req.BaseReq; + +import java.io.Serial; +import java.math.BigDecimal; + +/** + * 免费额度配置请求信息 + * + * @author jiay + * @since 2025/11/17 + */ +@Data +@Schema(description = "免费额度配置请求信息") +public class FreeQuotaConfigReq extends BaseReq { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 类别编码 + */ + @Schema(description = "类别编码", example = "TOKEN") + private String categoryCode; + + /** + * 类别名称 + */ + @Schema(description = "类别名称", example = "大模型") + @NotBlank(message = "类别名称不能为空") + private String categoryName; + + /** + * 单位 + */ + @Schema(description = "单位", example = "token") + @NotBlank(message = "单位不能为空") + private String unit; + + /** + * 额度 + */ + @Schema(description = "额度", example = "500000") + @NotNull(message = "额度不能为空") + private BigDecimal quota; + + /** + * 计费类型 + */ + @Schema(description = "计费类型", example = "0") + private Integer chargeType; + + /** + * 账户类型 1:次数 2:点数 + */ + @Schema(description = "账户类型", example = "1") + @NotNull(message = "账户类型不能为空") + private Integer type; + + /** + * 描述 + */ + @Schema(description = "描述", example = "大模型对话额度") + private String description; + + /** + * 排序 + */ + @Schema(description = "排序", example = "1") + private Integer sort; + + /** + * 是否启用 + */ + @Schema(description = "是否启用", example = "true") + private Boolean isEnabled; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/FreeQuotaGrantReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/FreeQuotaGrantReq.java new file mode 100644 index 0000000..29f1db9 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/FreeQuotaGrantReq.java @@ -0,0 +1,94 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.List; + +/** + * 免费额度赠送请求信息 + * + * @author jiay + * @since 2025/11/17 + */ +@Data +@Schema(description = "免费额度赠送请求信息") +public class FreeQuotaGrantReq implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户ID列表 + */ + @Schema(description = "用户ID列表") + private List userIds; + + /** + * 部门ID列表 + */ + @Schema(description = "部门ID列表") + private List deptIds; + + /** + * 额度配置列表(如果为空,则使用默认配置) + */ + @Schema(description = "额度配置列表") + private List quotaItems; + + /** + * 额度配置项 + */ + @Data + @Schema(description = "额度配置项") + public static class QuotaItem implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 配置ID(从FreeQuotaConfig表获取) + */ + @Schema(description = "配置ID", example = "1") + private Long configId; + + /** + * 类别编码 + */ + @Schema(description = "类别编码", example = "TOKEN") + private String categoryCode; + + /** + * 类别名称 + */ + @Schema(description = "类别名称", example = "大模型") + private String categoryName; + + /** + * 赠送额度(如果不指定,使用配置中的默认额度) + */ + @Schema(description = "赠送额度", example = "500000") + private BigDecimal grantQuota; + + /** + * 计费类型 + */ + @Schema(description = "计费类型", example = "0") + private Integer chargeType; + + /** + * 账户类型 1:次数 2:点数 + */ + @Schema(description = "账户类型", example = "1") + private Integer type; + + /** + * 是否启用此项赠送 + */ + @Schema(description = "是否启用", example = "true") + private Boolean enabled; + } +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/HomeworkAddReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/HomeworkAddReq.java new file mode 100644 index 0000000..b33e3cf --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/HomeworkAddReq.java @@ -0,0 +1,45 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 作业实体创建参数 + * + * @author jiay + * @since 2025/05/23 07:51 + */ +@Data +@Schema(description = "作业实体创建参数") +public class HomeworkAddReq implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + @Schema(description = "用户id") + private Long userId; + + /** + * 课程id + */ + @Schema(description = "课程id") + private Long courseId; + + /** + * 未交原因 + */ + @Schema(description = "未交原因") + private String cause; + + /** + * 1、已交 2、未交 + */ + @Schema(description = "1、已交 2、未交") + private Integer handedIn; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/HomeworkEditReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/HomeworkEditReq.java new file mode 100644 index 0000000..ab26b7b --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/HomeworkEditReq.java @@ -0,0 +1,46 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.starter.extension.crud.model.req.BaseReq; + +/** + * 作业实体修改参数 + * + * @author jiay + * @since 2025/06/25 08:52 + */ +@Data +@Schema(description = "作业实体修改参数") +public class HomeworkEditReq extends BaseReq { + + /** + * id + */ + @Schema(description = "id") + private Long id; + + /** + * 用户id + */ + @Schema(description = "用户id") + private Long userId; + + /** + * 课程id + */ + @Schema(description = "课程id") + private Long courseId; + + /** + * 未交原因 + */ + @Schema(description = "未交原因") + private String cause; + + /** + * 1、已交 2、未交 + */ + @Schema(description = "1、已交 2、未交") + private Integer handedIn; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/HuandaResourceReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/HuandaResourceReq.java new file mode 100644 index 0000000..3900273 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/HuandaResourceReq.java @@ -0,0 +1,55 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.starter.extension.crud.model.req.BaseReq; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 换哒-文件资源创建或修改参数 + * + * @author 王志维 + * @since 2025/12/23 16:53 + */ +@Data +@Schema(description = "换哒-文件资源创建或修改参数") +public class HuandaResourceReq extends BaseReq implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 资源ID + */ + @Schema(description = "资源ID") + private Long id; + + /** + * 用户ID + */ + @Schema(description = "用户ID") + private String userId; + + /** + * 资源地址 + */ + @Schema(description = "资源地址") + private String url; + + /** + * 资源类型:1.服装,2模特,3自定义服装,4.自定义背景 + */ + @Schema(description = "资源类型:1.服装,2模特,3自定义服装,4.自定义背景") + private Integer resourceType; + + @Schema(description = "人物图片年龄") + private String age; + + @Schema(description = "人物图片性别:男,女") + private String gender; + + @Schema(description = "人物图片区域") + private String region; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/ModelReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/ModelReq.java new file mode 100644 index 0000000..4a00450 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/ModelReq.java @@ -0,0 +1,108 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import jakarta.validation.constraints.NotNull; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.starter.extension.crud.model.req.BaseReq; + +import java.io.Serial; + +/** + * 创建或修改模型信息 + * + * @author 张波 + * @since 2024/10/19 15:47 + */ +@Data +@Schema(description = "创建或修改模型信息") +public class ModelReq extends BaseReq { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 模型名称 + */ + @Schema(description = "模型名称", example = "测试模型") + @NotNull(message = "模型名称不能为空") + private String name; + + /** + * 平台分类 + */ + @Schema(description = "平台分类", example = "绘画") + private String platformCategory; + + /** + * 类型 + */ + @Schema(description = "类型", example = "Lora") + private String type; + + /** + * 版本 + */ + @Schema(description = "版本", example = "SDXL") + private String version; + + /** + * 分类 + */ + @Schema(description = "分类", example = "动漫游戏") + private String category; + + /** + * 专业分类 + */ + @Schema(description = "专业分类", example = "环境艺术") + private String professionalCategory; + + /** + * 系列 + */ + @Schema(description = "系列", example = "室内系列") + private String series; + + /** + * 轮播图地址 + */ + @Schema(description = "轮播图地址", example = "无") + private String images; + + /** + * 推荐分辨率 + */ + @Schema(description = "推荐分辨率", example = "1536*1024") + private String recommendedResolution; + + /** + * 提示词是否开启 + */ + @Schema(description = "提示词是否开启", example = "是/否") + private DisEnableStatusEnum promptEnabled; + + /** + * 采样方法 + */ + @Schema(description = "采样方法", example = "Euler a") + private String samplingMethod; + + /** + * CFG + */ + @Schema(description = "CFG", example = "3") + private Integer cfg; + + /** + * VAE + */ + @Schema(description = "VAE", example = "无") + private String vae; + + /** + * 采样步数 + */ + @Schema(description = "采样步数", example = "20") + private Integer samplingSteps; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/OrderAudioReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/OrderAudioReq.java new file mode 100644 index 0000000..7761a29 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/OrderAudioReq.java @@ -0,0 +1,47 @@ +package top.continew.admin.business.model.req; + +import lombok.Data; +import top.continew.starter.extension.crud.model.req.BaseReq; + +import java.io.Serial; + +/** + * 音频平台任务订单表 + * + * @author hzy + * @since 2024/10/22 11:58 + */ +@Data +public class OrderAudioReq extends BaseReq { + + @Serial + private static final long serialVersionUID = 1L; + /** + * 状态 + */ + private String state; + /** + * 删除标示 + */ + private String deleteFlag; + /** + * 任务类型 + */ + private String type; + /** + * 使用的模型 + */ + private String model; + /** + * 消耗点数 + */ + private Integer inviteBeans; + /** + * 存储结果文件路径 + */ + private String fileUrl; + /** + * 订单 类型 + */ + private Integer orderType; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/OrderVideoReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/OrderVideoReq.java new file mode 100644 index 0000000..f4c6066 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/OrderVideoReq.java @@ -0,0 +1,47 @@ +package top.continew.admin.business.model.req; + +import lombok.Data; +import top.continew.starter.extension.crud.model.req.BaseReq; + +import java.io.Serial; + +/** + * 视频平台任务订单表 + * + * @author hzy + * @since 2024/10/22 11:58 + */ +@Data +public class OrderVideoReq extends BaseReq { + + @Serial + private static final long serialVersionUID = 1L; + /** + * 状态 + */ + private String state; + /** + * 删除标示 + */ + private String deleteFlag; + /** + * 任务类型 + */ + private String type; + /** + * 使用的模型 + */ + private String model; + /** + * 消耗点数 + */ + private Integer inviteBeans; + /** + * 存储结果文件路径 + */ + private String fileUrl; + /** + * 订单 类型 + */ + private Integer orderType; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/PlantformBalanceAddReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/PlantformBalanceAddReq.java new file mode 100644 index 0000000..e3fa47a --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/PlantformBalanceAddReq.java @@ -0,0 +1,65 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 用户子账号余额表创建参数 + * + * @author jiay + * @since 2025/05/23 07:51 + */ +@Data +@Schema(description = "用户子账号余额表创建参数") +public class PlantformBalanceAddReq implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + @Schema(description = "用户id") + private Long userId; + + /** + * 账户类型 1 次数 2 点数 + */ + @Schema(description = "账户类型") + private Integer type; + + /** + * 余额 + */ + @Schema(description = "余额") + private Double balance; + + /** + * 计费类型 + */ + @Schema(description = "计费类型") + private Integer chargeType; + + /** + * 平台 + */ + @Schema(description = "平台") + private Long platformId; + + public PlantformBalanceAddReq initPlantformBalance(CheckPlantBalanceReq req) { + initPlantformBalance(); + this.platformId = req.getPlatformId(); + this.chargeType = req.getChargeType(); + this.type = req.getType(); + this.userId = req.getUserId(); + return this; + } + + public PlantformBalanceAddReq initPlantformBalance() { + this.balance = 5D; + return this; + } +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/PlantformBalanceEditReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/PlantformBalanceEditReq.java new file mode 100644 index 0000000..fb26ac7 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/PlantformBalanceEditReq.java @@ -0,0 +1,46 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.starter.extension.crud.model.req.BaseReq; + +/** + * 用户子账号余额表修改参数 + * + * @author jiay + * @since 2025/05/23 07:51 + */ +@Data +@Schema(description = "用户子账号余额表修改参数") +public class PlantformBalanceEditReq extends BaseReq { + + /** + * id + */ + @Schema(description = "id") + private Long id; + + /** + * 用户id + */ + @Schema(description = "用户id") + private Long userId; + + /** + * 账户类型 1 次数 2 点数 + */ + @Schema(description = "账户类型") + private Integer type; + + /** + * 余额 + */ + @Schema(description = "余额") + private Long balance; + + /** + * 平台 + */ + @Schema(description = "平台") + private Long platformId; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/ScoreAddReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/ScoreAddReq.java new file mode 100644 index 0000000..64a2a67 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/ScoreAddReq.java @@ -0,0 +1,69 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 分数创建参数 + * + * @author jiay + * @since 2025/06/26 10:24 + */ +@Data +@Schema(description = "分数创建参数") +public class ScoreAddReq implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + @Schema(description = "用户id") + private Long userId; + + /** + * 课程id + */ + @Schema(description = "课程id") + private Long courseId; + + /** + * 考试分数 + */ + @Schema(description = "考试分数") + private Integer examScore; + + /** + * 作业分数 + */ + @Schema(description = "作业分数") + private Integer homeworkScore; + + /** + * 签到分数 + */ + @Schema(description = "签到分数") + private Integer score; + + /** + * 最终总分数 + */ + @Schema(description = "最终总分数") + private Integer signInScore; + + /** + * 1、已考 2、未考 + */ + @Schema(description = "1、已考 2、未考") + private Integer takeExam; + + /** + * 未考试原因 + */ + @Schema(description = "未考试原因") + private String cause; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/ScoreEditReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/ScoreEditReq.java new file mode 100644 index 0000000..4f5e5a1 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/ScoreEditReq.java @@ -0,0 +1,69 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.starter.extension.crud.model.req.BaseReq; + +/** + * 分数修改参数 + * + * @author jiay + * @since 2025/05/23 07:51 + */ +@Data +@Schema(description = "分数修改参数") +public class ScoreEditReq extends BaseReq { + + /** + * id + */ + @Schema(description = "id") + private Long id; + /** + * 用户id + */ + @Schema(description = "用户id") + private Long userId; + + /** + * 课程id + */ + @Schema(description = "课程id") + private Long courseId; + + /** + * 考试分数 + */ + @Schema(description = "考试分数") + private Integer examScore; + + /** + * 作业分数 + */ + @Schema(description = "作业分数") + private Integer homeworkScore; + + /** + * 签到分数 + */ + @Schema(description = "签到分数") + private Integer score; + + /** + * 最终总分数 + */ + @Schema(description = "最终总分数") + private Integer signInScore; + + /** + * 1、已考 2、未考 + */ + @Schema(description = "1、已考 2、未考") + private Integer takeExam; + + /** + * 未考试原因 + */ + @Schema(description = "未考试原因") + private String cause; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/SignInAddReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/SignInAddReq.java new file mode 100644 index 0000000..6f3cff6 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/SignInAddReq.java @@ -0,0 +1,64 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 签到创建参数 + * + * @author jiay + * @since 2025/05/23 07:51 + */ +@Data +@Schema(description = "签到创建参数") +public class SignInAddReq implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + @Schema(description = "用户id") + private Long userId; + + private String userName; + + /** + * 学号 + */ + private String stuNo; + + /** + * 手机 + */ + @Schema(description = "手机") + private String phone; + + /** + * 课程id + */ + @Schema(description = "课程id") + private Long courseId; + private String courseName; + + /** + * 请假原因 + */ + @Schema(description = "请假原因") + private String leaveCause; + + /** + * 1、签到 2、请假 3、旷课 + */ + @Schema(description = " 1、签到 2、请假 3、旷课") + private Integer signIn; + + /** + * 经纬度 + */ + private String Location; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/SignInEditReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/SignInEditReq.java new file mode 100644 index 0000000..a992a17 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/SignInEditReq.java @@ -0,0 +1,61 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.starter.extension.crud.model.req.BaseReq; + +/** + * 签到修改参数 + * + * @author jiay + * @since 2025/05/23 07:51 + */ +@Data +@Schema(description = "签到修改参数") +public class SignInEditReq extends BaseReq { + /** + * id + */ + @Schema(description = "id") + private Long id; + + /** + * 用户id + */ + @Schema(description = "用户id") + private Long userId; + private String userName; + /** + * 学号 + */ + private String stuNo; + + /** + * 手机 + */ + @Schema(description = "手机") + private String phone; + + /** + * 课程id + */ + @Schema(description = "课程id") + private Long courseId; + private String courseName; + /** + * 请假原因 + */ + @Schema(description = "请假原因") + private String leaveCause; + + /** + * 1、签到 2、请假 3、旷课 + */ + @Schema(description = " 1、签到 2、请假 3、旷课") + private Integer signIn; + + /** + * 经纬度 + */ + private String Location; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/SubtitleItemReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/SubtitleItemReq.java new file mode 100644 index 0000000..2c8dfce --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/SubtitleItemReq.java @@ -0,0 +1,36 @@ +package top.continew.admin.business.model.req; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.starter.extension.crud.model.req.BaseReq; + +/** + * 数字人-字幕项请求参数对象 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class SubtitleItemReq extends BaseReq { + + /** + * 关联的数字人ID + */ + private Long digitalHumanId; + + /** + * 字幕文本内容 + */ + private String text; + + /** + * 字幕开始时间(秒) + */ + private Double startTime; + + /** + * 字幕结束时间(秒) + */ + private Double endTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/SubtitleStyleReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/SubtitleStyleReq.java new file mode 100644 index 0000000..0020907 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/SubtitleStyleReq.java @@ -0,0 +1,66 @@ +package top.continew.admin.business.model.req; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.starter.extension.crud.model.req.BaseReq; + +/** + * 数字人-字幕样式请求参数对象 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class SubtitleStyleReq extends BaseReq { + + /** + * 关联的数字人ID(可为空,用于全局/默认样式) + */ + private Long digitalHumanId; + + /** + * 字体大小(像素) + */ + private Integer fontSize; + + /** + * 字体族(如微软雅黑、Arial等) + */ + private String fontFamily; + + /** + * 文字颜色(CSS格式,如 #FFFFFF) + */ + private String color; + + /** + * 描边颜色 + */ + private String strokeColor; + + /** + * 描边宽度(像素) + */ + private Integer strokeWidth; + + /** + * 阴影颜色 + */ + private String shadowColor; + + /** + * 阴影模糊半径(像素) + */ + private Integer shadowBlur; + + /** + * 底部内边距(像素) + */ + private Integer paddingBottom; + + /** + * 行高(像素或倍数) + */ + private Integer lineHeight; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/TaskCallbackReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/TaskCallbackReq.java new file mode 100644 index 0000000..8368b73 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/TaskCallbackReq.java @@ -0,0 +1,130 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +/** + * 任务回调请求参数 + * + * @author jiay + * @since 2025/08/28 16:35 + */ +@Data +public class TaskCallbackReq { + + /** + * 平台编码 + */ + @Schema(description = "平台编码") + @NotBlank(message = "平台编码不能为空") + private String platformCode; + + /** + * 生成平台 + */ + @Schema(description = "生成平台ID") + @NotNull(message = "生成平台不能为空") + private Long platformId; + + /** + * 任务订单号 + */ + @Schema(description = "任务订单号") + @NotBlank(message = "任务订单号不能为空") + private String taskId; + + /** + * 文件名 + */ + @Schema(description = "文件名") + private String title; + + /** + * 使用的模型 + */ + @Schema(description = "使用的模型") + private String modelName; + + /** + * 扣费编码 + */ + @Schema(description = "扣费编码", example = "C0001") + private String chargeCode; + + /** + * 实际生成数量 + */ + @Schema(description = "实际生成数量", example = "1") + private Integer quantity; + + /** + * 任务状态 (0:进行中, 1:成功, 2:失败) + */ + @Schema(description = "任务状态 (0:进行中, 1:成功, 2:失败)") + @NotNull(message = "任务状态不能为空") + private Integer status; + + /** + * 生成的文字内容(文字类型时填写) + */ + @Schema(description = "生成的文字内容(文字类型时填写)") + private String result; + + /** + * 消费的token数(文字类型时填写) + */ + @Schema(description = "消费的token数(文字类型时填写)") + private Long tokens; + + /** + * 文件路径(音视频图片类型时填写) + */ + @Schema(description = "文件路径(音视频图片类型时填写)") + private String fileUrl; + + /** + * 错误消息(失败时填写) + */ + @Schema(description = "错误消息(失败时填写)") + private String errorMessage; + + /** + * 计费类型(token:0, 图:1,音效生成:3,参考生视频:4, 白膜贴图:5) + */ + @Schema(description = "计费类型(token:0, 图:1, 音效:3, 视频:4, 白膜贴图:5)") + @NotNull(message = "计费类型不能为空") + private Integer chargeType; + + /** + * * 任务类型(token:0, 文生图:1,图生图:2,音效生成:3,参考生视频:4, 白膜贴图:5) + */ + @Schema(description = "任务类型(token:0, 文生图:1,图生图:2,音效生成:3,参考生视频:4, 白膜贴图:5)") + @NotNull(message = "任务类型不能为空") + private Integer taskType; + + /** + * 计费类型(0充点,1按次数,2按点数) + */ + @Schema(description = "消费类型(0充点,1按次数,2按点数)") + @NotNull(message = "消费类型不能为空") + private Integer type; + + /** + * 文件类型 + */ + @Schema(description = "文件类型") + private String fileType; + + @Schema(description = "父任务ID") + private String parentTaskId; + + @Schema(description = "父任务创建时间") + private String parentCreateTime; + + @Schema(description = "根任务ID") + @NotBlank(message = "根任务ID不能为空") + private String taskRootId; + +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/TaskCallbackWithAmountReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/TaskCallbackWithAmountReq.java new file mode 100644 index 0000000..253ed71 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/TaskCallbackWithAmountReq.java @@ -0,0 +1,25 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import jakarta.validation.constraints.NotNull; + +/** + * 任务回调请求参数(带第三方平台传入的实际消费金额) + * + * @author jiay + * @since 2025/11/14 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class TaskCallbackWithAmountReq extends TaskCallbackReq { + + /** + * 实际消费金额(分)- 由第三方平台传入 + */ + @Schema(description = "实际消费金额(分)", example = "100") + @NotNull(message = "实际消费金额不能为空") + private Long actualAmount; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/TaskQueryReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/TaskQueryReq.java new file mode 100644 index 0000000..cad2550 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/TaskQueryReq.java @@ -0,0 +1,32 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.Min; +import lombok.Data; + +import java.io.Serializable; + +/** + * 任务查询请求参数 + * + * @author wangzhiwei + * @since 2025/08/28 16:35 + */ +@Data +public class TaskQueryReq implements Serializable { + + @Schema(description = "根任务ID") + @NotBlank(message = "根任务ID不能为空") + private String taskRootId; + + @Min(value = 1, message = "查询页码必须大于0") + @Schema(description = "查询页码") + private int page = 1; + + @Positive(message = "每页数量必须大于0") + @Schema(description = "每页数量") + private int size = 10; + +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/TaskRecordHistoryAddReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/TaskRecordHistoryAddReq.java new file mode 100644 index 0000000..a9c814e --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/TaskRecordHistoryAddReq.java @@ -0,0 +1,74 @@ +package top.continew.admin.business.model.req; + +import jakarta.validation.constraints.*; + +import lombok.Data; + +import io.swagger.v3.oas.annotations.media.Schema; + +import java.io.Serial; +import java.io.Serializable; +import java.time.*; +import java.util.List; + +/** + * 音视图文生成记录创建或修改参数 + * + * @author jiay + * @since 2025/05/23 07:51 + */ +@Data +@Schema(description = "音视图文生成记录创建或修改参数") +public class TaskRecordHistoryAddReq implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + @Schema(description = "用户id") + private String userId; + + /** + * 生成平台 + */ + @Schema(description = "生成平台") + private Long platformId; + + /** + * 任务订单号 + */ + @Schema(description = "任务订单号") + private String taskId; + + /** + * 文件名 + */ + @Schema(description = "文件名") + private String title; + + /** + * 任务类型(token:0, 文生图:1,图生图:2,音效生成:3,参考生视频:4, 白膜贴图:5) + */ + @Schema(description = "任务类型(token:0, 文生图:1,图生图:2,音效生成:3,参考生视频:4, 白膜贴图:5)") + private Integer taskType; + + /** + * 存储结果文件路径 + */ + @Schema(description = "存储结果文件路径") + private List fileUrl; + + /** + * 创建人 + */ + @Schema(description = "创建人") + private Long createUser; + + /** + * 创建时间 + */ + @Schema(description = "创建时间") + private LocalDateTime createTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/req/TaskRecordHistoryEditReq.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/TaskRecordHistoryEditReq.java new file mode 100644 index 0000000..5b099f6 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/req/TaskRecordHistoryEditReq.java @@ -0,0 +1,72 @@ +package top.continew.admin.business.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.starter.extension.crud.model.req.BaseReq; + +import java.time.LocalDateTime; + +/** + * 音视图文生成记录创建或修改参数 + * + * @author jiay + * @since 2025/05/23 07:51 + */ +@Data +@Schema(description = "音视图文生成记录创建或修改参数") +public class TaskRecordHistoryEditReq extends BaseReq { + + /** + * id + */ + @Schema(description = "id") + private Long id; + + /** + * 生成平台 + */ + @Schema(description = "生成平台") + private Long platformId; + + /** + * 用户id + */ + @Schema(description = "用户id") + private Long userId; + + /** + * 任务订单号 + */ + @Schema(description = "任务订单号") + private String taskId; + + /** + * 文件名 + */ + @Schema(description = "文件名") + private String title; + + /** + * 任务类型(token:0, 文生图:1,图生图:2,音效生成:3,参考生视频:4, 白膜贴图:5) + */ + @Schema(description = "任务类型(token:0, 文生图:1,图生图:2,音效生成:3,参考生视频:4, 白膜贴图:5)") + private Integer taskType; + + /** + * 存储结果文件路径 + */ + @Schema(description = "存储结果文件路径") + private String fileUrl; + + /** + * 创建人 + */ + @Schema(description = "创建人") + private Long createUser; + + /** + * 创建时间 + */ + @Schema(description = "创建时间") + private LocalDateTime createTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/AssignmentsDetailResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/AssignmentsDetailResp.java new file mode 100644 index 0000000..e9206cc --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/AssignmentsDetailResp.java @@ -0,0 +1,80 @@ +package top.continew.admin.business.model.resp; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.starter.extension.crud.model.resp.BaseDetailResp; + +import java.io.Serial; +import java.time.LocalDateTime; + +/** + * 作业表详情信息 + * + * @author 张波 + * @since 2024/10/22 11:45 + */ +@Data +@ExcelIgnoreUnannotated +@Schema(description = "作业表详情信息") +public class AssignmentsDetailResp extends BaseDetailResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 课程表ID + */ + @Schema(description = "课程表ID") + @ExcelProperty(value = "课程表ID") + private Long courseId; + + /** + * 分类(1:作业,2:考试) + */ + @Schema(description = "分类(1:作业,2:考试)") + @ExcelProperty(value = "分类(1:作业,2:考试)") + private Integer type; + + /** + * 作业名称 + */ + @Schema(description = "作业名称") + @ExcelProperty(value = "作业名称") + private String name; + + /** + * 作业要求 + */ + @Schema(description = "作业要求") + @ExcelProperty(value = "作业要求") + private String requirements; + + /** + * 满分 + */ + @Schema(description = "满分") + @ExcelProperty(value = "满分") + private Integer fullScore; + + /** + * 开始时间 + */ + @Schema(description = "开始时间") + @ExcelProperty(value = "开始时间") + private LocalDateTime startTime; + + /** + * 结束时间 + */ + @Schema(description = "结束时间") + @ExcelProperty(value = "结束时间") + private LocalDateTime endTime; + + /** + * 示例图片 + */ + @Schema(description = "示例图片") + private String exampleImages; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/AssignmentsProgressResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/AssignmentsProgressResp.java new file mode 100644 index 0000000..d981010 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/AssignmentsProgressResp.java @@ -0,0 +1,34 @@ +package top.continew.admin.business.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * @author 张波 + * @since 2024/10/23 21:08 + */ +@Data +@Schema(description = "作业进度") +public class AssignmentsProgressResp { + + @Schema(description = "提交记录ID") + private Long submissionsId; + + /** + * 学生姓名 + */ + @Schema(description = "学生姓名") + private String nickname; + + /** + * 完成情况 + */ + @Schema(description = "是否提交作业") + private Boolean degreeOfCompletion; + + /** + * 批改情况 + */ + @Schema(description = "是否批改作业") + private Boolean degreeOfCorrection; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/AssignmentsResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/AssignmentsResp.java new file mode 100644 index 0000000..6e3d8b1 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/AssignmentsResp.java @@ -0,0 +1,51 @@ +package top.continew.admin.business.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.starter.extension.crud.model.resp.BaseResp; + +import java.io.Serial; + +/** + * 作业表信息 + * + * @author 张波 + * @since 2024/10/22 11:45 + */ +@Data +@Schema(description = "作业表信息") +public class AssignmentsResp extends BaseResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 课程表ID + */ + @Schema(description = "课程表ID") + private Long courseId; + + /** + * 作业名称 + */ + @Schema(description = "作业名称") + private String name; + + /** + * 完成情况 + */ + @Schema(description = "完成情况,学生为已完成/未完成,老师为百分比", example = "100") + private String degreeOfCompletion; + + /** + * 示例图片 + */ + private String exampleImages; + + /** + * 学生角色查询如果已提交作业返回作业提交ID + */ + @Schema(description = "学生角色查询如果已提交作业返回作业提交ID") + private Long assignmentsSubmissionsId; + +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/AssignmentsSubmissionsDetailResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/AssignmentsSubmissionsDetailResp.java new file mode 100644 index 0000000..ecb2541 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/AssignmentsSubmissionsDetailResp.java @@ -0,0 +1,88 @@ +package top.continew.admin.business.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.starter.extension.crud.model.resp.BaseDetailResp; + +import java.io.Serial; +import java.time.LocalDateTime; + +/** + * 作业提交记录表详情信息 + * + * @author 张波 + * @since 2024/10/22 11:45 + */ +@Data +@Schema(description = "作业提交记录表详情信息") +public class AssignmentsSubmissionsDetailResp extends BaseDetailResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 作业名称 + */ + @Schema(description = "作业名称") + private String name; + + /** + * 作业要求 + */ + @Schema(description = "作业要求") + private String requirements; + + /** + * 满分 + */ + @Schema(description = "满分") + private Integer fullScore; + + /** + * 开始时间 + */ + @Schema(description = "开始时间") + private LocalDateTime startTime; + + /** + * 结束时间 + */ + @Schema(description = "结束时间") + private LocalDateTime endTime; + + /** + * 示例图片 + */ + @Schema(description = "示例图片") + private String exampleImages; + + /** + * 图片标题 + */ + @Schema(description = "图片标题") + private String imageTitle; + + /** + * 图片标签 + */ + @Schema(description = "图片标签") + private String imageTag; + + /** + * 图片地址 + */ + @Schema(description = "图片地址") + private String imageUrl; + + /** + * 分数 + */ + @Schema(description = "分数") + private Integer score; + + /** + * 评语 + */ + @Schema(description = "评语") + private String feedback; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/BillingDeductResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/BillingDeductResp.java new file mode 100644 index 0000000..cd641ce --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/BillingDeductResp.java @@ -0,0 +1,34 @@ +package top.continew.admin.business.model.resp; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 计费扣减响应参数 + * + * @author jiay + * @since 2025/08/28 00:00 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BillingDeductResp { + + /** + * 扣减是否成功 + */ + private boolean success; + + /** + * 用户剩余余额 + */ + private Integer remainingBalance; + + /** + * 错误消息 + */ + private String errorMessage; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/BillingRuleResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/BillingRuleResp.java new file mode 100644 index 0000000..7c32a0f --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/BillingRuleResp.java @@ -0,0 +1,108 @@ +package top.continew.admin.business.model.resp; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.starter.extension.crud.model.resp.BaseResp; + +import java.io.Serial; + +/** + * 计费规则信息 + * + * @author jiay + * @since 2025/11/14 + */ +@Data +@ExcelIgnoreUnannotated +@Schema(description = "计费规则信息") +public class BillingRuleResp extends BaseResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 平台名称 + */ + @Schema(description = "平台名称", example = "AI绘画") + @ExcelProperty(value = "平台名称", order = 2) + private String platformName; + + /** + * 账户类型 + */ + @Schema(description = "账户类型", example = "图片") + @ExcelProperty(value = "账户类型", order = 3) + private String type; + + /** + * 平台ID + */ + @Schema(description = "平台ID", example = "10000") + @ExcelProperty(value = "平台ID", order = 4) + private Long platformId; + + /** + * 平台编码 + */ + @Schema(description = "平台编码", example = "KEXUE_CHAT") + @ExcelProperty(value = "平台编码", order = 5) + private String platformCode; + + /** + * 扣费编码 + */ + @Schema(description = "扣费编码", example = "C0001") + @ExcelProperty(value = "扣费编码", order = 6) + private String chargeCode; + + /** + * 计费类型 + */ + @Schema(description = "计费类型", example = "1") + @ExcelProperty(value = "计费类型", order = 7) + private Integer chargeType; + + /** + * 模型名称 + */ + @Schema(description = "模型名称", example = "flux") + @ExcelProperty(value = "模型名称", order = 8) + private String modelName; + + /** + * 主账户单价(分/次/整数量) + */ + @Schema(description = "主账户单价(分/次/整数量)", example = "13") + @ExcelProperty(value = "主账户单价(分)", order = 9) + private Long price; + + /** + * 子账户单价(次/个/数量) + */ + @Schema(description = "子账户单价(次/个/数量)", example = "1") + @ExcelProperty(value = "子账户单价", order = 10) + private Long subAccountPrice; + + /** + * 计费单位 + */ + @Schema(description = "计费单位", example = "张") + @ExcelProperty(value = "计费单位", order = 11) + private String unit; + + /** + * 备注 + */ + @Schema(description = "备注", example = "") + @ExcelProperty(value = "备注", order = 12) + private String remark; + + /** + * 是否启用(0:禁用 1:启用) + */ + @Schema(description = "是否启用", example = "1") + @ExcelProperty(value = "是否启用", order = 13) + private Integer enabled; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/CheckBalanceResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/CheckBalanceResp.java new file mode 100644 index 0000000..10d7f92 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/CheckBalanceResp.java @@ -0,0 +1,65 @@ +package top.continew.admin.business.model.resp; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.starter.extension.crud.model.resp.BaseResp; + +import java.io.Serial; + +/** + * @Author: jia + * @Date: 2025/05/26/16:26 + * @Description: + */ +@Data +@ExcelIgnoreUnannotated +@Schema(description = "用户充值消费历史记录详情信息") +public class CheckBalanceResp extends BaseResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + @Schema(description = "用户id") + @ExcelProperty(value = "用户id") + private Long userId; + + /** + * 任务订单号 + */ + @Schema(description = "任务订单号") + @ExcelProperty(value = "任务订单号") + private String taskId; + + /** + * 任务类型(token:0, 图:1,音效生成:3,参考生视频:4, 白膜贴图:5) + */ + @Schema(description = "任务类型(token:0, 图:1,音效生成:3,参考生视频:4, 白膜贴图:5)") + @ExcelProperty(value = "任务类型(token:0, 图:1,音效生成:3,参考生视频:4, 白膜贴图:5)") + private Integer taskType; + + /** + * 使用的模型 + */ + @Schema(description = "使用的模型") + @ExcelProperty(value = "使用的模型") + private String modelName; + + /** + * 消耗点数 + */ + @Schema(description = "消耗点数") + @ExcelProperty(value = "消耗点数") + private Long consumptionPoints; + + /** + * 存储结果文件路径 + */ + @Schema(description = "存储结果文件路径") + @ExcelProperty(value = "存储结果文件路径") + private Integer fileUrl; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/ConsumptionHistoryDetailResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/ConsumptionHistoryDetailResp.java new file mode 100644 index 0000000..f71b22a --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/ConsumptionHistoryDetailResp.java @@ -0,0 +1,67 @@ +package top.continew.admin.business.model.resp; + +import lombok.Data; + +import io.swagger.v3.oas.annotations.media.Schema; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import top.continew.starter.extension.crud.model.resp.BaseDetailResp; +import java.io.Serial; + +/** + * 用户充值消费历史记录详情信息 + * + * @author jiay + * @since 2025/05/23 07:51 + */ +@Data +@ExcelIgnoreUnannotated +@Schema(description = "用户充值消费历史记录详情信息") +public class ConsumptionHistoryDetailResp extends BaseDetailResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + @Schema(description = "用户id") + @ExcelProperty(value = "用户id") + private Long userId; + + /** + * 生成平台 + */ + @Schema(description = "生成平台") + @ExcelProperty(value = "生成平台") + private Long platformId; + + /** + * 任务类型(token:0, 文生图:1,图生图:2,音效生成:3,参考生视频:4, 白膜贴图:5) + */ + @Schema(description = "任务类型(token:0, 文生图:1,图生图:2,音效生成:3,参考生视频:4, 白膜贴图:5)") + @ExcelProperty(value = "任务类型(token:0, 文生图:1,图生图:2,音效生成:3,参考生视频:4, 白膜贴图:5)") + private Integer taskType; + + /** + * 点数量 + */ + @Schema(description = "点数量") + @ExcelProperty(value = "点数量") + private Long chargePoints; + + /** + * 剩余的点数 + */ + @Schema(description = "剩余的点数") + @ExcelProperty(value = "剩余的点数") + private Long remainingPoints; + + /** + * 记录类型(0充点 1 次数 2 点数) + */ + @Schema(description = "记录类型(0充点 1 次数 2 点数)") + @ExcelProperty(value = "记录类型(0充点 1 次数 2 点数)") + private Integer chargeType; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/ConsumptionHistoryResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/ConsumptionHistoryResp.java new file mode 100644 index 0000000..415e991 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/ConsumptionHistoryResp.java @@ -0,0 +1,71 @@ +package top.continew.admin.business.model.resp; + +import lombok.Data; + +import io.swagger.v3.oas.annotations.media.Schema; +import top.continew.starter.extension.crud.model.resp.BaseResp; + +import java.io.Serial; +import java.time.*; + +/** + * 用户充值消费历史记录信息 + * + * @author jiay + * @since 2025/05/23 07:51 + */ +@Data +@Schema(description = "用户充值消费历史记录信息") +public class ConsumptionHistoryResp extends BaseResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + @Schema(description = "用户id") + private Long userId; + + /** + * 生成平台 + */ + @Schema(description = "生成平台") + private Long platformId; + + /** + * 任务类型(token:0, 文生图:1,图生图:2,音效生成:3,参考生视频:4, 白膜贴图:5) + */ + @Schema(description = "任务类型(token:0, 文生图:1,图生图:2,音效生成:3,参考生视频:4, 白膜贴图:5)") + private Integer taskType; + + /** + * 点数量 + */ + @Schema(description = "点数量") + private Long chargePoints; + + /** + * 剩余的点数 + */ + @Schema(description = "剩余的点数") + private Long remainingPoints; + + /** + * 记录类型(0充点 1 次数 2 点数) + */ + @Schema(description = "记录类型(0充点 1 次数 2 点数)") + private Integer type; + + /** + * 修改人 + */ + @Schema(description = "修改人") + private Long updateUser; + + /** + * 修改时间 + */ + @Schema(description = "修改时间") + private LocalDateTime updateTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/CopyrightDetailResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/CopyrightDetailResp.java new file mode 100644 index 0000000..cef248b --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/CopyrightDetailResp.java @@ -0,0 +1,43 @@ +package top.continew.admin.business.model.resp; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.starter.extension.crud.model.resp.BaseResp; + +import java.time.LocalDateTime; + +/** + * 数字人-文案详情响应对象 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class CopyrightDetailResp extends BaseResp { + + /** + * 文案 ID + */ + private Long id; + + /** + * 用户 ID + */ + private String userId; + + /** + * 文案内容 + */ + private String text; + + /** + * 文案创建日期 + */ + private LocalDateTime createTime; + + /** + * 更新时间 + */ + private LocalDateTime updatedTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/CopyrightResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/CopyrightResp.java new file mode 100644 index 0000000..4986785 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/CopyrightResp.java @@ -0,0 +1,43 @@ +package top.continew.admin.business.model.resp; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.starter.extension.crud.model.resp.BaseResp; + +import java.time.LocalDateTime; + +/** + * 数字人-文案响应对象 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class CopyrightResp extends BaseResp { + + /** + * 文案 ID + */ + private Long id; + + /** + * 用户 ID + */ + private String userId; + + /** + * 文案内容 + */ + private String text; + + /** + * 文案创建日期 + */ + private LocalDateTime createTime; + + /** + * 更新时间 + */ + private LocalDateTime updatedTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/CoursesResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/CoursesResp.java new file mode 100644 index 0000000..b88c984 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/CoursesResp.java @@ -0,0 +1,46 @@ +package top.continew.admin.business.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.starter.extension.crud.model.resp.BaseResp; + +import java.time.LocalDateTime; + +/** + * @author 张波 + * @since 2024/10/22 14:34 + */ +@Data +@Schema(description = "课程表信息") +public class CoursesResp extends BaseResp { + + /** + * 课程名称 + */ + @Schema(description = "课程名称") + private String name; + + /** + * 班级 + */ + @Schema(description = "班级") + private String className; + + /** + * 封面 + */ + @Schema(description = "封面") + private String coverImage; + + /** + * 开始时间 + */ + @Schema(description = "开始时间") + private LocalDateTime startTime; + + /** + * 结束时间 + */ + @Schema(description = "结束时间") + private LocalDateTime endTime; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/DigitalHumanDetailResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/DigitalHumanDetailResp.java new file mode 100644 index 0000000..4f4baf6 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/DigitalHumanDetailResp.java @@ -0,0 +1,71 @@ +package top.continew.admin.business.model.resp; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.starter.extension.crud.model.resp.BaseResp; + +/** + * 数字人详情响应对象 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class DigitalHumanDetailResp extends BaseResp { + + /** + * 数字人唯一标识符 + */ + private Long id; + + /** + * 创建数字人的用户ID + */ + private String userId; + + /** + * 用户输入的文案提示 + */ + private String copywriting; + + /** + * 生成语音文件的URL + */ + private String generatedVoiceUrl; + + /** + * 生成帧图像URL(或空字符串) + */ + private String generatedFrameImageUrl; + + /** + * 最终合成视频URL + */ + private String generatedVideoUrl; + + /** + * 字幕项列表(冗余存储,用于快速回显) + */ + private String subtitle; + + /** + * 字幕样式配置(冗余或快照) + */ + private String subtitleStyle; + + /** + * 特效列表(冗余存储) + */ + private String effects; + + /** + * 创建时间 + */ + private java.time.LocalDateTime createTime; + + /** + * 更新时间 + */ + private java.time.LocalDateTime updateTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/DigitalHumanResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/DigitalHumanResp.java new file mode 100644 index 0000000..c81d6d7 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/DigitalHumanResp.java @@ -0,0 +1,80 @@ +package top.continew.admin.business.model.resp; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.admin.business.model.entity.Effect; +import top.continew.admin.business.model.entity.SubtitleItem; +import top.continew.admin.business.model.entity.SubtitleStyle; +import top.continew.starter.extension.crud.model.resp.BaseResp; + +import java.util.List; + +/** + * 数字人响应对象 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class DigitalHumanResp extends BaseResp { + + /** + * 数字人唯一标识符 + */ + private Long id; + + /** + * 创建数字人的用户ID + */ + private String userId; + + /** + * 用户输入的文案提示 + */ + private String copywriting; + + /** + * 生成语音文件的URL + */ + private String generatedVoiceUrl; + + /** + * 生成帧图像URL(或空字符串) + */ + private String generatedFrameImageUrl; + + /** + * 最终合成视频URL + */ + private String generatedVideoUrl; + +// /** +// * 字幕项列表(冗余存储,用于快速回显) +// */ +// private String subtitle; +// +// /** +// * 字幕样式配置(冗余或快照) +// */ +// private String subtitleStyle; +// +// /** +// * 特效列表(冗余存储) +// */ +// private String effects; + + /** + * 创建时间 + */ + private java.time.LocalDateTime createTime; + + /** + * 更新时间 + */ + private java.time.LocalDateTime updateTime; + + private List subtitles; + private SubtitleStyle subtitleStyle; + private List effects; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/EffectDetailResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/EffectDetailResp.java new file mode 100644 index 0000000..0d71c66 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/EffectDetailResp.java @@ -0,0 +1,111 @@ +package top.continew.admin.business.model.resp; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.starter.extension.crud.model.resp.BaseResp; + +/** + * 数字人-特效详情响应对象 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class EffectDetailResp extends BaseResp { + + /** + * 特效ID(通常来自模板或任务生成) + */ + private Long id; + + /** + * 特效名称 + */ + private String name; + + /** + * 特效模板ID + */ + private Long templateId; + + /** + * 关联的数字人任务ID + */ + private Long digitalHumanId; + + /** + * 特效类型('text','image','music','audio') + */ + private String type; + + /** + * 特效资源URL + */ + private String sourceUrl; + + /** + * 特效显示位置X坐标(像素) + */ + private Integer positionX; + + /** + * 特效显示位置Y坐标(像素) + */ + private Integer positionY; + + /** + * 特效宽度(像素) + */ + private Integer sizeWidth; + + /** + * 特效高度(像素) + */ + private Integer sizeHeight; + + /** + * 特效最大持续时间(秒) + */ + private Double length; + + /** + * 特效文本内容(仅文本类型有效) + */ + private String subtitleText; + + /** + * 字幕样式配置(JSON格式) + */ + private String subtitleStyle; + + /** + * 字幕中心位置({x, y}) + */ + private String subtitlePosition; + + /** + * 特效开始偏移时间(秒) + */ + private Double offset; + + /** + * 特效播放时间段({"start": x, "end": y}) + */ + private String duration; + + /** + * 渲染层级(Z轴顺序) + */ + private Integer zIndex; + + /** + * 创建时间 + */ + private java.time.LocalDateTime createTime; + + /** + * 更新时间 + */ + private java.time.LocalDateTime updateTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/EffectResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/EffectResp.java new file mode 100644 index 0000000..93ea881 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/EffectResp.java @@ -0,0 +1,111 @@ +package top.continew.admin.business.model.resp; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.starter.extension.crud.model.resp.BaseResp; + +/** + * 数字人-特效响应对象 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class EffectResp extends BaseResp { + + /** + * 特效ID(通常来自模板或任务生成) + */ + private Long id; + + /** + * 特效名称 + */ + private String name; + + /** + * 特效模板ID + */ + private Long templateId; + + /** + * 关联的数字人任务ID + */ + private Long digitalHumanId; + + /** + * 特效类型('text','image','music','audio') + */ + private String type; + + /** + * 特效资源URL + */ + private String sourceUrl; + + /** + * 特效显示位置X坐标(像素) + */ + private Integer positionX; + + /** + * 特效显示位置Y坐标(像素) + */ + private Integer positionY; + + /** + * 特效宽度(像素) + */ + private Integer sizeWidth; + + /** + * 特效高度(像素) + */ + private Integer sizeHeight; + + /** + * 特效最大持续时间(秒) + */ + private Double length; + + /** + * 特效文本内容(仅文本类型有效) + */ + private String subtitleText; + + /** + * 字幕样式配置(JSON格式) + */ + private String subtitleStyle; + + /** + * 字幕中心位置({x, y}) + */ + private String subtitlePosition; + + /** + * 特效开始偏移时间(秒) + */ + private Double offset; + + /** + * 特效播放时间段({"start": x, "end": y}) + */ + private String duration; + + /** + * 渲染层级(Z轴顺序) + */ + private Integer zIndex; + + /** + * 创建时间 + */ + private java.time.LocalDateTime createTime; + + /** + * 更新时间 + */ + private java.time.LocalDateTime updateTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/EffectTemplateDetailResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/EffectTemplateDetailResp.java new file mode 100644 index 0000000..05525f1 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/EffectTemplateDetailResp.java @@ -0,0 +1,91 @@ +package top.continew.admin.business.model.resp; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.starter.extension.crud.model.resp.BaseResp; + +/** + * 数字人-特效模板详情响应对象 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class EffectTemplateDetailResp extends BaseResp { + + /** + * 特效模板ID + */ + private Long id; + + /** + * 模板名称 + */ + private String name; + + /** + * 是否为用户自定义特效(true/false) + */ + private String isCustom; + + /** + * 上传自定义特效的用户ID(目前仅支持音乐) + */ + private String userId; + + /** + * 特效类型 + */ + private String type; + + /** + * 默认资源URL + */ + private String sourceUrl; + + /** + * 默认尺寸({"width": w, "height": h}) + */ + private String size; + + /** + * 默认持续时间(秒),0表示无限 + */ + private Double length; + + /** + * 默认字幕文本(仅文本类型) + */ + private String subtitleText; + + /** + * 关联的字幕样式ID + */ + private Long subtitleStyleId; + + /** + * 字幕默认X位置(可为固定值或表达式) + */ + private String subtitlePositionX; + + /** + * 字幕默认Y位置 + */ + private String subtitlePositionY; + + /** + * 封面图URL + */ + private String coverUrl; + + /** + * 创建时间 + */ + private java.time.LocalDateTime createTime; + + /** + * 更新时间 + */ + private java.time.LocalDateTime updateTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/EffectTemplateResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/EffectTemplateResp.java new file mode 100644 index 0000000..d3acf05 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/EffectTemplateResp.java @@ -0,0 +1,93 @@ +package top.continew.admin.business.model.resp; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.starter.extension.crud.model.resp.BaseResp; + +/** + * 数字人-特效模板响应对象 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class EffectTemplateResp extends BaseResp { + + /** + * 特效模板ID + */ + private Long id; + + /** + * 模板名称 + */ + private String name; + + /** + * 是否为用户自定义特效(true/false) + */ + private String isCustom; + + /** + * 上传自定义特效的用户ID(目前仅支持音乐) + */ + private String userId; + + /** + * 特效类型 + */ + private String type; + + /** + * 默认资源URL + */ + private String sourceUrl; + + /** + * 默认尺寸({"width": w, "height": h}) + */ + private String size_width; + + private String size_height; + + /** + * 默认持续时间(秒),0表示无限 + */ + private Double length; + + /** + * 默认字幕文本(仅文本类型) + */ + private String subtitleText; + + /** + * 关联的字幕样式ID + */ + private Long subtitleStyleId; + + /** + * 字幕默认X位置(可为固定值或表达式) + */ + private String subtitlePositionX; + + /** + * 字幕默认Y位置 + */ + private String subtitlePositionY; + + /** + * 封面图URL + */ + private String coverUrl; + + /** + * 创建时间 + */ + private java.time.LocalDateTime createTime; + + /** + * 更新时间 + */ + private java.time.LocalDateTime updateTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/FreeQuotaConfigResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/FreeQuotaConfigResp.java new file mode 100644 index 0000000..cad943c --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/FreeQuotaConfigResp.java @@ -0,0 +1,95 @@ +package top.continew.admin.business.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 免费额度配置响应信息 + * + * @author jiay + * @since 2025/11/17 + */ +@Data +@Schema(description = "免费额度配置响应信息") +public class FreeQuotaConfigResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @Schema(description = "ID", example = "1") + private Long id; + + /** + * 类别编码 + */ + @Schema(description = "类别编码", example = "TOKEN") + private String categoryCode; + + /** + * 类别名称 + */ + @Schema(description = "类别名称", example = "大模型") + private String categoryName; + + /** + * 单位 + */ + @Schema(description = "单位", example = "token") + private String unit; + + /** + * 额度 + */ + @Schema(description = "额度", example = "500000") + private BigDecimal quota; + + /** + * 计费类型 + */ + @Schema(description = "计费类型", example = "0") + private Integer chargeType; + + /** + * 账户类型 1:次数 2:点数 + */ + @Schema(description = "账户类型", example = "1") + private Integer type; + + /** + * 描述 + */ + @Schema(description = "描述", example = "大模型对话额度") + private String description; + + /** + * 排序 + */ + @Schema(description = "排序", example = "1") + private Integer sort; + + /** + * 是否启用 + */ + @Schema(description = "是否启用", example = "true") + private Boolean isEnabled; + + /** + * 创建时间 + */ + @Schema(description = "创建时间", example = "2025-11-17 10:00:00") + private LocalDateTime createTime; + + /** + * 更新时间 + */ + @Schema(description = "更新时间", example = "2025-11-17 10:00:00") + private LocalDateTime updateTime; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/FreeQuotaGrantResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/FreeQuotaGrantResp.java new file mode 100644 index 0000000..94b2085 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/FreeQuotaGrantResp.java @@ -0,0 +1,78 @@ +package top.continew.admin.business.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 免费额度赠送响应信息 + * + * @author jiay + * @since 2025/11/17 + */ +@Data +@Builder +@Schema(description = "免费额度赠送响应信息") +public class FreeQuotaGrantResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 赠送是否成功 + */ + @Schema(description = "赠送是否成功", example = "true") + private Boolean success; + + /** + * 总用户数 + */ + @Schema(description = "总用户数", example = "10") + private Integer totalUsers; + + /** + * 成功赠送的用户数 + */ + @Schema(description = "成功赠送的用户数", example = "10") + private Integer successUsers; + + /** + * 失败的用户数 + */ + @Schema(description = "失败的用户数", example = "0") + private Integer failedUsers; + + /** + * 总赠送项数 + */ + @Schema(description = "总赠送项数", example = "100") + private Integer totalItems; + + /** + * 成功的项数 + */ + @Schema(description = "成功的项数", example = "98") + private Integer successItems; + + /** + * 失败的项数 + */ + @Schema(description = "失败的项数", example = "2") + private Integer failedItems; + + /** + * 消息 + */ + @Schema(description = "消息", example = "批量赠送完成") + private String message; + + /** + * 详细日志 + */ + @Schema(description = "详细日志") + private List logs; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/HomeworkDetailResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/HomeworkDetailResp.java new file mode 100644 index 0000000..dbf61a9 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/HomeworkDetailResp.java @@ -0,0 +1,53 @@ +package top.continew.admin.business.model.resp; + +import lombok.Data; + +import io.swagger.v3.oas.annotations.media.Schema; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import top.continew.starter.extension.crud.model.resp.BaseDetailResp; +import java.io.Serial; + +/** + * business详情信息 + * + * @author jiay + * @since 2025/06/26 10:24 + */ +@Data +@ExcelIgnoreUnannotated +@Schema(description = "business详情信息") +public class HomeworkDetailResp extends BaseDetailResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + @Schema(description = "用户id") + @ExcelProperty(value = "用户id") + private Long userId; + + /** + * 课程id + */ + @Schema(description = "课程id") + @ExcelProperty(value = "课程id") + private Long courseId; + + /** + * 未交原因 + */ + @Schema(description = "未交原因") + @ExcelProperty(value = "未交原因") + private String cause; + + /** + * 1、已交 2、未交 + */ + @Schema(description = "1、已交 2、未交") + @ExcelProperty(value = "1、已交 2、未交") + private Integer handedIn; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/HomeworkResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/HomeworkResp.java new file mode 100644 index 0000000..9f90773 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/HomeworkResp.java @@ -0,0 +1,59 @@ +package top.continew.admin.business.model.resp; + +import lombok.Data; + +import io.swagger.v3.oas.annotations.media.Schema; +import top.continew.starter.extension.crud.model.resp.BaseResp; + +import java.io.Serial; +import java.time.*; + +/** + * business信息 + * + * @author jiay + * @since 2025/06/26 10:24 + */ +@Data +@Schema(description = "business信息") +public class HomeworkResp extends BaseResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + @Schema(description = "用户id") + private Long userId; + + /** + * 课程id + */ + @Schema(description = "课程id") + private Long courseId; + + /** + * 未交原因 + */ + @Schema(description = "未交原因") + private String cause; + + /** + * 1、已交 2、未交 + */ + @Schema(description = "1、已交 2、未交") + private Integer handedIn; + + /** + * 修改人 + */ + @Schema(description = "修改人") + private Long updateUser; + + /** + * 修改时间 + */ + @Schema(description = "修改时间") + private LocalDateTime updateTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/HuandaResourceDetailResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/HuandaResourceDetailResp.java new file mode 100644 index 0000000..6aa9f6a --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/HuandaResourceDetailResp.java @@ -0,0 +1,54 @@ +package top.continew.admin.business.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.starter.extension.crud.model.resp.BaseDetailResp; + +import java.io.Serial; + +/** + * 换哒-文件资源详情信息 + * + * @author 王志维 + * @since 2025/12/23 16:53 + */ +@Data +@Schema(description = "换哒-文件资源详情信息") +public class HuandaResourceDetailResp extends BaseDetailResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 资源ID + */ + @Schema(description = "资源ID") + private Long id; + + /** + * 用户ID + */ + @Schema(description = "用户ID") + private String userId; + + /** + * 资源地址 + */ + @Schema(description = "资源地址") + private String url; + + /** + * 资源类型:1.服装,2模特,3自定义服装,4.自定义背景 + */ + @Schema(description = "资源类型:1.服装,2模特,3自定义服装,4.自定义背景") + private Integer resourceType; + + @Schema(description = "人物图片年龄") + private String age; + + @Schema(description = "人物图片性别:男,女") + private String gender; + + @Schema(description = "人物图片区域") + private String region; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/HuandaResourceResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/HuandaResourceResp.java new file mode 100644 index 0000000..7c478e0 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/HuandaResourceResp.java @@ -0,0 +1,54 @@ +package top.continew.admin.business.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.starter.extension.crud.model.resp.BaseResp; + +import java.io.Serial; + +/** + * 换哒-文件资源信息 + * + * @author 王志维 + * @since 2025/12/23 16:53 + */ +@Data +@Schema(description = "换哒-文件资源信息") +public class HuandaResourceResp extends BaseResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 资源ID + */ + @Schema(description = "资源ID") + private Long id; + + /** + * 用户ID + */ + @Schema(description = "用户ID") + private String userId; + + /** + * 资源地址 + */ + @Schema(description = "资源地址") + private String url; + + /** + * 资源类型:1.服装,2模特,3自定义服装,4.自定义背景 + */ + @Schema(description = "资源类型:1.服装,2模特,3自定义服装,4.自定义背景") + private Integer resourceType; + + @Schema(description = "人物图片年龄") + private String age; + + @Schema(description = "人物图片性别:男,女") + private String gender; + + @Schema(description = "人物图片区域") + private String region; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/ModelCategoryResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/ModelCategoryResp.java new file mode 100644 index 0000000..9b31098 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/ModelCategoryResp.java @@ -0,0 +1,34 @@ +package top.continew.admin.business.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * @author 张波 + * @since 2024/10/20 19:45 + */ +@Data +@Schema(description = "模型信息") +public class ModelCategoryResp { + + @Schema(description = "ID", example = "1") + private Long id; + + /** + * 名称 + */ + @Schema(description = "名称", example = "绘画") + private String name; + + /** + * 上级ID + */ + @Schema(description = "上级ID", example = "0") + private Long parentId; + + /** + * 类型 + */ + @Schema(description = "类型", example = "平台分类") + private String type; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/ModelResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/ModelResp.java new file mode 100644 index 0000000..9baa777 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/ModelResp.java @@ -0,0 +1,81 @@ +package top.continew.admin.business.model.resp; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.starter.extension.crud.model.resp.BaseResp; +import top.continew.starter.file.excel.converter.ExcelBaseEnumConverter; + +import java.io.Serial; + +/** + * 模型信息 + * + * @author 张波 + * @since 2024/10/19 15:41 + */ +@Data +@ExcelIgnoreUnannotated +@Schema(description = "模型信息") +public class ModelResp extends BaseResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 模型名称 + */ + @Schema(description = "模型名称", example = "测试模型") + @ExcelProperty(value = "模型名称", order = 2) + private String name; + + @Schema(description = "轮播图地址", example = "无") + private String images; + + @Schema(description = "基础模型", example = "SDXL") + private String version; + + /** + * 推荐分辨率 + */ + @Schema(description = "推荐分辨率", example = "1536*1024") + @ExcelProperty(value = "推荐分辨率", order = 4) + private String recommendedResolution; + + /** + * 提示词是否开启 + */ + @Schema(description = "提示词是否开启", example = "是/否") + @ExcelProperty(value = "提示词是否开启", converter = ExcelBaseEnumConverter.class, order = 5) + private DisEnableStatusEnum promptEnabled; + + /** + * 采样方法 + */ + @Schema(description = "采样方法", example = "Euler a") + @ExcelProperty(value = "采样方法", order = 6) + private String samplingMethod; + + /** + * CFG + */ + @Schema(description = "CFG", example = "3") + @ExcelProperty(value = "CFG", order = 7) + private Integer cfg; + + /** + * VAE + */ + @Schema(description = "VAE", example = "无") + @ExcelProperty(value = "VAE", order = 8) + private String vae; + + /** + * 采样步数 + */ + @Schema(description = "采样步数", example = "20") + @ExcelProperty(value = "采样步数", order = 9) + private Integer samplingSteps; +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/PlantformBalanceDetailResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/PlantformBalanceDetailResp.java new file mode 100644 index 0000000..435ecee --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/PlantformBalanceDetailResp.java @@ -0,0 +1,51 @@ +package top.continew.admin.business.model.resp; + +import lombok.Data; +import io.swagger.v3.oas.annotations.media.Schema; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import top.continew.starter.extension.crud.model.resp.BaseResp; +import java.io.Serial; + +/** + * 用户子账号余额表 详情信息 + * + * @author jiay + * @since 2025/06/24 16:47 + */ +@Data +@ExcelIgnoreUnannotated +@Schema(description = "business详情信息") +public class PlantformBalanceDetailResp extends BaseResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + @Schema(description = "用户id") + @ExcelProperty(value = "用户id") + private Long userId; + + /** + * 账户类型 1 次数 2 点数 + */ + @Schema(description = "账户类型 1 次数 2 点数") + @ExcelProperty(value = "账户类型 1 次数 2 点数") + private Integer type; + + /** + * 余额 + */ + @Schema(description = "余额") + @ExcelProperty(value = "余额") + private Long balance; + + /** + * 平台 + */ + @Schema(description = "平台") + @ExcelProperty(value = "平台") + private Long platformId; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/PlantformBalanceResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/PlantformBalanceResp.java new file mode 100644 index 0000000..dd36470 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/PlantformBalanceResp.java @@ -0,0 +1,62 @@ +package top.continew.admin.business.model.resp; + +import lombok.Data; +import io.swagger.v3.oas.annotations.media.Schema; +import top.continew.starter.extension.crud.model.resp.BaseResp; +import java.io.Serial; +import java.time.*; + +/** + * 用户子账号余额表 信息 + * + * @author jiay + * @since 2025/06/24 16:47 + */ +@Data +@Schema(description = "business信息") +public class PlantformBalanceResp extends BaseResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + @Schema(description = "用户id") + private Long userId; + + /** + * 账户类型 1 次数 2 点数 + */ + @Schema(description = "账户类型 1 次数 2 点数") + private Integer type; + + /** + * 计费类型(token:0, 图:1, 音频:3, 视频:4, 白膜贴图/3D:5) + */ + private Integer chargeType; + + /** + * 余额 + */ + @Schema(description = "余额") + private Long balance; + + /** + * 平台 + */ + @Schema(description = "平台") + private Long platformId; + + /** + * 修改人 + */ + @Schema(description = "修改人") + private Long updateUser; + + /** + * 修改时间 + */ + @Schema(description = "修改时间") + private LocalDateTime updateTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/ScoreDetailResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/ScoreDetailResp.java new file mode 100644 index 0000000..699925c --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/ScoreDetailResp.java @@ -0,0 +1,82 @@ +package top.continew.admin.business.model.resp; + +import lombok.Data; + +import io.swagger.v3.oas.annotations.media.Schema; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import top.continew.starter.extension.crud.model.resp.BaseDetailResp; +import java.io.Serial; +import java.time.*; + +/** + * business详情信息 + * + * @author jiay + * @since 2025/06/26 10:24 + */ +@Data +@ExcelIgnoreUnannotated +@Schema(description = "business详情信息") +public class ScoreDetailResp extends BaseDetailResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + @Schema(description = "用户id") + @ExcelProperty(value = "用户id") + private Long userId; + + /** + * 课程id + */ + @Schema(description = "课程id") + @ExcelProperty(value = "课程id") + private Long courseId; + + /** + * 考试分数 + */ + @Schema(description = "考试分数") + @ExcelProperty(value = "考试分数") + private Integer examScore; + + /** + * 作业分数 + */ + @Schema(description = "作业分数") + @ExcelProperty(value = "作业分数") + private Integer homeworkScore; + + /** + * 签到分数 + */ + @Schema(description = "签到分数") + @ExcelProperty(value = "签到分数") + private Integer score; + + /** + * 最终总分数 + */ + @Schema(description = "最终总分数") + @ExcelProperty(value = "最终总分数") + private Integer signInScore; + + /** + * 1、已考 2、未考 + */ + @Schema(description = "1、已考 2、未考") + @ExcelProperty(value = "1、已考 2、未考") + private Integer takeExam; + + /** + * 未考试原因 + */ + @Schema(description = "未考试原因") + @ExcelProperty(value = "未考试原因") + private String cause; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/ScoreResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/ScoreResp.java new file mode 100644 index 0000000..ad9317d --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/ScoreResp.java @@ -0,0 +1,83 @@ +package top.continew.admin.business.model.resp; + +import lombok.Data; + +import io.swagger.v3.oas.annotations.media.Schema; +import top.continew.starter.extension.crud.model.resp.BaseResp; + +import java.io.Serial; +import java.time.*; + +/** + * business信息 + * + * @author jiay + * @since 2025/06/26 10:24 + */ +@Data +@Schema(description = "business信息") +public class ScoreResp extends BaseResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + @Schema(description = "用户id") + private Long userId; + + /** + * 课程id + */ + @Schema(description = "课程id") + private Long courseId; + + /** + * 考试分数 + */ + @Schema(description = "考试分数") + private Integer examScore; + + /** + * 作业分数 + */ + @Schema(description = "作业分数") + private Integer homeworkScore; + + /** + * 签到分数 + */ + @Schema(description = "签到分数") + private Integer score; + + /** + * 最终总分数 + */ + @Schema(description = "最终总分数") + private Integer signInScore; + + /** + * 1、已考 2、未考 + */ + @Schema(description = "1、已考 2、未考") + private Integer takeExam; + + /** + * 未考试原因 + */ + @Schema(description = "未考试原因") + private String cause; + + /** + * 修改人 + */ + @Schema(description = "修改人") + private Long updateUser; + + /** + * 修改时间 + */ + @Schema(description = "修改时间") + private LocalDateTime updateTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/SignInDetailResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/SignInDetailResp.java new file mode 100644 index 0000000..2975312 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/SignInDetailResp.java @@ -0,0 +1,55 @@ +package top.continew.admin.business.model.resp; + +import lombok.Data; + +import io.swagger.v3.oas.annotations.media.Schema; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import top.continew.starter.extension.crud.model.resp.BaseDetailResp; + +import java.io.Serial; +import java.time.*; + +/** + * business详情信息 + * + * @author jiay + * @since 2025/06/26 10:24 + */ +@Data +@ExcelIgnoreUnannotated +@Schema(description = "business详情信息") +public class SignInDetailResp extends BaseDetailResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + @Schema(description = "用户id") + @ExcelProperty(value = "用户id") + private Long userId; + + /** + * 课程id + */ + @Schema(description = "课程id") + @ExcelProperty(value = "课程id") + private Long courseId; + + /** + * 请假原因 + */ + @Schema(description = "请假原因") + @ExcelProperty(value = "请假原因") + private String leaveCause; + + /** + * 1、签到 2、请假 3、旷课 + */ + @Schema(description = "1、签到 2、请假 3、旷课") + @ExcelProperty(value = "1、签到 2、请假 3、旷课") + private Integer signIn; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/SignInExportResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/SignInExportResp.java new file mode 100644 index 0000000..765a8a8 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/SignInExportResp.java @@ -0,0 +1,67 @@ +package top.continew.admin.business.model.resp; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @Author: jia + * @Date: 2025/07/01/15:32 + * @Description: + */ + +@Data +@ExcelIgnoreUnannotated +@Schema(description = "导出信息") +public class SignInExportResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户名 + */ + @Schema(description = "用户名") + @ExcelProperty(value = "用户名") + private String userName; + + /** + * 异常 + */ + @Schema(description = "异常") + @ExcelProperty(value = "异常") + private Integer abnormal; + + /** + * 缺课次数 + */ + @Schema(description = "缺课次数") + @ExcelProperty(value = "缺课次数") + private Integer absent; + + /** + * 签到次数 + */ + @Schema(description = "签到次数") + @ExcelProperty(value = "签到次数") + private Integer present; + + /** + * 假条数量 + */ + @Schema(description = "假条数量") + @ExcelProperty(value = "假条数量") + private Integer leaveCount; + + /** + * 缺课率 + */ + @Schema(description = "缺课率") + @ExcelProperty(value = "缺课率") + private Double absenceRate; + +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/SignInResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/SignInResp.java new file mode 100644 index 0000000..b70e53a --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/SignInResp.java @@ -0,0 +1,98 @@ +package top.continew.admin.business.model.resp; + +import lombok.Data; + +import io.swagger.v3.oas.annotations.media.Schema; +import top.continew.starter.extension.crud.model.resp.BaseResp; + +import java.io.Serial; +import java.time.*; + +/** + * business信息 + * + * @author jiay + * @since 2025/06/26 10:24 + */ +@Data +@Schema(description = "business信息") +public class SignInResp extends BaseResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + @Schema(description = "用户id") + private Long userId; + private String userName; + + /** + * 学号 + */ + private String stuNo; + + /** + * 手机 + */ + @Schema(description = "手机") + private String phone; + + /** + * 课程id + */ + @Schema(description = "课程id") + private Long courseId; + private String courseName; + /** + * 请假原因 + */ + @Schema(description = "请假原因") + private String leaveCause; + + /** + * 1、签到 2、请假 3、旷课 + */ + @Schema(description = "1、签到 2、请假 3、旷课") + private Integer signIn; + /** + * 经纬度 + */ + private String location; + /** + * 修改人 + */ + @Schema(description = "修改人") + private Long updateUser; + + /** + * 修改时间 + */ + @Schema(description = "修改时间") + private LocalDateTime updateTime; + + /** + * 异常 + */ + @Schema(description = "异常") + private Integer abnormal; + + /** + * 缺课次数 + */ + @Schema(description = "缺课次数") + private Integer absent; + + /** + * 签到次数 + */ + @Schema(description = "签到次数") + private Integer present; + + /** + * 假条数量 + */ + @Schema(description = "假条数量") + private Integer leaveCount; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/SubtitleItemDetailResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/SubtitleItemDetailResp.java new file mode 100644 index 0000000..44217c8 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/SubtitleItemDetailResp.java @@ -0,0 +1,51 @@ +package top.continew.admin.business.model.resp; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.starter.extension.crud.model.resp.BaseResp; + +/** + * 数字人-字幕项详情响应对象 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class SubtitleItemDetailResp extends BaseResp { + + /** + * 字幕项ID + */ + private Long id; + + /** + * 关联的数字人ID + */ + private Long digitalHumanId; + + /** + * 字幕文本内容 + */ + private String text; + + /** + * 字幕开始时间(秒) + */ + private Double startTime; + + /** + * 字幕结束时间(秒) + */ + private Double endTime; + + /** + * 创建时间 + */ + private java.time.LocalDateTime createTime; + + /** + * 更新时间 + */ + private java.time.LocalDateTime updateTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/SubtitleItemResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/SubtitleItemResp.java new file mode 100644 index 0000000..4653b06 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/SubtitleItemResp.java @@ -0,0 +1,51 @@ +package top.continew.admin.business.model.resp; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.starter.extension.crud.model.resp.BaseResp; + +/** + * 数字人-字幕项响应对象 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class SubtitleItemResp extends BaseResp { + + /** + * 字幕项ID + */ + private Long id; + + /** + * 关联的数字人ID + */ + private Long digitalHumanId; + + /** + * 字幕文本内容 + */ + private String text; + + /** + * 字幕开始时间(秒) + */ + private Double startTime; + + /** + * 字幕结束时间(秒) + */ + private Double endTime; + + /** + * 创建时间 + */ + private java.time.LocalDateTime createTime; + + /** + * 更新时间 + */ + private java.time.LocalDateTime updateTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/SubtitleStyleDetailResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/SubtitleStyleDetailResp.java new file mode 100644 index 0000000..98bfc05 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/SubtitleStyleDetailResp.java @@ -0,0 +1,81 @@ +package top.continew.admin.business.model.resp; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.starter.extension.crud.model.resp.BaseResp; + +/** + * 数字人-字幕样式详情响应对象 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class SubtitleStyleDetailResp extends BaseResp { + + /** + * 字幕样式ID + */ + private Long id; + + /** + * 关联的数字人ID(可为空,用于全局/默认样式) + */ + private Long digitalHumanId; + + /** + * 字体大小(像素) + */ + private Integer fontSize; + + /** + * 字体族(如微软雅黑、Arial等) + */ + private String fontFamily; + + /** + * 文字颜色(CSS格式,如 #FFFFFF) + */ + private String color; + + /** + * 描边颜色 + */ + private String strokeColor; + + /** + * 描边宽度(像素) + */ + private Integer strokeWidth; + + /** + * 阴影颜色 + */ + private String shadowColor; + + /** + * 阴影模糊半径(像素) + */ + private Integer shadowBlur; + + /** + * 底部内边距(像素) + */ + private Integer paddingBottom; + + /** + * 行高(像素或倍数) + */ + private Integer lineHeight; + + /** + * 创建时间 + */ + private java.time.LocalDateTime createTime; + + /** + * 更新时间 + */ + private java.time.LocalDateTime updateTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/SubtitleStyleResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/SubtitleStyleResp.java new file mode 100644 index 0000000..f960888 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/SubtitleStyleResp.java @@ -0,0 +1,81 @@ +package top.continew.admin.business.model.resp; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.starter.extension.crud.model.resp.BaseResp; + +/** + * 数字人-字幕样式响应对象 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class SubtitleStyleResp extends BaseResp { + + /** + * 字幕样式ID + */ + private Long id; + + /** + * 关联的数字人ID(可为空,用于全局/默认样式) + */ + private Long digitalHumanId; + + /** + * 字体大小(像素) + */ + private Integer fontSize; + + /** + * 字体族(如微软雅黑、Arial等) + */ + private String fontFamily; + + /** + * 文字颜色(CSS格式,如 #FFFFFF) + */ + private String color; + + /** + * 描边颜色 + */ + private String strokeColor; + + /** + * 描边宽度(像素) + */ + private Integer strokeWidth; + + /** + * 阴影颜色 + */ + private String shadowColor; + + /** + * 阴影模糊半径(像素) + */ + private Integer shadowBlur; + + /** + * 底部内边距(像素) + */ + private Integer paddingBottom; + + /** + * 行高(像素或倍数) + */ + private Integer lineHeight; + + /** + * 创建时间 + */ + private java.time.LocalDateTime createTime; + + /** + * 更新时间 + */ + private java.time.LocalDateTime updateTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/TaskCallbackResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/TaskCallbackResp.java new file mode 100644 index 0000000..2642c96 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/TaskCallbackResp.java @@ -0,0 +1,34 @@ +package top.continew.admin.business.model.resp; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 任务回调响应参数 + * + * @author jiay + * @since 2025/08/28 00:00 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class TaskCallbackResp { + + /** + * 回调是否成功 + */ + private boolean success; + + /** + * 消息 + */ + private String message; + + /** + * 任务记录ID + */ + private Long taskRecordId; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/TaskRecordHistoryDetailResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/TaskRecordHistoryDetailResp.java new file mode 100644 index 0000000..a0fd2be --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/TaskRecordHistoryDetailResp.java @@ -0,0 +1,83 @@ +package top.continew.admin.business.model.resp; + +import lombok.Data; + +import io.swagger.v3.oas.annotations.media.Schema; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import top.continew.starter.extension.crud.model.resp.BaseResp; + +import java.io.Serial; +import java.time.*; + +/** + * 用户充值消费历史记录详情信息 + * + * @author jiay + * @since 2025/05/23 07:51 + */ +@Data +@ExcelIgnoreUnannotated +@Schema(description = "用户充值消费历史记录详情信息") +public class TaskRecordHistoryDetailResp extends BaseResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + @Schema(description = "用户id") + @ExcelProperty(value = "用户id") + private Long userId; + + /** + * 生成平台 + */ + @Schema(description = "生成平台") + @ExcelProperty(value = "生成平台") + private Long platformId; + + /** + * 任务订单号 + */ + @Schema(description = "任务订单号") + @ExcelProperty(value = "任务订单号") + private String taskId; + + /** + * 文件名 + */ + @Schema(description = "文件名") + @ExcelProperty(value = "文件名") + private String title; + + /** + * 任务类型(token:0, 文生图:1,图生图:2,音效生成:3,参考生视频:4, 白膜贴图:5) + */ + @Schema(description = "任务类型(token:0, 文生图:1,图生图:2,音效生成:3,参考生视频:4, 白膜贴图:5)") + @ExcelProperty(value = "任务类型(token:0, 文生图:1,图生图:2,音效生成:3,参考生视频:4, 白膜贴图:5)") + private Integer taskType; + + /** + * 使用的模型 + */ + @Schema(description = "使用的模型") + @ExcelProperty(value = "使用的模型") + private String modelName; + + /** + * 消耗点数 + */ + @Schema(description = "消耗点数") + @ExcelProperty(value = "消耗点数") + private Long consumptionPoints; + + /** + * 存储结果文件路径 + */ + @Schema(description = "存储结果文件路径") + @ExcelProperty(value = "存储结果文件路径") + private String fileUrl; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/TaskRecordHistoryResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/TaskRecordHistoryResp.java new file mode 100644 index 0000000..80f0aa4 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/TaskRecordHistoryResp.java @@ -0,0 +1,95 @@ +package top.continew.admin.business.model.resp; + +import lombok.Data; + +import io.swagger.v3.oas.annotations.media.Schema; +import top.continew.starter.extension.crud.model.resp.BaseResp; + +import java.io.Serial; +import java.time.*; + +/** + * 用户充值消费历史记录信息 + * + * @author jiay + * @since 2025/05/23 07:51 + */ +@Data +@Schema(description = "用户充值消费历史记录信息") +public class TaskRecordHistoryResp extends BaseResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + @Schema(description = "用户id") + private Long userId; + + /** + * 生成平台 + */ + @Schema(description = "生成平台") + private Long platformId; + + /** + * 任务订单号 + */ + @Schema(description = "任务订单号") + private String taskId; + + /** + * 文件名 + */ + @Schema(description = "文件名") + private String title; + + /** + * 任务类型(token:0, 文生图:1,图生图:2,音效生成:3,参考生视频:4, 白膜贴图:5) + */ + @Schema(description = "任务类型(token:0, 文生图:1,图生图:2,音效生成:3,参考生视频:4, 白膜贴图:5)") + private Integer taskType; + + /** + * 文件类型 + */ + @Schema(description = "文件类型") + private String fileType; + + /** + * 计费类型(token:0, 图:1,音效生成:3,参考生视频:4, 白膜贴图:5) + */ + @Schema(description = "计费类型(token:0, 图:1, 音效:3, 视频:4, 白膜贴图:5)") + private Integer chargeType; + + /** + * 使用的模型 + */ + @Schema(description = "使用的模型") + private String modelName; + + /** + * 消耗点数 + */ + @Schema(description = "消耗点数") + private Long consumptionPoints; + + /** + * 存储结果文件路径 + */ + @Schema(description = "存储结果文件路径") + private String fileUrl; + + /** + * 修改人 + */ + @Schema(description = "修改人") + private Long updateUser; + + /** + * 修改时间 + */ + @Schema(description = "修改时间") + private LocalDateTime updateTime; +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/TodayNosignInExportResp.java b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/TodayNosignInExportResp.java new file mode 100644 index 0000000..e72059c --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/model/resp/TodayNosignInExportResp.java @@ -0,0 +1,45 @@ +package top.continew.admin.business.model.resp; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @Author: jia + * @Date: 2025/07/01/15:31 + * @Description: + */ +@Data +@ExcelIgnoreUnannotated +@Schema(description = "导出信息") +public class TodayNosignInExportResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户名 + */ + @Schema(description = "用户名") + @ExcelProperty(value = "用户名") + private String userName; + + /** + * 请假原因 + */ + @Schema(description = "请假原因") + @ExcelProperty(value = "请假原因") + private String leaveCause; + + /** + * 1、签到 2、请假 3、旷课 + */ + @Schema(description = "1、签到 2、请假 3、旷课") + @ExcelProperty(value = "1、签到 2、请假 3、旷课") + private String signIn; + +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/AssignmentSubmissionsService.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/AssignmentSubmissionsService.java new file mode 100644 index 0000000..93c75bc --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/AssignmentSubmissionsService.java @@ -0,0 +1,56 @@ +package top.continew.admin.business.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import top.continew.admin.business.model.entity.AssignmentSubmissionsDO; +import top.continew.admin.business.model.req.AssignmentSubmissionsReq; +import top.continew.admin.business.model.req.AssignmentsCorrectReq; +import top.continew.admin.business.model.resp.AssignmentsProgressResp; +import top.continew.admin.business.model.resp.AssignmentsSubmissionsDetailResp; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; + +/** + * @author 张波 + * @since 2024/10/22 15:38 + */ +public interface AssignmentSubmissionsService extends IService { + + /** + * 提交作业 + * + * @param req 作业ID&作业图片信息 + */ + void submit(AssignmentSubmissionsReq req); + + /** + * 批改作业 + * + * @param req 作业提交记录ID&分叔&评语 + */ + void correct(AssignmentsCorrectReq req); + + /** + * 查询作业完成情况 + * + * @param assignmentsId 作业ID + * @param pageQuery 分页参数 + * @return 学生作业完成情况 + */ + PageResp degreeOfCompletion(Long assignmentsId, PageQuery pageQuery); + + /** + * 查询作业提交记录详情 + * + * @param submissionsId 提交记录ID + * @return 提交记录详情 + */ + AssignmentsSubmissionsDetailResp detail(Long submissionsId); + + /** + * 查询优秀图片列表 + * + * @param pageQuery 分页条件 + * @return 图片列表 + */ + PageResp getGoodImageList(String dbName, PageQuery pageQuery); +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/AssignmentsService.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/AssignmentsService.java new file mode 100644 index 0000000..7b6d824 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/AssignmentsService.java @@ -0,0 +1,26 @@ +package top.continew.admin.business.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import top.continew.admin.business.model.entity.AssignmentsDO; +import top.continew.admin.business.model.req.AssignmentsReq; +import top.continew.admin.business.model.resp.AssignmentsDetailResp; + +/** + * 作业表业务接口 + * + * @author 张波 + * @since 2024/10/22 11:45 + */ +public interface AssignmentsService extends IService { + + // 创建作业 + Long add(AssignmentsReq assignmentsReq); + + /** + * 根据作业ID查询作业详情 + * + * @param assignmentsId 作业ID + * @return 作业详情 + */ + AssignmentsDetailResp detail(Long assignmentsId); +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/BillingRuleService.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/BillingRuleService.java new file mode 100644 index 0000000..f284549 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/BillingRuleService.java @@ -0,0 +1,41 @@ +package top.continew.admin.business.service; + +import top.continew.admin.business.model.entity.BillingRuleDO; +import top.continew.admin.business.model.query.BillingRuleQuery; +import top.continew.admin.business.model.req.BillingRuleReq; +import top.continew.admin.business.model.resp.BillingRuleResp; +import top.continew.starter.data.mp.service.IService; +import top.continew.starter.extension.crud.service.BaseService; + +/** + * 计费规则服务 + * + * @author jiay + * @since 2025/11/14 + */ +public interface BillingRuleService extends BaseService, IService { + + /** + * 根据平台ID获取计费规则 + * + * @param platformId 平台ID + * @return 计费规则 + */ + BillingRuleDO getByPlatformId(Long platformId); + + /** + * 根据平台ID获取计费金额 + * + * @param platformId 平台ID + * @return 计费金额(分/次/整数量) + */ + Long getPriceByPlatformId(Long platformId); + + /** + * 根据扣费编码获取计费规则 + * + * @param chargeCode 扣费编码 + * @return 计费规则 + */ + BillingRuleDO getByChargeCode(String chargeCode); +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/BillingService.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/BillingService.java new file mode 100644 index 0000000..d7f33ea --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/BillingService.java @@ -0,0 +1,51 @@ +package top.continew.admin.business.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import jakarta.validation.Valid; +import top.continew.admin.business.model.entity.TaskRecordHistoryDO; +import top.continew.admin.business.model.req.*; +import top.continew.admin.business.model.resp.BillingDeductResp; +import top.continew.admin.business.model.resp.TaskCallbackResp; + +/** + * 计费服务接口 + * + * @author jiay + * @since 2025/08/28 00:00 + */ +public interface BillingService { + + /** + * 扣减用户余额(本地计算费用) + * + * @param req 扣减请求 + * @return 扣减结果 + */ + BillingDeductResp judgeBalance(BillingJudgeReq req); + + /** + * 扣减用户余额(第三方平台传入费用) + * + * @param req 扣减请求(包含预扣减金额) + * @return 扣减结果 + */ + BillingDeductResp judgeBalanceWithAmount(BillingJudgeWithAmountReq req); + + /** + * 任务完成回调(本地计算费用) + * + * @param req 回调请求 + * @return 回调结果 + */ + TaskCallbackResp taskCallback(TaskCallbackReq req); + + /** + * 任务完成回调(第三方平台传入费用) + * + * @param req 回调请求(包含实际消费金额) + * @return 回调结果 + */ + TaskCallbackResp taskCallbackWithAmount(TaskCallbackWithAmountReq req); + + Page queryTaskListByRootId(@Valid TaskQueryReq req); +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/ConsumptionHistoryService.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/ConsumptionHistoryService.java new file mode 100644 index 0000000..3de900a --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/ConsumptionHistoryService.java @@ -0,0 +1,81 @@ +package top.continew.admin.business.service; + +import top.continew.admin.business.model.entity.ConsumptionHistoryDO; +import top.continew.admin.business.model.query.ConsumptionHistoryQuery; +import top.continew.admin.business.model.req.ConsumptionHistoryAddReq; +import top.continew.admin.business.model.req.ConsumptionHistoryEditReq; +import top.continew.admin.business.model.req.ConsumptionReq; +import top.continew.admin.business.model.resp.ConsumptionHistoryResp; +import top.continew.starter.data.mp.service.IService; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.web.model.R; + +import java.util.List; + +/** + * 用户充值消费历史记录业务接口 + * + * @author jiay + * @since 2025/05/23 07:51 + */ + +public interface ConsumptionHistoryService extends IService { + + /** + * 获取用户充值消费历史记录列表 + * + * @param query 用户充值消费历史记录查询条件 + * @return 用户充值消费历史记录列表 + */ + List getConsumptionHistoryList(ConsumptionHistoryQuery query); + + /** + * 添加用户充值消费历史记录 + * + * @param req 用户充值消费历史记录 + * @return 添加是否成功 + */ + boolean addConsumptionHistory(ConsumptionHistoryAddReq req); + + /** + * 添加用户充值消费历史记录 + * + * @param req 用户充值消费历史记录 + * @return 添加是否成功 + */ + boolean editConsumptionHistory(ConsumptionHistoryEditReq req); + + /** + * 添加用户充值消费历史记录 + * + * @param id 用户充值消费历史记录id + * @return 添加是否成功 + */ + boolean deleteConsumptionHistory(Long id); + + /** + * 添加用户充值消费历史记录 + * + * @param id 用户充值消费历史记录id + * @return 用户充值消费历史记录 + */ + ConsumptionHistoryResp getConsumptionHistory(Long id); + + /** + * 添加用户充值消费历史记录 + * + * @param query 用户充值消费历史记录查询条件 + * @param pageQuery 分页参数 + * @return 用户充值消费历史记录分页列表 + */ + PageResp getConsumptionHistoryPage(ConsumptionHistoryQuery query, PageQuery pageQuery); + + /** + * 判断余额是否充足 + * + * @param req 用户充值消费历史记录查询条件 + * @return 余额是否充足 + */ + R checkBalance(ConsumptionReq req); +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/CopyrightService.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/CopyrightService.java new file mode 100644 index 0000000..b5b1702 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/CopyrightService.java @@ -0,0 +1,22 @@ +package top.continew.admin.business.service; + +import top.continew.admin.business.model.query.CopyrightQuery; +import top.continew.admin.business.model.query.CopyrightPageQuery; +import top.continew.admin.business.model.req.CopyrightReq; +import top.continew.admin.business.model.resp.CopyrightDetailResp; +import top.continew.admin.business.model.resp.CopyrightResp; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.extension.crud.service.BaseService; + +/** + * 数字人-文案服务接口 + * + * @author 王志维 + * @since 2025/12/29 + */ +public interface CopyrightService extends BaseService { + + PageResp pageList(CopyrightQuery query, CopyrightPageQuery pageQuery); + + // 可根据需要添加额外的业务方法 +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/CourseUserService.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/CourseUserService.java new file mode 100644 index 0000000..72ffda2 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/CourseUserService.java @@ -0,0 +1,11 @@ +package top.continew.admin.business.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import top.continew.admin.business.model.entity.CourseUserDO; + +/** + * @author 张波 + * @since 2024/10/22 12:20 + */ +public interface CourseUserService extends IService { +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/CoursesService.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/CoursesService.java new file mode 100644 index 0000000..5d0d85c --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/CoursesService.java @@ -0,0 +1,47 @@ +package top.continew.admin.business.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import top.continew.admin.business.enums.AssignmentsTypeEnum; +import top.continew.admin.business.model.entity.CoursesDO; +import top.continew.admin.business.model.req.CoursesReq; +import top.continew.admin.business.model.resp.AssignmentsResp; +import top.continew.admin.business.model.resp.CoursesResp; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; + +import java.util.List; + +/** + * 课程业务接口 + * + * @author 张波 + * @since 2024/10/22 11:58 + */ +public interface CoursesService extends IService { + + /** + * 创建课程 + * + * @param req 参数 + */ + void add(CoursesReq req); + + /** + * 分页查询课程列表 + * + * @param role 角色(学生/老师) + * @param pageQuery 分页查询条件 + * @return 课程列表 + */ + PageResp list(String db, String role, PageQuery pageQuery); + + /** + * 查询课程里作业列表 + * + * @param coursesId 课程ID + * @param type 类型(作业/考试) + * @param role 角色(学生/老师) + * @return 作业名称以及完成度 + */ + List assignmentsList(Long coursesId, AssignmentsTypeEnum type, String role); +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/DeptModelService.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/DeptModelService.java new file mode 100644 index 0000000..226fe39 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/DeptModelService.java @@ -0,0 +1,42 @@ +package top.continew.admin.business.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import top.continew.admin.business.model.entity.DeptModelDO; +import top.continew.admin.business.model.query.ModelQuery; +import top.continew.admin.business.model.req.DeptModelReq; +import top.continew.admin.business.model.resp.ModelResp; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; + +import java.util.List; + +/** + * @author 张波 + * @since 2024/10/20 14:42 + */ +public interface DeptModelService extends IService { + + /** + * 部门与模型绑定 + * + * @param req 请求参数 + */ + void bind(DeptModelReq req); + + /** + * 获取用户所在部门可见模型 + * + * @param query 查询条件 + * @param pageQuery 分页条件 + * @return 用户所在部门可见模型 + */ + PageResp getModels(ModelQuery query, PageQuery pageQuery); + + /** + * 获取部门绑定的操作平台列表 + * + * @param deptId 部门ID + * @return 操作平台列表 + */ + List getModelByDept(Long deptId); +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/DeptPlatformService.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/DeptPlatformService.java new file mode 100644 index 0000000..0ced4a0 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/DeptPlatformService.java @@ -0,0 +1,36 @@ +package top.continew.admin.business.service; + +import top.continew.admin.business.model.entity.DeptPlatformDO; +import top.continew.admin.business.model.req.DeptPlatformReq; +import top.continew.starter.data.mp.service.IService; + +import java.util.List; + +/** + * @author 张波 + * @since 2024/10/20 14:49 + */ +public interface DeptPlatformService extends IService { + + /** + * 部门与操作平台绑定接口 + * + * @param req 请求参数 + */ + void bind(DeptPlatformReq req); + + /** + * 获取用户可见操作平台 + * + * @return 用户所在部门可见操作平台 + */ + List getPlatforms(); + + /** + * 获取部门绑定的操作平台列表 + * + * @param deptId 部门ID + * @return 操作平台列表 + */ + List getPlatformByDept(Long deptId); +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/DigitalHumanService.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/DigitalHumanService.java new file mode 100644 index 0000000..0e63c0d --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/DigitalHumanService.java @@ -0,0 +1,20 @@ +package top.continew.admin.business.service; + +import top.continew.admin.business.model.query.DigitalHumanQuery; +import top.continew.admin.business.model.query.DigitalHumanPageQuery; +import top.continew.admin.business.model.req.DigitalHumanReq; +import top.continew.admin.business.model.resp.DigitalHumanDetailResp; +import top.continew.admin.business.model.resp.DigitalHumanResp; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.extension.crud.service.BaseService; + +/** + * 数字人服务接口 + * + * @author 王志维 + * @since 2025/12/29 + */ +public interface DigitalHumanService extends BaseService { + // 可根据需要添加额外的业务方法 + PageResp pageList(DigitalHumanQuery query, DigitalHumanPageQuery pageQuery); +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/EffectService.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/EffectService.java new file mode 100644 index 0000000..68d948c --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/EffectService.java @@ -0,0 +1,20 @@ +package top.continew.admin.business.service; + +import top.continew.admin.business.model.query.EffectPageQuery; +import top.continew.admin.business.model.query.EffectQuery; +import top.continew.admin.business.model.req.EffectReq; +import top.continew.admin.business.model.resp.EffectDetailResp; +import top.continew.admin.business.model.resp.EffectResp; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.extension.crud.service.BaseService; + +/** + * 数字人-特效服务接口 + * + * @author 王志维 + * @since 2025/12/29 + */ +public interface EffectService extends BaseService { + PageResp pageList(EffectQuery query, EffectPageQuery pageQuery); + // 可根据需要添加额外的业务方法 +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/EffectTemplateService.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/EffectTemplateService.java new file mode 100644 index 0000000..7711ea1 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/EffectTemplateService.java @@ -0,0 +1,23 @@ +package top.continew.admin.business.service; + +import org.springframework.web.multipart.MultipartFile; +import top.continew.admin.business.model.query.EffectTemplatePageQuery; +import top.continew.admin.business.model.query.EffectTemplateQuery; +import top.continew.admin.business.model.req.EffectTemplateReq; +import top.continew.admin.business.model.resp.EffectTemplateDetailResp; +import top.continew.admin.business.model.resp.EffectTemplateResp; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.extension.crud.service.BaseService; + +/** + * 数字人-特效模板服务接口 + * + * @author 王志维 + * @since 2025/12/29 + */ +public interface EffectTemplateService extends BaseService { + PageResp pageList(EffectTemplateQuery query, EffectTemplatePageQuery pageQuery); + + // 可根据需要添加额外的业务方法 + Long uploadTemplate(MultipartFile file, String name, String type, String userId); +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/FreeQuotaConfigService.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/FreeQuotaConfigService.java new file mode 100644 index 0000000..69bbbf4 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/FreeQuotaConfigService.java @@ -0,0 +1,42 @@ +package top.continew.admin.business.service; + +import top.continew.admin.business.model.entity.FreeQuotaConfigDO; +import top.continew.admin.business.model.query.FreeQuotaConfigQuery; +import top.continew.admin.business.model.req.FreeQuotaConfigReq; +import top.continew.admin.business.model.resp.FreeQuotaConfigResp; +import top.continew.starter.data.mp.service.IService; +import top.continew.starter.extension.crud.service.BaseService; + +import java.util.List; + +/** + * 免费额度配置服务 + * + * @author jiay + * @since 2025/11/17 + */ +public interface FreeQuotaConfigService extends BaseService, IService { + + /** + * 获取所有启用的配置 + * + * @return 配置列表 + */ + List listEnabled(); + + /** + * 根据类别编码获取配置 + * + * @param categoryCode 类别编码 + * @return 配置信息 + */ + FreeQuotaConfigDO getByCategoryCode(String categoryCode); + + /** + * 根据计费类型获取配置 + * + * @param chargeType 计费类型 + * @return 配置信息 + */ + FreeQuotaConfigDO getByChargeType(Integer chargeType); +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/FreeQuotaGrantService.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/FreeQuotaGrantService.java new file mode 100644 index 0000000..1cd4dcc --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/FreeQuotaGrantService.java @@ -0,0 +1,37 @@ +package top.continew.admin.business.service; + +import top.continew.admin.business.model.req.FreeQuotaGrantReq; +import top.continew.admin.business.model.resp.FreeQuotaGrantResp; + +/** + * 免费额度赠送服务 + * + * @author jiay + * @since 2025/11/17 + */ +public interface FreeQuotaGrantService { + + /** + * 批量赠送免费额度 + * + * @param req 赠送请求 + * @return 赠送结果 + */ + FreeQuotaGrantResp batchGrant(FreeQuotaGrantReq req); + + /** + * 给单个用户赠送默认免费额度 + * + * @param userId 用户ID + * @return 赠送结果 + */ + FreeQuotaGrantResp grantToUser(Long userId); + + /** + * 给部门下所有用户赠送默认免费额度 + * + * @param deptId 部门ID + * @return 赠送结果 + */ + FreeQuotaGrantResp grantToDept(Long deptId); +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/HomeworkService.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/HomeworkService.java new file mode 100644 index 0000000..2e5eabd --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/HomeworkService.java @@ -0,0 +1,69 @@ +package top.continew.admin.business.service; + +import top.continew.admin.business.model.entity.HomeworkDO; +import top.continew.admin.business.model.query.HomeworkQuery; +import top.continew.admin.business.model.req.HomeworkAddReq; +import top.continew.admin.business.model.req.HomeworkEditReq; +import top.continew.admin.business.model.resp.HomeworkResp; +import top.continew.starter.data.mp.service.IService; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; +import java.util.List; + +/** + * 作业业务接口 + * + * @author jiay + * @since 2025/06/26 10:24 + */ +public interface HomeworkService extends IService { + + /** + * 获取作业列表 + * + * @param query 作业查询条件 + * @return 作业列表 + */ + List getHomeworkList(HomeworkQuery query); + + /** + * 添加作业 + * + * @param req 作业 + * @return url + */ + boolean addHomework(HomeworkAddReq req); + + /** + * 添加作业 + * + * @param req 作业 + * @return 添加是否成功 + */ + boolean editHomework(HomeworkEditReq req); + + /** + * 添加作业 + * + * @param id 作业id + * @return 添加是否成功 + */ + boolean deleteHomework(Long id); + + /** + * 添加作业 + * + * @param id 作业id + * @return 作业 + */ + HomeworkResp getHomework(Long id); + + /** + * 添加作业 + * + * @param query 作业查询条件 + * @param pageQuery 分页参数 + * @return 作业分页列表 + */ + PageResp getHomeworkPage(HomeworkQuery query, PageQuery pageQuery); +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/HuandaResourceService.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/HuandaResourceService.java new file mode 100644 index 0000000..fae8b8f --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/HuandaResourceService.java @@ -0,0 +1,29 @@ +package top.continew.admin.business.service; + +import top.continew.admin.business.model.entity.HuandaResourceDO; +import top.continew.admin.business.model.query.HuandaResourcePageQuery; +import top.continew.admin.business.model.query.HuandaResourceQuery; +import top.continew.admin.business.model.req.HuandaResourceReq; +import top.continew.admin.business.model.resp.HuandaResourceDetailResp; +import top.continew.admin.business.model.resp.HuandaResourceResp; +import top.continew.starter.data.mp.service.IService; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.extension.crud.service.BaseService; + +/** + * 换哒-文件资源业务接口 + * + * @author 王志维 + * @since 2025/12/23 16:53 + */ + +/** + * 换哒-文件资源业务接口 + * + * @author 王志维 + * @since 2025/12/23 16:53 + */ +public interface HuandaResourceService extends BaseService, IService { + + PageResp pageList(HuandaResourceQuery query, HuandaResourcePageQuery pageQuery); +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/ModelCategoryService.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/ModelCategoryService.java new file mode 100644 index 0000000..e1a09c4 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/ModelCategoryService.java @@ -0,0 +1,11 @@ +package top.continew.admin.business.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import top.continew.admin.business.model.entity.ModelCategoryDO; + +/** + * @author 张波 + * @since 2024/10/20 19:51 + */ +public interface ModelCategoryService extends IService { +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/ModelService.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/ModelService.java new file mode 100644 index 0000000..b0528ae --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/ModelService.java @@ -0,0 +1,17 @@ +package top.continew.admin.business.service; + +import top.continew.admin.business.model.entity.ModelDO; +import top.continew.admin.business.model.query.ModelQuery; +import top.continew.admin.business.model.req.ModelReq; +import top.continew.admin.business.model.resp.ModelResp; +import top.continew.starter.data.mp.service.IService; +import top.continew.starter.extension.crud.service.BaseService; + +/** + * 模型服务 + * + * @author 张波 + * @since 2024/10/19 15:32 + */ +public interface ModelService extends BaseService, IService { +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/OrderAudioService.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/OrderAudioService.java new file mode 100644 index 0000000..aeea182 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/OrderAudioService.java @@ -0,0 +1,20 @@ +package top.continew.admin.business.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import top.continew.admin.business.model.entity.OrderAudioDo; +import top.continew.admin.business.model.req.OrderAudioReq; + +/** + * 音频订单任务业务接口 + * + * @author hzy + * @since 2024/10/22 11:58 + */ +public interface OrderAudioService extends IService { + /** + * 创建音频订单任务 + * + * @param req 参数 + */ + void add(OrderAudioReq req); +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/OrderHistoryService.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/OrderHistoryService.java new file mode 100644 index 0000000..69d9680 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/OrderHistoryService.java @@ -0,0 +1,14 @@ +package top.continew.admin.business.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import top.continew.admin.business.model.entity.OrderHistoryDo; + +/** + * 订单任务历史业务接口 + * + * @author hzy + * @since 2024/10/22 11:58 + */ +public interface OrderHistoryService extends IService { + +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/PlantformBalanceService.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/PlantformBalanceService.java new file mode 100644 index 0000000..61fbe65 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/PlantformBalanceService.java @@ -0,0 +1,88 @@ +package top.continew.admin.business.service; + +import top.continew.admin.business.model.entity.PlantformBalanceDO; +import top.continew.admin.business.model.query.PlantformBalanceQuery; +import top.continew.admin.business.model.req.CheckPlantBalanceReq; +import top.continew.admin.business.model.req.PlantformBalanceAddReq; +import top.continew.admin.business.model.req.PlantformBalanceEditReq; +import top.continew.admin.business.model.resp.PlantformBalanceResp; +import top.continew.starter.data.mp.service.IService; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.web.model.R; + +import java.util.List; + +/** + * 用户子账号余额表业务接口 + * + * @author jiay + * @since 2025/06/24 16:47 + */ +public interface PlantformBalanceService extends IService { + + /** + * 获取用户子账号余额列表 + * + * @param query 用户子账号余额查询条件 + * @return 用户子账号余额列表 + */ + List getPlantformBalanceList(PlantformBalanceQuery query); + + /** + * 添加用户子账号余额 + * + * @param req 用户子账号余额 + * @return url + */ + boolean addPlantformBalance(PlantformBalanceAddReq req); + + /** + * 添加用户子账号余额 + * + * @param req 用户子账号余额 + * @return 添加是否成功 + */ + boolean editPlantformBalance(PlantformBalanceEditReq req); + + /** + * 添加用户子账号余额 + * + * @param id 用户子账号余额id + * @return 添加是否成功 + */ + boolean deletePlantformBalance(Long id); + + /** + * 添加用户子账号余额 + * + * @param id 用户子账号余额id + * @return 用户子账号余额 + */ + PlantformBalanceResp getPlantformBalance(Long id); + + /** + * 添加用户子账号余额 + * + * @param query 用户子账号余额查询条件 + * @param pageQuery 分页参数 + * @return 用户子账号余额分页列表 + */ + PageResp getPlantformBalancePage(PlantformBalanceQuery query, PageQuery pageQuery); + + /** + * 根据用户ID获取用户所有子账户余额 + * + * @param userId 用户ID + * @return 用户子账号余额列表 + */ + List getUserBalancesByUserId(Long userId); + + /** + * 判断余额是否充足 + * + * @param req 用子账号余额查询条件 + * @return 子账号余额是否充足 + */ + R checkPlantBalance(CheckPlantBalanceReq req); +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/ScoreService.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/ScoreService.java new file mode 100644 index 0000000..e04837a --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/ScoreService.java @@ -0,0 +1,71 @@ +package top.continew.admin.business.service; + +import top.continew.admin.business.model.entity.ScoreDO; +import top.continew.admin.business.model.query.ScoreQuery; +import top.continew.admin.business.model.req.ScoreAddReq; +import top.continew.admin.business.model.req.ScoreEditReq; +import top.continew.admin.business.model.resp.ScoreResp; +import top.continew.starter.data.mp.service.IService; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; + +import java.util.List; + +/** + * 分数业务接口 + * + * @author jiay + * @since 2025/06/26 10:24 + */ +public interface ScoreService extends IService { + + /** + * 获取分数列表 + * + * @param query 分数查询条件 + * @return 分数列表 + */ + List getScoreList(ScoreQuery query); + + /** + * 添加分数 + * + * @param req 分数 + * @return url + */ + boolean addScore(ScoreAddReq req); + + /** + * 添加分数 + * + * @param req 分数 + * @return 添加是否成功 + */ + boolean editScore(ScoreEditReq req); + + /** + * 添加分数 + * + * @param id 分数id + * @return 添加是否成功 + */ + boolean deleteScore(Long id); + + /** + * 添加分数 + * + * @param id 分数id + * @return 分数 + */ + ScoreResp getScore(Long id); + + /** + * 添加分数 + * + * @param query 分数查询条件 + * @param pageQuery 分页参数 + * @return 分数分页列表 + */ + PageResp getScorePage(ScoreQuery query, PageQuery pageQuery); + +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/SignInService.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/SignInService.java new file mode 100644 index 0000000..2087aa2 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/SignInService.java @@ -0,0 +1,106 @@ +package top.continew.admin.business.service; + +import jakarta.servlet.http.HttpServletResponse; +import top.continew.admin.business.model.entity.SignInDO; +import top.continew.admin.business.model.query.SignInQuery; +import top.continew.admin.business.model.req.SignInAddReq; +import top.continew.admin.business.model.req.SignInEditReq; +import top.continew.admin.business.model.resp.SignInResp; +import top.continew.starter.data.mp.service.IService; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.query.SortQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; +import java.util.List; + +/** + * 签到业务接口 + * + * @author jiay + * @since 2025/06/26 10:24 + */ +public interface SignInService extends IService { + + /** + * 获取签到列表 + * + * @param query 签到查询条件 + * @return 签到列表 + */ + List getSignInList(SignInQuery query); + + /** + * 添加签到 + * + * @param req 签到 + * @return url + */ + boolean addSignIn(SignInAddReq req); + + /** + * 添加签到 + * + * @param req 签到 + * @return 添加是否成功 + */ + boolean editSignIn(SignInEditReq req); + + /** + * 添加签到 + * + * @param id 签到id + * @return 添加是否成功 + */ + boolean deleteSignIn(Long id); + + /** + * 添加签到 + * + * @param id 签到id + * @return 签到 + */ + SignInResp getSignIn(Long id); + + /** + * 添加签到 + * + * @param query 签到查询条件 + * @param pageQuery 分页参数 + * @return 签到分页列表 + */ + PageResp getSignInPage(SignInQuery query, PageQuery pageQuery); + + /** + * 查询今日未签到列表 + * + * @param query 签到查询条件 + * @return 今日未签到列表 + */ + List getTodayNosignInList(SignInQuery query); + + /** + * 分页查询历史签到列表 + * + * @param query 签到查询条件 + * @param pageQuery 分页参数 + * @return 签到分页列表 + */ + PageResp getHisSignInList(SignInQuery query, PageQuery pageQuery); + + /** + * 导出历史签到列表 + * + * @param query 查询条件 + * @param sortQuery 排序参数 + * @param response 参数 + */ + void exportHisSignInList(SignInQuery query, SortQuery sortQuery, HttpServletResponse response); + + /** + * 导出今日未签到列表 + * + * @param query 查询条件 + * @param sortQuery 排序参数 + * @param response 参数 + */ + void exportTodayNosignInList(SignInQuery query, SortQuery sortQuery, HttpServletResponse response); +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/SubtitleItemService.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/SubtitleItemService.java new file mode 100644 index 0000000..776addb --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/SubtitleItemService.java @@ -0,0 +1,22 @@ +package top.continew.admin.business.service; + +import top.continew.admin.business.model.query.SubtitleItemQuery; +import top.continew.admin.business.model.req.SubtitleItemReq; +import top.continew.admin.business.model.resp.SubtitleItemDetailResp; +import top.continew.admin.business.model.resp.SubtitleItemResp; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.extension.crud.service.BaseService; + +/** + * 数字人-字幕项服务接口 + * + * @author 王志维 + * @since 2025/12/29 + */ +public interface SubtitleItemService extends BaseService { + + PageResp pageList(SubtitleItemQuery query, + top.continew.admin.business.model.query.SubtitleItemPageQuery pageQuery); + + // 可根据需要添加额外的业务方法 +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/SubtitleStyleService.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/SubtitleStyleService.java new file mode 100644 index 0000000..1e3edd9 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/SubtitleStyleService.java @@ -0,0 +1,20 @@ +package top.continew.admin.business.service; + +import top.continew.admin.business.model.query.SubtitleStyleQuery; +import top.continew.admin.business.model.req.SubtitleStyleReq; +import top.continew.admin.business.model.resp.SubtitleStyleDetailResp; +import top.continew.admin.business.model.resp.SubtitleStyleResp; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.extension.crud.service.BaseService; + +/** + * 数字人-字幕样式服务接口 + * + * @author 王志维 + * @since 2025/12/29 + */ +public interface SubtitleStyleService extends BaseService { + // 可根据需要添加额外的业务方法 + PageResp pageList(SubtitleStyleQuery query, + top.continew.admin.business.model.query.SubtitleStylePageQuery pageQuery); +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/TaskRecordHistoryService.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/TaskRecordHistoryService.java new file mode 100644 index 0000000..5151370 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/TaskRecordHistoryService.java @@ -0,0 +1,70 @@ +package top.continew.admin.business.service; + +import top.continew.admin.business.model.entity.TaskRecordHistoryDO; +import top.continew.admin.business.model.query.TaskRecordHistoryQuery; +import top.continew.admin.business.model.req.TaskRecordHistoryAddReq; +import top.continew.admin.business.model.req.TaskRecordHistoryEditReq; +import top.continew.admin.business.model.resp.TaskRecordHistoryResp; +import top.continew.starter.data.mp.service.IService; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; +import java.util.List; + +/** + * 音视图文生成记录业务接口 + * + * @author jiay + * @since 2025/05/23 07:51 + */ +public interface TaskRecordHistoryService extends IService { + + /** + * 获取音视图文生成记录列表 + * + * @param query 音视图文生成记录查询条件 + * @return 音视图文生成记录列表 + */ + List getTaskRecordHistoryList(TaskRecordHistoryQuery query); + + /** + * 添加音视图文生成记录 + * + * @param req 音视图文生成记录 + * @return url + */ + List addTaskRecordHistory(TaskRecordHistoryAddReq req); + + /** + * 添加音视图文生成记录 + * + * @param req 音视图文生成记录 + * @return 添加是否成功 + */ + boolean editTaskRecordHistory(TaskRecordHistoryEditReq req); + + /** + * 添加音视图文生成记录 + * + * @param id 音视图文生成记录id + * @return 添加是否成功 + */ + boolean deleteTaskRecordHistory(Long id); + + /** + * 添加音视图文生成记录 + * + * @param id 音视图文生成记录id + * @return 音视图文生成记录 + */ + TaskRecordHistoryResp getTaskRecordHistory(Long id); + + /** + * 添加音视图文生成记录 + * + * @param query 音视图文生成记录查询条件 + * @param pageQuery 分页参数 + * @return 音视图文生成记录分页列表 + */ + PageResp getTaskRecordHistoryPage(TaskRecordHistoryQuery query, PageQuery pageQuery); + +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/AssignmentSubmissionsServiceImpl.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/AssignmentSubmissionsServiceImpl.java new file mode 100644 index 0000000..f53a3f7 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/AssignmentSubmissionsServiceImpl.java @@ -0,0 +1,151 @@ +package top.continew.admin.business.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.dynamic.datasource.annotation.DS; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import top.continew.admin.business.mapper.AssignmentSubmissionsMapper; +import top.continew.admin.business.model.entity.AssignmentSubmissionsDO; +import top.continew.admin.business.model.entity.AssignmentsDO; +import top.continew.admin.business.model.entity.CourseUserDO; +import top.continew.admin.business.model.req.AssignmentSubmissionsReq; +import top.continew.admin.business.model.req.AssignmentsCorrectReq; +import top.continew.admin.business.model.resp.AssignmentsProgressResp; +import top.continew.admin.business.model.resp.AssignmentsSubmissionsDetailResp; +import top.continew.admin.business.service.AssignmentSubmissionsService; +import top.continew.admin.business.service.AssignmentsService; +import top.continew.admin.business.service.CourseUserService; +import top.continew.admin.common.util.helper.LoginHelper; +import top.continew.starter.core.util.validate.CheckUtils; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author 张波 + * @since 2024/10/22 15:38 + */ +@Service +@RequiredArgsConstructor +public class AssignmentSubmissionsServiceImpl extends ServiceImpl implements AssignmentSubmissionsService { + + private final CourseUserService courseUserService; + + private final AssignmentsService assignmentsService; + + @Override + public void submit(AssignmentSubmissionsReq req) { + Long loginUserId = LoginHelper.getUserId(); + + boolean exists = lambdaQuery().eq(AssignmentSubmissionsDO::getAssignmentId, req.getAssignmentId()) + .eq(AssignmentSubmissionsDO::getUserId, loginUserId) + .exists(); + + CheckUtils.throwIf(exists, "作业已提交,请勿重复提交!"); + + AssignmentSubmissionsDO saveDb = BeanUtil.copyProperties(req, AssignmentSubmissionsDO.class); + saveDb.setUserId(loginUserId); + save(saveDb); + } + + @Override + public void correct(AssignmentsCorrectReq req) { + // 查询作业提交记录 + AssignmentSubmissionsDO assignmentSubmissions = getById(req.getId()); + + // 检查是否已经批改 + CheckUtils.throwIf(assignmentSubmissions.getScore() != null, "作业已批改,请勿重复批改!"); + + // 修改分数以及评语 + lambdaUpdate().eq(AssignmentSubmissionsDO::getId, req.getId()) + .set(AssignmentSubmissionsDO::getScore, req.getScore()) + .set(StrUtil.isNotBlank(req.getFeedback()), AssignmentSubmissionsDO::getFeedback, req.getFeedback()) + .update(); + } + + @Override + public AssignmentsSubmissionsDetailResp detail(Long submissionsId) { + // 查询提交记录 + AssignmentSubmissionsDO submissionsDO = getById(submissionsId); + CheckUtils.throwIfNull(submissionsDO, "作业提交记录不存在"); + + // 查询作业信息 + AssignmentsDO assignmentsDO = assignmentsService.getById(submissionsDO.getAssignmentId()); + + // 拷贝相关信息 构建返回对象 + AssignmentsSubmissionsDetailResp res = BeanUtil + .copyProperties(assignmentsDO, AssignmentsSubmissionsDetailResp.class); + BeanUtil.copyProperties(submissionsDO, res); + + return res; + } + + @DS("#dbName") + @Override + public PageResp getGoodImageList(String dbName, PageQuery pageQuery) { + // 查询分数80以上的作业 + Page page = lambdaQuery().select(AssignmentSubmissionsDO::getImageUrl) + .ge(AssignmentSubmissionsDO::getScore, 80) + .orderByDesc(AssignmentSubmissionsDO::getCreateTime) + .page(new Page<>(pageQuery.getPage(), pageQuery.getSize())); + + // 提取图片地址 + List imageUrlList = page.getRecords().stream().map(AssignmentSubmissionsDO::getImageUrl).toList(); + + // 构建分页返回 + return new PageResp<>(imageUrlList, page.getTotal()); + } + + @Override + public PageResp degreeOfCompletion(Long assignmentsId, PageQuery pageQuery) { + // 检查作业是否存在 + AssignmentsDO assignmentsDO = assignmentsService.getById(assignmentsId); + CheckUtils.throwIfNull(assignmentsDO, "作业不存在!"); + + // 分页查询作业有哪些学生 + Page page = new Page<>(pageQuery.getPage(), pageQuery.getSize()); + page = courseUserService.lambdaQuery().eq(CourseUserDO::getCourseId, assignmentsDO.getCourseId()).page(page); + + List records = page.getRecords(); + if (CollUtil.isEmpty(records)) { + return new PageResp<>(Collections.emptyList(), 0L); + } + + // 提取学生ID 查询提交记录表 + List userIds = records.stream().map(CourseUserDO::getUserId).toList(); + Map submissionsMap = lambdaQuery() + .eq(AssignmentSubmissionsDO::getAssignmentId, assignmentsId) + .in(AssignmentSubmissionsDO::getUserId, userIds) + .list() + .stream() + .collect(Collectors.toMap(AssignmentSubmissionsDO::getUserId, Function.identity())); + + // 查询是否有提交记录,判断学生是否提交作业,判断是否有分数,判断老师是否批改作业 + List res = records.stream().map(courseUser -> { + AssignmentsProgressResp temp = new AssignmentsProgressResp(); + temp.setNickname(courseUser.getNickname()); + + AssignmentSubmissionsDO submissions = submissionsMap.get(courseUser.getUserId()); + if (submissions == null) { + temp.setDegreeOfCompletion(false); + temp.setDegreeOfCorrection(false); + } else { + temp.setSubmissionsId(submissions.getId()); + temp.setDegreeOfCompletion(true); + temp.setDegreeOfCorrection(submissions.getScore() != null); + } + return temp; + }).toList(); + + return new PageResp<>(res, page.getTotal()); + } +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/AssignmentsServiceImpl.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/AssignmentsServiceImpl.java new file mode 100644 index 0000000..7178350 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/AssignmentsServiceImpl.java @@ -0,0 +1,48 @@ +package top.continew.admin.business.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import top.continew.admin.business.mapper.AssignmentsMapper; +import top.continew.admin.business.model.entity.AssignmentsDO; +import top.continew.admin.business.model.req.AssignmentsReq; +import top.continew.admin.business.model.resp.AssignmentsDetailResp; +import top.continew.admin.business.service.AssignmentsService; +import top.continew.starter.core.util.validate.CheckUtils; + +/** + * 作业表业务实现 + * + * @author 张波 + * @since 2024/10/22 11:45 + */ +@Service +@RequiredArgsConstructor +public class AssignmentsServiceImpl extends ServiceImpl implements AssignmentsService { + + @Override + public Long add(AssignmentsReq assignmentsReq) { + // 检查名字是否存在 + boolean exists = lambdaQuery().eq(AssignmentsDO::getCourseId, assignmentsReq.getCourseId()) + .eq(AssignmentsDO::getName, assignmentsReq.getName()) + .exists(); + CheckUtils.throwIf(exists, "作业名称已存在,请检查"); + + // copy数据 保存到数据库 + AssignmentsDO saveDb = BeanUtil.copyProperties(assignmentsReq, AssignmentsDO.class); + save(saveDb); + + // 返回存储的ID + return saveDb.getId(); + } + + @Override + public AssignmentsDetailResp detail(Long assignmentsId) { + // 查询库中数据 + AssignmentsDO assignmentsDO = getById(assignmentsId); + + // 转为Resp + return BeanUtil.copyProperties(assignmentsDO, AssignmentsDetailResp.class); + } +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/BillingRuleServiceImpl.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/BillingRuleServiceImpl.java new file mode 100644 index 0000000..e694dd2 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/BillingRuleServiceImpl.java @@ -0,0 +1,85 @@ +package top.continew.admin.business.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import top.continew.admin.business.mapper.BillingRuleMapper; +import top.continew.admin.business.model.entity.BillingRuleDO; +import top.continew.admin.business.model.query.BillingRuleQuery; +import top.continew.admin.business.model.req.BillingRuleReq; +import top.continew.admin.business.model.resp.BillingRuleResp; +import top.continew.admin.business.service.BillingRuleService; +import top.continew.starter.extension.crud.service.impl.BaseServiceImpl; + +import java.util.List; + +/** + * 计费规则业务实现 + * + * @author jiay + * @since 2025/11/14 + */ +@Service +@RequiredArgsConstructor +public class BillingRuleServiceImpl extends BaseServiceImpl implements BillingRuleService { + + /** + * 根据平台ID获取计费规则(带缓存) + */ + @Override + @Cacheable(value = "billingRule", key = "#platformId", unless = "#result == null") + public BillingRuleDO getByPlatformId(Long platformId) { + return baseMapper.selectOne(new LambdaQueryWrapper().eq(BillingRuleDO::getPlatformId, platformId) + .eq(BillingRuleDO::getEnabled, 1) + .last("LIMIT 1")); + } + + /** + * 根据平台ID获取计费金额 + */ + @Override + public Long getPriceByPlatformId(Long platformId) { + BillingRuleDO rule = getByPlatformId(platformId); + return rule != null && rule.getPrice() != null ? rule.getPrice() : 0L; + } + + /** + * 新增或修改后清除缓存 + */ + @Override + @CacheEvict(value = "billingRule", allEntries = true) + public Long add(BillingRuleReq req) { + return super.add(req); + } + + /** + * 更新后清除缓存 + */ + @Override + @CacheEvict(value = "billingRule", allEntries = true) + public void update(BillingRuleReq req, Long id) { + super.update(req, id); + } + + /** + * 删除后清除缓存(批量删除) + */ + @Override + @CacheEvict(value = "billingRule", allEntries = true) + public void delete(List ids) { + super.delete(ids); + } + + /** + * 根据扣费编码获取计费规则(带缓存) + */ + @Override + @Cacheable(value = "billingRule", key = "'charge_' + #chargeCode", unless = "#result == null") + public BillingRuleDO getByChargeCode(String chargeCode) { + return baseMapper.selectOne(new LambdaQueryWrapper().eq(BillingRuleDO::getChargeCode, chargeCode) + .eq(BillingRuleDO::getEnabled, 1) + .last("LIMIT 1")); + } +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/BillingServiceImpl.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/BillingServiceImpl.java new file mode 100644 index 0000000..ddb5827 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/BillingServiceImpl.java @@ -0,0 +1,913 @@ +package top.continew.admin.business.service.impl; + +import cn.hutool.core.util.StrUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.x.file.storage.core.FileInfo; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; + +import org.springframework.util.StringUtils; +import org.springframework.web.multipart.MultipartFile; +import top.continew.admin.business.file.CommonMultipartFile; +import top.continew.admin.business.model.entity.BillingRuleDO; +import top.continew.admin.business.model.entity.ConsumptionHistoryDO; +import top.continew.admin.business.model.entity.PlantformBalanceDO; +import top.continew.admin.business.model.entity.TaskRecordHistoryDO; +import top.continew.admin.business.model.req.*; +import top.continew.admin.system.model.entity.TaskPreDeductDO; +import top.continew.admin.business.model.resp.BillingDeductResp; +import top.continew.admin.business.model.resp.TaskCallbackResp; +import top.continew.admin.business.service.BillingService; +import top.continew.admin.business.service.BillingRuleService; +import top.continew.admin.business.mapper.ConsumptionHistoryMapper; +import top.continew.admin.business.mapper.PlantformBalanceMapper; +import top.continew.admin.business.mapper.TaskRecordHistoryMapper; +import top.continew.admin.system.mapper.TaskPreDeductMapper; +import top.continew.admin.system.model.entity.UserDO; +import top.continew.admin.system.mapper.UserMapper; +import cn.dev33.satoken.stp.StpUtil; +import top.continew.admin.system.service.FileService; + +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URL; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +/** + * 计费服务实现类 + * + * @author jiay + * @since 2025/08/28 00:00 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class BillingServiceImpl implements BillingService { + + private final TaskRecordHistoryMapper taskRecordHistoryMapper; + private final ConsumptionHistoryMapper consumptionHistoryMapper; + private final PlantformBalanceMapper plantformBalanceMapper; + private final TaskPreDeductMapper taskPreDeductMapper; + private final UserMapper userMapper; + private final FileService fileService; + private final BillingRuleService billingRuleService; + + @Override + @Transactional(rollbackFor = Exception.class) + public BillingDeductResp judgeBalance(BillingJudgeReq req) { + Long userId = StpUtil.getLoginIdAsLong(); + try { + // 1. 获取用户信息 + UserDO user = userMapper.selectById(userId); + if (user == null) { + return BillingDeductResp.builder().success(false).errorMessage("用户不存在").build(); + } + + // 2. 充点类型不需要预扣减 + if (req.getType() == 0) { + return BillingDeductResp.builder() + .success(true) + .remainingBalance(user.getSysBeans()) + .errorMessage("无需预扣减,充点类型") + .build(); + } + + // 3. 根据chargeCode从数据库查询计费规则 + BillingRuleDO billingRule = billingRuleService.getByChargeCode(req.getChargeCode()); + if (billingRule == null) { + log.error("未找到扣费编码 {} 的计费规则", req.getChargeCode()); + return BillingDeductResp.builder().success(false).errorMessage("未找到对应的计费规则").build(); + } + + Integer quantity = req.getQuantity(); + Long price = billingRule.getPrice() != null ? billingRule.getPrice() : 0L; + Long subAccountPrice = billingRule.getSubAccountPrice() != null ? billingRule.getSubAccountPrice() : 0L; + Integer chargeType = billingRule.getChargeType(); + + String deductSource = ""; + boolean canDeduct = false; + Long preDeductAmount = 0L; + + // 4. 优先级1:根据chargeType及用户id从t_plantform_balance取子账户 + if (chargeType != null && subAccountPrice > 0) { + PlantformBalanceDO subBalance = plantformBalanceMapper + .selectOne(new LambdaQueryWrapper().eq(PlantformBalanceDO::getUserId, userId) + .eq(PlantformBalanceDO::getChargeType, chargeType)); + + if (subBalance != null) { + Long availableBalance = subBalance.getBalance() - subBalance.getPreDeductBalance(); + Long requiredAmount = quantity * subAccountPrice; + //大语言模型 Token计费 + if (chargeType == 0) { + requiredAmount = req.getTokens(); + } + if (availableBalance >= requiredAmount) { + // 子账户余额足够,使用子账户单价 + deductSource = "sub_account_" + chargeType; + canDeduct = true; + preDeductAmount = requiredAmount; + + // 更新子账号预扣减金额 + plantformBalanceMapper.update(null, new LambdaUpdateWrapper() + .eq(PlantformBalanceDO::getUserId, userId) + .eq(PlantformBalanceDO::getChargeType, chargeType) + .setSql("pre_deduct_balance = pre_deduct_balance + " + preDeductAmount)); + + log.info("使用子账户扣减, chargeCode: {}, chargeType: {}, requiredAmount: {}, subAccountPrice: {}, preDeductAmount: {}, 剩余子账户: {}", req + .getChargeCode(), chargeType, requiredAmount, subAccountPrice, preDeductAmount, availableBalance - preDeductAmount); + } + } + } + + // 5. 优先级2:如果子账户余额不足,根据用户id及chargeType=11从t_plantform_balance取可学豆子账户 + if (!canDeduct) { + PlantformBalanceDO kexuedouBalance = plantformBalanceMapper + .selectOne(new LambdaQueryWrapper().eq(PlantformBalanceDO::getUserId, userId) + .eq(PlantformBalanceDO::getChargeType, 11)); // chargeType=11 是可学豆子账户 + + if (kexuedouBalance != null) { + Long requiredAmount; + if (chargeType != null && chargeType == 0) { + // Token类型,需要将token数量转换为点数 + requiredAmount = calculateTokenToPoints(req.getChargeCode(), req.getTokens()); + log.info("Token转换为点数, tokens: {}, points: {}", req.getTokens(), requiredAmount); + } else { + // 非token类型,直接使用 quantity * price + requiredAmount = quantity * price; + } + Long availableBalance = kexuedouBalance.getBalance() - kexuedouBalance.getPreDeductBalance(); + + if (availableBalance >= requiredAmount) { + deductSource = "kexuedou_account"; + canDeduct = true; + preDeductAmount = requiredAmount; + + // 更新可学豆子账户预扣减金额 + plantformBalanceMapper.update(null, new LambdaUpdateWrapper() + .eq(PlantformBalanceDO::getUserId, userId) + .eq(PlantformBalanceDO::getChargeType, 11) + .setSql("pre_deduct_balance = pre_deduct_balance + " + preDeductAmount)); + + } + } + } + + if (!canDeduct) { + Long requiredAmount; + if (chargeType != null && chargeType == 0) { + // Token类型,需要将token数量转换为点数 + requiredAmount = calculateTokenToPoints(req.getChargeCode(), req.getTokens()); + log.info("Token转换为点数(主账户), tokens: {}, points: {}", req.getTokens(), requiredAmount); + } else { + // 非token类型,直接使用 quantity * price + requiredAmount = quantity * price; + } + Long actualBalance = (user.getSysBeans() != null ? user.getSysBeans().longValue() : 0L); + Long preDeductedBalance = (user.getPreDeductBeans() != null + ? user.getPreDeductBeans().longValue() + : 0L); + Long availableBalance = actualBalance - preDeductedBalance; + + if (availableBalance >= requiredAmount) { + deductSource = "main_account"; + canDeduct = true; + preDeductAmount = requiredAmount; + + // 更新主账户预扣减金额 + userMapper.addPreDeductBeansById(userId, preDeductAmount); + + } else { + log.warn("主账户余额不足, chargeCode: {}, 需要: {}分, 可用: {}分", req + .getChargeCode(), requiredAmount, availableBalance); + } + } + + if (!canDeduct) { + return BillingDeductResp.builder().success(false).errorMessage("可用余额不足,请充值").build(); + } + + // 7. 创建或更新预扣减记录 + TaskPreDeductDO existingPreDeduct = taskPreDeductMapper.selectOne(new LambdaQueryWrapper() + .eq(TaskPreDeductDO::getUserId, userId) + .eq(TaskPreDeductDO::getChargeType, req.getChargeType())); + + if (existingPreDeduct != null) { + // 存在记录,更新 + existingPreDeduct.setPreDeductAmount(preDeductAmount + existingPreDeduct.getPreDeductAmount()); + existingPreDeduct.setDeductSource(deductSource); + existingPreDeduct.setStatus(0); // 0: 进行中 + + taskPreDeductMapper.updateById(existingPreDeduct); + } else { + // 不存在记录,新增 + TaskPreDeductDO preDeductRecord = new TaskPreDeductDO(); + preDeductRecord.setUserId(userId); + preDeductRecord.setChargeType(req.getChargeType()); + preDeductRecord.setPreDeductAmount(preDeductAmount); + preDeductRecord.setDeductSource(deductSource); + preDeductRecord.setStatus(0); // 0: 进行中 + + taskPreDeductMapper.insert(preDeductRecord); + } + + // 8. 返回校验成功结果 + return BillingDeductResp.builder() + .success(true) + .remainingBalance(user.getSysBeans()) + .errorMessage("余额校验通过并已预扣减,来源: " + deductSource) + .build(); + + } catch (Exception e) { + log.error("余额校验失败, userId: {}, req: {}", userId, req, e); + throw e; + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public TaskCallbackResp taskCallback(TaskCallbackReq req) { + Long userId = StpUtil.getLoginIdAsLong(); + try { + // 1. 查找预扣减记录 + TaskPreDeductDO preDeductRecord = taskPreDeductMapper.selectOne(new LambdaQueryWrapper() + .eq(TaskPreDeductDO::getUserId, userId) + .eq(TaskPreDeductDO::getChargeType, req.getChargeType()) + // .eq(TaskPreDeductDO::getStatus, 0) // 进行中的任务 + ); + + if (preDeductRecord == null) { + return TaskCallbackResp.builder().success(false).message("未找到对应的预扣减记录").build(); + } + + // 2. 根据任务状态处理 + if (req.getStatus() == 1) { // 任务成功 + return handleSuccessCallback(req, preDeductRecord, userId); + } else if (req.getStatus() == 0) { + return TaskCallbackResp.builder().success(false).message("任务进行中").build(); + } else { // 任务失败或取消 + return handleFailureCallback(req, preDeductRecord, userId); + } + + } catch (Exception e) { + log.error("任务回调异常, taskId: {}, req: {}", req.getTaskId(), req, e); + return TaskCallbackResp.builder().success(false).message("任务回调异常: " + e.getMessage()).build(); + } + } + + /** + * 处理任务成功的回调 + */ + private TaskCallbackResp handleSuccessCallback(TaskCallbackReq req, TaskPreDeductDO preDeductRecord, Long userId) { + // 1. 根据chargeCode查询计费规则,计算实际消费点数 + Long actualConsumptionPoints; + Integer actualQuantity = req.getQuantity() != null ? req.getQuantity() : 1; + + if (req.getChargeCode() == null) { + log.error("扣费编码不能为空"); + return TaskCallbackResp.builder().success(false).message("扣费编码不能为空").build(); + } + + // 查询计费规则 + BillingRuleDO billingRule = billingRuleService.getByChargeCode(req.getChargeCode()); + if (billingRule == null) { + log.error("未找到扣费编码 {} 的计费规则", req.getChargeCode()); + return TaskCallbackResp.builder().success(false).message("未找到对应的计费规则").build(); + } + + String deductSource = preDeductRecord.getDeductSource(); + Integer chargeType = billingRule.getChargeType(); + + // 特殊处理:chargeType=0 表示大语言模型,使用token计费 + if (chargeType != null && chargeType == 0) { + // Token计费类型 + if (deductSource != null && deductSource.startsWith("sub_account_")) { + actualConsumptionPoints = req.getTokens(); + // 从token子账户扣减,直接扣除token数量 + log.info("Token子账户扣费, chargeCode: {}, actualTokens: {}", req.getChargeCode(), actualConsumptionPoints); + } else { + // 从可学豆或主账户扣减,需要根据扣费编码计算点数 + actualConsumptionPoints = calculateTokenToPoints(req.getChargeCode(), req.getTokens()); + } + } else { + // 非token类型,按数量计费 + if (deductSource != null && deductSource.startsWith("sub_account_")) { + // 子账户扣减,使用子账户单价 + Long subAccountPrice = billingRule.getSubAccountPrice() != null ? billingRule.getSubAccountPrice() : 0L; + actualConsumptionPoints = actualQuantity * subAccountPrice; + } else { + // 可学豆子账户或主账户扣减,使用主账户单价 + Long price = billingRule.getPrice() != null ? billingRule.getPrice() : 0L; + actualConsumptionPoints = actualQuantity * price; + } + } + + // 2. 创建任务记录 + TaskRecordHistoryDO taskRecord = new TaskRecordHistoryDO(); + taskRecord.setUserId(userId); + taskRecord.setPlatformId(req.getPlatformId()); + taskRecord.setTaskId(req.getTaskId()); + taskRecord.setTitle(req.getTitle()); + taskRecord.setTaskType(req.getTaskType()); + taskRecord.setChargeType(req.getChargeType()); + taskRecord.setModelName(req.getModelName()); + taskRecord.setConsumptionPoints(actualConsumptionPoints); + taskRecord.setPlatformCode(req.getPlatformCode()); + taskRecord.setAccountType(deductSource); + + //换哒新表补充字段 + taskRecord.setParentCreateTime(extractDateTime(req.getParentCreateTime())); + taskRecord.setTaskRootId(req.getTaskRootId()); + if (req.getParentTaskId() != null) { + taskRecord.setParentTaskId(req.getParentTaskId()); + } + + // 设置文件路径或文字内容 + if (req.getTaskType() == 0 || !StringUtils.hasText(req.getFileUrl())) { // token类型或文字类型 + taskRecord.setResult(req.getResult()); + taskRecord.setTokens(req.getTokens()); + taskRecordHistoryMapper.insert(taskRecord); + } else { // 音视频图片类型(2,3,4,5) + addTaskRecordHistory(taskRecord, req); + } + + // 3. 创建消费记录 + ConsumptionHistoryDO consumption = new ConsumptionHistoryDO(); + consumption.setUserId(userId); + consumption.setPlatformId(req.getPlatformId()); + consumption.setTaskType(req.getTaskType()); + consumption.setChargePoints(actualConsumptionPoints); + + // 4. 根据扣减来源进行实际扣费和清零预扣减 + if (deductSource != null && deductSource.startsWith("sub_account_")) { + // 子账户扣费(chargeType特定的子账户,如sub_account_1, sub_account_3等) + PlantformBalanceDO balance = plantformBalanceMapper.selectOne(new LambdaQueryWrapper() + .eq(PlantformBalanceDO::getUserId, userId) + .eq(PlantformBalanceDO::getChargeType, chargeType)); + + consumption.setRemainingPoints(balance.getBalance() - actualConsumptionPoints); + consumption.setType(req.getType()); // 消费类型 + consumption.setAccountType(1); // 1子账号 + + // 扣减实际金额并清零预扣减 + plantformBalanceMapper.update(null, new LambdaUpdateWrapper() + .eq(PlantformBalanceDO::getUserId, userId) + .eq(PlantformBalanceDO::getChargeType, chargeType) + .setSql("balance = balance - " + actualConsumptionPoints + ", pre_deduct_balance = pre_deduct_balance - " + preDeductRecord + .getPreDeductAmount())); + + } else if ("kexuedou_account".equals(deductSource)) { + // 可学豆子账户扣费(chargeType=11的子账户) + PlantformBalanceDO balance = plantformBalanceMapper.selectOne(new LambdaQueryWrapper() + .eq(PlantformBalanceDO::getUserId, userId) + .eq(PlantformBalanceDO::getChargeType, 11)); + + consumption.setRemainingPoints(balance.getBalance() - actualConsumptionPoints); + consumption.setType(req.getType()); // 消费类型 + consumption.setAccountType(1); // 1子账号 + + // 扣减实际金额并清零预扣减 + plantformBalanceMapper.update(null, new LambdaUpdateWrapper() + .eq(PlantformBalanceDO::getUserId, userId) + .eq(PlantformBalanceDO::getChargeType, 11) + .setSql("balance = balance - " + actualConsumptionPoints + ", pre_deduct_balance = pre_deduct_balance - " + preDeductRecord + .getPreDeductAmount())); + + log.info("可学豆子账户扣费完成, actualConsumption: {}", actualConsumptionPoints); + } else if ("main_account".equals(deductSource)) { + // 主账户sys_beans扣费 + UserDO user = userMapper.selectById(userId); + consumption.setRemainingPoints(user.getSysBeans().longValue() - actualConsumptionPoints); + consumption.setType(req.getType()); // 消费类型 + consumption.setAccountType(0); // 0主账号 + + // 原子操作:扣减实际金额并清零预扣减 单位为分 + int updateResult = userMapper.deductActualAndClearPreDeduct(userId, preDeductRecord + .getPreDeductAmount(), actualConsumptionPoints); + if (updateResult == 0) { + throw new RuntimeException("扣减用户余额失败"); + } + + log.info("主账户扣费完成, actualConsumption: {}", actualConsumptionPoints); + } else { + log.error("未知的扣减来源: {}", deductSource); + throw new RuntimeException("未知的扣减来源: " + deductSource); + } + + consumptionHistoryMapper.insert(consumption); + + // 5. 更新预扣减记录状态 + preDeductRecord.setStatus(1); // 1: 已完成 + taskPreDeductMapper.updateById(preDeductRecord); + + log.info("任务成功回调完成, taskId: {}, userId: {}, platformCode: {}, preDeduct: {}, actual: {}", req + .getTaskId(), userId, req.getPlatformCode(), preDeductRecord.getPreDeductAmount(), actualConsumptionPoints); + + return TaskCallbackResp.builder().success(true).message("任务成功,扣费完成").taskRecordId(taskRecord.getId()).build(); + } + + /** + * 从字符串中提取时间 + * + * @param dateTimeString 时间字符串 + * @return LocalDateTime + */ + private static LocalDateTime extractDateTime(String dateTimeString) { + try { + if (dateTimeString == null || dateTimeString.isEmpty()) { + return null; + } + return LocalDateTime.parse(dateTimeString, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); + } catch (Exception e) { + return null; + } + } + + /** + * 将token数量转换为点数(用于从可学豆或主账户扣减) + * + * @param chargeCode 扣费编码 + * @param tokens token数量 + * @return 消费点数(分) + */ + private Long calculateTokenToPoints(String chargeCode, Long tokens) { + if (tokens == null || tokens <= 0) { + return 0L; + } + + Long points; + Long minPoints; + + // 根据扣费编码确定费率和最低扣费 + switch (chargeCode) { + case "C5000": + // 100万token=200分,单次最低2分 + points = (tokens * 200 + 999999) / 1000000; // 向上取整 + minPoints = 2L; + break; + case "C5001": + // 100万token=150分,单次最低2分 + points = (tokens * 150 + 999999) / 1000000; // 向上取整 + minPoints = 2L; + break; + case "C5002": + // 100万token=400分,单次最低5分 + points = (tokens * 400 + 999999) / 1000000; // 向上取整 + minPoints = 5L; + break; + case "C5003": + // 100万token=200分,单次最低6分 + points = (tokens * 200 + 999999) / 1000000; // 向上取整 + minPoints = 6L; + break; + case "C7000": + // 100万token=200分,单次最低3分 + points = (tokens * 200 + 999999) / 1000000; // 向上取整 + minPoints = 3L; + break; + default: + log.warn("未配置扣费编码 {} 的token转换规则,使用默认规则", chargeCode); + // 默认:100万token=200分,单次最低2分 + points = (tokens * 200 + 999999) / 1000000; + minPoints = 2L; + break; + } + + // 确保不低于最低扣费 + return Math.max(points, minPoints); + } + + /** + * 处理任务失败的回调 + */ + private TaskCallbackResp handleFailureCallback(TaskCallbackReq req, TaskPreDeductDO preDeductRecord, Long userId) { + // 1. 恢复预扣减的金额 + String deductSource = preDeductRecord.getDeductSource(); + if (deductSource != null && deductSource.startsWith("sub_account_")) { + // 恢复子账户预扣减金额(chargeType特定的子账户) + // 从deductSource中提取chargeType + Integer chargeType = Integer.parseInt(deductSource.substring("sub_account_".length())); + + plantformBalanceMapper.update(null, new LambdaUpdateWrapper() + .eq(PlantformBalanceDO::getUserId, userId) + .eq(PlantformBalanceDO::getChargeType, chargeType) + .setSql("pre_deduct_balance = pre_deduct_balance - " + preDeductRecord.getPreDeductAmount())); + + log.info("子账户预扣减恢复完成, chargeType: {}, amount: {}", chargeType, preDeductRecord.getPreDeductAmount()); + } else if ("kexuedou_account".equals(deductSource)) { + // 恢复可学豆子账户预扣减金额(chargeType=11) + plantformBalanceMapper.update(null, new LambdaUpdateWrapper() + .eq(PlantformBalanceDO::getUserId, userId) + .eq(PlantformBalanceDO::getChargeType, 11) + .setSql("pre_deduct_balance = pre_deduct_balance - " + preDeductRecord.getPreDeductAmount())); + + log.info("可学豆子账户预扣减恢复完成, amount: {}", preDeductRecord.getPreDeductAmount()); + } else if ("main_account".equals(deductSource)) { + // 恢复主账户预扣减金额 + userMapper.clearPreDeductBeansById(userId, preDeductRecord.getPreDeductAmount()); + + log.info("主账户预扣减恢复完成, amount: {}", preDeductRecord.getPreDeductAmount()); + } else { + log.error("未知的扣减来源: {}", deductSource); + throw new RuntimeException("未知的扣减来源: " + deductSource); + } + + // 2. 更新预扣减记录状态 + preDeductRecord.setStatus(req.getStatus() == 2 ? 2 : 3); // 2: 已失败, 3: 已取消 + taskPreDeductMapper.updateById(preDeductRecord); + + return TaskCallbackResp.builder().success(true).message("任务失败,预扣减金额已恢复").build(); + } + + private void addTaskRecordHistory(TaskRecordHistoryDO taskRecord, TaskCallbackReq req) { + String fileStr = req.getFileUrl(); + //先下载文件 + List fileUrls = Arrays.asList(fileStr.split(",")); + if (!fileUrls.isEmpty()) { + fileUrls.forEach(fileUrl -> { + if (StrUtil.isEmpty(fileUrl)) { + throw new RuntimeException("文件url地址为空"); + } + FileInfo upload; + try { + // 1. 构造URL格式 + URL url = new URL(fileUrl); + // 2. 打开HTTP连接并配置参数 + HttpURLConnection connection = (HttpURLConnection)url.openConnection(); + connection.setRequestMethod("GET"); + // 5秒连接超时 + connection.setConnectTimeout(5000); + // 10秒读取超时 + connection.setReadTimeout(10000); + // 设置User-Agent + connection.setRequestProperty("User-Agent", "Mozilla/5.0"); + // 3. 检查HTTP响应状态码 + int responseCode = connection.getResponseCode(); + if (responseCode != HttpURLConnection.HTTP_OK) { + throw new IOException("HTTP请求失败,状态码: " + responseCode); + } + // 4. 获取输入流(注意:调用者需负责关闭此流) + InputStream inputStream = connection.getInputStream(); + String fileName = extractFileName(fileUrl); + // 过滤非法字符 + String safeFileName = sanitizeFileName(fileName); + String uniqueName = UUID.randomUUID() + "_" + safeFileName; + MultipartFile multipartFile = new CommonMultipartFile(uniqueName, safeFileName, MediaType.MULTIPART_FORM_DATA_VALUE, inputStream); + upload = fileService.upload(multipartFile); + + } catch (Exception e) { + log.error(e.getMessage()); + throw new RuntimeException("文件下载失败"); + } + //插入记录到数据库 + taskRecord.setFileUrl(upload.getUrl()); + taskRecordHistoryMapper.insert(taskRecord); + }); + } + } + + private static String extractFileName(String fileUrl) { + try { + URI uri = URI.create(fileUrl); + String path = uri.getPath(); + if (path == null || path.isEmpty()) { + return "unknown_" + System.currentTimeMillis(); + } + // 提取最后一个斜杠后的内容 + String rawName = path.substring(path.lastIndexOf('/') + 1); + + // 去除URL参数(如?foo=bar) + return rawName.contains("?") ? rawName.split("\\?")[0] : rawName; + } catch (Exception e) { + return "unknown_" + System.currentTimeMillis(); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public BillingDeductResp judgeBalanceWithAmount(BillingJudgeWithAmountReq req) { + Long userId = StpUtil.getLoginIdAsLong(); + try { + // 1. 获取用户信息 + UserDO user = userMapper.selectById(userId); + if (user == null) { + return BillingDeductResp.builder().success(false).errorMessage("用户不存在").build(); + } + + String deductSource = ""; + boolean canDeduct = false; + Integer chargeType = req.getChargeType(); + Long preDeductAmount = 0L; + // 2. 充点类型不需要预扣减 + if (req.getType() == 0) { + return BillingDeductResp.builder() + .success(true) + .remainingBalance(user.getSysBeans()) + .errorMessage("无需预扣减,充点类型") + .build(); + } else if (req.getType() == 1) { + // 3. 使用第三方平台传入的预扣减金额 + preDeductAmount = req.getQuantity().longValue(); + // 4. 优先级1:检查子账户余额 + PlantformBalanceDO subBalance = plantformBalanceMapper + .selectOne(new LambdaQueryWrapper().eq(PlantformBalanceDO::getUserId, userId) + .eq(PlantformBalanceDO::getChargeType, chargeType)); + + if (subBalance != null) { + Long availableBalance = subBalance.getBalance() - subBalance.getPreDeductBalance(); + + if (availableBalance >= preDeductAmount) { + // 子账户余额足够 + deductSource = "sub_account_" + chargeType; + canDeduct = true; + + // 更新子账号预扣减金额 + plantformBalanceMapper.update(null, new LambdaUpdateWrapper() + .eq(PlantformBalanceDO::getUserId, userId) + .eq(PlantformBalanceDO::getChargeType, chargeType) + .setSql("pre_deduct_balance = pre_deduct_balance + " + preDeductAmount)); + } + } + } else if (req.getType() == 2) { + preDeductAmount = req.getPreDeductAmount(); + // 5. 优先级2:如果子账户余额不足,检查可学豆子账户 + if (!canDeduct) { + PlantformBalanceDO kexuedouBalance = plantformBalanceMapper + .selectOne(new LambdaQueryWrapper() + .eq(PlantformBalanceDO::getUserId, userId) + .eq(PlantformBalanceDO::getChargeType, 11)); // chargeType=11 是可学豆子账户 + + if (kexuedouBalance != null) { + Long availableBalance = kexuedouBalance.getBalance() - kexuedouBalance.getPreDeductBalance(); + + if (availableBalance >= preDeductAmount) { + deductSource = "kexuedou_account"; + canDeduct = true; + + // 更新可学豆子账户预扣减金额 + plantformBalanceMapper.update(null, new LambdaUpdateWrapper() + .eq(PlantformBalanceDO::getUserId, userId) + .eq(PlantformBalanceDO::getChargeType, 11) + .setSql("pre_deduct_balance = pre_deduct_balance + " + preDeductAmount)); + } + } + } + if (!canDeduct) { + Long actualBalance = (user.getSysBeans() != null ? user.getSysBeans().longValue() : 0L); + Long preDeductedBalance = (user.getPreDeductBeans() != null + ? user.getPreDeductBeans().longValue() + : 0L); + Long availableBalance = actualBalance - preDeductedBalance; + + if (availableBalance >= preDeductAmount) { + deductSource = "main_account"; + canDeduct = true; + + // 更新主账户预扣减金额 + userMapper.addPreDeductBeansById(userId, preDeductAmount); + + log.info("使用主账户扣减(第三方金额), preDeductAmount: {}分, 剩余主账户: {}分", preDeductAmount, availableBalance - preDeductAmount); + } else { + log.warn("主账户余额不足(第三方金额), 需要: {}分, 可用: {}分", preDeductAmount, availableBalance); + } + } + } + + if (!canDeduct) { + return BillingDeductResp.builder().success(false).errorMessage("可用余额不足,请充值").build(); + } + + // 7. 创建或更新预扣减记录 + TaskPreDeductDO existingPreDeduct = taskPreDeductMapper.selectOne(new LambdaQueryWrapper() + .eq(TaskPreDeductDO::getUserId, userId) + .eq(TaskPreDeductDO::getChargeType, chargeType)); + + if (existingPreDeduct != null) { + // 存在记录,更新 + existingPreDeduct.setPreDeductAmount(preDeductAmount + existingPreDeduct.getPreDeductAmount()); + existingPreDeduct.setDeductSource(deductSource); + existingPreDeduct.setStatus(0); // 0: 进行中 + + taskPreDeductMapper.updateById(existingPreDeduct); + log.info("更新预扣减记录成功(第三方金额), userId: {}, amount: {}, source: {}", userId, preDeductAmount, deductSource); + } else { + // 不存在记录,新增 + TaskPreDeductDO preDeductRecord = new TaskPreDeductDO(); + preDeductRecord.setUserId(userId); + preDeductRecord.setChargeType(chargeType); + preDeductRecord.setPreDeductAmount(preDeductAmount); + preDeductRecord.setDeductSource(deductSource); + preDeductRecord.setStatus(0); // 0: 进行中 + + taskPreDeductMapper.insert(preDeductRecord); + log.info("创建预扣减记录成功(第三方金额), userId: {}, amount: {}, source: {}", userId, preDeductAmount, deductSource); + } + + // 8. 返回校验成功结果 + return BillingDeductResp.builder() + .success(true) + .remainingBalance(user.getSysBeans()) + .errorMessage("余额校验通过并已预扣减(第三方金额),来源: " + deductSource) + .build(); + + } catch (Exception e) { + log.error("余额校验失败(第三方金额), userId: {}, req: {}", userId, req, e); + throw e; + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public TaskCallbackResp taskCallbackWithAmount(TaskCallbackWithAmountReq req) { + Long userId = StpUtil.getLoginIdAsLong(); + try { + // 1. 查找预扣减记录 + TaskPreDeductDO preDeductRecord = taskPreDeductMapper.selectOne(new LambdaQueryWrapper() + .eq(TaskPreDeductDO::getUserId, userId) + .eq(TaskPreDeductDO::getChargeType, req.getChargeType()) + //.eq(TaskPreDeductDO::getStatus, 0) // 进行中的任务 + ); + + if (preDeductRecord == null) { + return TaskCallbackResp.builder().success(false).message("未找到对应的预扣减记录").build(); + } + + // 2. 根据任务状态处理 + if (req.getStatus() == 1) { + // 任务成功,使用第三方传入的实际消费金额进行扣费 + return handleSuccessCallbackWithAmount(req, preDeductRecord, userId); + } else { + // 任务失败或取消,恢复预扣减(与原逻辑相同) + return handleFailureCallback(req, preDeductRecord, userId); + } + + } catch (Exception e) { + log.error("任务回调失败(第三方金额), userId: {}, taskId: {}", userId, req.getTaskId(), e); + throw e; + } + } + + @Override + public Page queryTaskListByRootId(TaskQueryReq req) { + try { + // 根据根任务ID查询任务列表 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TaskRecordHistoryDO::getTaskRootId, req.getTaskRootId()); + queryWrapper.orderByDesc(TaskRecordHistoryDO::getCreateTime); + + // 分页查询 + Page page = new Page<>(req.getPage(), req.getSize()); + Page resultPage = taskRecordHistoryMapper.selectPage(page, queryWrapper); + + return resultPage; + } catch (Exception e) { + log.error("查询任务列表失败, taskRootId: {}", req.getTaskRootId(), e); + return null; + } + } + + /** + * 处理任务成功的回调(使用第三方传入的实际消费金额) + */ + private TaskCallbackResp handleSuccessCallbackWithAmount(TaskCallbackWithAmountReq req, + TaskPreDeductDO preDeductRecord, + Long userId) { + String deductSource = preDeductRecord.getDeductSource(); + Integer chargeType = req.getChargeType(); + + // 使用第三方平台传入的实际消费金额 + Long actualConsumptionPoints = req.getActualAmount(); + if (req.getType() == 1) { + actualConsumptionPoints = req.getQuantity().longValue(); + } + + log.info("使用第三方传入的实际消费金额, actualAmount: {}", actualConsumptionPoints); + + // 2. 创建任务记录 + TaskRecordHistoryDO taskRecord = new TaskRecordHistoryDO(); + taskRecord.setUserId(userId); + taskRecord.setPlatformId(req.getPlatformId()); + taskRecord.setTaskId(req.getTaskId()); + taskRecord.setTitle(req.getTitle()); + taskRecord.setTaskType(req.getTaskType()); + taskRecord.setChargeType(req.getChargeType()); + taskRecord.setModelName(req.getModelName()); + taskRecord.setConsumptionPoints(actualConsumptionPoints); + taskRecord.setPlatformCode(req.getPlatformCode()); + taskRecord.setAccountType(deductSource); + + //换哒新表补充字段 + taskRecord.setParentCreateTime(extractDateTime(req.getParentCreateTime())); + taskRecord.setTaskRootId(req.getTaskRootId()); + if (req.getParentTaskId() != null) { + taskRecord.setParentTaskId(req.getParentTaskId()); + } + + // 设置文件路径或文字内容 + if (req.getTaskType() == 0 || !StringUtils.hasText(req.getFileUrl())) { // token类型或文字类型 + taskRecord.setResult(req.getResult()); + taskRecord.setTokens(req.getTokens()); + taskRecordHistoryMapper.insert(taskRecord); + } else { // 音视频图片类型(2,3,4,5) + addTaskRecordHistory(taskRecord, req); + } + + // 3. 创建消费记录 + ConsumptionHistoryDO consumption = new ConsumptionHistoryDO(); + consumption.setUserId(userId); + consumption.setPlatformId(req.getPlatformId()); + consumption.setTaskType(req.getTaskType()); + consumption.setChargePoints(actualConsumptionPoints); + + // 4. 根据扣减来源进行实际扣费和清零预扣减 + if (deductSource != null && deductSource.startsWith("sub_account_")) { + // 子账户扣费(chargeType特定的子账户) + Integer extractedChargeType = Integer.parseInt(deductSource.substring("sub_account_".length())); + + PlantformBalanceDO balance = plantformBalanceMapper.selectOne(new LambdaQueryWrapper() + .eq(PlantformBalanceDO::getUserId, userId) + .eq(PlantformBalanceDO::getChargeType, extractedChargeType)); + + consumption.setRemainingPoints(balance.getBalance() - actualConsumptionPoints); + consumption.setType(req.getType()); // 消费类型 + consumption.setAccountType(1); // 1子账号 + + // 扣减实际金额并清零预扣减 + plantformBalanceMapper.update(null, new LambdaUpdateWrapper() + .eq(PlantformBalanceDO::getUserId, userId) + .eq(PlantformBalanceDO::getChargeType, extractedChargeType) + .setSql("balance = balance - " + actualConsumptionPoints + ", pre_deduct_balance = pre_deduct_balance - " + preDeductRecord + .getPreDeductAmount())); + + log.info("子账户扣费完成(第三方金额), chargeType: {}, actualConsumption: {}", extractedChargeType, actualConsumptionPoints); + } else if ("kexuedou_account".equals(deductSource)) { + // 可学豆子账户扣费(chargeType=11的子账户) + PlantformBalanceDO balance = plantformBalanceMapper.selectOne(new LambdaQueryWrapper() + .eq(PlantformBalanceDO::getUserId, userId) + .eq(PlantformBalanceDO::getChargeType, 11)); + + consumption.setRemainingPoints(balance.getBalance() - actualConsumptionPoints); + consumption.setType(req.getType()); // 消费类型 + consumption.setAccountType(1); // 1子账号 + + // 扣减实际金额并清零预扣减 + plantformBalanceMapper.update(null, new LambdaUpdateWrapper() + .eq(PlantformBalanceDO::getUserId, userId) + .eq(PlantformBalanceDO::getChargeType, 11) + .setSql("balance = balance - " + actualConsumptionPoints + ", pre_deduct_balance = pre_deduct_balance - " + preDeductRecord + .getPreDeductAmount())); + + log.info("可学豆子账户扣费完成(第三方金额), actualConsumption: {}", actualConsumptionPoints); + } else if ("main_account".equals(deductSource)) { + // 主账户sys_beans扣费 + UserDO user = userMapper.selectById(userId); + consumption.setRemainingPoints(user.getSysBeans().longValue() - actualConsumptionPoints); + consumption.setType(req.getType()); // 消费类型 + consumption.setAccountType(0); // 0主账号 + + // 原子操作:扣减实际金额并清零预扣减 单位为分 + int updateResult = userMapper.deductActualAndClearPreDeduct(userId, preDeductRecord + .getPreDeductAmount(), actualConsumptionPoints); + if (updateResult == 0) { + throw new RuntimeException("扣减用户余额失败"); + } + + log.info("主账户扣费完成(第三方金额), actualConsumption: {}", actualConsumptionPoints); + } else { + log.error("未知的扣减来源: {}", deductSource); + throw new RuntimeException("未知的扣减来源: " + deductSource); + } + + consumptionHistoryMapper.insert(consumption); + + // 5. 更新预扣减记录状态 + preDeductRecord.setStatus(1); // 1: 已完成 + taskPreDeductMapper.updateById(preDeductRecord); + + log.info("任务成功回调完成(第三方金额), taskId: {}, userId: {}, platformCode: {}, preDeduct: {}, actual: {}", req + .getTaskId(), userId, req.getPlatformCode(), preDeductRecord.getPreDeductAmount(), actualConsumptionPoints); + + return TaskCallbackResp.builder() + .success(true) + .message("任务成功,扣费完成(第三方金额)") + .taskRecordId(taskRecord.getId()) + .build(); + } + + // 过滤非法文件名字符(Windows/Linux兼容) + private static String sanitizeFileName(String fileName) { + // 替换非法字符为下划线 + return fileName.replaceAll("[\\\\/:*?\"<>|]", "_"); + } +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/ConsumptionHistoryServiceImpl.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/ConsumptionHistoryServiceImpl.java new file mode 100644 index 0000000..83fbb79 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/ConsumptionHistoryServiceImpl.java @@ -0,0 +1,136 @@ +package top.continew.admin.business.service.impl; + +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.text.CharSequenceUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; +import top.continew.admin.business.mapper.ConsumptionHistoryMapper; +import top.continew.admin.business.model.entity.ConsumptionHistoryDO; +import top.continew.admin.business.model.query.ConsumptionHistoryQuery; +import top.continew.admin.business.model.req.ConsumptionHistoryAddReq; +import top.continew.admin.business.model.req.ConsumptionHistoryEditReq; +import top.continew.admin.business.model.req.ConsumptionReq; +import top.continew.admin.business.model.resp.ConsumptionHistoryResp; +import top.continew.admin.business.service.ConsumptionHistoryService; +import top.continew.admin.system.mapper.UserMapper; +import top.continew.admin.system.model.entity.UserDO; +import top.continew.admin.system.service.UserService; +import top.continew.starter.data.mp.service.impl.ServiceImpl; +import top.continew.starter.data.mp.util.QueryWrapperHelper; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.web.model.R; + +import java.util.Collections; +import java.util.List; + +/** + * 用户充值消费历史记录业务实现 + * + * @author jiay + * @since 2025/extends7:51 + */ +@Service +@RequiredArgsConstructor +public class ConsumptionHistoryServiceImpl extends ServiceImpl implements ConsumptionHistoryService { + private final ConsumptionHistoryMapper consumptionHistoryMapper; + private final UserMapper userMapper; + private final UserService userService; + + @Override + public List getConsumptionHistoryList(ConsumptionHistoryQuery query) { + QueryWrapper queryWrapper = QueryWrapperHelper.build(query); + List list = this.list(queryWrapper); + + if (CollectionUtil.isNotEmpty(list)) { + return BeanUtil.copyToList(list, ConsumptionHistoryResp.class); + } + return Collections.emptyList(); + } + + @Override + public boolean addConsumptionHistory(ConsumptionHistoryAddReq req) { + //先校验userId + String userId = req.getUserId(); + if (!StpUtil.isLogin(userId)) { + StpUtil.login(userId); + } + + userMapper.updateSysBeansById(Long.parseLong(userId), req.getChargePoints()); + + ConsumptionHistoryDO entity = BeanUtil.copyProperties(req, ConsumptionHistoryDO.class); + entity.setCreateUser(Long.parseLong(req.getUserId())); + return consumptionHistoryMapper.insert(entity) > 0; + } + + @Override + public boolean editConsumptionHistory(ConsumptionHistoryEditReq req) { + ConsumptionHistoryDO ConsumptionHistoryDO = BeanUtil.copyProperties(req, ConsumptionHistoryDO.class); + return consumptionHistoryMapper.updateById(ConsumptionHistoryDO) > 0; + + } + + @Override + public boolean deleteConsumptionHistory(Long id) { + if (id != null) { + return consumptionHistoryMapper.deleteById(id) > 0; + } + return false; + } + + @Override + public ConsumptionHistoryResp getConsumptionHistory(Long id) { + ConsumptionHistoryDO ConsumptionHistoryDO = consumptionHistoryMapper.selectById(id); + ConsumptionHistoryResp ConsumptionHistoryResp = new ConsumptionHistoryResp(); + if (ConsumptionHistoryDO != null) { + ConsumptionHistoryResp = BeanUtil.copyProperties(ConsumptionHistoryDO, ConsumptionHistoryResp.class); + } + return ConsumptionHistoryResp; + } + + @Override + public PageResp getConsumptionHistoryPage(ConsumptionHistoryQuery query, + PageQuery pageQuery) { + QueryWrapper queryWrapper = QueryWrapperHelper.build(query); + this.sort(queryWrapper, pageQuery.getSort()); + Page page = consumptionHistoryMapper.selectPage(new Page<>(pageQuery.getPage(), pageQuery + .getSize()), queryWrapper); + return PageResp.build(page); + } + + /** + * 设置排序 + * + * @param queryWrapper 查询条件封装对象 + * @param sort 排序查询条件 + */ + private void sort(QueryWrapper queryWrapper, Sort sort) { + if (sort == null || sort.isUnsorted()) { + return; + } + for (Sort.Order order : sort) { + String property = order.getProperty(); + queryWrapper.orderBy(true, order.isAscending(), CharSequenceUtil.toUnderlineCase(property)); + } + } + + @Override + public R checkBalance(ConsumptionReq req) { + //先校验userId + Long userId = req.getUserId(); + if (!StpUtil.isLogin(userId)) { + StpUtil.login(userId); + } + UserDO user = userService.getById(req.getUserId()); + if (user.getSysBeans() < req.getChargePoints()) { + return R.fail(R.fail().getCode(), "余额不足,请充值!"); + } + return R.ok(); + } + +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/CopyrightServiceImpl.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/CopyrightServiceImpl.java new file mode 100644 index 0000000..c0cbcba --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/CopyrightServiceImpl.java @@ -0,0 +1,69 @@ +package top.continew.admin.business.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; +import top.continew.admin.business.mapper.CopyrightMapper; +import top.continew.admin.business.model.entity.Copyright; +import top.continew.admin.business.model.query.CopyrightPageQuery; +import top.continew.admin.business.model.query.CopyrightQuery; +import top.continew.admin.business.model.req.CopyrightReq; +import top.continew.admin.business.model.resp.CopyrightDetailResp; +import top.continew.admin.business.model.resp.CopyrightResp; +import top.continew.admin.business.service.CopyrightService; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.extension.crud.service.impl.BaseServiceImpl; + +/** + * 数字人-文案服务实现类 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Service +@RequiredArgsConstructor +public class CopyrightServiceImpl extends BaseServiceImpl implements CopyrightService { + + private final CopyrightMapper copyrightMapper; + + @Override + public PageResp pageList(CopyrightQuery query, CopyrightPageQuery pageQuery) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + if (query.getUserId() != null) { + wrapper.eq(Copyright::getUserId, query.getUserId()); + } + if (query.getText() != null) { + wrapper.like(Copyright::getText, query.getText()); + } + + // 默认格局createTime 倒叙排列 + wrapper.orderByDesc(Copyright::getCreateTime); + + + Page page = new Page<>(pageQuery.getPage(), pageQuery.getSize()); + Page result = copyrightMapper.selectPage(page, wrapper); + + PageResp pageResp = new PageResp(); + pageResp.setTotal(result.getTotal()); + pageResp.setList(result.getRecords().stream().map(this::convertToResp).toList()); + return pageResp; + } + + @Override + public Long add(CopyrightReq req) { + Copyright copyright = new Copyright(); + BeanUtils.copyProperties(req, copyright); + copyright.setCreateTime(java.time.LocalDateTime.now()); + copyright.setUpdateTime(java.time.LocalDateTime.now()); + copyrightMapper.insert(copyright); + return copyright.getId(); + } + + private CopyrightResp convertToResp(Copyright copyright) { + CopyrightResp resp = new CopyrightResp(); + BeanUtils.copyProperties(copyright, resp); + return resp; + } +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/CourseUserServiceImpl.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/CourseUserServiceImpl.java new file mode 100644 index 0000000..b1632a8 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/CourseUserServiceImpl.java @@ -0,0 +1,15 @@ +package top.continew.admin.business.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; +import top.continew.admin.business.mapper.CourseUserMapper; +import top.continew.admin.business.model.entity.CourseUserDO; +import top.continew.admin.business.service.CourseUserService; + +/** + * @author 张波 + * @since 2024/10/22 12:21 + */ +@Service +public class CourseUserServiceImpl extends ServiceImpl implements CourseUserService { +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/CoursesServiceImpl.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/CoursesServiceImpl.java new file mode 100644 index 0000000..f3eb10e --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/CoursesServiceImpl.java @@ -0,0 +1,213 @@ +package top.continew.admin.business.service.impl; + +import cn.crane4j.core.support.OperateTemplate; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.baomidou.dynamic.datasource.annotation.DS; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import net.dreamlu.mica.core.utils.BeanUtil; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import top.continew.admin.business.enums.AssignmentsTypeEnum; +import top.continew.admin.business.mapper.CoursesMapper; +import top.continew.admin.business.model.entity.AssignmentSubmissionsDO; +import top.continew.admin.business.model.entity.AssignmentsDO; +import top.continew.admin.business.model.entity.CourseUserDO; +import top.continew.admin.business.model.entity.CoursesDO; +import top.continew.admin.business.model.req.CoursesReq; +import top.continew.admin.business.model.resp.AssignmentsResp; +import top.continew.admin.business.model.resp.CoursesResp; +import top.continew.admin.business.service.AssignmentSubmissionsService; +import top.continew.admin.business.service.AssignmentsService; +import top.continew.admin.business.service.CourseUserService; +import top.continew.admin.business.service.CoursesService; +import top.continew.admin.common.util.helper.LoginHelper; +import top.continew.admin.system.enums.MessageTemplateEnum; +import top.continew.admin.system.enums.MessageTypeEnum; +import top.continew.admin.system.model.req.MessageReq; +import top.continew.admin.system.service.MessageService; +import top.continew.admin.system.service.MessageUserService; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; + +import java.math.BigDecimal; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 课程业务实现 + * + * @author 张波 + * @since 2024/10/22 11:58 + */ +@Service +@RequiredArgsConstructor +public class CoursesServiceImpl extends ServiceImpl implements CoursesService { + + private final CourseUserService courseUserService; + + private final AssignmentsService assignmentsService; + + private final AssignmentSubmissionsService assignmentSubmissionsService; + + private final MessageService messageService; + + private final MessageUserService messageUserService; + + @Override + @Transactional(rollbackFor = Exception.class) + public void add(CoursesReq req) { + // 保存课程基础信息 + CoursesDO saveDb = new CoursesDO(); + BeanUtil.copyProperties(req, saveDb); + save(saveDb); + + // 构建课程-学生关联信息 并保存 + List courseUserList = req.getUsers().stream().map(user -> { + CourseUserDO temp = new CourseUserDO(); + + temp.setCourseId(saveDb.getId()); + temp.setUserId(user.getUserId()); + temp.setNickname(user.getNickname()); + //发送消息 + MessageReq messageReq = new MessageReq(); + MessageTemplateEnum socialRegister = MessageTemplateEnum.SOCIAL_COUR; + messageReq.setTitle(socialRegister.getTitle().formatted(req.getName())); + messageReq.setContent(socialRegister.getContent().formatted(user.getNickname(), req.getName())); + messageReq.setType(MessageTypeEnum.SECURITY); + messageService.add(messageReq, CollUtil.toList(user.getUserId())); + return temp; + }).toList(); + courseUserService.saveBatch(courseUserList); + } + + /** + * + * @param db 动态数据库 + * @param role 角色(学生/老师) + * @param pageQuery 分页查询条件 + * @return + */ + @Override + @DS("#db") + public PageResp list(String db, String role, PageQuery pageQuery) { + Page page = new Page<>(pageQuery.getPage(), pageQuery.getSize()); + + // 查询当前登录用户ID + Long loginUserId = LoginHelper.getUserId(); + + // 如果登录用户是学生则关联课程 + if (role.equals("pupil")) { + List courseIdList = courseUserService.lambdaQuery() + .select(CourseUserDO::getCourseId) + .eq(CourseUserDO::getUserId, loginUserId) + .list() + .stream() + .map(CourseUserDO::getCourseId) + .toList(); + if (CollUtil.isEmpty(courseIdList)) { + return new PageResp<>(Collections.emptyList(), 0L); + } + + // 根据关联ID查询课程信息 + page = lambdaQuery().in(CoursesDO::getId, courseIdList).orderByDesc(CoursesDO::getCreateTime).page(page); + // 如果为老师则查询当前登录人创建的课程信息 + } else if (role.equals("teacher")) { + page = lambdaQuery().eq(CoursesDO::getCreateUser, loginUserId) + .orderByDesc(CoursesDO::getCreateTime) + .page(page); + } + + // 封装响应 + PageResp pageResp = PageResp.build(page, CoursesResp.class); + pageResp.getList().forEach(obj -> { + if (null == obj) { + return; + } + + OperateTemplate operateTemplate = SpringUtil.getBean(OperateTemplate.class); + operateTemplate.execute(obj); + }); + + return pageResp; + } + + @Override + public List assignmentsList(Long coursesId, AssignmentsTypeEnum type, String role) { + // 查询课程所有作业 + List assignmentsList = assignmentsService.lambdaQuery() + .select(AssignmentsDO::getId, AssignmentsDO::getName, AssignmentsDO::getExampleImages) + .eq(AssignmentsDO::getCourseId, coursesId) + .eq(AssignmentsDO::getType, type.getValue()) + .list(); + + // 如果没有查询到作业直接返回 + if (CollUtil.isEmpty(assignmentsList)) { + return Collections.emptyList(); + } + + // 提取作业ID列表 后续查询用 + List assignmentsIdList = assignmentsList.stream().map(AssignmentsDO::getId).toList(); + + // 如果是学生 则判断学生是否提交作业,如果提交进度改为100 未提交进度0 + if (role.equals("pupil")) { + // 获取当前登录用户ID + Long userId = LoginHelper.getUserId(); + + // 查询已提交作业的作业ID与提交记录IDMap + Map submissionIdMap = assignmentSubmissionsService.lambdaQuery() + .select(AssignmentSubmissionsDO::getAssignmentId, AssignmentSubmissionsDO::getId) + .in(AssignmentSubmissionsDO::getAssignmentId, assignmentsIdList) + .eq(AssignmentSubmissionsDO::getUserId, userId) + .list() + .stream() + .collect(Collectors.toMap(AssignmentSubmissionsDO::getAssignmentId, AssignmentSubmissionsDO::getId)); + + // 计算作业完成度 + return assignmentsList.stream().map(assignmentsDO -> { + AssignmentsResp temp = new AssignmentsResp(); + temp.setCourseId(coursesId); + temp.setId(assignmentsDO.getId()); + temp.setName(assignmentsDO.getName()); + temp.setDegreeOfCompletion(submissionIdMap.containsKey(assignmentsDO.getId()) ? "已完成" : "未完成"); + temp.setAssignmentsSubmissionsId(submissionIdMap.get(assignmentsDO.getId())); + temp.setExampleImages(assignmentsDO.getExampleImages()); + return temp; + }).toList(); + // 如果是老师,则统计作业提交的学生数量计算进度 + } else if (role.equals("teacher")) { + // 查询课程绑定学生数量 + Long userCount = courseUserService.lambdaQuery().eq(CourseUserDO::getCourseId, coursesId).count(); + + // 查询各任务已提交作业数量 + Map assignmentSubmissionCountMap = assignmentSubmissionsService.listMaps(Wrappers + .query() + .select("assignment_id", "count(*)") + .in("assignment_id", assignmentsList) + .groupBy("assignment_id")) + .stream() + .collect(Collectors.toMap(map -> Long.valueOf(map.get("assignment_id").toString()), map -> Long + .valueOf(map.get("count(*)").toString()))); + + // 计算完成度 + return assignmentsList.stream().map(assignmentsDO -> { + BigDecimal div = NumberUtil.div(assignmentSubmissionCountMap.get(assignmentsDO.getId()), userCount); + AssignmentsResp temp = new AssignmentsResp(); + temp.setCourseId(coursesId); + temp.setId(assignmentsDO.getId()); + temp.setName(assignmentsDO.getName()); + temp.setDegreeOfCompletion(NumberUtil.decimalFormat("#.##%", div)); + temp.setExampleImages(assignmentsDO.getExampleImages()); + return temp; + }).toList(); + } + + return Collections.emptyList(); + } +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/DeptModeServiceImpl.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/DeptModeServiceImpl.java new file mode 100644 index 0000000..f10a313 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/DeptModeServiceImpl.java @@ -0,0 +1,93 @@ +package top.continew.admin.business.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import top.continew.admin.business.mapper.DeptModelMapper; +import top.continew.admin.business.model.entity.DeptModelDO; +import top.continew.admin.business.model.query.ModelQuery; +import top.continew.admin.business.model.req.DeptModelReq; +import top.continew.admin.business.model.resp.ModelResp; +import top.continew.admin.business.service.DeptModelService; +import top.continew.admin.business.service.ModelService; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; + +import java.util.List; + +/** + * 部门与模型绑定业务实现 + * + * @author 张波 + * @since 2024/10/20 14:43 + */ +@Service +@RequiredArgsConstructor +public class DeptModeServiceImpl extends ServiceImpl implements DeptModelService { + + private final ModelService modelService; + + @Override + @Transactional(rollbackFor = Exception.class) + public void bind(DeptModelReq req) { + // 查询数据库中已绑定的数据 + List dbList = lambdaQuery().eq(DeptModelDO::getDeptId, req.getDeptId()).list(); + + // 筛选出数据库中存在 新绑定不存在的 进行删除 + List removeIds = dbList.stream() + .filter(d -> !req.getModelIds().contains(d.getModelId())) + .map(DeptModelDO::getId) + .toList(); + if (CollUtil.isNotEmpty(removeIds)) { + removeBatchByIds(removeIds); + } + + if (CollUtil.isNotEmpty(req.getModelIds())) { + // 筛选出新列表存在 数据库中不存在的 进行新增 + List saveList = req.getModelIds() + .stream() + .filter(p -> dbList.stream().noneMatch(d -> p.equals(d.getModelId()))) + .map(i -> { + DeptModelDO temp = new DeptModelDO(); + temp.setDeptId(req.getDeptId()); + temp.setModelId(i); + return temp; + }) + .toList(); + saveBatch(saveList); + } + } + + @Override + public PageResp getModels(ModelQuery query, PageQuery pageQuery) { + // 获取当前登录用户信息 + //LoginUser loginUser = LoginHelper.getLoginUser(); + //List deptIds = loginUser.getModelDeptIds(); + + // 部门为空不限制 查询所有模型 + /*if (CollUtil.isNotEmpty(deptIds)) { + List modelIds = lambdaQuery().in(DeptModelDO::getDeptId, deptIds) + .list() + .stream() + .map(DeptModelDO::getModelId) + .toList(); + + // 不是管理员并且没有绑定的模型, 返回空 + if (!loginUser.isAdmin() && CollUtil.isEmpty(modelIds)) { + return PageResp.build(null); + } + + query.setIds(modelIds); + }*/ + + // 根据登录用户所在部门查询可见模型 + return modelService.page(query, pageQuery); + } + + @Override + public List getModelByDept(Long deptId) { + return lambdaQuery().eq(DeptModelDO::getDeptId, deptId).list().stream().map(DeptModelDO::getModelId).toList(); + } +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/DeptPlatformServiceImpl.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/DeptPlatformServiceImpl.java new file mode 100644 index 0000000..8035c86 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/DeptPlatformServiceImpl.java @@ -0,0 +1,85 @@ +package top.continew.admin.business.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import top.continew.admin.business.mapper.DeptPlatformMapper; +import top.continew.admin.business.model.entity.DeptPlatformDO; +import top.continew.admin.business.model.req.DeptPlatformReq; +import top.continew.admin.business.service.DeptPlatformService; +import top.continew.admin.common.model.dto.LoginUser; +import top.continew.admin.common.util.helper.LoginHelper; +import top.continew.starter.extension.crud.model.entity.BaseIdDO; + +import java.util.Collections; +import java.util.List; + +/** + * 部门与平台绑表业务实现 + * + * @author 张波 + * @since 2024/10/20 14:49 + */ +@Service +public class DeptPlatformServiceImpl extends ServiceImpl implements DeptPlatformService { + + @Override + @Transactional(rollbackFor = Exception.class) + public void bind(DeptPlatformReq req) { + // 查询现有绑定关系 + List list = lambdaQuery().eq(DeptPlatformDO::getDeptId, req.getDeptId()).list(); + + // 筛选出数据库中存在 新绑定不存在的 进行删除 + List removeIds = list.stream() + .filter(d -> !req.getPlatforms().contains(d.getPlatform())) + .map(BaseIdDO::getId) + .toList(); + if (CollUtil.isNotEmpty(removeIds)) { + removeBatchByIds(removeIds); + } + + if (CollUtil.isNotEmpty(req.getPlatforms())) { + // 筛选出新列表存在 数据库中不存在的 进行新增 + List saveList = req.getPlatforms() + .stream() + .filter(p -> list.stream().noneMatch(d -> p.equals(d.getPlatform()))) + .map(i -> { + DeptPlatformDO temp = new DeptPlatformDO(); + temp.setDeptId(req.getDeptId()); + temp.setPlatform(i); + return temp; + }) + .toList(); + saveBatch(saveList); + } + } + + @Override + public List getPlatforms() { + // 获取当前登录用户信息 + LoginUser loginUser = LoginHelper.getLoginUser(); + Long deptId = loginUser.getDeptId(); + + // 如果不存在部门ID,则返回空 + if (deptId == null) { + return Collections.emptyList(); + } + + // 根据登录用户所在部门查询可见操作平台 + return lambdaQuery().eq(DeptPlatformDO::getDeptId, deptId) + .list() + .stream() + .map(DeptPlatformDO::getPlatform) + .toList(); + } + + @Override + public List getPlatformByDept(Long deptId) { + return lambdaQuery().eq(DeptPlatformDO::getDeptId, deptId) + .list() + .stream() + .map(DeptPlatformDO::getPlatform) + .toList(); + } +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/DigitalHumanServiceImpl.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/DigitalHumanServiceImpl.java new file mode 100644 index 0000000..4f25f08 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/DigitalHumanServiceImpl.java @@ -0,0 +1,124 @@ +package top.continew.admin.business.service.impl; + +import cn.crane4j.core.util.CollectionUtils; +import cn.dev33.satoken.stp.StpUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import jodd.util.CollectionUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; +import top.continew.admin.business.mapper.DigitalHumanMapper; +import top.continew.admin.business.mapper.EffectMapper; +import top.continew.admin.business.mapper.SubtitleItemMapper; +import top.continew.admin.business.mapper.SubtitleStyleMapper; +import top.continew.admin.business.model.entity.*; +import top.continew.admin.business.model.query.DigitalHumanPageQuery; +import top.continew.admin.business.model.query.DigitalHumanQuery; +import top.continew.admin.business.model.req.DigitalHumanReq; +import top.continew.admin.business.model.resp.DigitalHumanDetailResp; +import top.continew.admin.business.model.resp.DigitalHumanResp; +import top.continew.admin.business.service.DigitalHumanService; +import top.continew.admin.common.util.helper.LoginHelper; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.extension.crud.service.impl.BaseServiceImpl; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * 数字人服务实现类 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Service +@RequiredArgsConstructor +public class DigitalHumanServiceImpl extends BaseServiceImpl implements DigitalHumanService { + + private final DigitalHumanMapper digitalHumanMapper; + private final SubtitleStyleMapper subtitleStyleMapper; + private final SubtitleItemMapper subtitleItemMapper; + private final EffectMapper effectMapper; + + @Override + public List list(DigitalHumanQuery query, + top.continew.starter.extension.crud.model.query.SortQuery sortQuery) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + if (query.getUserId() != null) { + wrapper.eq(DigitalHuman::getUserId, query.getUserId()); + } + if (query.getCopywriting() != null) { + wrapper.like(DigitalHuman::getCopywriting, query.getCopywriting()); + } + if (query.getGeneratedVoiceUrl() != null) { + wrapper.eq(DigitalHuman::getGeneratedVoiceUrl, query.getGeneratedVoiceUrl()); + } + if (query.getGeneratedFrameImageUrl() != null) { + wrapper.eq(DigitalHuman::getGeneratedFrameImageUrl, query.getGeneratedFrameImageUrl()); + } + if (query.getGeneratedVideoUrl() != null) { + wrapper.eq(DigitalHuman::getGeneratedVideoUrl, query.getGeneratedVideoUrl()); + } + + return digitalHumanMapper.selectList(wrapper).stream().map(this::convertToResp).collect(Collectors.toList()); + } + + @Override + public top.continew.starter.extension.crud.model.resp.PageResp pageList(DigitalHumanQuery query, + DigitalHumanPageQuery pageQuery) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + query.setUserId(LoginHelper.getUserId().toString()); + if (query.getUserId() != null) { + wrapper.eq(DigitalHuman::getUserId, query.getUserId()); + } + if (query.getCopywriting() != null) { + wrapper.like(DigitalHuman::getCopywriting, query.getCopywriting()); + } + if (query.getGeneratedVoiceUrl() != null) { + wrapper.eq(DigitalHuman::getGeneratedVoiceUrl, query.getGeneratedVoiceUrl()); + } + if (query.getGeneratedFrameImageUrl() != null) { + wrapper.eq(DigitalHuman::getGeneratedFrameImageUrl, query.getGeneratedFrameImageUrl()); + } + if (query.getGeneratedVideoUrl() != null) { + wrapper.eq(DigitalHuman::getGeneratedVideoUrl, query.getGeneratedVideoUrl()); + } + + wrapper.orderByDesc(DigitalHuman::getCreateTime); + + Page page = new Page<>(pageQuery.getPage(), pageQuery.getSize()); + Page result = digitalHumanMapper.selectPage(page, wrapper); + + PageResp pageResp = new PageResp(); + pageResp.setTotal(result.getTotal()); + pageResp.setList(result.getRecords().stream().map(this::convertToResp).toList()); + return pageResp; + } + + @Override + public Long add(DigitalHumanReq req) { + DigitalHuman digitalHuman = new DigitalHuman(); + BeanUtils.copyProperties(req, digitalHuman); + digitalHuman.setCreateTime(java.time.LocalDateTime.now()); + digitalHuman.setUpdateTime(java.time.LocalDateTime.now()); + digitalHumanMapper.insert(digitalHuman); + return digitalHuman.getId(); + } + + @Override + public void delete(List ids) { + digitalHumanMapper.deleteBatchIds(ids); + } + + private DigitalHumanResp convertToResp(DigitalHuman digitalHuman) { + DigitalHumanResp resp = new DigitalHumanResp(); + BeanUtils.copyProperties(digitalHuman, resp); + // 处理关联数据 + List subtitleStyles = subtitleStyleMapper.selectList(new LambdaQueryWrapper().eq(SubtitleStyle::getDigitalHumanId, digitalHuman.getId())); + resp.setSubtitleStyle(CollectionUtils.isNotEmpty(subtitleStyles)? subtitleStyles.get(0) : null); + resp.setEffects(effectMapper.selectList(new LambdaQueryWrapper().eq(Effect::getDigitalHumanId, digitalHuman.getId()))); + resp.setSubtitles(subtitleItemMapper.selectList(new LambdaQueryWrapper().eq(SubtitleItem::getDigitalHumanId, digitalHuman.getId()))); + return resp; + } +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/EffectServiceImpl.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/EffectServiceImpl.java new file mode 100644 index 0000000..f42e45e --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/EffectServiceImpl.java @@ -0,0 +1,74 @@ +package top.continew.admin.business.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; +import top.continew.admin.business.mapper.EffectMapper; +import top.continew.admin.business.model.entity.DigitalHuman; +import top.continew.admin.business.model.entity.Effect; +import top.continew.admin.business.model.query.EffectPageQuery; +import top.continew.admin.business.model.query.EffectQuery; +import top.continew.admin.business.model.req.EffectReq; +import top.continew.admin.business.model.resp.EffectDetailResp; +import top.continew.admin.business.model.resp.EffectResp; +import top.continew.admin.business.service.EffectService; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.extension.crud.service.impl.BaseServiceImpl; + +/** + * 数字人-特效服务实现类 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Service +@RequiredArgsConstructor +public class EffectServiceImpl extends BaseServiceImpl implements EffectService { + + private final EffectMapper effectMapper; + + @Override + public PageResp pageList(EffectQuery query, EffectPageQuery pageQuery) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + if (query.getName() != null) { + wrapper.like(Effect::getName, query.getName()); + } + if (query.getType() != null) { + wrapper.eq(Effect::getType, query.getType()); + } + if (query.getTemplateId() != null) { + wrapper.eq(Effect::getTemplateId, query.getTemplateId()); + } + if (query.getDigitalHumanId() != null) { + wrapper.eq(Effect::getDigitalHumanId, query.getDigitalHumanId()); + } + + wrapper.orderByDesc(Effect::getCreateTime); + + Page page = new Page<>(pageQuery.getPage(), pageQuery.getSize()); + Page result = effectMapper.selectPage(page, wrapper); + + PageResp pageResp = new PageResp(); + pageResp.setTotal(result.getTotal()); + pageResp.setList(result.getRecords().stream().map(this::convertToResp).toList()); + return pageResp; + } + + @Override + public Long add(EffectReq req) { + Effect effect = new Effect(); + BeanUtils.copyProperties(req, effect); + effect.setCreateTime(java.time.LocalDateTime.now()); + effect.setUpdateTime(java.time.LocalDateTime.now()); + effectMapper.insert(effect); + return effect.getId(); + } + + private EffectResp convertToResp(Effect effect) { + EffectResp resp = new EffectResp(); + BeanUtils.copyProperties(effect, resp); + return resp; + } +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/EffectTemplateServiceImpl.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/EffectTemplateServiceImpl.java new file mode 100644 index 0000000..9ff5752 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/EffectTemplateServiceImpl.java @@ -0,0 +1,97 @@ +package top.continew.admin.business.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.dromara.x.file.storage.core.FileInfo; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import top.continew.admin.business.mapper.EffectTemplateMapper; +import top.continew.admin.business.model.entity.Effect; +import top.continew.admin.business.model.entity.EffectTemplate; +import top.continew.admin.business.model.query.EffectTemplatePageQuery; +import top.continew.admin.business.model.query.EffectTemplateQuery; +import top.continew.admin.business.model.req.EffectTemplateReq; +import top.continew.admin.business.model.resp.EffectTemplateDetailResp; +import top.continew.admin.business.model.resp.EffectTemplateResp; +import top.continew.admin.business.service.EffectTemplateService; +import top.continew.admin.common.util.helper.LoginHelper; +import top.continew.admin.system.service.FileService; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.extension.crud.service.impl.BaseServiceImpl; + +@Service +@RequiredArgsConstructor +public class EffectTemplateServiceImpl extends BaseServiceImpl implements EffectTemplateService { + + private final EffectTemplateMapper effectTemplateMapper; + private final FileService fileService; + + @Override + public PageResp pageList(EffectTemplateQuery query, EffectTemplatePageQuery pageQuery) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + if (query.getName() != null) { + wrapper.like(EffectTemplate::getName, query.getName()); + } + if (query.getType() != null) { + wrapper.eq(EffectTemplate::getType, query.getType()); + } + if (query.getIsCustom() != null) { + wrapper.eq(EffectTemplate::getIsCustom, query.getIsCustom()); + } + query.setUserId(LoginHelper.getUserId().toString()); + if (query.getUserId() != null) { + wrapper.eq(EffectTemplate::getUserId, query.getUserId()); + } + + wrapper.orderByDesc(EffectTemplate::getCreateTime); + + Page page = new Page<>(pageQuery.getPage(), pageQuery.getSize()); + Page result = effectTemplateMapper.selectPage(page, wrapper); + + PageResp pageResp = new PageResp(); + pageResp.setTotal(result.getTotal()); + pageResp.setList(result.getRecords().stream().map(this::convertToResp).toList()); + return pageResp; + } + + @Override + public Long add(EffectTemplateReq req) { + EffectTemplate effectTemplate = new EffectTemplate(); + BeanUtils.copyProperties(req, effectTemplate); + effectTemplate.setCreateTime(java.time.LocalDateTime.now()); + effectTemplate.setUpdateTime(java.time.LocalDateTime.now()); + effectTemplateMapper.insert(effectTemplate); + return effectTemplate.getId(); + } + + @Override + public Long uploadTemplate(MultipartFile file, String name, String type, String userId) { + if (file == null || file.isEmpty()) { + throw new IllegalArgumentException("上传文件不能为空"); + } + + // 保存文件 + FileInfo uploadResult = fileService.upload(file); + + // 创建特效模板记录 + EffectTemplate effectTemplate = new EffectTemplate(); + effectTemplate.setName(name); + effectTemplate.setType(type); + effectTemplate.setUserId(userId); + effectTemplate.setIsCustom("true"); // 标记为自定义模板 + effectTemplate.setSourceUrl(uploadResult.getUrl()); + effectTemplate.setCreateTime(java.time.LocalDateTime.now()); + effectTemplate.setUpdateTime(java.time.LocalDateTime.now()); + + effectTemplateMapper.insert(effectTemplate); + return effectTemplate.getId(); + } + + private EffectTemplateResp convertToResp(EffectTemplate effectTemplate) { + EffectTemplateResp resp = new EffectTemplateResp(); + BeanUtils.copyProperties(effectTemplate, resp); + return resp; + } +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/FreeQuotaConfigServiceImpl.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/FreeQuotaConfigServiceImpl.java new file mode 100644 index 0000000..6e95fe4 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/FreeQuotaConfigServiceImpl.java @@ -0,0 +1,89 @@ +package top.continew.admin.business.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import top.continew.admin.business.mapper.FreeQuotaConfigMapper; +import top.continew.admin.business.model.entity.FreeQuotaConfigDO; +import top.continew.admin.business.model.query.FreeQuotaConfigQuery; +import top.continew.admin.business.model.req.FreeQuotaConfigReq; +import top.continew.admin.business.model.resp.FreeQuotaConfigResp; +import top.continew.admin.business.service.FreeQuotaConfigService; +import top.continew.starter.extension.crud.service.impl.BaseServiceImpl; + +import java.util.List; + +/** + * 免费额度配置业务实现 + * + * @author jiay + * @since 2025/11/17 + */ +@Service +@RequiredArgsConstructor +public class FreeQuotaConfigServiceImpl extends BaseServiceImpl implements FreeQuotaConfigService { + + /** + * 获取所有启用的配置 + */ + @Override + @Cacheable(value = "freeQuotaConfig", key = "'enabled'", unless = "#result == null || #result.isEmpty()") + public List listEnabled() { + return baseMapper.selectList(new LambdaQueryWrapper() + .eq(FreeQuotaConfigDO::getIsEnabled, true) + .orderByAsc(FreeQuotaConfigDO::getSort)); + } + + /** + * 根据类别编码获取配置 + */ + @Override + @Cacheable(value = "freeQuotaConfig", key = "'code_' + #categoryCode", unless = "#result == null") + public FreeQuotaConfigDO getByCategoryCode(String categoryCode) { + return baseMapper.selectOne(new LambdaQueryWrapper() + .eq(FreeQuotaConfigDO::getCategoryCode, categoryCode) + .eq(FreeQuotaConfigDO::getIsEnabled, true) + .last("LIMIT 1")); + } + + /** + * 根据计费类型获取配置 + */ + @Override + @Cacheable(value = "freeQuotaConfig", key = "'chargeType_' + #chargeType", unless = "#result == null") + public FreeQuotaConfigDO getByChargeType(Integer chargeType) { + return baseMapper.selectOne(new LambdaQueryWrapper() + .eq(FreeQuotaConfigDO::getChargeType, chargeType) + .eq(FreeQuotaConfigDO::getIsEnabled, true) + .last("LIMIT 1")); + } + + /** + * 新增后清除缓存 + */ + @Override + @CacheEvict(value = "freeQuotaConfig", allEntries = true) + public Long add(FreeQuotaConfigReq req) { + return super.add(req); + } + + /** + * 更新后清除缓存 + */ + @Override + @CacheEvict(value = "freeQuotaConfig", allEntries = true) + public void update(FreeQuotaConfigReq req, Long id) { + super.update(req, id); + } + + /** + * 删除后清除缓存 + */ + @Override + @CacheEvict(value = "freeQuotaConfig", allEntries = true) + public void delete(List ids) { + super.delete(ids); + } +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/FreeQuotaGrantServiceImpl.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/FreeQuotaGrantServiceImpl.java new file mode 100644 index 0000000..8eed730 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/FreeQuotaGrantServiceImpl.java @@ -0,0 +1,209 @@ +package top.continew.admin.business.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import top.continew.admin.business.mapper.PlantformBalanceMapper; +import top.continew.admin.business.model.entity.FreeQuotaConfigDO; +import top.continew.admin.business.model.entity.PlantformBalanceDO; +import top.continew.admin.business.model.req.FreeQuotaGrantReq; +import top.continew.admin.business.model.resp.FreeQuotaGrantResp; +import top.continew.admin.business.service.FreeQuotaConfigService; +import top.continew.admin.business.service.FreeQuotaGrantService; +import top.continew.admin.system.mapper.UserMapper; +import top.continew.admin.system.model.entity.UserDO; +import top.continew.starter.core.exception.BusinessException; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 免费额度赠送服务实现 + * + * @author jiay + * @since 2025/11/17 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class FreeQuotaGrantServiceImpl implements FreeQuotaGrantService { + + private final FreeQuotaConfigService freeQuotaConfigService; + private final PlantformBalanceMapper plantformBalanceMapper; + private final UserMapper userMapper; + + /** + * 批量赠送免费额度 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public FreeQuotaGrantResp batchGrant(FreeQuotaGrantReq req) { + List logs = new ArrayList<>(); + List allUserIds = new ArrayList<>(); + + // 1. 收集所有用户ID + if (CollUtil.isNotEmpty(req.getUserIds())) { + allUserIds.addAll(req.getUserIds()); + logs.add("指定用户数: " + req.getUserIds().size()); + } + + if (CollUtil.isNotEmpty(req.getDeptIds())) { + for (Long deptId : req.getDeptIds()) { + List deptUsers = userMapper.selectList(new LambdaQueryWrapper() + .eq(UserDO::getDeptId, deptId)); + List deptUserIds = deptUsers.stream().map(UserDO::getId).collect(Collectors.toList()); + allUserIds.addAll(deptUserIds); + logs.add(StrUtil.format("部门ID {} 用户数: {}", deptId, deptUserIds.size())); + } + } + + if (CollUtil.isEmpty(allUserIds)) { + throw new BusinessException("未找到需要赠送的用户"); + } + + // 去重 + allUserIds = allUserIds.stream().distinct().collect(Collectors.toList()); + logs.add("总用户数(去重后): " + allUserIds.size()); + + // 2. 准备赠送配置 + List quotaItems; + if (CollUtil.isNotEmpty(req.getQuotaItems())) { + // 使用自定义配置 + quotaItems = req.getQuotaItems() + .stream() + .filter(item -> item.getEnabled() != null && item.getEnabled()) + .collect(Collectors.toList()); + logs.add("使用自定义配置,项数: " + quotaItems.size()); + } else { + // 使用默认配置 + List configs = freeQuotaConfigService.listEnabled(); + quotaItems = configs.stream().map(config -> { + FreeQuotaGrantReq.QuotaItem item = new FreeQuotaGrantReq.QuotaItem(); + item.setConfigId(config.getId()); + item.setCategoryCode(config.getCategoryCode()); + item.setCategoryName(config.getCategoryName()); + item.setGrantQuota(config.getQuota()); + item.setChargeType(config.getChargeType()); + item.setType(config.getType()); + item.setEnabled(true); + return item; + }).collect(Collectors.toList()); + logs.add("使用默认配置,项数: " + quotaItems.size()); + } + + if (CollUtil.isEmpty(quotaItems)) { + throw new BusinessException("未找到可赠送的额度配置"); + } + + // 3. 执行批量赠送 + int totalUsers = allUserIds.size(); + int successUsers = 0; + int failedUsers = 0; + int totalItems = 0; + int successItems = 0; + int failedItems = 0; + + for (Long userId : allUserIds) { + boolean userSuccess = true; + logs.add(StrUtil.format("\n--- 用户 ID: {} ---", userId)); + + for (FreeQuotaGrantReq.QuotaItem item : quotaItems) { + totalItems++; + try { + // 执行赠送 + boolean success = grantQuotaToUser(userId, item); + if (success) { + successItems++; + logs.add(StrUtil.format("✓ {} : {} {}", item.getCategoryName(), item.getGrantQuota(), "[成功]")); + } else { + failedItems++; + userSuccess = false; + logs.add(StrUtil.format("✗ {} [失败]", item.getCategoryName())); + } + } catch (Exception e) { + failedItems++; + userSuccess = false; + log.error("赠送额度失败: userId={}, item={}", userId, item.getCategoryName(), e); + logs.add(StrUtil.format("✗ {} [异常: {}]", item.getCategoryName(), e.getMessage())); + } + } + + if (userSuccess) { + successUsers++; + } else { + failedUsers++; + } + } + + logs.add("\n========== 赠送完成 =========="); + logs.add(StrUtil.format("用户: 成功 {} 个, 失败 {} 个", successUsers, failedUsers)); + logs.add(StrUtil.format("项目: 成功 {} 项, 失败 {} 项", successItems, failedItems)); + + return FreeQuotaGrantResp.builder() + .success(failedItems == 0) + .totalUsers(totalUsers) + .successUsers(successUsers) + .failedUsers(failedUsers) + .totalItems(totalItems) + .successItems(successItems) + .failedItems(failedItems) + .message(StrUtil.format("批量赠送完成! 用户: {}/{}, 项目: {}/{}", successUsers, totalUsers, successItems, totalItems)) + .logs(logs) + .build(); + } + + /** + * 给单个用户赠送默认免费额度 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public FreeQuotaGrantResp grantToUser(Long userId) { + FreeQuotaGrantReq req = new FreeQuotaGrantReq(); + req.setUserIds(CollUtil.newArrayList(userId)); + return batchGrant(req); + } + + /** + * 给部门下所有用户赠送默认免费额度 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public FreeQuotaGrantResp grantToDept(Long deptId) { + FreeQuotaGrantReq req = new FreeQuotaGrantReq(); + req.setDeptIds(CollUtil.newArrayList(deptId)); + return batchGrant(req); + } + + /** + * 给用户赠送单项额度 + */ + private boolean grantQuotaToUser(Long userId, FreeQuotaGrantReq.QuotaItem item) { + // 查询是否已存在 + PlantformBalanceDO existingBalance = plantformBalanceMapper + .selectOne(new LambdaQueryWrapper().eq(PlantformBalanceDO::getUserId, userId) + .eq(PlantformBalanceDO::getType, item.getType()) + .eq(item.getChargeType() != null, PlantformBalanceDO::getChargeType, item.getChargeType())); + + Long grantAmount = item.getGrantQuota().longValue(); + + if (existingBalance != null) { + // 更新余额(增加) + existingBalance.setBalance(existingBalance.getBalance() + grantAmount); + return plantformBalanceMapper.updateById(existingBalance) > 0; + } else { + // 插入新记录 + PlantformBalanceDO newBalance = new PlantformBalanceDO(); + newBalance.setUserId(userId); + newBalance.setType(item.getType()); + newBalance.setBalance(grantAmount); + newBalance.setChargeType(item.getChargeType()); + newBalance.setPreDeductBalance(0L); + return plantformBalanceMapper.insert(newBalance) > 0; + } + } +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/HomeworkServiceImpl.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/HomeworkServiceImpl.java new file mode 100644 index 0000000..7de798a --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/HomeworkServiceImpl.java @@ -0,0 +1,87 @@ +package top.continew.admin.business.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollectionUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import top.continew.admin.business.mapper.HomeworkMapper; +import top.continew.admin.business.model.entity.HomeworkDO; +import top.continew.admin.business.model.query.HomeworkQuery; +import top.continew.admin.business.model.req.HomeworkAddReq; +import top.continew.admin.business.model.req.HomeworkEditReq; +import top.continew.admin.business.model.resp.HomeworkResp; +import top.continew.admin.business.service.HomeworkService; +import top.continew.starter.data.mp.util.QueryWrapperHelper; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; + +import java.util.Collections; +import java.util.List; + +/** + * business业务实现 + * + * @author jiay + * @since 2025/06/26 10:24 + */ +@Service +@RequiredArgsConstructor +public class HomeworkServiceImpl extends ServiceImpl implements HomeworkService { + + private final HomeworkMapper homeworkMapper; + + @Override + public List getHomeworkList(HomeworkQuery query) { + QueryWrapper queryWrapper = QueryWrapperHelper.build(query); + List list = this.list(queryWrapper); + + if (CollectionUtil.isNotEmpty(list)) { + return BeanUtil.copyToList(list, HomeworkResp.class); + } + return Collections.emptyList(); + } + + @Override + public boolean addHomework(HomeworkAddReq req) { + //插入记录到数据库 + HomeworkDO entity = BeanUtil.copyProperties(req, HomeworkDO.class); + return homeworkMapper.insert(entity) > 0; + + } + + @Override + public boolean editHomework(HomeworkEditReq req) { + HomeworkDO HomeworkDO = BeanUtil.copyProperties(req, HomeworkDO.class); + return homeworkMapper.updateById(HomeworkDO) > 0; + + } + + @Override + public boolean deleteHomework(Long id) { + if (id != null) { + return homeworkMapper.deleteById(id) > 0; + } + return false; + } + + @Override + public HomeworkResp getHomework(Long id) { + HomeworkDO HomeworkDO = homeworkMapper.selectById(id); + HomeworkResp HomeworkResp = new HomeworkResp(); + if (HomeworkDO != null) { + HomeworkResp = BeanUtil.copyProperties(HomeworkDO, HomeworkResp.class); + } + return HomeworkResp; + } + + @Override + public PageResp getHomeworkPage(HomeworkQuery query, PageQuery pageQuery) { + QueryWrapper queryWrapper = QueryWrapperHelper.build(query); + Page page = homeworkMapper.selectPage(new Page<>(pageQuery.getPage(), pageQuery.getSize()), queryWrapper); + return PageResp.build(page); + } + +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/HuandaResourceServiceImpl.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/HuandaResourceServiceImpl.java new file mode 100644 index 0000000..5d5f846 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/HuandaResourceServiceImpl.java @@ -0,0 +1,53 @@ +package top.continew.admin.business.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import io.micrometer.common.util.StringUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import top.continew.admin.business.mapper.HuandaResourceMapper; +import top.continew.admin.business.model.entity.HuandaResourceDO; +import top.continew.admin.business.model.query.HuandaResourcePageQuery; +import top.continew.admin.business.model.query.HuandaResourceQuery; +import top.continew.admin.business.model.req.HuandaResourceReq; +import top.continew.admin.business.model.resp.HuandaResourceDetailResp; +import top.continew.admin.business.model.resp.HuandaResourceResp; +import top.continew.admin.business.service.HuandaResourceService; +import top.continew.starter.data.mp.base.BaseMapper; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.extension.crud.service.impl.BaseServiceImpl; + +/** + * 换哒-文件资源服务实现 + * + * @author 王志维 + * @since 2025/12/23 16:53 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class HuandaResourceServiceImpl extends BaseServiceImpl implements HuandaResourceService { + @Override + public PageResp pageList(HuandaResourceQuery query, HuandaResourcePageQuery pageQuery) { + QueryWrapper queryWrapper = this.buildQueryWrapper(query); + //如果age,gender,region不为空,则添加相应的查询条件到queryWrapper + if (StringUtils.isNotBlank(pageQuery.getAge())) { + queryWrapper.eq("age", pageQuery.getAge()); + } + if (StringUtils.isNotBlank(pageQuery.getGender())) { + queryWrapper.eq("gender", pageQuery.getGender()); + } + if (StringUtils.isNotBlank(pageQuery.getRegion())) { + queryWrapper.eq("region", pageQuery.getRegion()); + } + this.sort(queryWrapper, pageQuery); + IPage page = ((BaseMapper)this.baseMapper).selectPage(new Page((long)pageQuery + .getPage(), (long)pageQuery.getSize()), queryWrapper); + PageResp pageResp = PageResp.build(page, this.getListClass()); + pageResp.getList().forEach(this::fill); + return pageResp; + } + +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/ModelCategoryServiceImpl.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/ModelCategoryServiceImpl.java new file mode 100644 index 0000000..81ede9a --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/ModelCategoryServiceImpl.java @@ -0,0 +1,17 @@ +package top.continew.admin.business.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; +import top.continew.admin.business.mapper.ModelCategoryMapper; +import top.continew.admin.business.model.entity.ModelCategoryDO; +import top.continew.admin.business.service.ModelCategoryService; + +/** + * 模型分类业务实现 + * + * @author 张波 + * @since 2024/10/20 19:51 + */ +@Service +public class ModelCategoryServiceImpl extends ServiceImpl implements ModelCategoryService { +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/ModelServiceImpl.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/ModelServiceImpl.java new file mode 100644 index 0000000..120e391 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/ModelServiceImpl.java @@ -0,0 +1,20 @@ +package top.continew.admin.business.service.impl; + +import org.springframework.stereotype.Service; +import top.continew.admin.business.mapper.ModelMapper; +import top.continew.admin.business.model.entity.ModelDO; +import top.continew.admin.business.model.query.ModelQuery; +import top.continew.admin.business.model.req.ModelReq; +import top.continew.admin.business.model.resp.ModelResp; +import top.continew.admin.business.service.ModelService; +import top.continew.starter.extension.crud.service.impl.BaseServiceImpl; + +/** + * 模型业务实现 + * + * @author 张波 + * @since 2024/10/19 15:32 + */ +@Service +public class ModelServiceImpl extends BaseServiceImpl implements ModelService { +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/OrderAudioServiceImpl.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/OrderAudioServiceImpl.java new file mode 100644 index 0000000..6d19d22 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/OrderAudioServiceImpl.java @@ -0,0 +1,74 @@ +package top.continew.admin.business.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import jakarta.annotation.Resource; +import lombok.RequiredArgsConstructor; +import net.dreamlu.mica.core.utils.BeanUtil; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import top.continew.admin.business.mapper.OrderAudioMapper; +import top.continew.admin.business.mapper.OrderHistoryMapper; +import top.continew.admin.business.mapper.OrderThreeMapper; +import top.continew.admin.business.mapper.OrderVideoMapper; +import top.continew.admin.business.model.entity.OrderAudioDo; +import top.continew.admin.business.model.entity.OrderHistoryDo; +import top.continew.admin.business.model.entity.OrderThreeDo; +import top.continew.admin.business.model.entity.OrderVideoDo; +import top.continew.admin.business.model.req.OrderAudioReq; +import top.continew.admin.business.service.OrderAudioService; +import top.continew.admin.system.mapper.UserMapper; +import top.continew.admin.system.model.entity.UserDO; + +/** + * 音频业务实现 + * + * @author hzy + * @since 2025/10/22 11:58 + */ +@Service +@RequiredArgsConstructor +public class OrderAudioServiceImpl extends ServiceImpl implements OrderAudioService { + @Resource + private OrderVideoMapper orderVideoMapper; + @Resource + private OrderThreeMapper orderThreeMapper; + @Resource + private OrderHistoryMapper orderHistoryMapper; + @Resource + private UserMapper userMapper; + + /** + * 保存音频任务订单信息 + * + * @param req 参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void add(OrderAudioReq req) { + OrderAudioDo saveDb = new OrderAudioDo(); + BeanUtil.copyProperties(req, saveDb); + save(saveDb); + //保存视频订单任务 + if (req.getOrderType() != null && req.getOrderType() == 2) { + OrderVideoDo orderVideoDo = new OrderVideoDo(); + BeanUtil.copyProperties(req, orderVideoDo); + orderVideoMapper.insert(orderVideoDo); + } + //保存3D订单任务 + if (req.getOrderType() != null && req.getOrderType() == 3) { + OrderThreeDo orderThreeDo = new OrderThreeDo(); + BeanUtil.copyProperties(req, orderThreeDo); + orderThreeMapper.insert(orderThreeDo); + } + //保存3D订单任务 + OrderHistoryDo orderHistoryDo = new OrderHistoryDo(); + BeanUtil.copyProperties(req, orderHistoryDo); + orderHistoryDo.setFormType("支出"); + //剩余点数 + UserDO userDO = userMapper.selectById(saveDb.getCreateUser()); + int payBeans = userDO.getSysBeans(); + + orderHistoryDo.setRemainder(payBeans - userDO.getInviteBeans()); + orderHistoryMapper.insert(orderHistoryDo); + } +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/OrderHistoryServiceImpl.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/OrderHistoryServiceImpl.java new file mode 100644 index 0000000..eaa4675 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/OrderHistoryServiceImpl.java @@ -0,0 +1,32 @@ +package top.continew.admin.business.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import jakarta.annotation.Resource; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import top.continew.admin.business.mapper.OrderHistoryMapper; +import top.continew.admin.business.mapper.OrderThreeMapper; +import top.continew.admin.business.mapper.OrderVideoMapper; +import top.continew.admin.business.model.entity.OrderHistoryDo; +import top.continew.admin.business.service.OrderHistoryService; +import top.continew.admin.system.mapper.UserMapper; + +/** + * 订单任务历史业务接口实现 + * + * @author hzy + * @since 2025/10/22 11:58 + */ +@Service +@RequiredArgsConstructor +public class OrderHistoryServiceImpl extends ServiceImpl implements OrderHistoryService { + @Resource + private OrderVideoMapper orderVideoMapper; + @Resource + private OrderThreeMapper orderThreeMapper; + @Resource + private OrderHistoryMapper orderHistoryMapper; + @Resource + private UserMapper userMapper; + +} diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/PlantformBalanceServiceImpl.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/PlantformBalanceServiceImpl.java new file mode 100644 index 0000000..765df48 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/PlantformBalanceServiceImpl.java @@ -0,0 +1,138 @@ +package top.continew.admin.business.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.text.CharSequenceUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; +import top.continew.admin.business.mapper.PlantformBalanceMapper; +import top.continew.admin.business.model.entity.PlantformBalanceDO; +import top.continew.admin.business.model.query.PlantformBalanceQuery; +import top.continew.admin.business.model.req.CheckPlantBalanceReq; +import top.continew.admin.business.model.req.PlantformBalanceAddReq; +import top.continew.admin.business.model.req.PlantformBalanceEditReq; +import top.continew.admin.business.model.resp.PlantformBalanceResp; +import top.continew.admin.business.service.PlantformBalanceService; +import top.continew.starter.data.mp.util.QueryWrapperHelper; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.web.model.R; + +import java.util.Collections; +import java.util.List; + +/** + * business业务实现 + * + * @author jiay + * @since 2025/06/24 16:47 + */ +@Service +@RequiredArgsConstructor +public class PlantformBalanceServiceImpl extends ServiceImpl implements PlantformBalanceService { + + private final PlantformBalanceMapper plantformBalanceMapper; + + @Override + public List getPlantformBalanceList(PlantformBalanceQuery query) { + QueryWrapper queryWrapper = QueryWrapperHelper.build(query); + List list = this.list(queryWrapper); + + if (CollectionUtil.isNotEmpty(list)) { + return BeanUtil.copyToList(list, PlantformBalanceResp.class); + } + return Collections.emptyList(); + } + + @Override + public boolean addPlantformBalance(PlantformBalanceAddReq req) { + //插入记录到数据库 + PlantformBalanceDO entity = BeanUtil.copyProperties(req, PlantformBalanceDO.class); + return plantformBalanceMapper.insert(entity) > 0; + + } + + @Override + public boolean editPlantformBalance(PlantformBalanceEditReq req) { + PlantformBalanceDO PlantformBalanceDO = BeanUtil.copyProperties(req, PlantformBalanceDO.class); + return plantformBalanceMapper.updateById(PlantformBalanceDO) > 0; + + } + + @Override + public boolean deletePlantformBalance(Long id) { + if (id != null) { + return plantformBalanceMapper.deleteById(id) > 0; + } + return false; + } + + @Override + public PlantformBalanceResp getPlantformBalance(Long id) { + PlantformBalanceDO PlantformBalanceDO = plantformBalanceMapper.selectById(id); + PlantformBalanceResp PlantformBalanceResp = new PlantformBalanceResp(); + if (PlantformBalanceDO != null) { + PlantformBalanceResp = BeanUtil.copyProperties(PlantformBalanceDO, PlantformBalanceResp.class); + } + return PlantformBalanceResp; + } + + @Override + public PageResp getPlantformBalancePage(PlantformBalanceQuery query, PageQuery pageQuery) { + QueryWrapper queryWrapper = QueryWrapperHelper.build(query); + this.sort(queryWrapper, pageQuery.getSort()); + Page page = plantformBalanceMapper.selectPage(new Page<>(pageQuery.getPage(), pageQuery + .getSize()), queryWrapper); + return PageResp.build(page); + } + + /** + * 设置排序 + * + * @param queryWrapper 查询条件封装对象 + * @param sort 排序查询条件 + */ + private void sort(QueryWrapper queryWrapper, Sort sort) { + if (sort == null || sort.isUnsorted()) { + return; + } + for (Sort.Order order : sort) { + String property = order.getProperty(); + queryWrapper.orderBy(true, order.isAscending(), CharSequenceUtil.toUnderlineCase(property)); + } + } + + @Override + public List getUserBalancesByUserId(Long userId) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(PlantformBalanceDO::getUserId, userId); + List list = this.list(queryWrapper); + + if (CollectionUtil.isNotEmpty(list)) { + return BeanUtil.copyToList(list, PlantformBalanceResp.class); + } + return Collections.emptyList(); + } + + @Override + public R checkPlantBalance(CheckPlantBalanceReq req) { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq("user_id", req.getUserId()); + queryWrapper.eq("charge_type", req.getChargeType()); + PlantformBalanceDO one = plantformBalanceMapper.selectOne(queryWrapper); + if (one == null) { + PlantformBalanceAddReq addReq = new PlantformBalanceAddReq().initPlantformBalance(req); + this.addPlantformBalance(addReq); + } + if (one.getBalance() < req.getChargePoints()) { + return R.fail(R.fail().getCode(), "余额不足,请充值!"); + } + return R.ok(); + } + +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/ScoreServiceImpl.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/ScoreServiceImpl.java new file mode 100644 index 0000000..97c9193 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/ScoreServiceImpl.java @@ -0,0 +1,85 @@ +package top.continew.admin.business.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollectionUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import top.continew.admin.business.mapper.ScoreMapper; +import top.continew.admin.business.model.entity.ScoreDO; +import top.continew.admin.business.model.query.ScoreQuery; +import top.continew.admin.business.model.req.ScoreAddReq; +import top.continew.admin.business.model.req.ScoreEditReq; +import top.continew.admin.business.model.resp.ScoreResp; +import top.continew.admin.business.service.ScoreService; +import top.continew.starter.data.mp.util.QueryWrapperHelper; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; +import java.util.Collections; +import java.util.List; + +/** + * business业务实现 + * + * @author jiay + * @since 2025/06/26 10:24 + */ +@Service +@RequiredArgsConstructor +public class ScoreServiceImpl extends ServiceImpl implements ScoreService { + + private final ScoreMapper scoreMapper; + + @Override + public List getScoreList(ScoreQuery query) { + QueryWrapper queryWrapper = QueryWrapperHelper.build(query); + List list = this.list(queryWrapper); + + if (CollectionUtil.isNotEmpty(list)) { + return BeanUtil.copyToList(list, ScoreResp.class); + } + return Collections.emptyList(); + } + + @Override + public boolean addScore(ScoreAddReq req) { + //插入记录到数据库 + ScoreDO entity = BeanUtil.copyProperties(req, ScoreDO.class); + return scoreMapper.insert(entity) > 0; + + } + + @Override + public boolean editScore(ScoreEditReq req) { + ScoreDO ScoreDO = BeanUtil.copyProperties(req, ScoreDO.class); + return scoreMapper.updateById(ScoreDO) > 0; + + } + + @Override + public boolean deleteScore(Long id) { + if (id != null) { + return scoreMapper.deleteById(id) > 0; + } + return false; + } + + @Override + public ScoreResp getScore(Long id) { + ScoreDO ScoreDO = scoreMapper.selectById(id); + ScoreResp ScoreResp = new ScoreResp(); + if (ScoreDO != null) { + ScoreResp = BeanUtil.copyProperties(ScoreDO, ScoreResp.class); + } + return ScoreResp; + } + + @Override + public PageResp getScorePage(ScoreQuery query, PageQuery pageQuery) { + QueryWrapper queryWrapper = QueryWrapperHelper.build(query); + Page page = scoreMapper.selectPage(new Page<>(pageQuery.getPage(), pageQuery.getSize()), queryWrapper); + return PageResp.build(page); + } +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/SignInServiceImpl.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/SignInServiceImpl.java new file mode 100644 index 0000000..18e23d0 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/SignInServiceImpl.java @@ -0,0 +1,186 @@ +package top.continew.admin.business.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.text.CharSequenceUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; +import top.continew.admin.business.mapper.SignInMapper; +import top.continew.admin.business.model.entity.SignInDO; +import top.continew.admin.business.model.query.SignInQuery; +import top.continew.admin.business.model.req.SignInAddReq; +import top.continew.admin.business.model.req.SignInEditReq; +import top.continew.admin.business.model.resp.SignInExportResp; +import top.continew.admin.business.model.resp.SignInResp; +import top.continew.admin.business.model.resp.TodayNosignInExportResp; +import top.continew.admin.business.service.SignInService; +import top.continew.starter.data.mp.service.impl.ServiceImpl; +import top.continew.starter.data.mp.util.QueryWrapperHelper; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.query.SortQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.file.excel.util.ExcelUtils; + +import java.util.Collections; +import java.util.List; +import java.util.Random; + +/** + * business业务实现 + * + * @author jiay + * @since 2025/06/26 10:24 + */ +@Service +@RequiredArgsConstructor +public class SignInServiceImpl extends ServiceImpl implements SignInService { + + private final SignInMapper signInMapper; + + @Override + public List getSignInList(SignInQuery query) { + QueryWrapper queryWrapper = QueryWrapperHelper.build(query); + List list = this.list(queryWrapper); + + if (CollectionUtil.isNotEmpty(list)) { + return BeanUtil.copyToList(list, SignInResp.class); + } + return Collections.emptyList(); + } + + @Override + public boolean addSignIn(SignInAddReq req) { + //插入记录到数据库 + SignInDO entity = BeanUtil.copyProperties(req, SignInDO.class); + return signInMapper.insert(entity) > 0; + + } + + @Override + public boolean editSignIn(SignInEditReq req) { + SignInDO signInDO = BeanUtil.copyProperties(req, SignInDO.class); + return signInMapper.updateById(signInDO) > 0; + + } + + @Override + public boolean deleteSignIn(Long id) { + if (id != null) { + return signInMapper.deleteById(id) > 0; + } + return false; + } + + @Override + public SignInResp getSignIn(Long id) { + SignInDO signInDO = signInMapper.selectById(id); + SignInResp signInResp = new SignInResp(); + if (signInDO != null) { + signInResp = BeanUtil.copyProperties(signInDO, SignInResp.class); + } + return signInResp; + } + + @Override + public PageResp getSignInPage(SignInQuery query, PageQuery pageQuery) { + QueryWrapper queryWrapper = QueryWrapperHelper.build(query); + Page page = signInMapper.selectPage(new Page<>(pageQuery.getPage(), pageQuery.getSize()), queryWrapper); + return PageResp.build(page); + } + + @Override + public List getTodayNosignInList(SignInQuery query) { + QueryWrapper queryWrapper = QueryWrapperHelper.build(query); + List list = this.list(queryWrapper); + + if (CollectionUtil.isNotEmpty(list)) { + List signInResps = BeanUtil.copyToList(list, SignInResp.class); + signInResps.stream().forEach(e -> { + //todo + Random random = new Random(100); + e.setPresent(random.nextInt(10)); + e.setAbnormal(random.nextInt(10)); + e.setAbsent(random.nextInt(10)); + e.setLeaveCount(random.nextInt(10)); + e.setLeaveCause("生病假!"); + }); + return signInResps; + } + return Collections.emptyList(); + } + + @Override + public PageResp getHisSignInList(SignInQuery query, PageQuery pageQuery) { + QueryWrapper queryWrapper = QueryWrapperHelper.build(query); + Page page = signInMapper.selectPage(new Page<>(pageQuery.getPage(), pageQuery.getSize()), queryWrapper); + if (CollectionUtil.isNotEmpty(page.getRecords())) { + List signInResps = BeanUtil.copyToList(page.getRecords(), SignInResp.class); + signInResps.forEach(e -> { + //todo + Random random = new Random(100); + e.setPresent(random.nextInt(10)); + e.setAbnormal(random.nextInt(10)); + e.setAbsent(random.nextInt(10)); + e.setLeaveCount(random.nextInt(10)); + e.setLeaveCause("生病假"); + }); + page.setRecords(signInResps); + } + return PageResp.build(page); + } + + @Override + public void exportHisSignInList(SignInQuery query, SortQuery sortQuery, HttpServletResponse response) { + List list = getTodayNosignInList(query); + List res = BeanUtil.copyToList(list, SignInExportResp.class); + res.forEach(e -> { + + }); + ExcelUtils.export(res, "导出历史签到列表", SignInExportResp.class, response); + } + + @Override + public void exportTodayNosignInList(SignInQuery query, SortQuery sortQuery, HttpServletResponse response) { + PageResp list = getHisSignInList(query, null); + List res = BeanUtil.copyToList(list.getList(), TodayNosignInExportResp.class); + res.forEach(e -> { + + }); + ExcelUtils.export(res, "导出今日未签到列表", TodayNosignInExportResp.class, response); + } + + /** + * 设置排序 + * + * @param queryWrapper 查询条件封装对象 + * @param sortQuery 排序查询条件 + */ + private void sort(QueryWrapper queryWrapper, SortQuery sortQuery) { + if (sortQuery == null || sortQuery.getSort().isUnsorted()) { + return; + } + for (Sort.Order order : sortQuery.getSort()) { + String property = order.getProperty(); + queryWrapper.orderBy(true, order.isAscending(), CharSequenceUtil.toUnderlineCase(property)); + } + } + + /** + * 构建 QueryWrapper + * + * @param query 查询条件 + * @return QueryWrapper + */ + private QueryWrapper buildQueryWrapper(SignInQuery query) { + String courseName = query.getCourseName(); + String userName = query.getUserName(); + return new QueryWrapper().and(StrUtil.isNotBlank(courseName), q -> q + .like("t1.course_name", courseName)) + .and(StrUtil.isNotBlank(userName), q -> q.like("t1.user_name", userName)); + } +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/SubtitleItemServiceImpl.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/SubtitleItemServiceImpl.java new file mode 100644 index 0000000..2591e9d --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/SubtitleItemServiceImpl.java @@ -0,0 +1,74 @@ +package top.continew.admin.business.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; +import top.continew.admin.business.mapper.SubtitleItemMapper; +import top.continew.admin.business.model.entity.EffectTemplate; +import top.continew.admin.business.model.entity.SubtitleItem; +import top.continew.admin.business.model.query.SubtitleItemPageQuery; +import top.continew.admin.business.model.query.SubtitleItemQuery; +import top.continew.admin.business.model.req.SubtitleItemReq; +import top.continew.admin.business.model.resp.SubtitleItemDetailResp; +import top.continew.admin.business.model.resp.SubtitleItemResp; +import top.continew.admin.business.service.SubtitleItemService; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.extension.crud.service.impl.BaseServiceImpl; + +/** + * 数字人-字幕项服务实现类 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Service +@RequiredArgsConstructor +public class SubtitleItemServiceImpl extends BaseServiceImpl implements SubtitleItemService { + + private final SubtitleItemMapper subtitleItemMapper; + + @Override + public PageResp pageList(SubtitleItemQuery query, SubtitleItemPageQuery pageQuery) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + if (query.getDigitalHumanId() != null) { + wrapper.eq(SubtitleItem::getDigitalHumanId, query.getDigitalHumanId()); + } + if (query.getText() != null) { + wrapper.like(SubtitleItem::getText, query.getText()); + } + if (query.getStartTime() != null) { + wrapper.ge(SubtitleItem::getStartTime, query.getStartTime()); + } + if (query.getEndTime() != null) { + wrapper.le(SubtitleItem::getEndTime, query.getEndTime()); + } + + wrapper.orderByDesc(SubtitleItem::getCreateTime); + + Page page = new Page<>(pageQuery.getPage(), pageQuery.getSize()); + Page result = subtitleItemMapper.selectPage(page, wrapper); + + PageResp pageResp = new PageResp(); + pageResp.setTotal(result.getTotal()); + pageResp.setList(result.getRecords().stream().map(this::convertToResp).toList()); + return pageResp; + } + + @Override + public Long add(SubtitleItemReq req) { + SubtitleItem subtitleItem = new SubtitleItem(); + BeanUtils.copyProperties(req, subtitleItem); + subtitleItem.setCreateTime(java.time.LocalDateTime.now()); + subtitleItem.setUpdateTime(java.time.LocalDateTime.now()); + subtitleItemMapper.insert(subtitleItem); + return subtitleItem.getId(); + } + + private SubtitleItemResp convertToResp(SubtitleItem subtitleItem) { + SubtitleItemResp resp = new SubtitleItemResp(); + BeanUtils.copyProperties(subtitleItem, resp); + return resp; + } +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/SubtitleStyleServiceImpl.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/SubtitleStyleServiceImpl.java new file mode 100644 index 0000000..2738788 --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/SubtitleStyleServiceImpl.java @@ -0,0 +1,74 @@ +package top.continew.admin.business.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; +import top.continew.admin.business.mapper.SubtitleStyleMapper; +import top.continew.admin.business.model.entity.SubtitleItem; +import top.continew.admin.business.model.entity.SubtitleStyle; +import top.continew.admin.business.model.query.SubtitleStylePageQuery; +import top.continew.admin.business.model.query.SubtitleStyleQuery; +import top.continew.admin.business.model.req.SubtitleStyleReq; +import top.continew.admin.business.model.resp.SubtitleStyleDetailResp; +import top.continew.admin.business.model.resp.SubtitleStyleResp; +import top.continew.admin.business.service.SubtitleStyleService; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.extension.crud.service.impl.BaseServiceImpl; + +/** + * 数字人-字幕样式服务实现类 + * + * @author 王志维 + * @since 2025/12/29 + */ +@Service +@RequiredArgsConstructor +public class SubtitleStyleServiceImpl extends BaseServiceImpl implements SubtitleStyleService { + + private final SubtitleStyleMapper subtitleStyleMapper; + + @Override + public PageResp pageList(SubtitleStyleQuery query, SubtitleStylePageQuery pageQuery) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + if (query.getDigitalHumanId() != null) { + wrapper.eq(SubtitleStyle::getDigitalHumanId, query.getDigitalHumanId()); + } + if (query.getFontSize() != null) { + wrapper.eq(SubtitleStyle::getFontSize, query.getFontSize()); + } + if (query.getFontFamily() != null) { + wrapper.eq(SubtitleStyle::getFontFamily, query.getFontFamily()); + } + if (query.getColor() != null) { + wrapper.eq(SubtitleStyle::getColor, query.getColor()); + } + + wrapper.orderByDesc(SubtitleStyle::getCreateTime); + + Page page = new Page<>(pageQuery.getPage(), pageQuery.getSize()); + Page result = subtitleStyleMapper.selectPage(page, wrapper); + + PageResp pageResp = new PageResp(); + pageResp.setTotal(result.getTotal()); + pageResp.setList(result.getRecords().stream().map(this::convertToResp).toList()); + return pageResp; + } + + @Override + public Long add(SubtitleStyleReq req) { + SubtitleStyle subtitleStyle = new SubtitleStyle(); + BeanUtils.copyProperties(req, subtitleStyle); + subtitleStyle.setCreateTime(java.time.LocalDateTime.now()); + subtitleStyle.setUpdateTime(java.time.LocalDateTime.now()); + subtitleStyleMapper.insert(subtitleStyle); + return subtitleStyle.getId(); + } + + private SubtitleStyleResp convertToResp(SubtitleStyle subtitleStyle) { + SubtitleStyleResp resp = new SubtitleStyleResp(); + BeanUtils.copyProperties(subtitleStyle, resp); + return resp; + } +} \ No newline at end of file diff --git a/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/TaskRecordHistoryServiceImpl.java b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/TaskRecordHistoryServiceImpl.java new file mode 100644 index 0000000..3ed842e --- /dev/null +++ b/continew-admin-business/src/main/java/top/continew/admin/business/service/impl/TaskRecordHistoryServiceImpl.java @@ -0,0 +1,182 @@ +package top.continew.admin.business.service.impl; + +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.dromara.x.file.storage.core.FileInfo; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import top.continew.admin.business.file.CommonMultipartFile; +import top.continew.admin.business.mapper.TaskRecordHistoryMapper; +import top.continew.admin.business.model.entity.TaskRecordHistoryDO; +import top.continew.admin.business.model.query.TaskRecordHistoryQuery; +import top.continew.admin.business.model.req.TaskRecordHistoryAddReq; +import top.continew.admin.business.model.req.TaskRecordHistoryEditReq; +import top.continew.admin.business.model.resp.TaskRecordHistoryResp; +import top.continew.admin.business.service.TaskRecordHistoryService; +import top.continew.admin.system.service.FileService; +import top.continew.admin.system.service.UserService; +import top.continew.starter.data.mp.util.QueryWrapperHelper; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; + +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +/** + * 音视图文生成记录业务实现 + * + * @author jiay + * @since 2025/05/23 07:51 + */ +@Service +@RequiredArgsConstructor +public class TaskRecordHistoryServiceImpl extends ServiceImpl implements TaskRecordHistoryService { + + private final TaskRecordHistoryMapper taskRecordHistoryMapper; + private final FileService fileService; + private final UserService userService; + + @Override + public List getTaskRecordHistoryList(TaskRecordHistoryQuery query) { + QueryWrapper queryWrapper = QueryWrapperHelper.build(query); + List list = this.list(queryWrapper); + + if (CollectionUtil.isNotEmpty(list)) { + return BeanUtil.copyToList(list, TaskRecordHistoryResp.class); + } + return Collections.emptyList(); + } + + @Override + public List addTaskRecordHistory(TaskRecordHistoryAddReq req) { + //先校验userId + String userId = req.getUserId(); + if (!StpUtil.isLogin(userId) || StpUtil.getTokenValue() == null) { + String tokenValue = StpUtil.getTokenValueByLoginId(userId); + StpUtil.setTokenValue(tokenValue); + } + //先下载文件 + List fileUrls = req.getFileUrl(); + List res = new ArrayList<>(); + if (!fileUrls.isEmpty()) { + fileUrls.forEach(fileUrl -> { + if (StrUtil.isEmpty(fileUrl)) { + throw new RuntimeException("文件url地址为空"); + } + FileInfo upload; + try { + // 1. 构造URL格式 + URL url = new URL(fileUrl); + // 2. 打开HTTP连接并配置参数 + HttpURLConnection connection = (HttpURLConnection)url.openConnection(); + connection.setRequestMethod("GET"); + // 5秒连接超时 + connection.setConnectTimeout(5000); + // 10秒读取超时 + connection.setReadTimeout(10000); + // 设置User-Agent + connection.setRequestProperty("User-Agent", "Mozilla/5.0"); + // 3. 检查HTTP响应状态码 + int responseCode = connection.getResponseCode(); + if (responseCode != HttpURLConnection.HTTP_OK) { + throw new IOException("HTTP请求失败,状态码: " + responseCode); + } + // 4. 获取输入流(注意:调用者需负责关闭此流) + InputStream inputStream = connection.getInputStream(); + String fileName = extractFileName(fileUrl); + // 过滤非法字符 + String safeFileName = sanitizeFileName(fileName); + String uniqueName = UUID.randomUUID() + "_" + safeFileName; + MultipartFile multipartFile = new CommonMultipartFile(uniqueName, safeFileName, MediaType.MULTIPART_FORM_DATA_VALUE, inputStream); + upload = fileService.upload(multipartFile); + + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("文件下载失败"); + } + //插入记录到数据库 + TaskRecordHistoryDO entity = BeanUtil.copyProperties(req, TaskRecordHistoryDO.class); + entity.setFileUrl(upload.getUrl()); + taskRecordHistoryMapper.insert(entity); + res.add(upload.getUrl()); + }); + } + return res; + + } + + /** + * 从URL提取文件名(处理含参数的URL) + */ + private static String extractFileName(String fileUrl) { + try { + URI uri = URI.create(fileUrl); + String path = uri.getPath(); + if (path == null || path.isEmpty()) { + return "unknown_" + System.currentTimeMillis(); + } + + // 提取最后一个斜杠后的内容 + String rawName = path.substring(path.lastIndexOf('/') + 1); + + // 去除URL参数(如?foo=bar) + return rawName.contains("?") ? rawName.split("\\?")[0] : rawName; + } catch (Exception e) { + return "unknown_" + System.currentTimeMillis(); + } + } + + // 过滤非法文件名字符(Windows/Linux兼容) + private static String sanitizeFileName(String fileName) { + // 替换非法字符为下划线 + return fileName.replaceAll("[\\\\/:*?\"<>|]", "_"); + } + + @Override + public boolean editTaskRecordHistory(TaskRecordHistoryEditReq req) { + TaskRecordHistoryDO taskRecordHistoryDO = BeanUtil.copyProperties(req, TaskRecordHistoryDO.class); + return taskRecordHistoryMapper.updateById(taskRecordHistoryDO) > 0; + + } + + @Override + public boolean deleteTaskRecordHistory(Long id) { + if (id != null) { + return taskRecordHistoryMapper.deleteById(id) > 0; + } + return false; + } + + @Override + public TaskRecordHistoryResp getTaskRecordHistory(Long id) { + TaskRecordHistoryDO taskRecordHistoryDO = taskRecordHistoryMapper.selectById(id); + TaskRecordHistoryResp taskRecordHistoryResp = new TaskRecordHistoryResp(); + if (taskRecordHistoryDO != null) { + taskRecordHistoryResp = BeanUtil.copyProperties(taskRecordHistoryDO, TaskRecordHistoryResp.class); + } + return taskRecordHistoryResp; + } + + @Override + public PageResp getTaskRecordHistoryPage(TaskRecordHistoryQuery query, PageQuery pageQuery) { + QueryWrapper queryWrapper = QueryWrapperHelper.build(query); + Page page = taskRecordHistoryMapper.selectPage(new Page<>(pageQuery.getPage(), pageQuery + .getSize()), queryWrapper); + return PageResp.build(page); + } + +} \ No newline at end of file diff --git a/continew-admin-business/src/main/resources/mapper/OrderAudioMapper.xml b/continew-admin-business/src/main/resources/mapper/OrderAudioMapper.xml new file mode 100644 index 0000000..129bee4 --- /dev/null +++ b/continew-admin-business/src/main/resources/mapper/OrderAudioMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/continew-admin-common/pom.xml b/continew-admin-common/pom.xml new file mode 100644 index 0000000..31e8f1b --- /dev/null +++ b/continew-admin-common/pom.xml @@ -0,0 +1,157 @@ + + + 4.0.0 + + top.continew + continew-admin + ${revision} + + + continew-admin-common + 公共模块(存放公共工具类,公共配置等) + + + + + me.ahoo.cosid + cosid-spring-boot-starter + + + me.ahoo.cosid + cosid-spring-redis + + + + + org.dromara.sms4j + sms4j-spring-boot-starter + + + + + org.dromara.x-file-storage + x-file-storage-spring + + + + com.amazonaws + aws-java-sdk-s3 + + + + + org.freemarker + freemarker + + + + + com.mysql + mysql-connector-j + + + + + top.continew + continew-starter-extension-crud-mp + + + + + top.continew + continew-starter-auth-justauth + + + + + top.continew + continew-starter-data-mp + + + + + top.continew + continew-starter-cache-jetcache + + + + + top.continew + continew-starter-messaging-websocket + + + org.springframework.boot + spring-boot-starter-web + + + + + + + top.continew + continew-starter-messaging-mail + + + + + top.continew + continew-starter-captcha-graphic + + + + + top.continew + continew-starter-captcha-behavior + + + + + top.continew + continew-starter-file-excel + + + + + top.continew + continew-starter-security-limiter + + + + + top.continew + continew-starter-security-crypto + + + + + top.continew + continew-starter-security-mask + + + + + top.continew + continew-starter-security-password + + + + + top.continew + continew-starter-api-doc + + + + + top.continew + continew-starter-json-jackson + + + + io.jsonwebtoken + jjwt + 0.9.1 + + + diff --git a/continew-admin-common/src/main/java/resource/.style/p3c-codestyle.xml b/continew-admin-common/src/main/java/resource/.style/p3c-codestyle.xml new file mode 100644 index 0000000..fb5f47f --- /dev/null +++ b/continew-admin-common/src/main/java/resource/.style/p3c-codestyle.xml @@ -0,0 +1,380 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/config/exception/GlobalExceptionHandler.java b/continew-admin-common/src/main/java/top/continew/admin/common/config/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..d317d3d --- /dev/null +++ b/continew-admin-common/src/main/java/top/continew/admin/common/config/exception/GlobalExceptionHandler.java @@ -0,0 +1,71 @@ +package top.continew.admin.common.config.exception; + +import cn.hutool.core.text.CharSequenceUtil; +import cn.hutool.core.util.NumberUtil; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.annotation.Order; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.multipart.MultipartException; +import top.continew.starter.core.exception.BadRequestException; +import top.continew.starter.core.exception.BusinessException; +import top.continew.starter.web.model.R; + +/** + * 全局异常处理器 + * + * @author Charles7c + * @since 2024/8/7 20:21 + */ +@Slf4j +@Order(99) +@RestControllerAdvice +public class GlobalExceptionHandler { + + /** + * 拦截业务异常 + */ + @ExceptionHandler(BusinessException.class) + public R handleBusinessException(BusinessException e, HttpServletRequest request) { + log.error("[{}] {}", request.getMethod(), request.getRequestURI(), e); + return R.fail(String.valueOf(HttpStatus.INTERNAL_SERVER_ERROR.value()), e.getMessage()); + } + + /** + * 拦截自定义验证异常-错误请求 + */ + @ExceptionHandler(BadRequestException.class) + public R handleBadRequestException(BadRequestException e, HttpServletRequest request) { + log.error("[{}] {}", request.getMethod(), request.getRequestURI(), e); + return R.fail(String.valueOf(HttpStatus.BAD_REQUEST.value()), e.getMessage()); + } + + /** + * 拦截文件上传异常-超过上传大小限制 + */ + @ExceptionHandler(MultipartException.class) + public R handleRequestTooBigException(MultipartException e, HttpServletRequest request) { + log.error("[{}] {}", request.getMethod(), request.getRequestURI(), e); + String msg = e.getMessage(); + R defaultFail = R.fail(String.valueOf(HttpStatus.BAD_REQUEST.value()), msg); + if (CharSequenceUtil.isBlank(msg)) { + return defaultFail; + } + String sizeLimit; + Throwable cause = e.getCause(); + if (null != cause) { + msg = msg.concat(cause.getMessage().toLowerCase()); + } + if (msg.contains("size") && msg.contains("exceed")) { + sizeLimit = CharSequenceUtil.subBetween(msg, "the maximum size ", " for"); + } else if (msg.contains("larger than")) { + sizeLimit = CharSequenceUtil.subAfter(msg, "larger than ", true); + } else { + return defaultFail; + } + String errorMsg = "请上传小于 %sKB 的文件".formatted(NumberUtil.parseLong(sizeLimit) / 1024); + return R.fail(String.valueOf(HttpStatus.BAD_REQUEST.value()), errorMsg); + } +} \ No newline at end of file diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/config/exception/GlobalSaTokenExceptionHandler.java b/continew-admin-common/src/main/java/top/continew/admin/common/config/exception/GlobalSaTokenExceptionHandler.java new file mode 100644 index 0000000..2be3b6f --- /dev/null +++ b/continew-admin-common/src/main/java/top/continew/admin/common/config/exception/GlobalSaTokenExceptionHandler.java @@ -0,0 +1,56 @@ +package top.continew.admin.common.config.exception; + +import cn.dev33.satoken.exception.NotLoginException; +import cn.dev33.satoken.exception.NotPermissionException; +import cn.dev33.satoken.exception.NotRoleException; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.annotation.Order; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import top.continew.starter.web.model.R; + +/** + * 全局 SaToken 异常处理器 + * + * @author Charles7c + * @since 2024/8/7 20:21 + */ +@Slf4j +@Order(99) +@RestControllerAdvice +public class GlobalSaTokenExceptionHandler { + + /** + * 认证异常-登录认证 + */ + @ExceptionHandler(NotLoginException.class) + public R handleNotLoginException(NotLoginException e, HttpServletRequest request) { + log.error("[{}] {}", request.getMethod(), request.getRequestURI(), e); + String errorMsg = switch (e.getType()) { + case NotLoginException.KICK_OUT -> "您已被踢下线"; + case NotLoginException.BE_REPLACED_MESSAGE -> "您已被顶下线"; + default -> "您的登录状态已过期,请重新登录"; + }; + return R.fail(String.valueOf(HttpStatus.UNAUTHORIZED.value()), errorMsg); + } + + /** + * 认证异常-权限认证 + */ + @ExceptionHandler(NotPermissionException.class) + public R handleNotPermissionException(NotPermissionException e, HttpServletRequest request) { + log.error("[{}] {}", request.getMethod(), request.getRequestURI(), e); + return R.fail(String.valueOf(HttpStatus.FORBIDDEN.value()), "没有访问权限,请联系管理员授权"); + } + + /** + * 认证异常-角色认证 + */ + @ExceptionHandler(NotRoleException.class) + public R handleNotRoleException(NotRoleException e, HttpServletRequest request) { + log.error("[{}] {}", request.getMethod(), request.getRequestURI(), e); + return R.fail(String.valueOf(HttpStatus.FORBIDDEN.value()), "没有访问权限,请联系管理员授权"); + } +} \ No newline at end of file diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/config/mybatis/BCryptEncryptor.java b/continew-admin-common/src/main/java/top/continew/admin/common/config/mybatis/BCryptEncryptor.java new file mode 100644 index 0000000..29bb5a0 --- /dev/null +++ b/continew-admin-common/src/main/java/top/continew/admin/common/config/mybatis/BCryptEncryptor.java @@ -0,0 +1,29 @@ +package top.continew.admin.common.config.mybatis; + +import org.springframework.security.crypto.password.PasswordEncoder; +import top.continew.starter.security.crypto.encryptor.IEncryptor; + +/** + * BCrypt 加/解密处理器(不可逆) + * + * @author Charles7c + * @since 2024/2/8 22:29 + */ +public class BCryptEncryptor implements IEncryptor { + + private final PasswordEncoder passwordEncoder; + + public BCryptEncryptor(PasswordEncoder passwordEncoder) { + this.passwordEncoder = passwordEncoder; + } + + @Override + public String encrypt(String plaintext, String password, String publicKey) throws Exception { + return passwordEncoder.encode(plaintext); + } + + @Override + public String decrypt(String ciphertext, String password, String privateKey) throws Exception { + return ciphertext; + } +} diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/config/mybatis/DataPermissionFilterImpl.java b/continew-admin-common/src/main/java/top/continew/admin/common/config/mybatis/DataPermissionFilterImpl.java new file mode 100644 index 0000000..c77ba2f --- /dev/null +++ b/continew-admin-common/src/main/java/top/continew/admin/common/config/mybatis/DataPermissionFilterImpl.java @@ -0,0 +1,48 @@ +package top.continew.admin.common.config.mybatis; + +import cn.hutool.core.convert.Convert; +import top.continew.admin.common.model.dto.LoginUser; +import top.continew.admin.common.util.helper.LoginHelper; +import top.continew.starter.data.mp.datapermission.DataPermissionCurrentUser; +import top.continew.starter.data.mp.datapermission.DataPermissionFilter; +import top.continew.starter.data.mp.datapermission.DataScope; + +import java.util.stream.Collectors; + +/** + * 数据权限过滤器实现类 + * + * @author Charles7c + * @since 2023/12/21 21:19 + */ +public class DataPermissionFilterImpl implements DataPermissionFilter { + + @Override + public boolean isFilter() { + LoginUser loginUser = LoginHelper.getLoginUserSafely(); + // 如果用户未登录,不进行数据权限过滤 + if (loginUser == null) { + return false; + } + return !loginUser.isAdmin(); + } + + @Override + public DataPermissionCurrentUser getCurrentUser() { + LoginUser loginUser = LoginHelper.getLoginUserSafely(); + // 如果用户未登录,返回null + if (loginUser == null) { + return null; + } + DataPermissionCurrentUser currentUser = new DataPermissionCurrentUser(); + currentUser.setUserId(Convert.toStr(loginUser.getId())); + currentUser.setDeptId(Convert.toStr(loginUser.getDeptId())); + currentUser.setRoles(loginUser.getRoles() + .stream() + .map(r -> new DataPermissionCurrentUser.CurrentUserRole(Convert.toStr(r.getId()), DataScope.valueOf(r + .getDataScope() + .name()))) + .collect(Collectors.toSet())); + return currentUser; + } +} \ No newline at end of file diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/config/mybatis/DataPermissionMapper.java b/continew-admin-common/src/main/java/top/continew/admin/common/config/mybatis/DataPermissionMapper.java new file mode 100644 index 0000000..c99e72f --- /dev/null +++ b/continew-admin-common/src/main/java/top/continew/admin/common/config/mybatis/DataPermissionMapper.java @@ -0,0 +1,41 @@ +package top.continew.admin.common.config.mybatis; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Param; +import top.continew.starter.data.mp.base.BaseMapper; +import top.continew.starter.data.mp.datapermission.DataPermission; + +import java.util.List; + +/** + * 数据权限 Mapper 基类 + * + * @param 实体类 + * @author Charles7c + * @since 2023/9/3 21:50 + */ +public interface DataPermissionMapper extends BaseMapper { + + /** + * 根据 entity 条件,查询全部记录 + * + * @param queryWrapper 实体对象封装操作类(可以为 null) + * @return 全部记录 + */ + @Override + @DataPermission + List selectList(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 根据 entity 条件,查询全部记录(并翻页) + * + * @param page 分页查询条件 + * @param queryWrapper 实体对象封装操作类(可以为 null) + * @return 全部记录(并翻页) + */ + @Override + @DataPermission + List selectList(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper); +} diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/config/mybatis/MyBatisPlusMetaObjectHandler.java b/continew-admin-common/src/main/java/top/continew/admin/common/config/mybatis/MyBatisPlusMetaObjectHandler.java new file mode 100644 index 0000000..ab1ef79 --- /dev/null +++ b/continew-admin-common/src/main/java/top/continew/admin/common/config/mybatis/MyBatisPlusMetaObjectHandler.java @@ -0,0 +1,107 @@ +package top.continew.admin.common.config.mybatis; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import org.apache.ibatis.reflection.MetaObject; +import top.continew.admin.common.util.helper.LoginHelper; +import top.continew.starter.core.exception.BusinessException; +import top.continew.starter.core.util.ExceptionUtils; +import top.continew.starter.extension.crud.model.entity.BaseDO; + +import java.time.LocalDateTime; + +/** + * MyBatis Plus 元对象处理器配置(插入或修改时自动填充) + * + * @author Charles7c + * @since 2022/12/22 19:52 + */ +public class MyBatisPlusMetaObjectHandler implements MetaObjectHandler { + + /** + * 创建人 + */ + private static final String CREATE_USER = "createUser"; + /** + * 创建时间 + */ + private static final String CREATE_TIME = "createTime"; + /** + * 修改人 + */ + private static final String UPDATE_USER = "updateUser"; + /** + * 修改时间 + */ + private static final String UPDATE_TIME = "updateTime"; + + /** + * 插入数据时填充 + * + * @param metaObject 元对象 + */ + @Override + public void insertFill(MetaObject metaObject) { + try { + if (null == metaObject) { + return; + } + Long createUser = ExceptionUtils.exToNull(LoginHelper::getUserId); + LocalDateTime createTime = LocalDateTime.now(); + if (metaObject.getOriginalObject() instanceof BaseDO baseDO) { + // 继承了 BaseDO 的类,填充创建信息字段 + baseDO.setCreateUser(ObjectUtil.defaultIfNull(baseDO.getCreateUser(), createUser)); + baseDO.setCreateTime(ObjectUtil.defaultIfNull(baseDO.getCreateTime(), createTime)); + } else { + // 未继承 BaseDO 的类,如存在创建信息字段则进行填充 + this.fillFieldValue(metaObject, CREATE_USER, createUser, false); + this.fillFieldValue(metaObject, CREATE_TIME, createTime, false); + } + } catch (Exception e) { + throw new BusinessException("插入数据时自动填充异常:" + e.getMessage()); + } + } + + /** + * 修改数据时填充 + * + * @param metaObject 元对象 + */ + @Override + public void updateFill(MetaObject metaObject) { + try { + if (null == metaObject) { + return; + } + + Long updateUser = LoginHelper.getUserId(); + LocalDateTime updateTime = LocalDateTime.now(); + if (metaObject.getOriginalObject() instanceof BaseDO baseDO) { + // 继承了 BaseDO 的类,填充修改信息 + baseDO.setUpdateUser(updateUser); + baseDO.setUpdateTime(updateTime); + } else { + // 未继承 BaseDO 的类,根据类中拥有的修改信息字段进行填充,不存在修改信息字段不进行填充 + this.fillFieldValue(metaObject, UPDATE_USER, updateUser, true); + this.fillFieldValue(metaObject, UPDATE_TIME, updateTime, true); + } + } catch (Exception e) { + throw new BusinessException("修改数据时自动填充异常:" + e.getMessage()); + } + } + + /** + * 填充字段值 + * + * @param metaObject 元数据对象 + * @param fieldName 要填充的字段名 + * @param fillFieldValue 要填充的字段值 + * @param isOverride 如果字段值不为空,是否覆盖(true:覆盖;false:不覆盖) + */ + private void fillFieldValue(MetaObject metaObject, String fieldName, Object fillFieldValue, boolean isOverride) { + if (metaObject.hasSetter(fieldName)) { + Object fieldValue = metaObject.getValue(fieldName); + setFieldValByName(fieldName, null != fieldValue && !isOverride ? fieldValue : fillFieldValue, metaObject); + } + } +} diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/config/mybatis/MybatisPlusConfiguration.java b/continew-admin-common/src/main/java/top/continew/admin/common/config/mybatis/MybatisPlusConfiguration.java new file mode 100644 index 0000000..7e4eb7e --- /dev/null +++ b/continew-admin-common/src/main/java/top/continew/admin/common/config/mybatis/MybatisPlusConfiguration.java @@ -0,0 +1,41 @@ +package top.continew.admin.common.config.mybatis; + +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.password.PasswordEncoder; +import top.continew.starter.data.mp.datapermission.DataPermissionFilter; + +/** + * MyBatis Plus 配置 + * + * @author Charles7c + * @since 2022/12/22 19:51 + */ +@Configuration +public class MybatisPlusConfiguration { + + /** + * 元对象处理器配置(插入或修改时自动填充) + */ + @Bean + public MetaObjectHandler metaObjectHandler() { + return new MyBatisPlusMetaObjectHandler(); + } + + /** + * 数据权限过滤器 + */ + @Bean + public DataPermissionFilter dataPermissionFilter() { + return new DataPermissionFilterImpl(); + } + + /** + * BCrypt 加/解密处理器 + */ + @Bean + public BCryptEncryptor bCryptEncryptor(PasswordEncoder passwordEncoder) { + return new BCryptEncryptor(passwordEncoder); + } +} diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/config/properties/CaptchaProperties.java b/continew-admin-common/src/main/java/top/continew/admin/common/config/properties/CaptchaProperties.java new file mode 100644 index 0000000..4055c91 --- /dev/null +++ b/continew-admin-common/src/main/java/top/continew/admin/common/config/properties/CaptchaProperties.java @@ -0,0 +1,76 @@ +package top.continew.admin.common.config.properties; + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 验证码配置属性 + * + * @author Charles7c + * @since 2022/12/11 13:35 + */ +@Data +@Component +@ConfigurationProperties(prefix = "captcha") +public class CaptchaProperties { + + /** + * 图形验证码过期时间 + */ + @Value("${continew-starter.captcha.graphic.expirationInMinutes}") + private long expirationInMinutes; + + /** + * 邮箱验证码配置 + */ + private CaptchaMail mail; + + /** + * 短信验证码配置 + */ + private CaptchaSms sms; + + /** + * 邮箱验证码配置 + */ + @Data + public static class CaptchaMail { + /** + * 内容长度 + */ + private int length; + + /** + * 过期时间 + */ + private long expirationInMinutes; + + /** + * 模板路径 + */ + private String templatePath; + } + + /** + * 短信验证码配置 + */ + @Data + public static class CaptchaSms { + /** + * 内容长度 + */ + private int length; + + /** + * 过期时间 + */ + private long expirationInMinutes; + + /** + * 模板 ID + */ + private String templateId; + } +} diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/config/properties/JwtProperties.java b/continew-admin-common/src/main/java/top/continew/admin/common/config/properties/JwtProperties.java new file mode 100644 index 0000000..fffeb46 --- /dev/null +++ b/continew-admin-common/src/main/java/top/continew/admin/common/config/properties/JwtProperties.java @@ -0,0 +1,27 @@ +package top.continew.admin.common.config.properties; + +import cn.hutool.extra.spring.SpringUtil; + +/** + * RSA 配置属性 + * + * @author Zheng Jie(ELADMIN) + * @author Charles7c + * @since 2022/12/21 20:21 + */ +public class JwtProperties { + + /** + * 私钥 + */ + public static final String PRIVATE_KEY; + public static final String PUBLIC_KEY; + + static { + PRIVATE_KEY = SpringUtil.getProperty("jwt.secret.private"); + PUBLIC_KEY = SpringUtil.getProperty("jwt.secret.public"); + } + + private JwtProperties() { + } +} diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/config/properties/KeyGenerator.java b/continew-admin-common/src/main/java/top/continew/admin/common/config/properties/KeyGenerator.java new file mode 100644 index 0000000..a06e434 --- /dev/null +++ b/continew-admin-common/src/main/java/top/continew/admin/common/config/properties/KeyGenerator.java @@ -0,0 +1,26 @@ +package top.continew.admin.common.config.properties; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; + +public class KeyGenerator { + public static void main(String[] args) throws NoSuchAlgorithmException { + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); + keyGen.initialize(2048); + KeyPair pair = keyGen.generateKeyPair(); + X509EncodedKeySpec pub = new X509EncodedKeySpec(pair.getPublic().getEncoded()); + PKCS8EncodedKeySpec priv = new PKCS8EncodedKeySpec(pair.getPrivate().getEncoded()); + + // 将密钥转换为Base64编码的字符串以便配置文件使用 + String publicKeyStr = Base64.getEncoder().encodeToString(pair.getPublic().getEncoded()); + String privateKeyStr = Base64.getEncoder().encodeToString(pair.getPrivate().getEncoded()); + + // 保存到文件或配置文件中 + System.out.println("Public Key: " + publicKeyStr); + System.out.println("Private Key: " + privateKeyStr); + } +} diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/config/properties/RsaProperties.java b/continew-admin-common/src/main/java/top/continew/admin/common/config/properties/RsaProperties.java new file mode 100644 index 0000000..052fc5f --- /dev/null +++ b/continew-admin-common/src/main/java/top/continew/admin/common/config/properties/RsaProperties.java @@ -0,0 +1,27 @@ +package top.continew.admin.common.config.properties; + +import cn.hutool.extra.spring.SpringUtil; + +/** + * RSA 配置属性 + * + * @author Zheng Jie(ELADMIN) + * @author Charles7c + * @since 2022/12/21 20:21 + */ +public class RsaProperties { + + /** + * 私钥 + */ + public static final String PRIVATE_KEY; + public static final String PUBLIC_KEY; + + static { + PRIVATE_KEY = SpringUtil.getProperty("continew-starter.security.crypto.private-key"); + PUBLIC_KEY = SpringUtil.getProperty("continew-starter.security.crypto.public-key"); + } + + private RsaProperties() { + } +} diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/config/websocket/WebSocketClientServiceImpl.java b/continew-admin-common/src/main/java/top/continew/admin/common/config/websocket/WebSocketClientServiceImpl.java new file mode 100644 index 0000000..fd7b082 --- /dev/null +++ b/continew-admin-common/src/main/java/top/continew/admin/common/config/websocket/WebSocketClientServiceImpl.java @@ -0,0 +1,26 @@ +package top.continew.admin.common.config.websocket; + +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.http.server.ServletServerHttpRequest; +import org.springframework.stereotype.Component; +import top.continew.admin.common.model.dto.LoginUser; +import top.continew.admin.common.util.helper.LoginHelper; +import top.continew.starter.messaging.websocket.core.WebSocketClientService; + +/** + * 当前登录用户 Provider + * + * @author Charles7c + * @since 2024/6/4 22:13 + */ +@Component +public class WebSocketClientServiceImpl implements WebSocketClientService { + + @Override + public String getClientId(ServletServerHttpRequest request) { + HttpServletRequest servletRequest = request.getServletRequest(); + String token = servletRequest.getParameter("token"); + LoginUser loginUser = LoginHelper.getLoginUser(token); + return loginUser.getToken(); + } +} diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/constant/CacheConstants.java b/continew-admin-common/src/main/java/top/continew/admin/common/constant/CacheConstants.java new file mode 100644 index 0000000..6bb6853 --- /dev/null +++ b/continew-admin-common/src/main/java/top/continew/admin/common/constant/CacheConstants.java @@ -0,0 +1,65 @@ +package top.continew.admin.common.constant; + +import top.continew.starter.core.constant.StringConstants; + +/** + * 缓存相关常量 + * + * @author Charles7c + * @since 2022/12/22 19:30 + */ +public class CacheConstants { + + /** + * 分隔符 + */ + public static final String DELIMITER = StringConstants.COLON; + + /** + * 登录用户键 + */ + public static final String LOGIN_USER_KEY = "LOGIN_USER"; + + /** + * 验证码键前缀 + */ + public static final String CAPTCHA_KEY_PREFIX = "CAPTCHA" + DELIMITER; + + /** + * 用户缓存键前缀 + */ + public static final String USER_KEY_PREFIX = "USER" + DELIMITER; + + /** + * 菜单缓存键前缀 + */ + public static final String MENU_KEY_PREFIX = "MENU" + DELIMITER; + + /** + * 字典缓存键前缀 + */ + public static final String DICT_KEY_PREFIX = "DICT" + DELIMITER; + + /** + * 参数缓存键前缀 + */ + public static final String OPTION_KEY_PREFIX = "OPTION" + DELIMITER; + + /** + * 仪表盘缓存键前缀 + */ + public static final String DASHBOARD_KEY_PREFIX = "DASHBOARD" + DELIMITER; + + /** + * 用户密码错误次数缓存键前缀 + */ + public static final String USER_PASSWORD_ERROR_KEY_PREFIX = USER_KEY_PREFIX + "PASSWORD_ERROR" + DELIMITER; + + /** + * 数据导入临时会话key + */ + public static final String DATA_IMPORT_KEY = "SYSTEM" + DELIMITER + "DATA_IMPORT" + DELIMITER; + + private CacheConstants() { + } +} diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/constant/ContainerConstants.java b/continew-admin-common/src/main/java/top/continew/admin/common/constant/ContainerConstants.java new file mode 100644 index 0000000..8de7d16 --- /dev/null +++ b/continew-admin-common/src/main/java/top/continew/admin/common/constant/ContainerConstants.java @@ -0,0 +1,37 @@ +package top.continew.admin.common.constant; + +import top.continew.starter.extension.crud.constant.ContainerPool; + +import java.util.Arrays; +import java.util.List; + +/** + * 数据源容器相关常量(Crane4j 数据填充组件使用) + * + * @author Charles7c + * @since 2024/1/20 12:33 + */ +public class ContainerConstants extends ContainerPool { + + /** + * 用户昵称 + */ + public static final String USER_NICKNAME = ContainerPool.USER_NICKNAME; + + /** + * 用户角色 ID 列表 + */ + public static final String USER_ROLE_ID_LIST = "UserRoleIdList"; + + /** + * 用户角色名称列表 + */ + public static final String USER_ROLE_NAME_LIST = "UserRoleNameList"; + /** + * 学校数据库 + */ + public static final List schools = Arrays.asList("dev", "kexue", "test"); + + private ContainerConstants() { + } +} diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/constant/RegexConstants.java b/continew-admin-common/src/main/java/top/continew/admin/common/constant/RegexConstants.java new file mode 100644 index 0000000..13d1692 --- /dev/null +++ b/continew-admin-common/src/main/java/top/continew/admin/common/constant/RegexConstants.java @@ -0,0 +1,48 @@ +package top.continew.admin.common.constant; + +/** + * 正则相关常量 + * + * @author Charles7c + * @since 2023/1/10 20:06 + */ +public class RegexConstants { + + /** + * 用户名正则(用户名长度为 4-64 个字符,支持大小写字母、数字、下划线) + */ + public static final String USERNAME = "^[a-zA-Z0-9_]{3,64}$"; + + /** + * 密码正则模板(密码长度为 min-max 个字符,支持大小写字母、数字、特殊字符,至少包含字母和数字) + */ + public static final String PASSWORD_TEMPLATE = "^(?=.*\\d)(?=.*[a-z]).{%s,%s}$"; + + /** + * 密码正则(密码长度为 8-32 个字符,支持大小写字母、数字、特殊字符,至少包含字母和数字) + */ + public static final String PASSWORD = "^(?=.*\\d)(?=.*[a-z]).{8,32}$"; + + /** + * 特殊字符正则 + */ + public static final String SPECIAL_CHARACTER = "[-_`~!@#$%^&*()+=|{}':;',\\\\[\\\\].<>/?~!@#¥%……&*()——+|{}【】‘;:”“’。,、?]|\\n|\\r|\\t"; + + /** + * 通用编码正则(长度为 2-30 个字符,支持大小写字母、数字、下划线,以字母开头) + */ + public static final String GENERAL_CODE = "^[a-zA-Z][a-zA-Z0-9_]{1,29}$"; + + /** + * 通用名称正则(长度为 2-30 个字符,支持中文、字母、数字、下划线,短横线) + */ + public static final String GENERAL_NAME = "^[\\u4e00-\\u9fa5a-zA-Z0-9_-]{2,30}$"; + + /** + * 包名正则(可以包含大小写字母、数字、下划线,每一级包名不能以数字开头) + */ + public static final String PACKAGE_NAME = "^(?:[a-zA-Z_][a-zA-Z0-9_]*\\.)*[a-zA-Z_][a-zA-Z0-9_]*$"; + + private RegexConstants() { + } +} diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/constant/SysConstants.java b/continew-admin-common/src/main/java/top/continew/admin/common/constant/SysConstants.java new file mode 100644 index 0000000..653d52d --- /dev/null +++ b/continew-admin-common/src/main/java/top/continew/admin/common/constant/SysConstants.java @@ -0,0 +1,58 @@ +package top.continew.admin.common.constant; + +/** + * 系统相关常量 + * + * @author Charles7c + * @since 2023/2/9 22:11 + */ +public class SysConstants { + + /** + * 否 + */ + public static final Integer NO = 0; + + /** + * 是 + */ + public static final Integer YES = 1; + + /** + * 管理员角色编码 + */ + public static final String ADMIN_ROLE_CODE = "admin"; + + /** + * 顶级部门 ID + */ + public static final Long SUPER_DEPT_ID = 1L; + + /** + * 顶级父 ID + */ + public static final Long SUPER_PARENT_ID = 0L; + + /** + * 全部权限标识 + */ + public static final String ALL_PERMISSION = "*:*:*"; + + /** + * 账号登录 URI + */ + public static final String LOGIN_URI = "/auth/account"; + + /** + * 退出 URI + */ + public static final String LOGOUT_URI = "/auth/logout"; + + /** + * 描述类字段后缀 + */ + public static final String DESCRIPTION_FIELD_SUFFIX = "String"; + + private SysConstants() { + } +} diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/constant/UiConstants.java b/continew-admin-common/src/main/java/top/continew/admin/common/constant/UiConstants.java new file mode 100644 index 0000000..c86176f --- /dev/null +++ b/continew-admin-common/src/main/java/top/continew/admin/common/constant/UiConstants.java @@ -0,0 +1,38 @@ +package top.continew.admin.common.constant; + +/** + * UI 相关常量 + * + * @author Charles7c + * @since 2023/9/17 14:12 + */ +public class UiConstants { + + /** + * 主色(极致蓝) + */ + public static final String COLOR_PRIMARY = "arcoblue"; + + /** + * 成功色(仙野绿) + */ + public static final String COLOR_SUCCESS = "green"; + + /** + * 警告色(活力橙) + */ + public static final String COLOR_WARNING = "orangered"; + + /** + * 错误色(浪漫红) + */ + public static final String COLOR_ERROR = "red"; + + /** + * 默认色(中性灰) + */ + public static final String COLOR_DEFAULT = "gray"; + + private UiConstants() { + } +} \ No newline at end of file diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/enums/DataScopeEnum.java b/continew-admin-common/src/main/java/top/continew/admin/common/enums/DataScopeEnum.java new file mode 100644 index 0000000..c4b77f1 --- /dev/null +++ b/continew-admin-common/src/main/java/top/continew/admin/common/enums/DataScopeEnum.java @@ -0,0 +1,44 @@ +package top.continew.admin.common.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import top.continew.starter.core.enums.BaseEnum; + +/** + * 数据权限枚举 + * + * @author Charles7c + * @since 2023/2/8 22:58 + */ +@Getter +@RequiredArgsConstructor +public enum DataScopeEnum implements BaseEnum { + + /** + * 全部数据权限 + */ + ALL(1, "全部数据权限"), + + /** + * 本部门及以下数据权限 + */ + DEPT_AND_CHILD(2, "本部门及以下数据权限"), + + /** + * 本部门数据权限 + */ + DEPT(3, "本部门数据权限"), + + /** + * 仅本人数据权限 + */ + SELF(4, "仅本人数据权限"), + + /** + * 自定义数据权限 + */ + CUSTOM(5, "自定义数据权限"),; + + private final Integer value; + private final String description; +} diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/enums/DisEnableStatusEnum.java b/continew-admin-common/src/main/java/top/continew/admin/common/enums/DisEnableStatusEnum.java new file mode 100644 index 0000000..a91915e --- /dev/null +++ b/continew-admin-common/src/main/java/top/continew/admin/common/enums/DisEnableStatusEnum.java @@ -0,0 +1,31 @@ +package top.continew.admin.common.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import top.continew.admin.common.constant.UiConstants; +import top.continew.starter.core.enums.BaseEnum; + +/** + * 启用/禁用状态枚举 + * + * @author Charles7c + * @since 2022/12/29 22:38 + */ +@Getter +@RequiredArgsConstructor +public enum DisEnableStatusEnum implements BaseEnum { + + /** + * 启用 + */ + ENABLE(1, "启用", UiConstants.COLOR_SUCCESS), + + /** + * 禁用 + */ + DISABLE(2, "禁用", UiConstants.COLOR_ERROR),; + + private final Integer value; + private final String description; + private final String color; +} diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/enums/GenderEnum.java b/continew-admin-common/src/main/java/top/continew/admin/common/enums/GenderEnum.java new file mode 100644 index 0000000..c4987b2 --- /dev/null +++ b/continew-admin-common/src/main/java/top/continew/admin/common/enums/GenderEnum.java @@ -0,0 +1,34 @@ +package top.continew.admin.common.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import top.continew.starter.core.enums.BaseEnum; + +/** + * 性别枚举 + * + * @author Charles7c + * @since 2022/12/29 21:59 + */ +@Getter +@RequiredArgsConstructor +public enum GenderEnum implements BaseEnum { + + /** + * 未知 + */ + UNKNOWN(0, "未知"), + + /** + * 男 + */ + MALE(1, "男"), + + /** + * 女 + */ + FEMALE(2, "女"),; + + private final Integer value; + private final String description; +} diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/enums/SuccessFailureStatusEnum.java b/continew-admin-common/src/main/java/top/continew/admin/common/enums/SuccessFailureStatusEnum.java new file mode 100644 index 0000000..fc6eb2e --- /dev/null +++ b/continew-admin-common/src/main/java/top/continew/admin/common/enums/SuccessFailureStatusEnum.java @@ -0,0 +1,31 @@ +package top.continew.admin.common.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import top.continew.admin.common.constant.UiConstants; +import top.continew.starter.core.enums.BaseEnum; + +/** + * 成功/失败状态枚举 + * + * @author Charles7c + * @since 2023/2/26 21:35 + */ +@Getter +@RequiredArgsConstructor +public enum SuccessFailureStatusEnum implements BaseEnum { + + /** + * 成功 + */ + SUCCESS(1, "成功", UiConstants.COLOR_SUCCESS), + + /** + * 失败 + */ + FAILURE(2, "失败", UiConstants.COLOR_ERROR),; + + private final Integer value; + private final String description; + private final String color; +} diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/model/dto/LoginUser.java b/continew-admin-common/src/main/java/top/continew/admin/common/model/dto/LoginUser.java new file mode 100644 index 0000000..d51a16b --- /dev/null +++ b/continew-admin-common/src/main/java/top/continew/admin/common/model/dto/LoginUser.java @@ -0,0 +1,165 @@ +package top.continew.admin.common.model.dto; + +import cn.hutool.core.collection.CollUtil; +import lombok.Data; +import lombok.NoArgsConstructor; +import top.continew.admin.common.constant.SysConstants; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * 登录用户信息 + * + * @author Charles7c + * @since 2022/12/24 13:01 + */ +@Data +@NoArgsConstructor +public class LoginUser implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + private Long id; + + /** + * 用户名 + */ + private String username; + + /** + * 密码 + */ + private String password; + + /** + * 部门 ID + */ + private Long deptId; + + /** + * 关联模型使用的部门ID列表 + */ + private List modelDeptIds; + + /** + * 权限码集合 + */ + private Set permissions; + + /** + * 角色编码集合 + */ + private Set roleCodes; + + /** + * 角色集合 + */ + private Set roles; + + /** + * 令牌 + */ + private String token; + + /** + * AIBot令牌 + */ + private String aiBotToken; + + private String sdBotToken; + + /** + * IP + */ + private String ip; + + /** + * IP 归属地 + */ + private String address; + + /** + * 浏览器 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 登录时间 + */ + private LocalDateTime loginTime; + + /** + * 最后一次修改密码时间 + */ + private LocalDateTime pwdResetTime; + + /** + * 登录时系统设置的密码过期天数 + */ + private Integer passwordExpirationDays; + /** + * 数据库名称 + */ + private String dbName; + + /** + * 所处部门权限 + */ + private String routers; + + public LoginUser(Set permissions, Set roles, Integer passwordExpirationDays) { + this.permissions = permissions; + if (roles != null && !roles.isEmpty()) { + this.setRoles(roles); + } + this.passwordExpirationDays = passwordExpirationDays; + } + + public void setRoles(Set roles) { + this.roles = roles; + this.roleCodes = roles.stream().map(RoleDTO::getCode).collect(Collectors.toSet()); + } + + /** + * 是否为管理员 + * + * @return true:是;false:否 + */ + public boolean isAdmin() { + if (CollUtil.isEmpty(roleCodes)) { + return false; + } + return roleCodes.contains(SysConstants.ADMIN_ROLE_CODE); + } + + /** + * 密码是否已过期 + * + * @return 是否过期 + */ + public boolean isPasswordExpired() { + // 永久有效 + if (this.passwordExpirationDays == null || this.passwordExpirationDays <= SysConstants.NO) { + return false; + } + // 初始密码(第三方登录用户)暂不提示修改 + if (this.pwdResetTime == null) { + return false; + } + return this.pwdResetTime.plusDays(this.passwordExpirationDays).isBefore(LocalDateTime.now()); + } +} diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/model/dto/RoleDTO.java b/continew-admin-common/src/main/java/top/continew/admin/common/model/dto/RoleDTO.java new file mode 100644 index 0000000..db659b9 --- /dev/null +++ b/continew-admin-common/src/main/java/top/continew/admin/common/model/dto/RoleDTO.java @@ -0,0 +1,35 @@ +package top.continew.admin.common.model.dto; + +import lombok.Data; +import top.continew.admin.common.enums.DataScopeEnum; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 角色信息 + * + * @author Charles7c + * @since 2023/3/7 22:08 + */ +@Data +public class RoleDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + private Long id; + + /** + * 角色编码 + */ + private String code; + + /** + * 数据权限 + */ + private DataScopeEnum dataScope; +} diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/model/resp/CaptchaResp.java b/continew-admin-common/src/main/java/top/continew/admin/common/model/resp/CaptchaResp.java new file mode 100644 index 0000000..e107cf8 --- /dev/null +++ b/continew-admin-common/src/main/java/top/continew/admin/common/model/resp/CaptchaResp.java @@ -0,0 +1,41 @@ +package top.continew.admin.common.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 验证码信息 + * + * @author Charles7c + * @since 2022/12/11 13:55 + */ +@Data +@Builder +@Schema(description = "验证码信息") +public class CaptchaResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 验证码标识 + */ + @Schema(description = "验证码标识", example = "090b9a2c-1691-4fca-99db-e4ed0cff362f") + private String uuid; + + /** + * 验证码图片(Base64编码,带图片格式:data:image/gif;base64) + */ + @Schema(description = "验证码图片(Base64编码,带图片格式:data:image/gif;base64)", example = "data:image/png;base64,iVBORw0KGgoAAAAN...") + private String img; + + /** + * 过期时间戳 + */ + @Schema(description = "过期时间戳", example = "1714376969409") + private Long expireTime; +} diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/util/AiBotUtil.java b/continew-admin-common/src/main/java/top/continew/admin/common/util/AiBotUtil.java new file mode 100644 index 0000000..9c0d54a --- /dev/null +++ b/continew-admin-common/src/main/java/top/continew/admin/common/util/AiBotUtil.java @@ -0,0 +1,226 @@ +package top.continew.admin.common.util; + +import cn.dev33.satoken.error.SaErrorCode; +import cn.dev33.satoken.exception.NotLoginException; +import cn.hutool.core.codec.Base64; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import cn.hutool.http.HttpUtil; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import top.continew.admin.common.util.helper.LoginHelper; +import top.continew.starter.core.util.validate.CheckUtils; +import top.continew.starter.extension.crud.model.resp.PageResp; + +import java.util.List; + +import static cn.dev33.satoken.exception.NotLoginException.INVALID_TOKEN; +import static cn.dev33.satoken.exception.NotLoginException.INVALID_TOKEN_MESSAGE; + +/** + * @author 张波 + * @since 2024/10/26 14:19 + */ +public class AiBotUtil { + private static final Logger logger = LoggerFactory.getLogger(AiBotUtil.class); + public static final String AI_BOT_URL; + public static final String ACCESS_KEY; + + static { + AI_BOT_URL = SpringUtil.getProperty("ai-bot.url"); + ACCESS_KEY = SpringUtil.getProperty("ai-bot.Wxcz-Access-Key"); + } + + /** + * 注册 + * + * @param username 账户 + * @param password 密码 + */ + public static void usersRegister(String username, String password) { + // 创建请求体 + JSONObject requestBody = new JSONObject(2); + requestBody.put("username", username); + requestBody.put("password", password); + + // 创建请求 + HttpRequest request = HttpRequest.post(AI_BOT_URL + "/users/register") + .header("Wxcz-Access-Key", ACCESS_KEY) + .setConnectionTimeout(2000) + .setReadTimeout(2000) + .body(requestBody.toJSONString()); + + // 执行请求 + try (HttpResponse execute = request.execute()) { + String responseBody = execute.body(); + + // 将请求返回转为Json + JSONObject responseJson = JSONObject.parse(responseBody); + logger.info("responseJson:{}", responseJson); + + // 判断注册是否成功 + if (!"user is exists".equals(responseJson.getString("message"))) { + CheckUtils.throwIf(responseJson.getIntValue("code") != 0, responseJson.getString("message")); + } + } catch (cn.hutool.core.io.IORuntimeException e) { + logger.error("用户注册请求超时: {}", e.getMessage()); + //throw new RuntimeException("用户注册请求超时,请稍后重试"); + } + } + + /** + * 登录 + */ + public static String login(String username, String password) { + // 创建请求体 + JSONObject requestBody = new JSONObject(2); + requestBody.put("username", username); + requestBody.put("password", password); + + // 创建请求 + HttpRequest request = HttpRequest.post(AI_BOT_URL + "/users/login") + .header("Wxcz-Access-Key", ACCESS_KEY) + .setConnectionTimeout(2000) + .setReadTimeout(2000) + .body(requestBody.toJSONString()); + + // 执行请求 + try (HttpResponse execute = request.execute()) { + String responseBody = execute.body(); + + // 将请求返回转为Json + JSONObject responseJson = JSONObject.parse(responseBody); + + // 判断登录是否成功 + CheckUtils.throwIf(!"true".equals(responseJson.getString("success")), "AI Bot登录失败!"); + + // 返回Token + return responseJson.getJSONObject("data").getString("token"); + } catch (cn.hutool.core.io.IORuntimeException e) { + logger.error("用户登录超时: {}", e.getMessage()); + //throw new RuntimeException("用户注册请求超时,请稍后重试"); + } + return ""; + } + + /** + * 修改密码 + * + * @param oldPassword 原密码 + * @param newPassword 新密码 + */ + public static void modifyPassword(String oldPassword, String newPassword) { + // 创建请求体 + JSONObject requestBody = new JSONObject(2); + requestBody.put("oldPassword", oldPassword); + requestBody.put("newPassword", newPassword); + + // 创建请求 + HttpRequest request = HttpRequest.post(AI_BOT_URL + "/users/password") + .header("Wxcz-Access-Key", ACCESS_KEY) + .header("Authorization", "Bearer " + LoginHelper.getLoginUser().getAiBotToken()) + .body(requestBody.toJSONString()); + + // 执行请求 + try (HttpResponse execute = request.execute()) { + String responseBody = execute.body(); + + // 将请求返回转为Json + JSONObject responseJson = JSONObject.parse(responseBody); + + // 判断注册是否成功 + CheckUtils.throwIf(responseJson.getIntValue("code") != 0, "修改失败!"); + } + } + + /** + * 个人图库列表 + * + * @param page 当前页 + * @param size 每页数量 + * @param isAsc 是否升序 + * @return 图片URL + */ + public static PageResp getImagePage(Integer page, Integer size, Boolean isAsc) { + // 创建请求参数 + JSONObject requestParamsMap = new JSONObject(4); + requestParamsMap.put("category", "generated"); + requestParamsMap.put("sort", isAsc ? "+id" : "-id"); + requestParamsMap.put("page", page); + requestParamsMap.put("size", size); + + // 创建请求 + HttpRequest request = HttpRequest.get(AI_BOT_URL + "/resources/private/many?" + HttpUtil + .toParams(requestParamsMap)) + .header("Wxcz-Access-Key", ACCESS_KEY) + .header("Authorization", "Bearer " + LoginHelper.getLoginUser().getAiBotToken()); + + // 执行请求 + try (HttpResponse execute = request.execute()) { + String responseBody = execute.body(); + + // 将请求返回转为Json + JSONObject responseJson = JSONObject.parse(responseBody); + + // 判断查询是否成功 + if (!"true".equals(responseJson.getString("success"))) { + throw NotLoginException.newInstance("AI-BOT", INVALID_TOKEN, INVALID_TOKEN_MESSAGE, null) + .setCode(SaErrorCode.CODE_11012); + } + + JSONArray items = responseJson.getJSONObject("data").getJSONArray("items"); + List urlList = items.stream().map(item -> { + JSONObject jsonObject = (JSONObject)item; + return jsonObject.getString("url"); + }).toList(); + + return new PageResp<>(urlList, responseJson.getJSONObject("data").getInteger("total")); + } + } + + /** + * 获取图片拓展信息 + * + * @param imageUrl 图片地址 + * @return 图片拓展信息 + */ + public static JSONObject getImageExif(String imageUrl) { + if (StrUtil.startWith(imageUrl, AI_BOT_URL)) { + return new JSONObject(); + } + + // 获取图片ID + String imageId = Base64.decodeStr(StrUtil.removePrefix(imageUrl, AI_BOT_URL + "/resources/download/")); + + // 创建请求参数 + JSONObject requestParamsMap = new JSONObject(1); + requestParamsMap.put("fields", "exif"); + + // 创建请求 + HttpRequest request = HttpRequest.get(AI_BOT_URL + "/resources/" + imageId + "/private?" + HttpUtil + .toParams(requestParamsMap)) + .header("Wxcz-Access-Key", ACCESS_KEY) + .header("Authorization", "Bearer " + LoginHelper.getLoginUser().getAiBotToken()); + + // 执行请求 + try (HttpResponse execute = request.execute()) { + String responseBody = execute.body(); + + // 将请求返回转为Json + JSONObject responseJson = JSONObject.parse(responseBody); + + // 判断查询是否成功 + if (!"true".equals(responseJson.getString("success"))) { + throw NotLoginException.newInstance("AI-BOT", INVALID_TOKEN, INVALID_TOKEN_MESSAGE, null) + .setCode(SaErrorCode.CODE_11012); + } + + // 获取拓展信息 + return responseJson.getJSONObject("data").getJSONObject("exif"); + } + } +} diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/util/JwtUtil.java b/continew-admin-common/src/main/java/top/continew/admin/common/util/JwtUtil.java new file mode 100644 index 0000000..5863422 --- /dev/null +++ b/continew-admin-common/src/main/java/top/continew/admin/common/util/JwtUtil.java @@ -0,0 +1,51 @@ +package top.continew.admin.common.util; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; + +import java.util.Date; +import java.util.function.Function; + +public class JwtUtil { + private static final String SECRET_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhF1cy7j57pBdPlO3Sm7N8expgroxq0br6fAinuXxiIAn3NklpgwLpw/V+leeKMzyYIJ2pFR6KmVybF9Z38Qs1H3C88r7ImYopBmO+IEqoEdm3UzNsNcdOSW4S9Lk1N5qtiniHDC/ZhlANzxeW7QbtS76RfZ4SSaeVUbfLJk/7RfFZ5HfLi1pBrT59SpfaJpKsbaMUBmdrEgGuGrT/K6jLsxqUcnOsbqAirtuP/NRpcO9OTsgpAh4tlFZKGg1hImIuAKwFZO0euZbjrQ9z7JwZoVSk0W99n0ULlofSIF939sSfhEX3UmgrEGsAwGVtZKROtJMHc/ywgBvaKpXKZsqjwIDAQAB"; // 应该使用强密钥,例如UUID.randomUUID().toString() + private static final long EXPIRATION_TIME = 3600000; // 1 hour in milliseconds + + public static String generateToken(String username) { + return Jwts.builder() + .setSubject(username) + .setIssuedAt(new Date()) + .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) + .signWith(SignatureAlgorithm.HS512, SECRET_KEY) + .compact(); + } + + public static String getUsernameFromToken(String token) { + return getClaimFromToken(token, Claims::getSubject); + } + + public static Date getExpirationDateFromToken(String token) { + return getClaimFromToken(token, Claims::getExpiration); + } + + public static T getClaimFromToken(String token, Function claimsResolver) { + final Claims claims = getAllClaimsFromToken(token); + return claimsResolver.apply(claims); + } + + private static Claims getAllClaimsFromToken(String token) { + return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody(); + } + + public static void main(String[] args) { + // 生成JWT Token + String token = JwtUtil.generateToken("aa123"); + System.out.println("Generated Token: " + token); + + // 从Token中获取用户名和其他信息(例如过期时间) + String username = JwtUtil.getUsernameFromToken(token); + Date expirationDate = JwtUtil.getExpirationDateFromToken(token); + System.out.println("Username: " + username); + System.out.println("Expiration Date: " + expirationDate); + } +} diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/util/RsaTestUtil.java b/continew-admin-common/src/main/java/top/continew/admin/common/util/RsaTestUtil.java new file mode 100644 index 0000000..2d494b3 --- /dev/null +++ b/continew-admin-common/src/main/java/top/continew/admin/common/util/RsaTestUtil.java @@ -0,0 +1,197 @@ +package top.continew.admin.common.util; + +import cn.hutool.core.codec.Base64; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.crypto.asymmetric.KeyType; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; + +/** + * RSA加密测试工具类 + * 独立运行,不依赖Spring容器 + * + * @author Charles7c + * @since 2024/12/04 16:30 + */ +public class RsaTestUtil { + + // 项目中使用的RSA密钥对 + private static final String PUBLIC_KEY = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAM51dgYtMyF+tTQt80sfFOpSV27a7t9uaUVeFrdGiVxscuizE7H8SMntYqfn9lp8a5GH5P1/GGehVjUD2gF/4kcCAwEAAQ=="; + private static final String PRIVATE_KEY = "MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAznV2Bi0zIX61NC3zSx8U6lJXbtru325pRV4Wt0aJXGxy6LMTsfxIye1ip+f2WnxrkYfk/X8YZ6FWNQPaAX/iRwIDAQABAkEAk/VcAusrpIqA5Ac2P5Tj0VX3cOuXmyouaVcXonr7f+6y2YTjLQuAnkcfKKocQI/juIRQBFQIqqW/m1nmz1wGeQIhAO8XaA/KxzOIgU0l/4lm0A2Wne6RokJ9HLs1YpOzIUmVAiEA3Q9DQrpAlIuiT1yWAGSxA9RxcjUM/1kdVLTkv0avXWsCIE0X8woEjK7lOSwzMG6RpEx9YHdopjViOj1zPVH61KTxAiBmv/dlhqkJ4rV46fIXELZur0pj6WC3N7a4brR8a+CLLQIhAMQyerWl2cPNVtE/8tkziHKbwW3ZUiBXU24wFxedT9iV"; + + /** + * RSA公钥加密 + * + * @param data 要加密的内容 + * @param publicKey 公钥 + * @return 加密后的内容(Base64编码) + */ + public static String encrypt(String data, String publicKey) { + return Base64.encode(SecureUtil.rsa(null, publicKey).encrypt(data.getBytes(), KeyType.PublicKey)); + } + + /** + * RSA私钥解密 + * + * @param data 要解密的内容(Base64编码) + * @param privateKey 私钥 + * @return 解密后的内容 + */ + public static String decrypt(String data, String privateKey) { + return new String(SecureUtil.rsa(privateKey, null).decrypt(Base64.decode(data), KeyType.PrivateKey)); + } + + /** + * 使用项目默认密钥加密 + * + * @param data 要加密的内容 + * @return 加密后的内容 + */ + public static String encryptWithDefaultKey(String data) { + return encrypt(data, PUBLIC_KEY); + } + + /** + * 使用项目默认密钥解密 + * + * @param data 要解密的内容 + * @return 解密后的内容 + */ + public static String decryptWithDefaultKey(String data) { + return decrypt(data, PRIVATE_KEY); + } + + /** + * 生成新的RSA密钥对 + * + * @return 密钥对数组 [公钥, 私钥] + */ + public static String[] generateKeyPair() { + try { + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); + keyGen.initialize(1024); // 可以改为2048获得更高安全性 + KeyPair pair = keyGen.generateKeyPair(); + + String publicKey = Base64.encode(pair.getPublic().getEncoded()); + String privateKey = Base64.encode(pair.getPrivate().getEncoded()); + + return new String[] {publicKey, privateKey}; + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("生成RSA密钥对失败", e); + } + } + + /** + * 测试RSA加密解密功能 + */ + public static void testRsaEncryption() { + System.out.println("=== RSA加密解密测试 ==="); + + String[] testData = {"Test123456", "Admin@2024", "MyPassword123!", "简单密码123", "Special!@#$%^&*()Chars", "" // 空字符串测试 + }; + + for (String data : testData) { + try { + System.out.println("\n测试数据: \"" + data + "\""); + + // 加密 + String encrypted = encryptWithDefaultKey(data); + System.out.println("加密结果: " + encrypted); + + // 解密 + String decrypted = decryptWithDefaultKey(encrypted); + System.out.println("解密结果: \"" + decrypted + "\""); + + // 验证 + if (data.equals(decrypted)) { + System.out.println("✓ 测试通过"); + } else { + System.out.println("✗ 测试失败 - 原始数据与解密结果不匹配"); + } + + } catch (Exception e) { + System.out.println("✗ 测试失败 - " + e.getMessage()); + } + } + } + + /** + * 测试密钥生成功能 + */ + public static void testKeyGeneration() { + System.out.println("\n=== RSA密钥生成测试 ==="); + + try { + String[] keyPair = generateKeyPair(); + String publicKey = keyPair[0]; + String privateKey = keyPair[1]; + + System.out.println("生成的公钥: " + publicKey); + System.out.println("生成的私钥: " + privateKey); + + // 使用新生成的密钥测试加密解密 + String testData = "TestWithNewKeys123"; + String encrypted = encrypt(testData, publicKey); + String decrypted = decrypt(encrypted, privateKey); + + System.out.println("\n使用新密钥测试:"); + System.out.println("原始数据: " + testData); + System.out.println("加密结果: " + encrypted); + System.out.println("解密结果: " + decrypted); + + if (testData.equals(decrypted)) { + System.out.println("✓ 新密钥测试通过"); + } else { + System.out.println("✗ 新密钥测试失败"); + } + + } catch (Exception e) { + System.out.println("✗ 密钥生成测试失败 - " + e.getMessage()); + } + } + + /** + * 生成用于测试的加密密码 + * + * @param passwords 要加密的密码数组 + */ + public static void generateEncryptedPasswords(String[] passwords) { + System.out.println("\n=== 生成测试用加密密码 ==="); + + for (String password : passwords) { + try { + String encrypted = encryptWithDefaultKey(password); + System.out.println("原始密码: " + password); + System.out.println("加密密码: " + encrypted); + System.out.println("---"); + } catch (Exception e) { + System.out.println("加密失败: " + password + " - " + e.getMessage()); + } + } + } + + /** + * 主方法 - 运行所有测试 + */ + public static void main(String[] args) { + try { + // 基本加密解密测试 + testRsaEncryption(); + + // 密钥生成测试 + testKeyGeneration(); + + // 生成一些常用测试密码的加密版本 + String[] commonPasswords = {"Test123456", "Admin@2024", "Password123!", "SimplePass"}; + generateEncryptedPasswords(commonPasswords); + + System.out.println("\n=== 所有测试完成 ==="); + + } catch (Exception e) { + System.err.println("测试过程中发生错误: " + e.getMessage()); + e.printStackTrace(); + } + } +} diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/util/SecureUtils.java b/continew-admin-common/src/main/java/top/continew/admin/common/util/SecureUtils.java new file mode 100644 index 0000000..b92776c --- /dev/null +++ b/continew-admin-common/src/main/java/top/continew/admin/common/util/SecureUtils.java @@ -0,0 +1,134 @@ +package top.continew.admin.common.util; + +import cn.hutool.core.codec.Base64; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.crypto.asymmetric.KeyType; +import cn.hutool.extra.spring.SpringUtil; +import top.continew.admin.common.config.properties.RsaProperties; +import top.continew.starter.core.exception.BusinessException; +import top.continew.starter.core.util.validate.ValidationUtils; +import top.continew.starter.security.crypto.autoconfigure.CryptoProperties; +import top.continew.starter.security.crypto.encryptor.AesEncryptor; +import top.continew.starter.security.crypto.encryptor.IEncryptor; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * 加密/解密工具类 + * + * @author Charles7c + * @since 2022/12/21 21:41 + */ +public class SecureUtils { + + private SecureUtils() { + } + + /** + * 公钥加密 + * + * @param data 要加密的内容 + * @return 加密后的内容(Base64 编码) + */ + public static String encryptByRsaPublicKey(String data) { + String publicKey = RsaProperties.PUBLIC_KEY; + ValidationUtils.throwIfBlank(publicKey, "请配置 RSA 公钥"); + return encryptByRsaPublicKey(data, publicKey); + } + + /** + * 公钥加密 + * + * @param data 要加密的内容 + * @param publicKey 公钥 + * @return 加密后的内容(Base64 编码) + */ + public static String encryptByRsaPublicKey(String data, String publicKey) { + return Base64.encode(SecureUtil.rsa(null, publicKey).encrypt(data.getBytes(), KeyType.PublicKey)); + } + + /** + * 私钥解密 + * + * @param data 要解密的内容(Base64 加密过) + * @return 解密后的内容 + */ + public static String decryptByRsaPrivateKey(String data) { + String privateKey = RsaProperties.PRIVATE_KEY; + ValidationUtils.throwIfBlank(privateKey, "请配置 RSA 私钥"); + return decryptByRsaPrivateKey(data, privateKey); + } + + /** + * 私钥解密 + * + * @param data 要解密的内容(Base64 加密过) + * @param privateKey 私钥 + * @return 解密后的内容 + */ + public static String decryptByRsaPrivateKey(String data, String privateKey) { + return new String(SecureUtil.rsa(privateKey, null).decrypt(Base64.decode(data), KeyType.PrivateKey)); + } + + /** + * 对普通加密字段列表进行AES加密,优化starter加密模块后优化这个方法 + * + * @param values 待加密内容 + * @return 加密后内容 + */ + public static List encryptFieldByAes(List values) { + IEncryptor encryptor = new AesEncryptor(); + CryptoProperties properties = SpringUtil.getBean(CryptoProperties.class); + return values.stream().map(value -> { + try { + return encryptor.encrypt(value, properties.getPassword(), properties.getPublicKey()); + } catch (Exception e) { + throw new BusinessException("字段加密异常"); + } + }).collect(Collectors.toList()); + } + + /** + * 测试RSA加密解密功能的main方法 + * 注意:这个方法不依赖Spring容器,可以直接运行 + */ + public static void main(String[] args) { + try { + // 测试RSA加密解密 + System.out.println("=== RSA加密解密测试 ==="); + + // 使用配置文件中的密钥进行测试 + String publicKey = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAM51dgYtMyF+tTQt80sfFOpSV27a7t9uaUVeFrdGiVxscuizE7H8SMntYqfn9lp8a5GH5P1/GGehVjUD2gF/4kcCAwEAAQ=="; + String privateKey = "MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAznV2Bi0zIX61NC3zSx8U6lJXbtru325pRV4Wt0aJXGxy6LMTsfxIye1ip+f2WnxrkYfk/X8YZ6FWNQPaAX/iRwIDAQABAkEAk/VcAusrpIqA5Ac2P5Tj0VX3cOuXmyouaVcXonr7f+6y2YTjLQuAnkcfKKocQI/juIRQBFQIqqW/m1nmz1wGeQIhAO8XaA/KxzOIgU0l/4lm0A2Wne6RokJ9HLs1YpOzIUmVAiEA3Q9DQrpAlIuiT1yWAGSxA9RxcjUM/1kdVLTkv0avXWsCIE0X8woEjK7lOSwzMG6RpEx9YHdopjViOj1zPVH61KTxAiBmv/dlhqkJ4rV46fIXELZur0pj6WC3N7a4brR8a+CLLQIhAMQyerWl2cPNVtE/8tkziHKbwW3ZUiBXU24wFxedT9iV"; + + // 测试数据 + String[] testPasswords = {"Test123456", "Admin@2024", "MyPassword123!", "简单密码123"}; + + for (String password : testPasswords) { + System.out.println("\n测试密码: " + password); + + // 公钥加密 + String encrypted = encryptByRsaPublicKey(password, publicKey); + System.out.println("加密后: " + encrypted); + + // 私钥解密 + String decrypted = decryptByRsaPrivateKey(encrypted, privateKey); + System.out.println("解密后: " + decrypted); + + // 验证结果 + if (password.equals(decrypted)) { + System.out.println("✓ 测试通过"); + } else { + System.out.println("✗ 测试失败"); + } + } + + System.out.println("\n=== 测试完成 ==="); + + } catch (Exception e) { + System.err.println("测试失败: " + e.getMessage()); + e.printStackTrace(); + } + } +} diff --git a/continew-admin-common/src/main/java/top/continew/admin/common/util/helper/LoginHelper.java b/continew-admin-common/src/main/java/top/continew/admin/common/util/helper/LoginHelper.java new file mode 100644 index 0000000..842036f --- /dev/null +++ b/continew-admin-common/src/main/java/top/continew/admin/common/util/helper/LoginHelper.java @@ -0,0 +1,163 @@ +package top.continew.admin.common.util.helper; + +import cn.dev33.satoken.context.SaHolder; +import cn.dev33.satoken.exception.NotLoginException; +import cn.dev33.satoken.session.SaSession; +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.servlet.JakartaServletUtil; +import cn.hutool.extra.spring.SpringUtil; +import jakarta.servlet.http.HttpServletRequest; +import top.continew.admin.common.constant.CacheConstants; +import top.continew.admin.common.model.dto.LoginUser; +import top.continew.admin.common.util.AiBotUtil; +import top.continew.starter.core.util.ExceptionUtils; +import top.continew.starter.core.util.IpUtils; +import top.continew.starter.extension.crud.service.CommonUserService; +import top.continew.starter.web.util.ServletUtils; +import top.continew.starter.web.util.SpringWebUtils; + +import java.time.LocalDateTime; + +/** + * 登录助手 + * + * @author Charles7c + * @author Lion Li(RuoYi-Vue-Plus) + * @since 2022/12/24 12:58 + */ +public class LoginHelper { + + private LoginHelper() { + } + + /** + * 用户登录并缓存用户信息 + * + * @param loginUser 登录用户信息 + * @return 令牌 + */ + public static LoginUser login(LoginUser loginUser) { + // 记录登录信息 + HttpServletRequest request = SpringWebUtils.getRequest(); + loginUser.setIp(JakartaServletUtil.getClientIP(request)); + loginUser.setAddress(ExceptionUtils.exToNull(() -> IpUtils.getIpv4Address(loginUser.getIp()))); + loginUser.setBrowser(ServletUtils.getBrowser(request)); + loginUser.setLoginTime(LocalDateTime.now()); + loginUser.setOs(StrUtil.subBefore(ServletUtils.getOs(request), " or", false)); + + // 非管理员登录第三方平台 + if (!loginUser.isAdmin()) { + String aiBotToken = AiBotUtil.login(loginUser.getUsername(), loginUser.getPassword()); + loginUser.setAiBotToken(aiBotToken); + } + + // 登录并缓存用户信息 + StpUtil.login(loginUser.getId()); + SaHolder.getStorage().set(CacheConstants.LOGIN_USER_KEY, loginUser); + String tokenValue = StpUtil.getTokenValue(); + loginUser.setToken(tokenValue); + StpUtil.getTokenSession().set(CacheConstants.LOGIN_USER_KEY, loginUser); + return loginUser; + } + + /** + * 更新登录用户信息 + * + * @param loginUser + * 登录用户信息 + * @param token 令牌 + */ + public static void updateLoginUser(LoginUser loginUser, String token) { + SaHolder.getStorage().delete(CacheConstants.LOGIN_USER_KEY); + StpUtil.getTokenSessionByToken(token).set(CacheConstants.LOGIN_USER_KEY, loginUser); + } + + /** + * 获取登录用户信息 + * + * @return 登录用户信息 + * @throws NotLoginException 未登录异常 + */ + public static LoginUser getLoginUser() throws NotLoginException { + StpUtil.checkLogin(); + LoginUser loginUser = (LoginUser)SaHolder.getStorage().get(CacheConstants.LOGIN_USER_KEY); + if (null != loginUser) { + return loginUser; + } + SaSession tokenSession = StpUtil.getTokenSession(); + loginUser = (LoginUser)tokenSession.get(CacheConstants.LOGIN_USER_KEY); + SaHolder.getStorage().set(CacheConstants.LOGIN_USER_KEY, loginUser); + return loginUser; + } + + /** + * 安全获取登录用户信息,如果未登录返回null + * + * @return 登录用户信息,未登录时返回null + */ + public static LoginUser getLoginUserSafely() { + if (!StpUtil.isLogin()) { + return null; + } + LoginUser loginUser = (LoginUser)SaHolder.getStorage().get(CacheConstants.LOGIN_USER_KEY); + if (null != loginUser) { + return loginUser; + } + SaSession tokenSession = StpUtil.getTokenSession(); + loginUser = (LoginUser)tokenSession.get(CacheConstants.LOGIN_USER_KEY); + SaHolder.getStorage().set(CacheConstants.LOGIN_USER_KEY, loginUser); + return loginUser; + } + + /** + * 根据 Token 获取登录用户信息 + * + * @param token 用户 Token + * @return 登录用户信息 + */ + public static LoginUser getLoginUser(String token) { + SaSession tokenSession = StpUtil.getStpLogic().getTokenSessionByToken(token, false); + if (null == tokenSession) { + return null; + } + return (LoginUser)tokenSession.get(CacheConstants.LOGIN_USER_KEY); + } + + /** + * 获取登录用户 ID + * + * @return 登录用户 ID + */ + public static Long getUserId() { + return getLoginUser().getId(); + } + + /** + * 获取登录用户名 + * + * @return 登录用户名 + */ + public static String getUsername() { + return getLoginUser().getUsername(); + } + + /** + * 获取登录用户昵称 + * + * @return 登录用户昵称 + */ + public static String getNickname() { + return getNickname(getUserId()); + } + + /** + * 获取登录用户昵称 + * + * @param userId 登录用户 ID + * @return 登录用户昵称 + */ + public static String getNickname(Long userId) { + return ExceptionUtils.exToNull(() -> SpringUtil.getBean(CommonUserService.class).getNicknameById(userId)); + } +} \ No newline at end of file diff --git a/continew-admin-extension/continew-admin-job-server/pom.xml b/continew-admin-extension/continew-admin-job-server/pom.xml new file mode 100644 index 0000000..93324c5 --- /dev/null +++ b/continew-admin-extension/continew-admin-job-server/pom.xml @@ -0,0 +1,54 @@ + + + 4.0.0 + + top.continew + continew-admin-extension + ${revision} + + + continew-admin-job-server + 任务调度服务模块 + + + + 1.1.2 + + + + + + com.aizuda + snail-job-server-starter + 1.4.0 + + + + + + org.liquibase + liquibase-core + + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + diff --git a/continew-admin-extension/continew-admin-job-server/src/main/java/top/continew/admin/extension/job/JobServerApplication.java b/continew-admin-extension/continew-admin-job-server/src/main/java/top/continew/admin/extension/job/JobServerApplication.java new file mode 100644 index 0000000..2a16076 --- /dev/null +++ b/continew-admin-extension/continew-admin-job-server/src/main/java/top/continew/admin/extension/job/JobServerApplication.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.extension.job; + +import cn.hutool.core.net.NetUtil; +import cn.hutool.core.util.URLUtil; +import cn.hutool.extra.spring.SpringUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.web.ServerProperties; + +/** + * 任务调度服务端启动程序 + * + * @author KAI + * @since 2024/6/25 22:24 + */ +@Slf4j +@SpringBootApplication +@RequiredArgsConstructor +public class JobServerApplication extends com.aizuda.snailjob.server.SnailJobServerApplication implements ApplicationRunner { + + private final ServerProperties serverProperties; + + public static void main(String[] args) { + SpringApplication.run(JobServerApplication.class, args); + } + + @Override + public void run(ApplicationArguments args) { + String hostAddress = NetUtil.getLocalhostStr(); + Integer port = serverProperties.getPort(); + String contextPath = serverProperties.getServlet().getContextPath(); + String baseUrl = URLUtil.normalize("%s:%s%s".formatted(hostAddress, port, contextPath)); + log.info("----------------------------------------------"); + log.info("{} service started successfully.", SpringUtil.getApplicationName()); + log.info("访问地址:{}", baseUrl); + log.info("在线文档:https://snailjob.opensnail.com"); + log.info("----------------------------------------------"); + } +} diff --git a/continew-admin-extension/continew-admin-job-server/src/main/resources/config/application-dev.yml b/continew-admin-extension/continew-admin-job-server/src/main/resources/config/application-dev.yml new file mode 100644 index 0000000..e2a9409 --- /dev/null +++ b/continew-admin-extension/continew-admin-job-server/src/main/resources/config/application-dev.yml @@ -0,0 +1,58 @@ +server: + port: 8001 + +--- ### 数据源配置 +spring.datasource: + url: jdbc:mysql://${DB_HOST:127.0.0.1}:${DB_PORT:3306}/${DB_NAME:continew_admin_job}?serverTimezone=Asia/Shanghai&useSSL=true&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&autoReconnect=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true + username: ${DB_USER:root} + password: ${DB_PWD:abc123} + driver-class-name: com.mysql.cj.jdbc.Driver +# # PostgreSQL 配置 +# url: jdbc:postgresql://${DB_HOST:127.0.0.1}:${DB_PORT:5432}/${DB_NAME:continew_admin_job}?serverTimezone=Asia/Shanghai&useSSL=true&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&autoReconnect=true&stringtype=unspecified +# username: ${DB_USER:root} +# password: ${DB_PWD:123456} +# driver-class-name: org.postgresql.Driver +## Liquibase 配置 +spring.liquibase: + # 是否启用 + enabled: true + # 配置文件路径 + change-log: classpath:/db/changelog/db.changelog-master.yaml + +--- ### Snail Job 服务端配置 +snail-job: + # Netty 端口 + netty-port: 1788 + # 合并日志默认保存天数 + merge-Log-days: 1 + # 合并日志默认的条数 + merge-Log-num: 500 + # 配置日志保存时间(单位:天) + log-storage: 90 + # 配置每批次拉取重试数据的大小 + retry-pull-page-size: 100 + # 配置一个客户端每秒最多接收的重试数量指令 + limiter: 10 + # 配置号段模式下的步长 + step: 100 + # bucket 的总数量 + bucket-total: 128 + # Dashboard 任务容错天数 + summary-day: 7 + # 配置负载均衡周期时间 + load-balance-cycle-time: 10 + ## 回调配置 + callback: + # 回调 uniqueId 前缀 + prefix: CB + # 配置回调的最大执行次数 + max-count: 288 + # 配置回调触发的间隔时间 + trigger-interval: 900 + +--- ### 日志配置 +logging: + level: + com.aizuda.snailjob: DEBUG + file: + path: ./logs diff --git a/continew-admin-extension/continew-admin-job-server/src/main/resources/config/application-prod.yml b/continew-admin-extension/continew-admin-job-server/src/main/resources/config/application-prod.yml new file mode 100644 index 0000000..6aaff90 --- /dev/null +++ b/continew-admin-extension/continew-admin-job-server/src/main/resources/config/application-prod.yml @@ -0,0 +1,69 @@ +server: + port: 18001 + +--- ### 数据源配置 +spring.datasource: + type: com.zaxxer.hikari.HikariDataSource + ## 动态数据源配置(可配多主多从:m1、s1...;纯粹多库:mysql、oracle...;混合配置:m1、s1、oracle...) + dynamic: + # 设置默认的数据源或者数据源组(默认:master) + primary: master + # 严格匹配数据源(true:未匹配到指定数据源时抛异常;false:使用默认数据源;默认 false) + strict: false + datasource: + # 主库配置(可配多个,构成多主) + master: + url: jdbc:mysql://${DB_HOST:127.0.0.1}:${DB_PORT:3306}/${DB_NAME:continew_admin_job}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&rewriteBatchedStatements=true&autoReconnect=true&maxReconnects=10&failOverReadOnly=false + username: ${DB_USER:root} + password: ${DB_PWD:123456} + driver-class-name: com.mysql.cj.jdbc.Driver + type: ${spring.datasource.type} + hikari: + # 最大连接数量(默认 10,根据实际环境调整) + # 注意:当连接达到上限,并且没有空闲连接可用时,获取连接将在超时前阻塞最多 connectionTimeout 毫秒 + max-pool-size: 20 + # 获取连接超时时间(默认 30000 毫秒,30 秒) + connection-timeout: 30000 + # 空闲连接最大存活时间(默认 600000 毫秒,10 分钟) + idle-timeout: 600000 + # 保持连接活动的频率,以防止它被数据库或网络基础设施超时。该值必须小于 maxLifetime(默认 0,禁用) + keepaliveTime: 30000 + # 连接最大生存时间(默认 1800000 毫秒,30 分钟) + max-lifetime: 1800000 +## Liquibase 配置 +spring.liquibase: + # 是否启用 + enabled: true + # 配置文件路径 + change-log: classpath:/db/changelog/db.changelog-master.yaml + +--- ### Snail Job 服务端配置 +snail-job: + # Netty 端口 + netty-port: 1788 + # 合并日志默认保存天数 + merge-Log-days: 1 + # 合并日志默认的条数 + merge-Log-num: 500 + # 配置日志保存时间(单位:天) + log-storage: 90 + # 配置每批次拉取重试数据的大小 + retry-pull-page-size: 100 + # 配置一个客户端每秒最多接收的重试数量指令 + limiter: 10 + # 配置号段模式下的步长 + step: 100 + # bucket 的总数量 + bucket-total: 128 + # Dashboard 任务容错天数 + summary-day: 7 + # 配置负载均衡周期时间 + load-balance-cycle-time: 10 + ## 回调配置 + callback: + # 回调 uniqueId 前缀 + prefix: CB + # 配置回调的最大执行次数 + max-count: 288 + # 配置回调触发的间隔时间 + trigger-interval: 900 diff --git a/continew-admin-extension/continew-admin-job-server/src/main/resources/config/application.yml b/continew-admin-extension/continew-admin-job-server/src/main/resources/config/application.yml new file mode 100644 index 0000000..f8a3c6e --- /dev/null +++ b/continew-admin-extension/continew-admin-job-server/src/main/resources/config/application.yml @@ -0,0 +1,12 @@ +--- ### Spring 配置 +spring: + application: + name: continew-admin-job-server + ## 环境配置 + profiles: + # 启用的环境 + active: dev + +--- ### 日志配置 +logging: + config: classpath:logback-spring.xml diff --git a/continew-admin-extension/continew-admin-job-server/src/main/resources/db/changelog/db.changelog-master.yaml b/continew-admin-extension/continew-admin-job-server/src/main/resources/db/changelog/db.changelog-master.yaml new file mode 100644 index 0000000..286c9bb --- /dev/null +++ b/continew-admin-extension/continew-admin-job-server/src/main/resources/db/changelog/db.changelog-master.yaml @@ -0,0 +1,5 @@ +databaseChangeLog: + - include: + file: db/changelog/mysql/snail-job_table.sql + - include: + file: db/changelog/mysql/snail-job_data.sql \ No newline at end of file diff --git a/continew-admin-extension/continew-admin-job-server/src/main/resources/db/changelog/mysql/snail-job_data.sql b/continew-admin-extension/continew-admin-job-server/src/main/resources/db/changelog/mysql/snail-job_data.sql new file mode 100644 index 0000000..7c8953d --- /dev/null +++ b/continew-admin-extension/continew-admin-job-server/src/main/resources/db/changelog/mysql/snail-job_data.sql @@ -0,0 +1,14 @@ +-- liquibase formatted sql + +-- changeset snail-job-server:1.1.0 +-- 默认命名空间:Default +INSERT INTO `sj_namespace` (`id`, `name`, `unique_id`, `create_dt`, `update_dt`, `deleted`) +VALUES (1, 'Default', '764d604ec6fc45f68cd92514c40e9e1a', NOW(), NOW(), 0); + +-- 默认用户:admin/admin +INSERT INTO `sj_system_user` (username, password, role) +VALUES ('admin', '465c194afb65670f38322df087f0a9bb225cc257e43eb4ac5a0c98ef5b3173ac', 2); + +-- 默认分组:continew-admin +INSERT INTO `sj_group_config` (`id`, `namespace_id`, `group_name`, `description`, `token`, `group_status`, `version`, `group_partition`, `id_generator_mode`, `init_scene`, `create_dt`, `update_dt`) +VALUES (1, '764d604ec6fc45f68cd92514c40e9e1a', 'continew-admin', '默认分组', 'SJ_Wyz3dmsdbDOkDujOTSSoBjGQP1BMsVnj', 1, 1, 0, 2, 1, NOW(), NOW()); diff --git a/continew-admin-extension/continew-admin-job-server/src/main/resources/db/changelog/mysql/snail-job_table.sql b/continew-admin-extension/continew-admin-job-server/src/main/resources/db/changelog/mysql/snail-job_table.sql new file mode 100644 index 0000000..ac19abe --- /dev/null +++ b/continew-admin-extension/continew-admin-job-server/src/main/resources/db/changelog/mysql/snail-job_table.sql @@ -0,0 +1,521 @@ +-- liquibase formatted sql + +-- changeset snail-job-server:1.1.0 +SET NAMES utf8mb4; + +CREATE TABLE `sj_namespace` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `name` varchar(64) NOT NULL COMMENT '名称', + `unique_id` varchar(64) NOT NULL COMMENT '唯一id', + `description` varchar(256) NOT NULL DEFAULT '' COMMENT '描述', + `deleted` tinyint(4) NOT NULL DEFAULT 0 COMMENT '逻辑删除 1、删除', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_name` (`name`), + UNIQUE KEY `uk_unique_id` (`unique_id`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 COMMENT ='命名空间'; + +CREATE TABLE `sj_group_config` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` varchar(64) NOT NULL DEFAULT '' COMMENT '组名称', + `description` varchar(256) NOT NULL DEFAULT '' COMMENT '组描述', + `token` varchar(64) NOT NULL DEFAULT 'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT' COMMENT 'token', + `group_status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '组状态 0、未启用 1、启用', + `version` int(11) NOT NULL COMMENT '版本号', + `group_partition` int(11) NOT NULL COMMENT '分区', + `id_generator_mode` tinyint(4) NOT NULL DEFAULT 1 COMMENT '唯一id生成模式 默认号段模式', + `init_scene` tinyint(4) NOT NULL DEFAULT 0 COMMENT '是否初始化场景 0:否 1:是', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_namespace_id_group_name` (`namespace_id`, `group_name`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='组配置' +; + +CREATE TABLE `sj_notify_config` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` varchar(64) NOT NULL COMMENT '组名称', + `notify_name` varchar(64) NOT NULL DEFAULT '' COMMENT '通知名称', + `system_task_type` tinyint(4) NOT NULL DEFAULT 3 COMMENT '任务类型 1. 重试任务 2. 重试回调 3、JOB任务 4、WORKFLOW任务', + `notify_status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '通知状态 0、未启用 1、启用', + `recipient_ids` varchar(128) NOT NULL COMMENT '接收人id列表', + `notify_threshold` int(11) NOT NULL DEFAULT 0 COMMENT '通知阈值', + `notify_scene` tinyint(4) NOT NULL DEFAULT 0 COMMENT '通知场景', + `rate_limiter_status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '限流状态 0、未启用 1、启用', + `rate_limiter_threshold` int(11) NOT NULL DEFAULT 0 COMMENT '每秒限流阈值', + `description` varchar(256) NOT NULL DEFAULT '' COMMENT '描述', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_namespace_id_group_name_scene_name` (`namespace_id`, `group_name`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='通知配置' +; + +CREATE TABLE `sj_notify_recipient` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `recipient_name` varchar(64) NOT NULL COMMENT '接收人名称', + `notify_type` tinyint(4) NOT NULL DEFAULT 0 COMMENT '通知类型 1、钉钉 2、邮件 3、企业微信 4 飞书 5 webhook', + `notify_attribute` varchar(512) NOT NULL COMMENT '配置属性', + `description` varchar(256) NOT NULL DEFAULT '' COMMENT '描述', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_namespace_id` (`namespace_id`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='告警通知接收人' +; + +CREATE TABLE `sj_retry_dead_letter` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` varchar(64) NOT NULL COMMENT '组名称', + `scene_name` varchar(64) NOT NULL COMMENT '场景名称', + `idempotent_id` varchar(64) NOT NULL COMMENT '幂等id', + `biz_no` varchar(64) NOT NULL DEFAULT '' COMMENT '业务编号', + `executor_name` varchar(512) NOT NULL DEFAULT '' COMMENT '执行器名称', + `args_str` text NOT NULL COMMENT '执行方法参数', + `ext_attrs` text NOT NULL COMMENT '扩展字段', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`), + KEY `idx_namespace_id_group_name_scene_name` (`namespace_id`, `group_name`, `scene_name`), + KEY `idx_idempotent_id` (`idempotent_id`), + KEY `idx_biz_no` (`biz_no`), + KEY `idx_create_dt` (`create_dt`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='死信队列表' +; + +CREATE TABLE `sj_retry` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` varchar(64) NOT NULL COMMENT '组名称', + `scene_name` varchar(64) NOT NULL COMMENT '场景名称', + `idempotent_id` varchar(64) NOT NULL COMMENT '幂等id', + `biz_no` varchar(64) NOT NULL DEFAULT '' COMMENT '业务编号', + `executor_name` varchar(512) NOT NULL DEFAULT '' COMMENT '执行器名称', + `args_str` text NOT NULL COMMENT '执行方法参数', + `ext_attrs` text NOT NULL COMMENT '扩展字段', + `next_trigger_at` bigint(13) NOT NULL COMMENT '下次触发时间', + `retry_count` int(11) NOT NULL DEFAULT 0 COMMENT '重试次数', + `retry_status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '重试状态 0、重试中 1、成功 2、最大重试次数', + `task_type` tinyint(4) NOT NULL DEFAULT 1 COMMENT '任务类型 1、重试数据 2、回调数据', + `bucket_index` int(11) NOT NULL DEFAULT 0 COMMENT 'bucket', + `parent_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '父节点id', + `deleted` bigint(20) NOT NULL DEFAULT 0 COMMENT '逻辑删除', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_namespace_id_group_name_scene_name` (`namespace_id`, `group_name`, `scene_name`), + KEY `idx_namespace_id_group_name_retry_status` (`namespace_id`, `group_name`, `retry_status`), + KEY `idx_idempotent_id` (`idempotent_id`), + KEY `idx_biz_no` (`biz_no`), + KEY `idx_parent_id` (`parent_id`), + KEY `idx_create_dt` (`create_dt`), + UNIQUE KEY `uk_name_task_type_idempotent_id_deleted` (`namespace_id`, `group_name`, `task_type`, `idempotent_id`, `deleted`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='重试信息表' +; + +CREATE TABLE `sj_retry_task` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` varchar(64) NOT NULL COMMENT '组名称', + `scene_name` varchar(64) NOT NULL COMMENT '场景名称', + `retry_id` bigint(20) NOT NULL COMMENT '重试信息Id', + `ext_attrs` text NOT NULL COMMENT '扩展字段', + `task_status` tinyint(4) NOT NULL DEFAULT 1 COMMENT '重试状态', + `task_type` tinyint(4) NOT NULL DEFAULT 1 COMMENT '任务类型 1、重试数据 2、回调数据', + `operation_reason` tinyint(4) NOT NULL DEFAULT 0 COMMENT '操作原因', + `client_info` varchar(128) DEFAULT NULL COMMENT '客户端地址 clientId#ip:port', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_group_name_scene_name` (`namespace_id`, `group_name`, `scene_name`), + KEY `task_status` (`task_status`), + KEY `idx_create_dt` (`create_dt`), + KEY `idx_retry_id` (`retry_id`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='重试任务表' +; + +CREATE TABLE `sj_retry_task_log_message` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` varchar(64) NOT NULL COMMENT '组名称', + `retry_id` bigint(20) NOT NULL COMMENT '重试信息Id', + `retry_task_id` bigint(20) NOT NULL COMMENT '重试任务Id', + `message` longtext NOT NULL COMMENT '异常信息', + `log_num` int(11) NOT NULL DEFAULT 1 COMMENT '日志数量', + `real_time` bigint(13) NOT NULL DEFAULT 0 COMMENT '上报时间', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`), + KEY `idx_namespace_id_group_name_retry_task_id` (`namespace_id`, `group_name`, `retry_task_id`), + KEY `idx_create_dt` (`create_dt`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='任务调度日志信息记录表' +; + +CREATE TABLE `sj_retry_scene_config` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `scene_name` varchar(64) NOT NULL COMMENT '场景名称', + `group_name` varchar(64) NOT NULL COMMENT '组名称', + `scene_status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '组状态 0、未启用 1、启用', + `max_retry_count` int(11) NOT NULL DEFAULT 5 COMMENT '最大重试次数', + `back_off` tinyint(4) NOT NULL DEFAULT 1 COMMENT '1、默认等级 2、固定间隔时间 3、CRON 表达式', + `trigger_interval` varchar(16) NOT NULL DEFAULT '' COMMENT '间隔时长', + `notify_ids` varchar(128) NOT NULL DEFAULT '' COMMENT '通知告警场景配置id列表', + `deadline_request` bigint(20) unsigned NOT NULL DEFAULT 60000 COMMENT 'Deadline Request 调用链超时 单位毫秒', + `executor_timeout` int(11) unsigned NOT NULL DEFAULT 5 COMMENT '任务执行超时时间,单位秒', + `route_key` tinyint(4) NOT NULL DEFAULT 4 COMMENT '路由策略', + `block_strategy` tinyint(4) NOT NULL DEFAULT 1 COMMENT '阻塞策略 1、丢弃 2、覆盖 3、并行', + `cb_status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '回调状态 0、不开启 1、开启', + `cb_trigger_type` tinyint(4) NOT NULL DEFAULT 1 COMMENT '1、默认等级 2、固定间隔时间 3、CRON 表达式', + `cb_max_count` int(11) NOT NULL DEFAULT 16 COMMENT '回调的最大执行次数', + `cb_trigger_interval` varchar(16) NOT NULL DEFAULT '' COMMENT '回调的最大执行次数', + `description` varchar(256) NOT NULL DEFAULT '' COMMENT '描述', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_namespace_id_group_name_scene_name` (`namespace_id`, `group_name`, `scene_name`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='场景配置' +; + +CREATE TABLE `sj_server_node` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` varchar(64) NOT NULL COMMENT '组名称', + `host_id` varchar(64) NOT NULL COMMENT '主机id', + `host_ip` varchar(64) NOT NULL COMMENT '机器ip', + `host_port` int(16) NOT NULL COMMENT '机器端口', + `expire_at` datetime NOT NULL COMMENT '过期时间', + `node_type` tinyint(4) NOT NULL COMMENT '节点类型 1、客户端 2、是服务端', + `ext_attrs` varchar(256) NULL DEFAULT '' COMMENT '扩展字段', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`), + KEY `idx_expire_at_node_type` (`expire_at`, `node_type`), + UNIQUE KEY `uk_host_id_host_ip` (`host_id`, `host_ip`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='服务器节点' +; + +CREATE TABLE `sj_distributed_lock` +( + `name` varchar(64) NOT NULL COMMENT '锁名称', + `lock_until` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '锁定时长', + `locked_at` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '锁定时间', + `locked_by` varchar(255) NOT NULL COMMENT '锁定者', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`name`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='锁定表' +; + +CREATE TABLE `sj_system_user` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `username` varchar(64) NOT NULL COMMENT '账号', + `password` varchar(128) NOT NULL COMMENT '密码', + `role` tinyint(4) NOT NULL DEFAULT 0 COMMENT '角色:1-普通用户、2-管理员', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_username` (`username`) USING BTREE +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 COMMENT ='系统用户表'; + +CREATE TABLE `sj_system_user_permission` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `group_name` varchar(64) NOT NULL COMMENT '组名称', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `system_user_id` bigint(20) NOT NULL COMMENT '系统用户id', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_namespace_id_group_name_system_user_id` (`namespace_id`, `group_name`, `system_user_id`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 COMMENT ='系统用户权限表'; + +CREATE TABLE `sj_sequence_alloc` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` varchar(64) NOT NULL DEFAULT '' COMMENT '组名称', + `max_id` bigint(20) NOT NULL DEFAULT 1 COMMENT '最大id', + `step` int(11) NOT NULL DEFAULT 100 COMMENT '步长', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_namespace_id_group_name` (`namespace_id`, `group_name`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 COMMENT ='号段模式序号ID分配表'; + +-- 分布式调度DDL +CREATE TABLE `sj_job` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` varchar(64) NOT NULL COMMENT '组名称', + `job_name` varchar(64) NOT NULL COMMENT '名称', + `args_str` text DEFAULT NULL COMMENT '执行方法参数', + `args_type` tinyint(4) NOT NULL DEFAULT 1 COMMENT '参数类型 ', + `next_trigger_at` bigint(13) NOT NULL COMMENT '下次触发时间', + `job_status` tinyint(4) NOT NULL DEFAULT 1 COMMENT '任务状态 0、关闭、1、开启', + `task_type` tinyint(4) NOT NULL DEFAULT 1 COMMENT '任务类型 1、集群 2、广播 3、切片', + `route_key` tinyint(4) NOT NULL DEFAULT 4 COMMENT '路由策略', + `executor_type` tinyint(4) NOT NULL DEFAULT 1 COMMENT '执行器类型', + `executor_info` varchar(255) DEFAULT NULL COMMENT '执行器名称', + `trigger_type` tinyint(4) NOT NULL COMMENT '触发类型 1.CRON 表达式 2. 固定时间', + `trigger_interval` varchar(255) NOT NULL COMMENT '间隔时长', + `block_strategy` tinyint(4) NOT NULL DEFAULT 1 COMMENT '阻塞策略 1、丢弃 2、覆盖 3、并行 4、恢复', + `executor_timeout` int(11) NOT NULL DEFAULT 0 COMMENT '任务执行超时时间,单位秒', + `max_retry_times` int(11) NOT NULL DEFAULT 0 COMMENT '最大重试次数', + `parallel_num` int(11) NOT NULL DEFAULT 1 COMMENT '并行数', + `retry_interval` int(11) NOT NULL DEFAULT 0 COMMENT '重试间隔(s)', + `bucket_index` int(11) NOT NULL DEFAULT 0 COMMENT 'bucket', + `resident` tinyint(4) NOT NULL DEFAULT 0 COMMENT '是否是常驻任务', + `notify_ids` varchar(128) NOT NULL DEFAULT '' COMMENT '通知告警场景配置id列表', + `owner_id` bigint(20) NULL COMMENT '负责人id', + `description` varchar(256) NOT NULL DEFAULT '' COMMENT '描述', + `ext_attrs` varchar(256) NULL DEFAULT '' COMMENT '扩展字段', + `deleted` tinyint(4) NOT NULL DEFAULT 0 COMMENT '逻辑删除 1、删除', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`), + KEY `idx_job_status_bucket_index` (`job_status`, `bucket_index`), + KEY `idx_create_dt` (`create_dt`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='任务信息'; + +CREATE TABLE `sj_job_log_message` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` varchar(64) NOT NULL COMMENT '组名称', + `job_id` bigint(20) NOT NULL COMMENT '任务信息id', + `task_batch_id` bigint(20) NOT NULL COMMENT '任务批次id', + `task_id` bigint(20) NOT NULL COMMENT '调度任务id', + `message` longtext NOT NULL COMMENT '调度信息', + `log_num` int(11) NOT NULL DEFAULT 1 COMMENT '日志数量', + `real_time` bigint(13) NOT NULL DEFAULT 0 COMMENT '上报时间', + `ext_attrs` varchar(256) NULL DEFAULT '' COMMENT '扩展字段', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`), + KEY `idx_task_batch_id_task_id` (`task_batch_id`, `task_id`), + KEY `idx_create_dt` (`create_dt`), + KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='调度日志'; + +CREATE TABLE `sj_job_task` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` varchar(64) NOT NULL COMMENT '组名称', + `job_id` bigint(20) NOT NULL COMMENT '任务信息id', + `task_batch_id` bigint(20) NOT NULL COMMENT '调度任务id', + `parent_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '父执行器id', + `task_status` tinyint NOT NULL DEFAULT 0 COMMENT '执行的状态 0、失败 1、成功', + `retry_count` int(11) NOT NULL DEFAULT 0 COMMENT '重试次数', + `mr_stage` tinyint DEFAULT NULL COMMENT '动态分片所处阶段 1:map 2:reduce 3:mergeReduce', + `leaf` tinyint NOT NULL DEFAULT '1' COMMENT '叶子节点', + `task_name` varchar(255) NOT NULL DEFAULT '' COMMENT '任务名称', + `client_info` varchar(128) DEFAULT NULL COMMENT '客户端地址 clientId#ip:port', + `wf_context` text DEFAULT NULL COMMENT '工作流全局上下文', + `result_message` text NOT NULL COMMENT '执行结果', + `args_str` text DEFAULT NULL COMMENT '执行方法参数', + `args_type` tinyint NOT NULL DEFAULT 1 COMMENT '参数类型 ', + `ext_attrs` varchar(256) NULL DEFAULT '' COMMENT '扩展字段', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_task_batch_id_task_status` (`task_batch_id`, `task_status`), + KEY `idx_create_dt` (`create_dt`), + KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='任务实例'; + +CREATE TABLE `sj_job_task_batch` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` varchar(64) NOT NULL COMMENT '组名称', + `job_id` bigint(20) NOT NULL COMMENT '任务id', + `workflow_node_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '工作流节点id', + `parent_workflow_node_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '工作流任务父批次id', + `workflow_task_batch_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '工作流任务批次id', + `task_batch_status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '任务批次状态 0、失败 1、成功', + `operation_reason` tinyint(4) NOT NULL DEFAULT 0 COMMENT '操作原因', + `execution_at` bigint(13) NOT NULL DEFAULT 0 COMMENT '任务执行时间', + `system_task_type` tinyint(4) NOT NULL DEFAULT 3 COMMENT '任务类型 3、JOB任务 4、WORKFLOW任务', + `parent_id` varchar(64) NOT NULL DEFAULT '' COMMENT '父节点', + `ext_attrs` varchar(256) NULL DEFAULT '' COMMENT '扩展字段', + `deleted` tinyint(4) NOT NULL DEFAULT 0 COMMENT '逻辑删除 1、删除', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_job_id_task_batch_status` (`job_id`, `task_batch_status`), + KEY `idx_create_dt` (`create_dt`), + KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`), + KEY `idx_workflow_task_batch_id_workflow_node_id` (`workflow_task_batch_id`, `workflow_node_id`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='任务批次'; + +CREATE TABLE `sj_job_summary` +( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` VARCHAR(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '组名称', + `business_id` bigint NOT NULL COMMENT '业务id (job_id或workflow_id)', + `system_task_type` tinyint(4) NOT NULL DEFAULT 3 COMMENT '任务类型 3、JOB任务 4、WORKFLOW任务', + `trigger_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '统计时间', + `success_num` int NOT NULL DEFAULT 0 COMMENT '执行成功-日志数量', + `fail_num` int NOT NULL DEFAULT 0 COMMENT '执行失败-日志数量', + `fail_reason` varchar(512) NOT NULL DEFAULT '' COMMENT '失败原因', + `stop_num` int NOT NULL DEFAULT 0 COMMENT '执行失败-日志数量', + `stop_reason` varchar(512) NOT NULL DEFAULT '' COMMENT '失败原因', + `cancel_num` int NOT NULL DEFAULT 0 COMMENT '执行失败-日志数量', + `cancel_reason` varchar(512) NOT NULL DEFAULT '' COMMENT '失败原因', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_namespace_id_group_name_business_id` (`namespace_id`, `group_name`, business_id), + UNIQUE KEY `uk_trigger_at_system_task_type_business_id` (`trigger_at`, `system_task_type`, `business_id`) USING BTREE +) ENGINE = InnoDB + AUTO_INCREMENT = 1 + DEFAULT CHARSET = utf8mb4 COMMENT ='DashBoard_Job'; + +CREATE TABLE `sj_retry_summary` +( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` VARCHAR(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '组名称', + `scene_name` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '场景名称', + `trigger_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '统计时间', + `running_num` int NOT NULL DEFAULT 0 COMMENT '重试中-日志数量', + `finish_num` int NOT NULL DEFAULT 0 COMMENT '重试完成-日志数量', + `max_count_num` int NOT NULL DEFAULT 0 COMMENT '重试到达最大次数-日志数量', + `suspend_num` int NOT NULL DEFAULT 0 COMMENT '暂停重试-日志数量', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_trigger_at` (`trigger_at`), + UNIQUE KEY `uk_scene_name_trigger_at` (`namespace_id`, `group_name`, `scene_name`, `trigger_at`) USING BTREE +) ENGINE = InnoDB + AUTO_INCREMENT = 1 + DEFAULT CHARSET = utf8mb4 COMMENT ='DashBoard_Retry'; + +CREATE TABLE `sj_workflow` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `workflow_name` varchar(64) NOT NULL COMMENT '工作流名称', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` varchar(64) NOT NULL COMMENT '组名称', + `workflow_status` tinyint(4) NOT NULL DEFAULT 1 COMMENT '工作流状态 0、关闭、1、开启', + `trigger_type` tinyint(4) NOT NULL COMMENT '触发类型 1.CRON 表达式 2. 固定时间', + `trigger_interval` varchar(255) NOT NULL COMMENT '间隔时长', + `next_trigger_at` bigint NOT NULL COMMENT '下次触发时间', + `block_strategy` tinyint(4) NOT NULL DEFAULT 1 COMMENT '阻塞策略 1、丢弃 2、覆盖 3、并行', + `executor_timeout` int(11) NOT NULL DEFAULT 0 COMMENT '任务执行超时时间,单位秒', + `description` varchar(256) NOT NULL DEFAULT '' COMMENT '描述', + `flow_info` text DEFAULT NULL COMMENT '流程信息', + `wf_context` text DEFAULT NULL COMMENT '上下文', + `notify_ids` varchar(128) NOT NULL DEFAULT '' COMMENT '通知告警场景配置id列表', + `bucket_index` int(11) NOT NULL DEFAULT 0 COMMENT 'bucket', + `version` int(11) NOT NULL COMMENT '版本号', + `ext_attrs` varchar(256) NULL DEFAULT '' COMMENT '扩展字段', + `deleted` tinyint(4) NOT NULL DEFAULT 0 COMMENT '逻辑删除 1、删除', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_create_dt` (`create_dt`), + KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='工作流'; + +CREATE TABLE `sj_workflow_node` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `node_name` varchar(64) NOT NULL COMMENT '节点名称', + `group_name` varchar(64) NOT NULL COMMENT '组名称', + `job_id` bigint(20) NOT NULL COMMENT '任务信息id', + `workflow_id` bigint(20) NOT NULL COMMENT '工作流ID', + `node_type` tinyint(4) NOT NULL DEFAULT 1 COMMENT '1、任务节点 2、条件节点', + `expression_type` tinyint(4) NOT NULL DEFAULT 0 COMMENT '1、SpEl、2、Aviator 3、QL', + `fail_strategy` tinyint(4) NOT NULL DEFAULT 1 COMMENT '失败策略 1、跳过 2、阻塞', + `workflow_node_status` tinyint(4) NOT NULL DEFAULT 1 COMMENT '工作流节点状态 0、关闭、1、开启', + `priority_level` int(11) NOT NULL DEFAULT 1 COMMENT '优先级', + `node_info` text DEFAULT NULL COMMENT '节点信息 ', + `version` int(11) NOT NULL COMMENT '版本号', + `ext_attrs` varchar(256) NULL DEFAULT '' COMMENT '扩展字段', + `deleted` tinyint(4) NOT NULL DEFAULT 0 COMMENT '逻辑删除 1、删除', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_create_dt` (`create_dt`), + KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='工作流节点'; + +CREATE TABLE `sj_workflow_task_batch` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` varchar(64) NOT NULL COMMENT '组名称', + `workflow_id` bigint(20) NOT NULL COMMENT '工作流任务id', + `task_batch_status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '任务批次状态 0、失败 1、成功', + `operation_reason` tinyint(4) NOT NULL DEFAULT 0 COMMENT '操作原因', + `flow_info` text DEFAULT NULL COMMENT '流程信息', + `wf_context` text DEFAULT NULL COMMENT '全局上下文', + `execution_at` bigint(13) NOT NULL DEFAULT 0 COMMENT '任务执行时间', + `ext_attrs` varchar(256) NULL DEFAULT '' COMMENT '扩展字段', + `version` int(11) NOT NULL DEFAULT 1 COMMENT '版本号', + `deleted` tinyint(4) NOT NULL DEFAULT 0 COMMENT '逻辑删除 1、删除', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_job_id_task_batch_status` (`workflow_id`, `task_batch_status`), + KEY `idx_create_dt` (`create_dt`), + KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='工作流批次'; \ No newline at end of file diff --git a/continew-admin-extension/continew-admin-job-server/src/main/resources/db/changelog/postgresql/snail-job_data.sql b/continew-admin-extension/continew-admin-job-server/src/main/resources/db/changelog/postgresql/snail-job_data.sql new file mode 100644 index 0000000..44e2088 --- /dev/null +++ b/continew-admin-extension/continew-admin-job-server/src/main/resources/db/changelog/postgresql/snail-job_data.sql @@ -0,0 +1,14 @@ +-- liquibase formatted sql + +-- changeset snail-job-server:1.1.0 +-- 默认命名空间:Default +INSERT INTO sj_namespace (id, name, unique_id, create_dt, update_dt, deleted) +VALUES (1, 'Default', '764d604ec6fc45f68cd92514c40e9e1a', NOW(), NOW(), 0); + +-- 默认分组:continew-admin +INSERT INTO sj_group_config (id, namespace_id, group_name, description, token, group_status, version, group_partition, id_generator_mode, init_scene, bucket_index, create_dt, update_dt) +VALUES (1, '764d604ec6fc45f68cd92514c40e9e1a', 'continew-admin', '默认分组', 'SJ_Wyz3dmsdbDOkDujOTSSoBjGQP1BMsVnj', 1, 1, 0, 2, 1, 119, NOW(), NOW()); + +-- 默认用户:admin/admin +INSERT INTO sj_system_user (username, password, role) +VALUES ('admin', '465c194afb65670f38322df087f0a9bb225cc257e43eb4ac5a0c98ef5b3173ac', 2); diff --git a/continew-admin-extension/continew-admin-job-server/src/main/resources/db/changelog/postgresql/snail-job_table.sql b/continew-admin-extension/continew-admin-job-server/src/main/resources/db/changelog/postgresql/snail-job_table.sql new file mode 100644 index 0000000..00282ca --- /dev/null +++ b/continew-admin-extension/continew-admin-job-server/src/main/resources/db/changelog/postgresql/snail-job_table.sql @@ -0,0 +1,821 @@ +-- liquibase formatted sql + +-- changeset snail-job-server:1.1.0 +-- sj_namespace +CREATE TABLE sj_namespace +( + id bigserial PRIMARY KEY, + name varchar(64) NOT NULL, + unique_id varchar(64) NOT NULL, + description varchar(256) NOT NULL DEFAULT '', + deleted smallint NOT NULL DEFAULT 0, + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_sj_namespace_01 ON sj_namespace (name); + +COMMENT ON COLUMN sj_namespace.id IS '主键'; +COMMENT ON COLUMN sj_namespace.name IS '名称'; +COMMENT ON COLUMN sj_namespace.unique_id IS '唯一id'; +COMMENT ON COLUMN sj_namespace.description IS '描述'; +COMMENT ON COLUMN sj_namespace.deleted IS '逻辑删除 1、删除'; +COMMENT ON COLUMN sj_namespace.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_namespace.update_dt IS '修改时间'; +COMMENT ON TABLE sj_namespace IS '命名空间'; + +-- sj_group_config +CREATE TABLE sj_group_config +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name varchar(64) NOT NULL DEFAULT '', + description varchar(256) NOT NULL DEFAULT '', + token varchar(64) NOT NULL DEFAULT 'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT', + group_status smallint NOT NULL DEFAULT 0, + version int NOT NULL, + group_partition int NOT NULL, + id_generator_mode smallint NOT NULL DEFAULT 1, + init_scene smallint NOT NULL DEFAULT 0, + bucket_index int NOT NULL DEFAULT 0, + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX uk_sj_group_config_01 ON sj_group_config (namespace_id, group_name); + +COMMENT ON COLUMN sj_group_config.id IS '主键'; +COMMENT ON COLUMN sj_group_config.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_group_config.group_name IS '组名称'; +COMMENT ON COLUMN sj_group_config.description IS '组描述'; +COMMENT ON COLUMN sj_group_config.token IS 'token'; +COMMENT ON COLUMN sj_group_config.group_status IS '组状态 0、未启用 1、启用'; +COMMENT ON COLUMN sj_group_config.version IS '版本号'; +COMMENT ON COLUMN sj_group_config.group_partition IS '分区'; +COMMENT ON COLUMN sj_group_config.id_generator_mode IS '唯一id生成模式 默认号段模式'; +COMMENT ON COLUMN sj_group_config.init_scene IS '是否初始化场景 0:否 1:是'; +COMMENT ON COLUMN sj_group_config.bucket_index IS 'bucket'; +COMMENT ON COLUMN sj_group_config.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_group_config.update_dt IS '修改时间'; +COMMENT ON TABLE sj_group_config IS '组配置'; + +-- sj_notify_config +CREATE TABLE sj_notify_config +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name varchar(64) NOT NULL, + business_id varchar(64) NOT NULL, + system_task_type smallint NOT NULL DEFAULT 3, + notify_status smallint NOT NULL DEFAULT 0, + recipient_ids varchar(128) NOT NULL, + notify_threshold int NOT NULL DEFAULT 0, + notify_scene smallint NOT NULL DEFAULT 0, + rate_limiter_status smallint NOT NULL DEFAULT 0, + rate_limiter_threshold int NOT NULL DEFAULT 0, + description varchar(256) NOT NULL DEFAULT '', + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_sj_notify_config_01 ON sj_notify_config (namespace_id, group_name, business_id); + +COMMENT ON COLUMN sj_notify_config.id IS '主键'; +COMMENT ON COLUMN sj_notify_config.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_notify_config.group_name IS '组名称'; +COMMENT ON COLUMN sj_notify_config.business_id IS '业务id ( job_id或workflow_id或scene_name ) '; +COMMENT ON COLUMN sj_notify_config.system_task_type IS '任务类型 1. 重试任务 2. 重试回调 3、JOB任务 4、WORKFLOW任务'; +COMMENT ON COLUMN sj_notify_config.notify_status IS '通知状态 0、未启用 1、启用'; +COMMENT ON COLUMN sj_notify_config.recipient_ids IS '接收人id列表'; +COMMENT ON COLUMN sj_notify_config.notify_threshold IS '通知阈值'; +COMMENT ON COLUMN sj_notify_config.notify_scene IS '通知场景'; +COMMENT ON COLUMN sj_notify_config.rate_limiter_status IS '限流状态 0、未启用 1、启用'; +COMMENT ON COLUMN sj_notify_config.rate_limiter_threshold IS '每秒限流阈值'; +COMMENT ON COLUMN sj_notify_config.description IS '描述'; +COMMENT ON COLUMN sj_notify_config.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_notify_config.update_dt IS '修改时间'; +COMMENT ON TABLE sj_notify_config IS '通知配置'; + +-- sj_notify_recipient +CREATE TABLE sj_notify_recipient +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + recipient_name varchar(64) NOT NULL, + notify_type smallint NOT NULL DEFAULT 0, + notify_attribute varchar(512) NOT NULL, + description varchar(256) NOT NULL DEFAULT '', + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_sj_notify_recipient_01 ON sj_notify_recipient (namespace_id); + +COMMENT ON COLUMN sj_notify_recipient.id IS '主键'; +COMMENT ON COLUMN sj_notify_recipient.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_notify_recipient.recipient_name IS '接收人名称'; +COMMENT ON COLUMN sj_notify_recipient.notify_type IS '通知类型 1、钉钉 2、邮件 3、企业微信 4 飞书 5 webhook'; +COMMENT ON COLUMN sj_notify_recipient.notify_attribute IS '配置属性'; +COMMENT ON COLUMN sj_notify_recipient.description IS '描述'; +COMMENT ON COLUMN sj_notify_recipient.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_notify_recipient.update_dt IS '修改时间'; +COMMENT ON TABLE sj_notify_recipient IS '告警通知接收人'; + +-- sj_retry_dead_letter_0 +CREATE TABLE sj_retry_dead_letter_0 +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + unique_id varchar(64) NOT NULL, + group_name varchar(64) NOT NULL, + scene_name varchar(64) NOT NULL, + idempotent_id varchar(64) NOT NULL, + biz_no varchar(64) NOT NULL DEFAULT '', + executor_name varchar(512) NOT NULL DEFAULT '', + args_str text NOT NULL, + ext_attrs text NOT NULL, + task_type smallint NOT NULL DEFAULT 1, + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX uk_sj_retry_dead_letter_0_01 ON sj_retry_dead_letter_0 (namespace_id, group_name, unique_id); + +CREATE INDEX idx_sj_retry_dead_letter_0_01 ON sj_retry_dead_letter_0 (namespace_id, group_name, scene_name); +CREATE INDEX idx_sj_retry_dead_letter_0_02 ON sj_retry_dead_letter_0 (idempotent_id); +CREATE INDEX idx_sj_retry_dead_letter_0_03 ON sj_retry_dead_letter_0 (biz_no); +CREATE INDEX idx_sj_retry_dead_letter_0_04 ON sj_retry_dead_letter_0 (create_dt); + +COMMENT ON COLUMN sj_retry_dead_letter_0.id IS '主键'; +COMMENT ON COLUMN sj_retry_dead_letter_0.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_retry_dead_letter_0.unique_id IS '同组下id唯一'; +COMMENT ON COLUMN sj_retry_dead_letter_0.group_name IS '组名称'; +COMMENT ON COLUMN sj_retry_dead_letter_0.scene_name IS '场景名称'; +COMMENT ON COLUMN sj_retry_dead_letter_0.idempotent_id IS '幂等id'; +COMMENT ON COLUMN sj_retry_dead_letter_0.biz_no IS '业务编号'; +COMMENT ON COLUMN sj_retry_dead_letter_0.executor_name IS '执行器名称'; +COMMENT ON COLUMN sj_retry_dead_letter_0.args_str IS '执行方法参数'; +COMMENT ON COLUMN sj_retry_dead_letter_0.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_retry_dead_letter_0.task_type IS '任务类型 1、重试数据 2、回调数据'; +COMMENT ON COLUMN sj_retry_dead_letter_0.create_dt IS '创建时间'; +COMMENT ON TABLE sj_retry_dead_letter_0 IS '死信队列表'; + +-- sj_retry_task_0 +CREATE TABLE sj_retry_task_0 +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + unique_id varchar(64) NOT NULL, + group_name varchar(64) NOT NULL, + scene_name varchar(64) NOT NULL, + idempotent_id varchar(64) NOT NULL, + biz_no varchar(64) NOT NULL DEFAULT '', + executor_name varchar(512) NOT NULL DEFAULT '', + args_str text NOT NULL, + ext_attrs text NOT NULL, + next_trigger_at timestamp NOT NULL, + retry_count int NOT NULL DEFAULT 0, + retry_status smallint NOT NULL DEFAULT 0, + task_type smallint NOT NULL DEFAULT 1, + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX uk_sj_retry_task_0_01 ON sj_retry_task_0 (namespace_id, group_name, unique_id); + +CREATE INDEX idx_sj_retry_task_0_01 ON sj_retry_task_0 (namespace_id, group_name, scene_name); +CREATE INDEX idx_sj_retry_task_0_02 ON sj_retry_task_0 (namespace_id, group_name, task_type); +CREATE INDEX idx_sj_retry_task_0_03 ON sj_retry_task_0 (namespace_id, group_name, retry_status); +CREATE INDEX idx_sj_retry_task_0_04 ON sj_retry_task_0 (idempotent_id); +CREATE INDEX idx_sj_retry_task_0_05 ON sj_retry_task_0 (biz_no); +CREATE INDEX idx_sj_retry_task_0_06 ON sj_retry_task_0 (create_dt); + +COMMENT ON COLUMN sj_retry_task_0.id IS '主键'; +COMMENT ON COLUMN sj_retry_task_0.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_retry_task_0.unique_id IS '同组下id唯一'; +COMMENT ON COLUMN sj_retry_task_0.group_name IS '组名称'; +COMMENT ON COLUMN sj_retry_task_0.scene_name IS '场景名称'; +COMMENT ON COLUMN sj_retry_task_0.idempotent_id IS '幂等id'; +COMMENT ON COLUMN sj_retry_task_0.biz_no IS '业务编号'; +COMMENT ON COLUMN sj_retry_task_0.executor_name IS '执行器名称'; +COMMENT ON COLUMN sj_retry_task_0.args_str IS '执行方法参数'; +COMMENT ON COLUMN sj_retry_task_0.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_retry_task_0.next_trigger_at IS '下次触发时间'; +COMMENT ON COLUMN sj_retry_task_0.retry_count IS '重试次数'; +COMMENT ON COLUMN sj_retry_task_0.retry_status IS '重试状态 0、重试中 1、成功 2、最大重试次数'; +COMMENT ON COLUMN sj_retry_task_0.task_type IS '任务类型 1、重试数据 2、回调数据'; +COMMENT ON COLUMN sj_retry_task_0.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_retry_task_0.update_dt IS '修改时间'; +COMMENT ON TABLE sj_retry_task_0 IS '任务表'; + +-- sj_retry_task_log +CREATE TABLE sj_retry_task_log +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + unique_id varchar(64) NOT NULL, + group_name varchar(64) NOT NULL, + scene_name varchar(64) NOT NULL, + idempotent_id varchar(64) NOT NULL, + biz_no varchar(64) NOT NULL DEFAULT '', + executor_name varchar(512) NOT NULL DEFAULT '', + args_str text NOT NULL, + ext_attrs text NOT NULL, + retry_status smallint NOT NULL DEFAULT 0, + task_type smallint NOT NULL DEFAULT 1, + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_sj_retry_task_log_01 ON sj_retry_task_log (namespace_id, group_name, scene_name); +CREATE INDEX idx_sj_retry_task_log_02 ON sj_retry_task_log (retry_status); +CREATE INDEX idx_sj_retry_task_log_03 ON sj_retry_task_log (idempotent_id); +CREATE INDEX idx_sj_retry_task_log_04 ON sj_retry_task_log (unique_id); +CREATE INDEX idx_sj_retry_task_log_05 ON sj_retry_task_log (biz_no); +CREATE INDEX idx_sj_retry_task_log_06 ON sj_retry_task_log (create_dt); + +COMMENT ON COLUMN sj_retry_task_log.id IS '主键'; +COMMENT ON COLUMN sj_retry_task_log.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_retry_task_log.unique_id IS '同组下id唯一'; +COMMENT ON COLUMN sj_retry_task_log.group_name IS '组名称'; +COMMENT ON COLUMN sj_retry_task_log.scene_name IS '场景名称'; +COMMENT ON COLUMN sj_retry_task_log.idempotent_id IS '幂等id'; +COMMENT ON COLUMN sj_retry_task_log.biz_no IS '业务编号'; +COMMENT ON COLUMN sj_retry_task_log.executor_name IS '执行器名称'; +COMMENT ON COLUMN sj_retry_task_log.args_str IS '执行方法参数'; +COMMENT ON COLUMN sj_retry_task_log.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_retry_task_log.retry_status IS '重试状态 0、重试中 1、成功 2、最大次数'; +COMMENT ON COLUMN sj_retry_task_log.task_type IS '任务类型 1、重试数据 2、回调数据'; +COMMENT ON COLUMN sj_retry_task_log.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_retry_task_log.update_dt IS '修改时间'; +COMMENT ON TABLE sj_retry_task_log IS '任务日志基础信息表'; + +-- sj_retry_task_log_message +CREATE TABLE sj_retry_task_log_message +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name varchar(64) NOT NULL, + unique_id varchar(64) NOT NULL, + message text NOT NULL, + log_num int NOT NULL DEFAULT 1, + real_time bigint NOT NULL DEFAULT 0, + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_sj_retry_task_log_message_01 ON sj_retry_task_log_message (namespace_id, group_name, unique_id); +CREATE INDEX idx_sj_retry_task_log_message_02 ON sj_retry_task_log_message (create_dt); + +COMMENT ON COLUMN sj_retry_task_log_message.id IS '主键'; +COMMENT ON COLUMN sj_retry_task_log_message.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_retry_task_log_message.group_name IS '组名称'; +COMMENT ON COLUMN sj_retry_task_log_message.unique_id IS '同组下id唯一'; +COMMENT ON COLUMN sj_retry_task_log_message.message IS '异常信息'; +COMMENT ON COLUMN sj_retry_task_log_message.log_num IS '日志数量'; +COMMENT ON COLUMN sj_retry_task_log_message.real_time IS '上报时间'; +COMMENT ON COLUMN sj_retry_task_log_message.create_dt IS '创建时间'; +COMMENT ON TABLE sj_retry_task_log_message IS '任务调度日志信息记录表'; + +-- sj_retry_scene_config +CREATE TABLE sj_retry_scene_config +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + scene_name varchar(64) NOT NULL, + group_name varchar(64) NOT NULL, + scene_status smallint NOT NULL DEFAULT 0, + max_retry_count int NOT NULL DEFAULT 5, + back_off smallint NOT NULL DEFAULT 1, + trigger_interval varchar(16) NOT NULL DEFAULT '', + deadline_request bigint NOT NULL DEFAULT 60000, + executor_timeout int NOT NULL DEFAULT 5, + route_key smallint NOT NULL DEFAULT 4, + description varchar(256) NOT NULL DEFAULT '', + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX uk_sj_retry_scene_config_01 ON sj_retry_scene_config (namespace_id, group_name, scene_name); + +COMMENT ON COLUMN sj_retry_scene_config.id IS '主键'; +COMMENT ON COLUMN sj_retry_scene_config.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_retry_scene_config.scene_name IS '场景名称'; +COMMENT ON COLUMN sj_retry_scene_config.group_name IS '组名称'; +COMMENT ON COLUMN sj_retry_scene_config.scene_status IS '组状态 0、未启用 1、启用'; +COMMENT ON COLUMN sj_retry_scene_config.max_retry_count IS '最大重试次数'; +COMMENT ON COLUMN sj_retry_scene_config.back_off IS '1、默认等级 2、固定间隔时间 3、CRON 表达式'; +COMMENT ON COLUMN sj_retry_scene_config.trigger_interval IS '间隔时长'; +COMMENT ON COLUMN sj_retry_scene_config.deadline_request IS 'Deadline Request 调用链超时 单位毫秒'; +COMMENT ON COLUMN sj_retry_scene_config.executor_timeout IS '任务执行超时时间,单位秒'; +COMMENT ON COLUMN sj_retry_scene_config.route_key IS '路由策略'; +COMMENT ON COLUMN sj_retry_scene_config.description IS '描述'; +COMMENT ON COLUMN sj_retry_scene_config.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_retry_scene_config.update_dt IS '修改时间'; +COMMENT ON TABLE sj_retry_scene_config IS '场景配置'; + +-- sj_server_node +CREATE TABLE sj_server_node +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name varchar(64) NOT NULL, + host_id varchar(64) NOT NULL, + host_ip varchar(64) NOT NULL, + host_port int NOT NULL, + expire_at timestamp NOT NULL, + node_type smallint NOT NULL, + ext_attrs varchar(256) NULL DEFAULT '', + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX uk_sj_server_node_01 ON sj_server_node (host_id, host_ip); + +CREATE INDEX idx_sj_server_node_01 ON sj_server_node (namespace_id, group_name); +CREATE INDEX idx_sj_server_node_02 ON sj_server_node (expire_at, node_type); + +COMMENT ON COLUMN sj_server_node.id IS '主键'; +COMMENT ON COLUMN sj_server_node.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_server_node.group_name IS '组名称'; +COMMENT ON COLUMN sj_server_node.host_id IS '主机id'; +COMMENT ON COLUMN sj_server_node.host_ip IS '机器ip'; +COMMENT ON COLUMN sj_server_node.host_port IS '机器端口'; +COMMENT ON COLUMN sj_server_node.expire_at IS '过期时间'; +COMMENT ON COLUMN sj_server_node.node_type IS '节点类型 1、客户端 2、是服务端'; +COMMENT ON COLUMN sj_server_node.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_server_node.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_server_node.update_dt IS '修改时间'; +COMMENT ON TABLE sj_server_node IS '服务器节点'; + +-- sj_distributed_lock +CREATE TABLE sj_distributed_lock +( + name varchar(64) PRIMARY KEY, + lock_until timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + locked_at timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + locked_by varchar(255) NOT NULL, + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +COMMENT ON COLUMN sj_distributed_lock.name IS '锁名称'; +COMMENT ON COLUMN sj_distributed_lock.lock_until IS '锁定时长'; +COMMENT ON COLUMN sj_distributed_lock.locked_at IS '锁定时间'; +COMMENT ON COLUMN sj_distributed_lock.locked_by IS '锁定者'; +COMMENT ON COLUMN sj_distributed_lock.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_distributed_lock.update_dt IS '修改时间'; +COMMENT ON TABLE sj_distributed_lock IS '锁定表'; + +-- sj_system_user +CREATE TABLE sj_system_user +( + id bigserial PRIMARY KEY, + username varchar(64) NOT NULL, + password varchar(128) NOT NULL, + role smallint NOT NULL DEFAULT 0, + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +COMMENT ON COLUMN sj_system_user.id IS '主键'; +COMMENT ON COLUMN sj_system_user.username IS '账号'; +COMMENT ON COLUMN sj_system_user.password IS '密码'; +COMMENT ON COLUMN sj_system_user.role IS '角色:1-普通用户、2-管理员'; +COMMENT ON COLUMN sj_system_user.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_system_user.update_dt IS '修改时间'; +COMMENT ON TABLE sj_system_user IS '系统用户表'; + +-- sj_system_user_permission +CREATE TABLE sj_system_user_permission +( + id bigserial PRIMARY KEY, + group_name varchar(64) NOT NULL, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + system_user_id bigint NOT NULL, + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX uk_sj_system_user_permission_01 ON sj_system_user_permission (namespace_id, group_name, system_user_id); + +COMMENT ON COLUMN sj_system_user_permission.id IS '主键'; +COMMENT ON COLUMN sj_system_user_permission.group_name IS '组名称'; +COMMENT ON COLUMN sj_system_user_permission.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_system_user_permission.system_user_id IS '系统用户id'; +COMMENT ON COLUMN sj_system_user_permission.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_system_user_permission.update_dt IS '修改时间'; +COMMENT ON TABLE sj_system_user_permission IS '系统用户权限表'; + +-- sj_sequence_alloc +CREATE TABLE sj_sequence_alloc +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name varchar(64) NOT NULL DEFAULT '', + max_id bigint NOT NULL DEFAULT 1, + step int NOT NULL DEFAULT 100, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX uk_sj_sequence_alloc_01 ON sj_sequence_alloc (namespace_id, group_name); + +COMMENT ON COLUMN sj_sequence_alloc.id IS '主键'; +COMMENT ON COLUMN sj_sequence_alloc.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_sequence_alloc.group_name IS '组名称'; +COMMENT ON COLUMN sj_sequence_alloc.max_id IS '最大id'; +COMMENT ON COLUMN sj_sequence_alloc.step IS '步长'; +COMMENT ON COLUMN sj_sequence_alloc.update_dt IS '更新时间'; +COMMENT ON TABLE sj_sequence_alloc IS '号段模式序号ID分配表'; + +-- sj_job +CREATE TABLE sj_job +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name varchar(64) NOT NULL, + job_name varchar(64) NOT NULL, + args_str text NULL DEFAULT NULL, + args_type smallint NOT NULL DEFAULT 1, + next_trigger_at bigint NOT NULL, + job_status smallint NOT NULL DEFAULT 1, + task_type smallint NOT NULL DEFAULT 1, + route_key smallint NOT NULL DEFAULT 4, + executor_type smallint NOT NULL DEFAULT 1, + executor_info varchar(255) NULL DEFAULT NULL, + trigger_type smallint NOT NULL, + trigger_interval varchar(255) NOT NULL, + block_strategy smallint NOT NULL DEFAULT 1, + executor_timeout int NOT NULL DEFAULT 0, + max_retry_times int NOT NULL DEFAULT 0, + parallel_num int NOT NULL DEFAULT 1, + retry_interval int NOT NULL DEFAULT 0, + bucket_index int NOT NULL DEFAULT 0, + resident smallint NOT NULL DEFAULT 0, + description varchar(256) NOT NULL DEFAULT '', + ext_attrs varchar(256) NULL DEFAULT '', + deleted smallint NOT NULL DEFAULT 0, + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_sj_job_01 ON sj_job (namespace_id, group_name); +CREATE INDEX idx_sj_job_02 ON sj_job (job_status, bucket_index); +CREATE INDEX idx_sj_job_03 ON sj_job (create_dt); + +COMMENT ON COLUMN sj_job.id IS '主键'; +COMMENT ON COLUMN sj_job.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_job.group_name IS '组名称'; +COMMENT ON COLUMN sj_job.job_name IS '名称'; +COMMENT ON COLUMN sj_job.args_str IS '执行方法参数'; +COMMENT ON COLUMN sj_job.args_type IS '参数类型 '; +COMMENT ON COLUMN sj_job.next_trigger_at IS '下次触发时间'; +COMMENT ON COLUMN sj_job.job_status IS '任务状态 0、关闭、1、开启'; +COMMENT ON COLUMN sj_job.task_type IS '任务类型 1、集群 2、广播 3、切片'; +COMMENT ON COLUMN sj_job.route_key IS '路由策略'; +COMMENT ON COLUMN sj_job.executor_type IS '执行器类型'; +COMMENT ON COLUMN sj_job.executor_info IS '执行器名称'; +COMMENT ON COLUMN sj_job.trigger_type IS '触发类型 1.CRON 表达式 2. 固定时间'; +COMMENT ON COLUMN sj_job.trigger_interval IS '间隔时长'; +COMMENT ON COLUMN sj_job.block_strategy IS '阻塞策略 1、丢弃 2、覆盖 3、并行'; +COMMENT ON COLUMN sj_job.executor_timeout IS '任务执行超时时间,单位秒'; +COMMENT ON COLUMN sj_job.max_retry_times IS '最大重试次数'; +COMMENT ON COLUMN sj_job.parallel_num IS '并行数'; +COMMENT ON COLUMN sj_job.retry_interval IS '重试间隔 ( s ) '; +COMMENT ON COLUMN sj_job.bucket_index IS 'bucket'; +COMMENT ON COLUMN sj_job.resident IS '是否是常驻任务'; +COMMENT ON COLUMN sj_job.description IS '描述'; +COMMENT ON COLUMN sj_job.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_job.deleted IS '逻辑删除 1、删除'; +COMMENT ON COLUMN sj_job.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_job.update_dt IS '修改时间'; +COMMENT ON TABLE sj_job IS '任务信息'; + +-- sj_job_log_message +CREATE TABLE sj_job_log_message +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name varchar(64) NOT NULL, + job_id bigint NOT NULL, + task_batch_id bigint NOT NULL, + task_id bigint NOT NULL, + message text NOT NULL, + log_num int NOT NULL DEFAULT 1, + real_time bigint NOT NULL DEFAULT 0, + ext_attrs varchar(256) NULL DEFAULT '', + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_sj_job_log_message_01 ON sj_job_log_message (task_batch_id, task_id); +CREATE INDEX idx_sj_job_log_message_02 ON sj_job_log_message (create_dt); +CREATE INDEX idx_sj_job_log_message_03 ON sj_job_log_message (namespace_id, group_name); + +COMMENT ON COLUMN sj_job_log_message.id IS '主键'; +COMMENT ON COLUMN sj_job_log_message.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_job_log_message.group_name IS '组名称'; +COMMENT ON COLUMN sj_job_log_message.job_id IS '任务信息id'; +COMMENT ON COLUMN sj_job_log_message.task_batch_id IS '任务批次id'; +COMMENT ON COLUMN sj_job_log_message.task_id IS '调度任务id'; +COMMENT ON COLUMN sj_job_log_message.message IS '调度信息'; +COMMENT ON COLUMN sj_job_log_message.log_num IS '日志数量'; +COMMENT ON COLUMN sj_job_log_message.real_time IS '上报时间'; +COMMENT ON COLUMN sj_job_log_message.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_job_log_message.create_dt IS '创建时间'; +COMMENT ON TABLE sj_job_log_message IS '调度日志'; + +-- sj_job_task +CREATE TABLE sj_job_task +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name varchar(64) NOT NULL, + job_id bigint NOT NULL, + task_batch_id bigint NOT NULL, + parent_id bigint NOT NULL DEFAULT 0, + task_status smallint NOT NULL DEFAULT 0, + retry_count int NOT NULL DEFAULT 0, + mr_stage smallint NULL DEFAULT NULL, + leaf smallint NOT NULL DEFAULT '1', + task_name varchar(255) NOT NULL DEFAULT '', + client_info varchar(128) NULL DEFAULT NULL, + wf_context text NULL DEFAULT NULL, + result_message text NOT NULL, + args_str text NULL DEFAULT NULL, + args_type smallint NOT NULL DEFAULT 1, + ext_attrs varchar(256) NULL DEFAULT '', + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_sj_job_task_01 ON sj_job_task (task_batch_id, task_status); +CREATE INDEX idx_sj_job_task_02 ON sj_job_task (create_dt); +CREATE INDEX idx_sj_job_task_03 ON sj_job_task (namespace_id, group_name); + +COMMENT ON COLUMN sj_job_task.id IS '主键'; +COMMENT ON COLUMN sj_job_task.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_job_task.group_name IS '组名称'; +COMMENT ON COLUMN sj_job_task.job_id IS '任务信息id'; +COMMENT ON COLUMN sj_job_task.task_batch_id IS '调度任务id'; +COMMENT ON COLUMN sj_job_task.parent_id IS '父执行器id'; +COMMENT ON COLUMN sj_job_task.task_status IS '执行的状态 0、失败 1、成功'; +COMMENT ON COLUMN sj_job_task.retry_count IS '重试次数'; +COMMENT ON COLUMN sj_job_task.mr_stage IS '动态分片所处阶段 1:map 2:reduce 3:mergeReduce'; +COMMENT ON COLUMN sj_job_task.leaf IS '叶子节点'; +COMMENT ON COLUMN sj_job_task.task_name IS '任务名称'; +COMMENT ON COLUMN sj_job_task.client_info IS '客户端地址 clientId#ip:port'; +COMMENT ON COLUMN sj_job_task.wf_context IS '工作流全局上下文'; +COMMENT ON COLUMN sj_job_task.result_message IS '执行结果'; +COMMENT ON COLUMN sj_job_task.args_str IS '执行方法参数'; +COMMENT ON COLUMN sj_job_task.args_type IS '参数类型 '; +COMMENT ON COLUMN sj_job_task.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_job_task.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_job_task.update_dt IS '修改时间'; +COMMENT ON TABLE sj_job_task IS '任务实例'; + +-- sj_job_task_batch +CREATE TABLE sj_job_task_batch +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name varchar(64) NOT NULL, + job_id bigint NOT NULL, + workflow_node_id bigint NOT NULL DEFAULT 0, + parent_workflow_node_id bigint NOT NULL DEFAULT 0, + workflow_task_batch_id bigint NOT NULL DEFAULT 0, + task_batch_status smallint NOT NULL DEFAULT 0, + operation_reason smallint NOT NULL DEFAULT 0, + execution_at bigint NOT NULL DEFAULT 0, + system_task_type smallint NOT NULL DEFAULT 3, + parent_id varchar(64) NOT NULL DEFAULT '', + ext_attrs varchar(256) NULL DEFAULT '', + deleted smallint NOT NULL DEFAULT 0, + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_sj_job_task_batch_01 ON sj_job_task_batch (job_id, task_batch_status); +CREATE INDEX idx_sj_job_task_batch_02 ON sj_job_task_batch (create_dt); +CREATE INDEX idx_sj_job_task_batch_03 ON sj_job_task_batch (namespace_id, group_name); +CREATE INDEX idx_sj_job_task_batch_04 ON sj_job_task_batch (workflow_task_batch_id, workflow_node_id); + +COMMENT ON COLUMN sj_job_task_batch.id IS '主键'; +COMMENT ON COLUMN sj_job_task_batch.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_job_task_batch.group_name IS '组名称'; +COMMENT ON COLUMN sj_job_task_batch.job_id IS '任务id'; +COMMENT ON COLUMN sj_job_task_batch.workflow_node_id IS '工作流节点id'; +COMMENT ON COLUMN sj_job_task_batch.parent_workflow_node_id IS '工作流任务父批次id'; +COMMENT ON COLUMN sj_job_task_batch.workflow_task_batch_id IS '工作流任务批次id'; +COMMENT ON COLUMN sj_job_task_batch.task_batch_status IS '任务批次状态 0、失败 1、成功'; +COMMENT ON COLUMN sj_job_task_batch.operation_reason IS '操作原因'; +COMMENT ON COLUMN sj_job_task_batch.execution_at IS '任务执行时间'; +COMMENT ON COLUMN sj_job_task_batch.system_task_type IS '任务类型 3、JOB任务 4、WORKFLOW任务'; +COMMENT ON COLUMN sj_job_task_batch.parent_id IS '父节点'; +COMMENT ON COLUMN sj_job_task_batch.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_job_task_batch.deleted IS '逻辑删除 1、删除'; +COMMENT ON COLUMN sj_job_task_batch.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_job_task_batch.update_dt IS '修改时间'; +COMMENT ON TABLE sj_job_task_batch IS '任务批次'; + +-- sj_job_summary +CREATE TABLE sj_job_summary +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name varchar(64) NOT NULL DEFAULT '', + business_id bigint NOT NULL, + system_task_type smallint NOT NULL DEFAULT 3, + trigger_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + success_num int NOT NULL DEFAULT 0, + fail_num int NOT NULL DEFAULT 0, + fail_reason varchar(512) NOT NULL DEFAULT '', + stop_num int NOT NULL DEFAULT 0, + stop_reason varchar(512) NOT NULL DEFAULT '', + cancel_num int NOT NULL DEFAULT 0, + cancel_reason varchar(512) NOT NULL DEFAULT '', + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX uk_sj_job_summary_01 ON sj_job_summary (trigger_at, system_task_type, business_id); + +CREATE INDEX idx_sj_job_summary_01 ON sj_job_summary (namespace_id, group_name, business_id); + +COMMENT ON COLUMN sj_job_summary.id IS '主键'; +COMMENT ON COLUMN sj_job_summary.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_job_summary.group_name IS '组名称'; +COMMENT ON COLUMN sj_job_summary.business_id IS '业务id ( job_id或workflow_id ) '; +COMMENT ON COLUMN sj_job_summary.system_task_type IS '任务类型 3、JOB任务 4、WORKFLOW任务'; +COMMENT ON COLUMN sj_job_summary.trigger_at IS '统计时间'; +COMMENT ON COLUMN sj_job_summary.success_num IS '执行成功-日志数量'; +COMMENT ON COLUMN sj_job_summary.fail_num IS '执行失败-日志数量'; +COMMENT ON COLUMN sj_job_summary.fail_reason IS '失败原因'; +COMMENT ON COLUMN sj_job_summary.stop_num IS '执行失败-日志数量'; +COMMENT ON COLUMN sj_job_summary.stop_reason IS '失败原因'; +COMMENT ON COLUMN sj_job_summary.cancel_num IS '执行失败-日志数量'; +COMMENT ON COLUMN sj_job_summary.cancel_reason IS '失败原因'; +COMMENT ON COLUMN sj_job_summary.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_job_summary.update_dt IS '修改时间'; +COMMENT ON TABLE sj_job_summary IS 'DashBoard_Job'; + +-- sj_retry_summary +CREATE TABLE sj_retry_summary +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name varchar(64) NOT NULL DEFAULT '', + scene_name varchar(50) NOT NULL DEFAULT '', + trigger_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + running_num int NOT NULL DEFAULT 0, + finish_num int NOT NULL DEFAULT 0, + max_count_num int NOT NULL DEFAULT 0, + suspend_num int NOT NULL DEFAULT 0, + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX uk_sj_retry_summary_01 ON sj_retry_summary (namespace_id, group_name, scene_name, trigger_at); + +CREATE INDEX idx_sj_retry_summary_01 ON sj_retry_summary (trigger_at); + +COMMENT ON COLUMN sj_retry_summary.id IS '主键'; +COMMENT ON COLUMN sj_retry_summary.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_retry_summary.group_name IS '组名称'; +COMMENT ON COLUMN sj_retry_summary.scene_name IS '场景名称'; +COMMENT ON COLUMN sj_retry_summary.trigger_at IS '统计时间'; +COMMENT ON COLUMN sj_retry_summary.running_num IS '重试中-日志数量'; +COMMENT ON COLUMN sj_retry_summary.finish_num IS '重试完成-日志数量'; +COMMENT ON COLUMN sj_retry_summary.max_count_num IS '重试到达最大次数-日志数量'; +COMMENT ON COLUMN sj_retry_summary.suspend_num IS '暂停重试-日志数量'; +COMMENT ON COLUMN sj_retry_summary.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_retry_summary.update_dt IS '修改时间'; +COMMENT ON TABLE sj_retry_summary IS 'DashBoard_Retry'; + +-- sj_workflow +CREATE TABLE sj_workflow +( + id bigserial PRIMARY KEY, + workflow_name varchar(64) NOT NULL, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name varchar(64) NOT NULL, + workflow_status smallint NOT NULL DEFAULT 1, + trigger_type smallint NOT NULL, + trigger_interval varchar(255) NOT NULL, + next_trigger_at bigint NOT NULL, + block_strategy smallint NOT NULL DEFAULT 1, + executor_timeout int NOT NULL DEFAULT 0, + description varchar(256) NOT NULL DEFAULT '', + flow_info text NULL DEFAULT NULL, + wf_context text NULL DEFAULT NULL, + bucket_index int NOT NULL DEFAULT 0, + version int NOT NULL, + ext_attrs varchar(256) NULL DEFAULT '', + deleted smallint NOT NULL DEFAULT 0, + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_sj_workflow_01 ON sj_workflow (create_dt); +CREATE INDEX idx_sj_workflow_02 ON sj_workflow (namespace_id, group_name); + +COMMENT ON COLUMN sj_workflow.id IS '主键'; +COMMENT ON COLUMN sj_workflow.workflow_name IS '工作流名称'; +COMMENT ON COLUMN sj_workflow.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_workflow.group_name IS '组名称'; +COMMENT ON COLUMN sj_workflow.workflow_status IS '工作流状态 0、关闭、1、开启'; +COMMENT ON COLUMN sj_workflow.trigger_type IS '触发类型 1.CRON 表达式 2. 固定时间'; +COMMENT ON COLUMN sj_workflow.trigger_interval IS '间隔时长'; +COMMENT ON COLUMN sj_workflow.next_trigger_at IS '下次触发时间'; +COMMENT ON COLUMN sj_workflow.block_strategy IS '阻塞策略 1、丢弃 2、覆盖 3、并行'; +COMMENT ON COLUMN sj_workflow.executor_timeout IS '任务执行超时时间,单位秒'; +COMMENT ON COLUMN sj_workflow.description IS '描述'; +COMMENT ON COLUMN sj_workflow.flow_info IS '流程信息'; +COMMENT ON COLUMN sj_workflow.wf_context IS '上下文'; +COMMENT ON COLUMN sj_workflow.bucket_index IS 'bucket'; +COMMENT ON COLUMN sj_workflow.version IS '版本号'; +COMMENT ON COLUMN sj_workflow.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_workflow.deleted IS '逻辑删除 1、删除'; +COMMENT ON COLUMN sj_workflow.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_workflow.update_dt IS '修改时间'; +COMMENT ON TABLE sj_workflow IS '工作流'; + +-- sj_workflow_node +CREATE TABLE sj_workflow_node +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + node_name varchar(64) NOT NULL, + group_name varchar(64) NOT NULL, + job_id bigint NOT NULL, + workflow_id bigint NOT NULL, + node_type smallint NOT NULL DEFAULT 1, + expression_type smallint NOT NULL DEFAULT 0, + fail_strategy smallint NOT NULL DEFAULT 1, + workflow_node_status smallint NOT NULL DEFAULT 1, + priority_level int NOT NULL DEFAULT 1, + node_info text NULL DEFAULT NULL, + version int NOT NULL, + ext_attrs varchar(256) NULL DEFAULT '', + deleted smallint NOT NULL DEFAULT 0, + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_sj_workflow_node_01 ON sj_workflow_node (create_dt); +CREATE INDEX idx_sj_workflow_node_02 ON sj_workflow_node (namespace_id, group_name); + +COMMENT ON COLUMN sj_workflow_node.id IS '主键'; +COMMENT ON COLUMN sj_workflow_node.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_workflow_node.node_name IS '节点名称'; +COMMENT ON COLUMN sj_workflow_node.group_name IS '组名称'; +COMMENT ON COLUMN sj_workflow_node.job_id IS '任务信息id'; +COMMENT ON COLUMN sj_workflow_node.workflow_id IS '工作流ID'; +COMMENT ON COLUMN sj_workflow_node.node_type IS '1、任务节点 2、条件节点'; +COMMENT ON COLUMN sj_workflow_node.expression_type IS '1、SpEl、2、Aviator 3、QL'; +COMMENT ON COLUMN sj_workflow_node.fail_strategy IS '失败策略 1、跳过 2、阻塞'; +COMMENT ON COLUMN sj_workflow_node.workflow_node_status IS '工作流节点状态 0、关闭、1、开启'; +COMMENT ON COLUMN sj_workflow_node.priority_level IS '优先级'; +COMMENT ON COLUMN sj_workflow_node.node_info IS '节点信息 '; +COMMENT ON COLUMN sj_workflow_node.version IS '版本号'; +COMMENT ON COLUMN sj_workflow_node.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_workflow_node.deleted IS '逻辑删除 1、删除'; +COMMENT ON COLUMN sj_workflow_node.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_workflow_node.update_dt IS '修改时间'; +COMMENT ON TABLE sj_workflow_node IS '工作流节点'; + +-- sj_workflow_task_batch +CREATE TABLE sj_workflow_task_batch +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name varchar(64) NOT NULL, + workflow_id bigint NOT NULL, + task_batch_status smallint NOT NULL DEFAULT 0, + operation_reason smallint NOT NULL DEFAULT 0, + flow_info text NULL DEFAULT NULL, + wf_context text NULL DEFAULT NULL, + execution_at bigint NOT NULL DEFAULT 0, + ext_attrs varchar(256) NULL DEFAULT '', + version int NOT NULL DEFAULT 1, + deleted smallint NOT NULL DEFAULT 0, + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_sj_workflow_task_batch_01 ON sj_workflow_task_batch (workflow_id, task_batch_status); +CREATE INDEX idx_sj_workflow_task_batch_02 ON sj_workflow_task_batch (create_dt); +CREATE INDEX idx_sj_workflow_task_batch_03 ON sj_workflow_task_batch (namespace_id, group_name); + +COMMENT ON COLUMN sj_workflow_task_batch.id IS '主键'; +COMMENT ON COLUMN sj_workflow_task_batch.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_workflow_task_batch.group_name IS '组名称'; +COMMENT ON COLUMN sj_workflow_task_batch.workflow_id IS '工作流任务id'; +COMMENT ON COLUMN sj_workflow_task_batch.task_batch_status IS '任务批次状态 0、失败 1、成功'; +COMMENT ON COLUMN sj_workflow_task_batch.operation_reason IS '操作原因'; +COMMENT ON COLUMN sj_workflow_task_batch.flow_info IS '流程信息'; +COMMENT ON COLUMN sj_workflow_task_batch.wf_context IS '全局上下文'; +COMMENT ON COLUMN sj_workflow_task_batch.execution_at IS '任务执行时间'; +COMMENT ON COLUMN sj_workflow_task_batch.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_workflow_task_batch.version IS '版本号'; +COMMENT ON COLUMN sj_workflow_task_batch.deleted IS '逻辑删除 1、删除'; +COMMENT ON COLUMN sj_workflow_task_batch.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_workflow_task_batch.update_dt IS '修改时间'; +COMMENT ON TABLE sj_workflow_task_batch IS '工作流批次'; \ No newline at end of file diff --git a/continew-admin-extension/continew-admin-job-server/src/main/resources/logback-spring.xml b/continew-admin-extension/continew-admin-job-server/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..8a911d2 --- /dev/null +++ b/continew-admin-extension/continew-admin-job-server/src/main/resources/logback-spring.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + ${CONSOLE_LOG_PATTERN} + ${LOG_CHARSET} + + + + + + + ${FILE_LOG_PATTERN} + ${LOG_CHARSET} + + + + + + + ${LOG_PATH}/${APP_NAME}.log + + + + ${LOG_PATH}/%d{yyyy-MM-dd}/${APP_NAME}.%d{yyyy-MM-dd}.%i.log.gz + + ${FILE_MAX_SIZE} + + ${FILE_MAX_HISTORY} + + + ${FILE_LOG_PATTERN} + ${LOG_CHARSET} + + + + + + + 0 + + 512 + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/continew-admin-extension/pom.xml b/continew-admin-extension/pom.xml new file mode 100644 index 0000000..486ac55 --- /dev/null +++ b/continew-admin-extension/pom.xml @@ -0,0 +1,18 @@ + + 4.0.0 + + top.continew + continew-admin + ${revision} + + + continew-admin-extension + pom + 扩展模块(存放其他扩展模块) + + + + continew-admin-job-server + + diff --git a/continew-admin-plugins/continew-admin-generator/pom.xml b/continew-admin-plugins/continew-admin-generator/pom.xml new file mode 100644 index 0000000..5d2b488 --- /dev/null +++ b/continew-admin-plugins/continew-admin-generator/pom.xml @@ -0,0 +1,14 @@ + + + 4.0.0 + + top.continew + continew-admin-plugins + ${revision} + + + continew-admin-generator + 代码生成器插件 + \ No newline at end of file diff --git a/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/config/properties/GeneratorProperties.java b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/config/properties/GeneratorProperties.java new file mode 100644 index 0000000..286f1cc --- /dev/null +++ b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/config/properties/GeneratorProperties.java @@ -0,0 +1,70 @@ +package top.continew.admin.generator.config.properties; + +import cn.hutool.core.io.file.FileNameUtil; +import cn.hutool.core.map.MapUtil; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; +import top.continew.starter.data.core.enums.DatabaseType; + +import java.util.List; +import java.util.Map; + +/** + * 代码生成器配置属性 + * + * @author Charles7c + * @since 2023/8/5 11:08 + */ +@Data +@Component +@ConfigurationProperties(prefix = "generator") +public class GeneratorProperties { + + /** + * 排除数据表(哪些数据表不展示在代码生成中) + */ + private String[] excludeTables = new String[0]; + + /** + * 类型映射 + */ + private Map>> typeMappings = MapUtil.newHashMap(); + + /** + * 模板配置 + */ + private Map templateConfigs = MapUtil.newHashMap(true); + + /** + * 模板配置 + */ + @Data + public static class TemplateConfig { + + /** + * 模板路径 + */ + private String templatePath; + + /** + * 包名称 + */ + private String packageName; + + /** + * 排除字段 + */ + private String[] excludeFields; + + /** + * 扩展名 + */ + private String extension = FileNameUtil.EXT_JAVA; + + /** + * 是否为后端模板 + */ + private boolean backend = true; + } +} diff --git a/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/enums/FormTypeEnum.java b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/enums/FormTypeEnum.java new file mode 100644 index 0000000..f2e50c1 --- /dev/null +++ b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/enums/FormTypeEnum.java @@ -0,0 +1,75 @@ +package top.continew.admin.generator.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import top.continew.starter.core.enums.BaseEnum; + +/** + * 表单类型枚举 + * + * @author Charles7c + * @since 2023/8/6 10:49 + */ +@Getter +@RequiredArgsConstructor +public enum FormTypeEnum implements BaseEnum { + + /** + * 输入框 + */ + INPUT(1, "输入框"), + + /** + * 数字输入框 + */ + INPUT_NUMBER(2, "数字输入框"), + + /** + * 密码输入框 + */ + INPUT_PASSWORD(3, "密码输入框"), + + /** + * 下拉框 + */ + SELECT(4, "下拉框"), + + /** + * 单选框 + */ + RADIO(5, "单选框"), + + /** + * 开关 + */ + SWITCH(6, "开关"), + + /** + * 复选框 + */ + CHECK_BOX(7, "复选框"), + + /** + * 文本域 + */ + TEXT_AREA(8, "文本域"), + + /** + * 日期时间框 + */ + DATE_TIME(9, "日期时间框"), + + /** + * 日期框 + */ + DATE(10, "日期框"), + + /** + * 树形选择 + */ + TREE_SELECT(11, "树选择"),; + + private final Integer value; + private final String description; +} diff --git a/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/enums/QueryTypeEnum.java b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/enums/QueryTypeEnum.java new file mode 100644 index 0000000..9f6314e --- /dev/null +++ b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/enums/QueryTypeEnum.java @@ -0,0 +1,90 @@ +package top.continew.admin.generator.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import top.continew.starter.core.enums.BaseEnum; + +/** + * 查询类型枚举 + * + * @author Charles7c + * @since 2023/8/6 10:49 + */ +@Getter +@RequiredArgsConstructor +public enum QueryTypeEnum implements BaseEnum { + + /** + * 等于 =,例如:WHERE age = 18 + */ + EQ(1, "="), + + /** + * 不等于 !=,例如:WHERE age != 18 + */ + NE(2, "!="), + + /** + * 大于 >,例如:WHERE age > 18 + */ + GT(3, ">"), + + /** + * 大于等于 >= ,例如:WHERE age >= 18 + */ + GE(4, ">="), + + /** + * 小于 <,例如:WHERE age < 18 + */ + LT(5, "<"), + + /** + * 小于等于 <=,例如:WHERE age <= 18 + */ + LE(6, "<="), + + /** + * 范围查询,例如:WHERE age BETWEEN 10 AND 18 + */ + BETWEEN(7, "BETWEEN"), + + /** + * LIKE '%值%',例如:WHERE nickname LIKE '%s%' + */ + LIKE(8, "LIKE '%s%'"), + + /** + * LIKE '%值',例如:WHERE nickname LIKE '%s' + */ + LIKE_LEFT(9, "LIKE '%s'"), + + /** + * LIKE '值%',例如:WHERE nickname LIKE 's%' + */ + LIKE_RIGHT(10, "LIKE 's%'"), + + /** + * 包含查询,例如:WHERE age IN (10, 20, 30) + */ + IN(11, "IN"), + + /** + * 不包含查询,例如:WHERE age NOT IN (20, 30) + */ + NOT_IN(12, "NOT IN"), + + /** + * 空查询,例如:WHERE email IS NULL + */ + IS_NULL(13, "IS NULL"), + + /** + * 非空查询,例如:WHERE email IS NOT NULL + */ + IS_NOT_NULL(14, "IS NOT NULL"),; + + private final Integer value; + private final String description; +} diff --git a/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/mapper/FieldConfigMapper.java b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/mapper/FieldConfigMapper.java new file mode 100644 index 0000000..cb1b794 --- /dev/null +++ b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/mapper/FieldConfigMapper.java @@ -0,0 +1,26 @@ +package top.continew.admin.generator.mapper; + +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import top.continew.admin.generator.model.entity.FieldConfigDO; +import top.continew.starter.data.mp.base.BaseMapper; + +import java.util.List; + +/** + * 字段配置 Mapper + * + * @author Charles7c + * @since 2023/4/12 23:56 + */ +public interface FieldConfigMapper extends BaseMapper { + + /** + * 根据表名称查询 + * + * @param tableName 表名称 + * @return 字段配置信息 + */ + @Select("SELECT * FROM gen_field_config WHERE table_name = #{tableName} ORDER BY field_sort ASC") + List selectListByTableName(@Param("tableName") String tableName); +} diff --git a/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/mapper/GenConfigMapper.java b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/mapper/GenConfigMapper.java new file mode 100644 index 0000000..32655a9 --- /dev/null +++ b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/mapper/GenConfigMapper.java @@ -0,0 +1,13 @@ +package top.continew.admin.generator.mapper; + +import top.continew.admin.generator.model.entity.GenConfigDO; +import top.continew.starter.data.mp.base.BaseMapper; + +/** + * 生成配置 Mapper + * + * @author Charles7c + * @since 2023/4/12 23:56 + */ +public interface GenConfigMapper extends BaseMapper { +} diff --git a/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/model/entity/FieldConfigDO.java b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/model/entity/FieldConfigDO.java new file mode 100644 index 0000000..775365b --- /dev/null +++ b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/model/entity/FieldConfigDO.java @@ -0,0 +1,162 @@ +package top.continew.admin.generator.model.entity; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; +import cn.hutool.db.meta.Column; +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import top.continew.admin.generator.enums.FormTypeEnum; +import top.continew.admin.generator.enums.QueryTypeEnum; +import top.continew.starter.core.constant.StringConstants; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 字段配置实体 + * + * @author Charles7c + * @since 2023/4/12 20:21 + */ +@Data +@NoArgsConstructor +@TableName("gen_field_config") +@Schema(description = "字段配置信息") +public class FieldConfigDO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 表名称 + */ + @Schema(description = "表名称", example = "sys_user") + @NotBlank(message = "表名称不能为空") + private String tableName; + + /** + * 列名称 + */ + @Schema(description = "列名称", example = "nickname") + @NotBlank(message = "列名称不能为空") + private String columnName; + + /** + * 列类型 + */ + @Schema(description = "列类型", example = "varchar") + @NotBlank(message = "列类型不能为空") + private String columnType; + + /** + * 列大小 + */ + @Schema(description = "列大小", example = "255") + private String columnSize; + + /** + * 字段名称 + */ + @Schema(description = "字段名称", example = "nickname") + @NotBlank(message = "字段名称不能为空") + private String fieldName; + + /** + * 字段类型 + */ + @Schema(description = "字段类型", example = "String") + @NotBlank(message = "字段类型不能为空") + private String fieldType; + + /** + * 字段排序 + */ + @Schema(description = "字段排序", example = "字段排序") + @NotNull(message = "字段排序不能为空") + private Integer fieldSort; + + /** + * 注释 + */ + @Schema(description = "注释", example = "昵称") + private String comment; + + /** + * 是否必填 + */ + @Schema(description = "是否必填", example = "true") + private Boolean isRequired; + + /** + * 是否在列表中显示 + */ + @Schema(description = "是否在列表中显示", example = "true") + private Boolean showInList; + + /** + * 是否在表单中显示 + */ + @Schema(description = "是否在表单中显示", example = "true") + private Boolean showInForm; + + /** + * 是否在查询中显示 + */ + @Schema(description = "是否在查询中显示", example = "true") + private Boolean showInQuery; + + /** + * 表单类型 + */ + @Schema(description = "表单类型", example = "1") + private FormTypeEnum formType; + + /** + * 查询方式 + */ + @Schema(description = "查询方式", example = "1") + private QueryTypeEnum queryType; + + /** + * 字典编码 + */ + @Schema(description = "字典编码", example = "notice_type") + private String dictCode; + + /** + * 创建时间 + */ + @Schema(description = "创建时间", example = "2023-08-08 08:08:08", type = "string") + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + public FieldConfigDO(@NonNull Column column) { + this.setTableName(column.getTableName()); + this.setColumnName(column.getName()); + this.setColumnType(column.getTypeName()); + this.setColumnSize(Convert.toStr(column.getSize())); + this.setComment(column.getComment()); + this.setIsRequired(!column.isPk() && !column.isNullable()); + this.setShowInList(true); + this.setShowInForm(this.getIsRequired()); + this.setShowInQuery(this.getIsRequired()); + } + + public void setColumnName(String columnName) { + this.columnName = columnName; + this.fieldName = StrUtil.toCamelCase(this.columnName); + } + + public void setColumnType(String columnType) { + String[] arr = StrUtil.splitToArray(columnType, StringConstants.SPACE); + this.columnType = arr.length > 1 ? arr[0].toLowerCase() : columnType.toLowerCase(); + } +} diff --git a/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/model/entity/GenConfigDO.java b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/model/entity/GenConfigDO.java new file mode 100644 index 0000000..aff020a --- /dev/null +++ b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/model/entity/GenConfigDO.java @@ -0,0 +1,114 @@ +package top.continew.admin.generator.model.entity; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.annotation.*; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; +import top.continew.admin.common.constant.RegexConstants; +import top.continew.starter.core.constant.StringConstants; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 生成配置实体 + * + * @author Charles7c + * @since 2023/4/12 20:21 + */ +@Data +@NoArgsConstructor +@TableName("gen_config") +@Schema(description = "生成配置信息") +public class GenConfigDO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 表名称 + */ + @Schema(description = "表名称", example = "sys_user") + @TableId(type = IdType.INPUT) + @NotBlank(message = "表名称不能为空") + private String tableName; + + /** + * 模块名称 + */ + @Schema(description = "模块名称", example = "continew-admin-system") + @NotBlank(message = "模块名称不能为空") + @Length(max = 60, message = "模块名称不能超过 {max} 个字符") + private String moduleName; + + /** + * 包名称 + */ + @Schema(description = "包名称", example = "top.continew.admin.system") + @NotBlank(message = "包名称不能为空") + @Pattern(regexp = RegexConstants.PACKAGE_NAME, message = "包名称格式错误") + @Length(max = 60, message = "包名称不能超过 {max} 个字符") + private String packageName; + + /** + * 业务名称 + */ + @Schema(description = "业务名称", example = "用户") + @NotBlank(message = "业务名称不能为空") + @Length(max = 50, message = "业务名称不能超过 {max} 个字符") + private String businessName; + + /** + * 作者 + */ + @Schema(description = "作者", example = "Charles7c") + @NotBlank(message = "作者名称不能为空") + @Length(max = 100, message = "作者名称不能超过 {max} 个字符") + private String author; + + /** + * 表前缀 + */ + @Schema(description = "表前缀", example = "sys_") + private String tablePrefix; + + /** + * 是否覆盖 + */ + @Schema(description = "是否覆盖", example = "false") + @NotNull(message = "是否覆盖不能为空") + private Boolean isOverride; + + /** + * 创建时间 + */ + @Schema(description = "创建时间", example = "2023-08-08 08:08:08", type = "string") + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + /** + * 修改时间 + */ + @Schema(description = "修改时间", example = "2023-08-08 08:08:08", type = "string") + @TableField(fill = FieldFill.UPDATE) + private LocalDateTime updateTime; + + public GenConfigDO(String tableName) { + this.setTableName(tableName); + } + + public void setTableName(String tableName) { + this.tableName = tableName; + // 默认表前缀(sys_user -> sys_) + int underLineIndex = StrUtil.indexOf(tableName, StringConstants.C_UNDERLINE); + if (-1 != underLineIndex) { + this.tablePrefix = StrUtil.subPre(tableName, underLineIndex + 1); + } + } +} diff --git a/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/model/entity/InnerGenConfigDO.java b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/model/entity/InnerGenConfigDO.java new file mode 100644 index 0000000..544d0ad --- /dev/null +++ b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/model/entity/InnerGenConfigDO.java @@ -0,0 +1,116 @@ +package top.continew.admin.generator.model.entity; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.continew.starter.core.constant.StringConstants; +import top.continew.starter.core.util.StrUtils; + +import java.io.Serial; +import java.util.List; +import java.util.Set; + +/** + * 内部生成配置信息 + * + * @author zhangqcc + * @since 2024/8/30 19:35 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class InnerGenConfigDO extends GenConfigDO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 字段配置信息 + */ + private List fieldConfigs; + + /** + * 生成时间 + */ + private String datetime; + + /** + * API 模块名称 + */ + private String apiModuleName; + + /** + * API 名称 + */ + private String apiName; + + /** + * 类名 + */ + private String className; + + /** + * 类名前缀 + */ + private String classNamePrefix; + + /** + * 子包名称 + */ + private String subPackageName; + + /** + * 字典编码列表 + */ + private Set dictCodes; + + /** + * 是否包含必填字段 + */ + private boolean hasRequiredField; + + /** + * 是否包含字典字段 + */ + private boolean hasDictField; + + /** + * 是否包含 BigDecimal 字段 + */ + private boolean hasBigDecimalField; + + /** + * 是否包含 List 字段 + */ + private boolean hasListField; + + /** + * 是否包含 Time 包字段 + */ + private boolean hasTimeField; + + public InnerGenConfigDO() { + } + + public InnerGenConfigDO(GenConfigDO genConfig) { + BeanUtil.copyProperties(genConfig, this); + this.setDatetime(DateUtil.date().toString("yyyy/MM/dd HH:mm")); + this.setApiName(StrUtil.lowerFirst(this.getClassNamePrefix())); + } + + @Override + public void setPackageName(String packageName) { + super.setPackageName(packageName); + String realPackageName = this.getPackageName(); + this.setApiModuleName(StrUtil.subSuf(realPackageName, StrUtil + .lastIndexOfIgnoreCase(realPackageName, StringConstants.DOT) + 1)); + } + + public String getClassNamePrefix() { + String tableName = super.getTableName(); + String rawClassName = StrUtils.blankToDefault(super.getTablePrefix(), tableName, prefix -> StrUtil + .removePrefix(tableName, prefix)); + return StrUtil.upperFirst(StrUtil.toCamelCase(rawClassName)); + } +} diff --git a/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/model/query/TableQuery.java b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/model/query/TableQuery.java new file mode 100644 index 0000000..6160624 --- /dev/null +++ b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/model/query/TableQuery.java @@ -0,0 +1,27 @@ +package top.continew.admin.generator.model.query; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 表信息查询条件 + * + * @author Charles7c + * @since 2023/4/12 20:21 + */ +@Data +@Schema(description = "表信息查询条件") +public class TableQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 表名称 + */ + @Schema(description = "表名称", example = "sys_user") + private String tableName; +} diff --git a/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/model/req/GenConfigReq.java b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/model/req/GenConfigReq.java new file mode 100644 index 0000000..1dc872e --- /dev/null +++ b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/model/req/GenConfigReq.java @@ -0,0 +1,47 @@ +package top.continew.admin.generator.model.req; + +import java.io.Serial; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +import lombok.Data; + +import io.swagger.v3.oas.annotations.media.Schema; + +import top.continew.admin.generator.model.entity.FieldConfigDO; +import top.continew.admin.generator.model.entity.GenConfigDO; + +/** + * 代码生成配置信息 + * + * @author Charles7c + * @since 2023/8/8 20:40 + */ +@Data +@Schema(description = "代码生成配置信息") +public class GenConfigReq implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 字段配置信息 + */ + @Valid + @Schema(description = "字段配置信息") + @NotEmpty(message = "字段配置不能为空") + private List fieldConfigs = new ArrayList<>(); + + /** + * 生成配置信息 + */ + @Valid + @Schema(description = "生成配置信息") + @NotNull(message = "生成配置不能为空") + private GenConfigDO genConfig; +} diff --git a/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/model/resp/GeneratePreviewResp.java b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/model/resp/GeneratePreviewResp.java new file mode 100644 index 0000000..ac55371 --- /dev/null +++ b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/model/resp/GeneratePreviewResp.java @@ -0,0 +1,44 @@ +package top.continew.admin.generator.model.resp; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 生成预览信息 + * + * @author Charles7c + * @since 2023/12/19 21:34 + */ +@Data +@Schema(description = "生成预览信息") +public class GeneratePreviewResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "生成的文件路径", example = "continew-admin\\continew-admin\\continew-admin-generator\\src\\main\\java\\top\\continew\\admin\\generator\\service") + private String path; + + /** + * 文件名 + */ + @Schema(description = "文件名", example = "UserController.java") + private String fileName; + + /** + * 内容 + */ + @Schema(description = "内容", example = "public class UserController {...}") + private String content; + + /** + * 是否为后端代码 + */ + @Schema(hidden = true) + @JsonIgnore + private boolean backend; +} diff --git a/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/model/resp/TableResp.java b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/model/resp/TableResp.java new file mode 100644 index 0000000..b4457a2 --- /dev/null +++ b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/model/resp/TableResp.java @@ -0,0 +1,69 @@ +package top.continew.admin.generator.model.resp; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; + +import lombok.Data; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * 表信息 + * + * @author Charles7c + * @since 2023/4/12 20:21 + */ +@Data +@Schema(description = "表信息") +public class TableResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 表名称 + */ + @Schema(description = "表名称", example = "sys_user") + private String tableName; + + /** + * 描述 + */ + @Schema(description = "描述", example = "用户表") + private String comment; + + /** + * 存储引擎 + */ + @Schema(description = "存储引擎", example = "InnoDB") + private String engine; + + /** + * 字符集 + */ + @Schema(description = "字符集", example = "utf8mb4_general_ci") + private String charset; + + /** + * 创建时间 + */ + @Schema(description = "创建时间", example = "2023-08-08 08:08:08", type = "string") + private LocalDateTime createTime; + + /** + * 是否已配置 + */ + @Schema(description = "是否已配置", example = "true") + private Boolean isConfiged; + + /** + * 是否禁用 + */ + @Schema(description = "是否禁用", example = "true") + private Boolean disabled; + + public Boolean getDisabled() { + return !isConfiged; + } +} diff --git a/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/service/GeneratorService.java b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/service/GeneratorService.java new file mode 100644 index 0000000..c245537 --- /dev/null +++ b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/service/GeneratorService.java @@ -0,0 +1,75 @@ +package top.continew.admin.generator.service; + +import jakarta.servlet.http.HttpServletResponse; +import top.continew.admin.generator.model.entity.FieldConfigDO; +import top.continew.admin.generator.model.entity.GenConfigDO; +import top.continew.admin.generator.model.query.TableQuery; +import top.continew.admin.generator.model.req.GenConfigReq; +import top.continew.admin.generator.model.resp.GeneratePreviewResp; +import top.continew.admin.generator.model.resp.TableResp; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; + +import java.sql.SQLException; +import java.util.List; + +/** + * 代码生成业务接口 + * + * @author Charles7c + * @since 2023/4/12 23:57 + */ +public interface GeneratorService { + + /** + * 分页查询表信息列表 + * + * @param query 查询条件 + * @param pageQuery 分页查询条件 + * @return 表信息分页列表 + * @throws SQLException / + */ + PageResp pageTable(TableQuery query, PageQuery pageQuery) throws SQLException; + + /** + * 查询生成配置信息 + * + * @param tableName 表名称 + * @return 生成配置信息 + * @throws SQLException / + */ + GenConfigDO getGenConfig(String tableName) throws SQLException; + + /** + * 查询字段配置列表 + * + * @param tableName 表名称 + * @param requireSync 是否需要同步 + * @return 字段配置列表 + */ + List listFieldConfig(String tableName, Boolean requireSync); + + /** + * 保存代码生成配置信息 + * + * @param req 代码生成配置信息 + * @param tableName 表名称 + */ + void saveConfig(GenConfigReq req, String tableName); + + /** + * 生成预览 + * + * @param tableName 表名称 + * @return 预览信息 + */ + List preview(String tableName); + + /** + * 生成代码 + * + * @param tableNames 表明层 + * @param response 响应对象 + */ + void generate(List tableNames, HttpServletResponse response); +} diff --git a/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/service/impl/GeneratorServiceImpl.java b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/service/impl/GeneratorServiceImpl.java new file mode 100644 index 0000000..042863f --- /dev/null +++ b/continew-admin-plugins/continew-admin-generator/src/main/java/top/continew/admin/generator/service/impl/GeneratorServiceImpl.java @@ -0,0 +1,367 @@ +package top.continew.admin.generator.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.ClassUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.ZipUtil; +import cn.hutool.db.meta.Column; +import cn.hutool.system.SystemUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import top.continew.admin.generator.config.properties.GeneratorProperties; +import top.continew.admin.generator.enums.FormTypeEnum; +import top.continew.admin.generator.enums.QueryTypeEnum; +import top.continew.admin.generator.mapper.FieldConfigMapper; +import top.continew.admin.generator.mapper.GenConfigMapper; +import top.continew.admin.generator.model.entity.FieldConfigDO; +import top.continew.admin.generator.model.entity.GenConfigDO; +import top.continew.admin.generator.model.entity.InnerGenConfigDO; +import top.continew.admin.generator.model.query.TableQuery; +import top.continew.admin.generator.model.req.GenConfigReq; +import top.continew.admin.generator.model.resp.GeneratePreviewResp; +import top.continew.admin.generator.model.resp.TableResp; +import top.continew.admin.generator.service.GeneratorService; +import top.continew.starter.core.autoconfigure.project.ProjectProperties; +import top.continew.starter.core.constant.StringConstants; +import top.continew.starter.core.exception.BusinessException; +import top.continew.starter.core.util.TemplateUtils; +import top.continew.starter.core.util.validate.CheckUtils; +import top.continew.starter.data.core.enums.DatabaseType; +import top.continew.starter.data.core.util.MetaUtils; +import top.continew.starter.data.core.util.Table; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.web.util.FileUploadUtils; + +import javax.sql.DataSource; +import java.io.File; +import java.sql.SQLException; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 代码生成业务实现 + * + * @author Charles7c + * @since 2023/4/12 23:58 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class GeneratorServiceImpl implements GeneratorService { + + private final DataSource dataSource; + private final GeneratorProperties generatorProperties; + private final ProjectProperties projectProperties; + private final FieldConfigMapper fieldConfigMapper; + private final GenConfigMapper genConfigMapper; + private static final List TIME_PACKAGE_CLASS = Arrays.asList("LocalDate", "LocalTime", "LocalDateTime"); + + @Override + public PageResp pageTable(TableQuery query, PageQuery pageQuery) throws SQLException { + List tableList = MetaUtils.getTables(dataSource); + String tableName = query.getTableName(); + if (StrUtil.isNotBlank(tableName)) { + tableList.removeIf(table -> !StrUtil.containsAnyIgnoreCase(table.getTableName(), tableName)); + } + tableList.removeIf(table -> StrUtil.equalsAnyIgnoreCase(table.getTableName(), generatorProperties + .getExcludeTables())); + CollUtil.sort(tableList, Comparator.comparing(Table::getCreateTime) + .thenComparing(table -> Optional.ofNullable(table.getUpdateTime()).orElse(table.getCreateTime())) + .reversed()); + List tableRespList = BeanUtil.copyToList(tableList, TableResp.class); + PageResp pageResp = PageResp.build(pageQuery.getPage(), pageQuery.getSize(), tableRespList); + for (TableResp tableResp : pageResp.getList()) { + long count = genConfigMapper.selectCount(Wrappers.lambdaQuery(GenConfigDO.class) + .eq(GenConfigDO::getTableName, tableResp.getTableName())); + tableResp.setIsConfiged(count > 0); + } + return pageResp; + } + + @Override + public GenConfigDO getGenConfig(String tableName) throws SQLException { + GenConfigDO genConfig = genConfigMapper.selectById(tableName); + if (null == genConfig) { + genConfig = new GenConfigDO(tableName); + // 默认包名(当前包名) + String packageName = ClassUtil.getPackage(GeneratorService.class); + genConfig.setPackageName(StrUtil.subBefore(packageName, StringConstants.DOT, true)); + // 默认业务名(表注释) + List
tableList = MetaUtils.getTables(dataSource, tableName); + if (CollUtil.isNotEmpty(tableList)) { + Table table = tableList.get(0); + genConfig.setBusinessName(StrUtil.replace(table.getComment(), "表", StringConstants.EMPTY)); + } + // 默认作者名称(上次保存使用的作者名称) + GenConfigDO lastGenConfig = genConfigMapper.selectOne(Wrappers.lambdaQuery(GenConfigDO.class) + .orderByDesc(GenConfigDO::getCreateTime) + .last("LIMIT 1")); + if (null != lastGenConfig) { + genConfig.setAuthor(lastGenConfig.getAuthor()); + } + } + return genConfig; + } + + @Override + public List listFieldConfig(String tableName, Boolean requireSync) { + List fieldConfigList = fieldConfigMapper.selectListByTableName(tableName); + if (CollUtil.isNotEmpty(fieldConfigList) && Boolean.FALSE.equals(requireSync)) { + return fieldConfigList; + } + List latestFieldConfigList = new ArrayList<>(); + // 获取最新数据表列信息 + Collection columnList = MetaUtils.getColumns(dataSource, tableName); + // 获取数据库对应的类型映射配置 + DatabaseType databaseType = MetaUtils.getDatabaseType(dataSource); + Map> typeMappingMap = generatorProperties.getTypeMappings().get(databaseType); + CheckUtils.throwIfEmpty(typeMappingMap, "请先配置对应数据库的类型映射"); + Set>> typeMappingEntrySet = typeMappingMap.entrySet(); + // 新增或更新字段配置 + Map fieldConfigMap = fieldConfigList.stream() + .collect(Collectors.toMap(FieldConfigDO::getColumnName, Function.identity(), (key1, key2) -> key2)); + int i = 1; + for (Column column : columnList) { + FieldConfigDO fieldConfig = Optional.ofNullable(fieldConfigMap.get(column.getName())) + .orElseGet(() -> new FieldConfigDO(column)); + // 更新已有字段配置 + if (null != fieldConfig.getCreateTime()) { + fieldConfig.setColumnType(column.getTypeName()); + fieldConfig.setColumnSize(Convert.toStr(column.getSize())); + } + String fieldType = typeMappingEntrySet.stream() + .filter(entry -> entry.getValue().contains(fieldConfig.getColumnType())) + .map(Map.Entry::getKey) + .findFirst() + .orElse(null); + fieldConfig.setFieldType(fieldType); + fieldConfig.setFieldSort(i++); + latestFieldConfigList.add(fieldConfig); + } + return latestFieldConfigList; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void saveConfig(GenConfigReq req, String tableName) { + // 保存字段配置(先删除再保存) + fieldConfigMapper.delete(Wrappers.lambdaQuery(FieldConfigDO.class).eq(FieldConfigDO::getTableName, tableName)); + List fieldConfigList = req.getFieldConfigs(); + for (int i = 0; i < fieldConfigList.size(); i++) { + FieldConfigDO fieldConfig = fieldConfigList.get(i); + // 重新设置排序 + fieldConfig.setFieldSort(i + 1); + if (Boolean.TRUE.equals(fieldConfig.getShowInForm())) { + fieldConfig.setFormType(ObjectUtil.defaultIfNull(fieldConfig.getFormType(), FormTypeEnum.INPUT)); + } else { + // 在表单中不显示,不需要设置必填 + fieldConfig.setIsRequired(false); + } + if (Boolean.TRUE.equals(fieldConfig.getShowInQuery())) { + fieldConfig.setFormType(ObjectUtil.defaultIfNull(fieldConfig.getFormType(), FormTypeEnum.INPUT)); + fieldConfig.setQueryType(ObjectUtil.defaultIfNull(fieldConfig.getQueryType(), QueryTypeEnum.EQ)); + } else { + // 在查询中不显示,不需要设置查询方式 + fieldConfig.setQueryType(null); + } + // 既不在表单也不在查询中显示,不需要设置表单类型 + if (Boolean.FALSE.equals(fieldConfig.getShowInForm()) && Boolean.FALSE.equals(fieldConfig + .getShowInQuery())) { + fieldConfig.setFormType(null); + } + fieldConfig.setTableName(tableName); + } + fieldConfigMapper.insertBatch(fieldConfigList); + // 保存或更新生成配置信息 + GenConfigDO newGenConfig = req.getGenConfig(); + GenConfigDO oldGenConfig = genConfigMapper.selectById(tableName); + if (null != oldGenConfig) { + BeanUtil.copyProperties(newGenConfig, oldGenConfig); + genConfigMapper.updateById(oldGenConfig); + } else { + genConfigMapper.insert(newGenConfig); + } + } + + @Override + public List preview(String tableName) { + List generatePreviewList = new ArrayList<>(); + // 初始化配置 + GenConfigDO genConfig = genConfigMapper.selectById(tableName); + CheckUtils.throwIfNull(genConfig, "请先进行数据表 [{}] 生成配置", tableName); + List fieldConfigList = fieldConfigMapper.selectListByTableName(tableName); + CheckUtils.throwIfEmpty(fieldConfigList, "请先进行数据表 [{}] 字段配置", tableName); + InnerGenConfigDO innerGenConfig = new InnerGenConfigDO(genConfig); + // 渲染代码 + String classNamePrefix = innerGenConfig.getClassNamePrefix(); + Map templateConfigMap = generatorProperties.getTemplateConfigs(); + for (Map.Entry templateConfigEntry : templateConfigMap.entrySet()) { + GeneratorProperties.TemplateConfig templateConfig = templateConfigEntry.getValue(); + // 移除需要忽略的字段 + innerGenConfig.setFieldConfigs(fieldConfigList.stream() + .filter(fieldConfig -> !StrUtil.equalsAny(fieldConfig.getFieldName(), templateConfig + .getExcludeFields())) + .toList()); + // 预处理配置 + this.pretreatment(innerGenConfig); + // 处理其他配置 + innerGenConfig.setSubPackageName(templateConfig.getPackageName()); + String classNameSuffix = templateConfigEntry.getKey(); + String className = classNamePrefix + classNameSuffix; + innerGenConfig.setClassName(className); + boolean isBackend = templateConfig.isBackend(); + String extension = templateConfig.getExtension(); + GeneratePreviewResp generatePreview = new GeneratePreviewResp(); + generatePreview.setBackend(isBackend); + generatePreviewList.add(generatePreview); + String fileName = className + extension; + if (!isBackend) { + fileName = ".vue".equals(extension) && "index".equals(classNameSuffix) + ? "index.vue" + : this.getFrontendFileName(classNamePrefix, className, extension); + } + generatePreview.setFileName(fileName); + generatePreview.setContent(TemplateUtils.render(templateConfig.getTemplatePath(), BeanUtil + .beanToMap(innerGenConfig))); + this.setPreviewPath(generatePreview, innerGenConfig, templateConfig); + } + return generatePreviewList; + } + + private void setPreviewPath(GeneratePreviewResp generatePreview, + InnerGenConfigDO genConfig, + GeneratorProperties.TemplateConfig templateConfig) { + // 获取前后端基础路径 + String backendBasicPackagePath = this.buildBackendBasicPackagePath(genConfig); + String frontendBasicPackagePath = String.join(File.separator, projectProperties.getAppName(), projectProperties + .getAppName() + "-ui"); + String packagePath; + if (generatePreview.isBackend()) { + // 例如:continew-admin/continew-system/src/main/java/top/continew/admin/system/service/impl + packagePath = String.join(File.separator, backendBasicPackagePath, templateConfig.getPackageName() + .replace(StringConstants.DOT, File.separator)); + } else { + // 例如:continew-admin/continew-admin-ui/src/views/system + packagePath = String.join(File.separator, frontendBasicPackagePath, templateConfig.getPackageName() + .replace(StringConstants.SLASH, File.separator), genConfig.getApiModuleName()); + // 例如:continew-admin/continew-admin-ui/src/views/system/user + packagePath = ".vue".equals(templateConfig.getExtension()) + ? packagePath + File.separator + StrUtil.lowerFirst(genConfig.getClassNamePrefix()) + : packagePath; + } + generatePreview.setPath(packagePath); + } + + @Override + public void generate(List tableNames, HttpServletResponse response) { + try { + String tempDir = SystemUtil.getUserInfo().getTempDir(); + // 删除旧代码 + FileUtil.del(tempDir + projectProperties.getAppName()); + tableNames.forEach(tableName -> { + // 初始化配置及数据 + List generatePreviewList = this.preview(tableName); + // 生成代码 + this.generateCode(generatePreviewList, genConfigMapper.selectById(tableName)); + }); + // 打包下载 + File tempDirFile = new File(tempDir, projectProperties.getAppName()); + String zipFilePath = tempDirFile.getPath() + jodd.io.ZipUtil.ZIP_EXT; + ZipUtil.zip(tempDirFile.getPath(), zipFilePath); + FileUploadUtils.download(response, new File(zipFilePath)); + } catch (Exception e) { + log.error("Generate code of table '{}' occurred an error. {}", tableNames, e.getMessage(), e); + throw new BusinessException("代码生成失败,请手动清理生成文件"); + } + } + + /** + * 生成代码 + * + * @param generatePreviewList 生成预览列表 + * @param genConfig 生成配置 + */ + private void generateCode(List generatePreviewList, GenConfigDO genConfig) { + for (GeneratePreviewResp generatePreview : generatePreviewList) { + // 后端:continew-admin/continew-system/src/main/java/top/continew/admin/system/service/impl/XxxServiceImpl.java + // 前端:continew-admin/continew-admin-ui/src/views/system/user/index.vue + File file = new File(SystemUtil.getUserInfo().getTempDir() + generatePreview.getPath(), generatePreview + .getFileName()); + // 如果已经存在,且不允许覆盖,则跳过 + if (!file.exists() || Boolean.TRUE.equals(genConfig.getIsOverride())) { + FileUtil.writeUtf8String(generatePreview.getContent(), file); + } + } + } + + /** + * 构建后端包路径 + * + * @param genConfig 生成配置 + * @return 后端包路径 + */ + private String buildBackendBasicPackagePath(GenConfigDO genConfig) { + // 例如:continew-admin/continew-system/src/main/java/top/continew/admin/system + return String.join(File.separator, projectProperties.getAppName(), projectProperties.getAppName(), genConfig + .getModuleName(), "src", "main", "java", genConfig.getPackageName() + .replace(StringConstants.DOT, File.separator)); + } + + /** + * 获取前端文件名 + * + * @param classNamePrefix 类名前缀 + * @param className 类名 + * @param extension 扩展名 + * @return 前端文件名 + */ + private String getFrontendFileName(String classNamePrefix, String className, String extension) { + return (".ts".equals(extension) ? StrUtil.lowerFirst(classNamePrefix) : className) + extension; + } + + /** + * 预处理生成配置 + * + * @param genConfig 生成配置 + */ + private void pretreatment(InnerGenConfigDO genConfig) { + List fieldConfigList = genConfig.getFieldConfigs(); + // 统计部分特殊字段特征 + Set dictCodeSet = new HashSet<>(); + for (FieldConfigDO fieldConfig : fieldConfigList) { + String fieldType = fieldConfig.getFieldType(); + // 必填项 + if (Boolean.TRUE.equals(fieldConfig.getIsRequired())) { + genConfig.setHasRequiredField(true); + } + // 数据类型 + if ("BigDecimal".equals(fieldType)) { + genConfig.setHasBigDecimalField(true); + } + if (TIME_PACKAGE_CLASS.contains(fieldType)) { + genConfig.setHasTimeField(true); + } + QueryTypeEnum queryType = fieldConfig.getQueryType(); + if (null != queryType && StrUtil.equalsAny(queryType.name(), QueryTypeEnum.IN.name(), QueryTypeEnum.NOT_IN + .name(), QueryTypeEnum.BETWEEN.name())) { + genConfig.setHasListField(true); + } + // 字典码 + if (StrUtil.isNotBlank(fieldConfig.getDictCode())) { + genConfig.setHasDictField(true); + dictCodeSet.add(fieldConfig.getDictCode()); + } + } + genConfig.setDictCodes(dictCodeSet); + } +} diff --git a/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/backend/Controller.ftl b/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/backend/Controller.ftl new file mode 100644 index 0000000..80b578e --- /dev/null +++ b/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/backend/Controller.ftl @@ -0,0 +1,26 @@ +package ${packageName}.${subPackageName}; + +import top.continew.starter.extension.crud.enums.Api; + +import io.swagger.v3.oas.annotations.tags.Tag; + +import org.springframework.web.bind.annotation.*; + +import top.continew.starter.extension.crud.annotation.CrudRequestMapping; +import top.continew.starter.extension.crud.controller.BaseController; +import ${packageName}.model.query.${classNamePrefix}Query; +import ${packageName}.model.req.${classNamePrefix}Req; +import ${packageName}.model.resp.${classNamePrefix}DetailResp; +import ${packageName}.model.resp.${classNamePrefix}Resp; +import ${packageName}.service.${classNamePrefix}Service; + +/** + * ${businessName}管理 API + * + * @author ${author} + * @since ${datetime} + */ +@Tag(name = "${businessName}管理 API") +@RestController +@CrudRequestMapping(value = "/${apiModuleName}/${apiName}", api = {Api.PAGE, Api.GET, Api.ADD, Api.UPDATE, Api.DELETE, Api.EXPORT}) +public class ${className} extends BaseController<${classNamePrefix}Service, ${classNamePrefix}Resp, ${classNamePrefix}DetailResp, ${classNamePrefix}Query, ${classNamePrefix}Req> {} \ No newline at end of file diff --git a/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/backend/DetailResp.ftl b/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/backend/DetailResp.ftl new file mode 100644 index 0000000..5f8dfc7 --- /dev/null +++ b/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/backend/DetailResp.ftl @@ -0,0 +1,44 @@ +package ${packageName}.${subPackageName}; + +import java.io.Serial; +<#if hasTimeField> +import java.time.*; + +<#if hasBigDecimalField> +import java.math.BigDecimal; + + +import lombok.Data; + +import io.swagger.v3.oas.annotations.media.Schema; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; + +import top.continew.starter.extension.crud.model.resp.BaseDetailResp; + +/** + * ${businessName}详情信息 + * + * @author ${author} + * @since ${datetime} + */ +@Data +@ExcelIgnoreUnannotated +@Schema(description = "${businessName}详情信息") +public class ${className} extends BaseDetailResp { + + @Serial + private static final long serialVersionUID = 1L; +<#if fieldConfigs??> + <#list fieldConfigs as fieldConfig> + + /** + * ${fieldConfig.comment} + */ + @Schema(description = "${fieldConfig.comment}") + @ExcelProperty(value = "${fieldConfig.comment}") + private ${fieldConfig.fieldType} ${fieldConfig.fieldName}; + + +} \ No newline at end of file diff --git a/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/backend/Entity.ftl b/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/backend/Entity.ftl new file mode 100644 index 0000000..f5707e9 --- /dev/null +++ b/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/backend/Entity.ftl @@ -0,0 +1,38 @@ +package ${packageName}.${subPackageName}; + +import java.io.Serial; +<#if hasTimeField> +import java.time.*; + +<#if hasBigDecimalField> +import java.math.BigDecimal; + + +import lombok.Data; + +import com.baomidou.mybatisplus.annotation.TableName; + +import top.continew.starter.extension.crud.model.entity.BaseDO; + +/** + * ${businessName}实体 + * + * @author ${author} + * @since ${datetime} + */ +@Data +@TableName("${tableName}") +public class ${className} extends BaseDO { + + @Serial + private static final long serialVersionUID = 1L; +<#if fieldConfigs??> + <#list fieldConfigs as fieldConfig> + + /** + * ${fieldConfig.comment} + */ + private ${fieldConfig.fieldType} ${fieldConfig.fieldName}; + + +} \ No newline at end of file diff --git a/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/backend/Mapper.ftl b/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/backend/Mapper.ftl new file mode 100644 index 0000000..f84e38e --- /dev/null +++ b/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/backend/Mapper.ftl @@ -0,0 +1,12 @@ +package ${packageName}.${subPackageName}; + +import top.continew.starter.data.mp.base.BaseMapper; +import ${packageName}.model.entity.${classNamePrefix}DO; + +/** +* ${businessName} Mapper +* +* @author ${author} +* @since ${datetime} +*/ +public interface ${className} extends BaseMapper<${classNamePrefix}DO> {} \ No newline at end of file diff --git a/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/backend/Query.ftl b/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/backend/Query.ftl new file mode 100644 index 0000000..ebb6b58 --- /dev/null +++ b/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/backend/Query.ftl @@ -0,0 +1,51 @@ +package ${packageName}.${subPackageName}; + +import java.io.Serial; +import java.io.Serializable; +<#if hasTimeField> +import java.time.*; + +<#if hasBigDecimalField> +import java.math.BigDecimal; + +<#if hasListField> +import java.util.List; + + +import lombok.Data; + +import io.swagger.v3.oas.annotations.media.Schema; + +import top.continew.starter.data.core.annotation.Query; +import top.continew.starter.data.core.enums.QueryType; + +/** + * ${businessName}查询条件 + * + * @author ${author} + * @since ${datetime} + */ +@Data +@Schema(description = "${businessName}查询条件") +public class ${className} implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; +<#if fieldConfigs??> + <#list fieldConfigs as fieldConfig> + <#if fieldConfig.showInQuery> + + /** + * ${fieldConfig.comment} + */ + @Schema(description = "${fieldConfig.comment}") + @Query(type = QueryType.${fieldConfig.queryType}) + <#if fieldConfig.queryType = 'IN' || fieldConfig.queryType = 'NOT_IN' || fieldConfig.queryType = 'BETWEEN'> + private List<${fieldConfig.fieldType}> ${fieldConfig.fieldName}; + <#else> + private ${fieldConfig.fieldType} ${fieldConfig.fieldName}; + + + + +} \ No newline at end of file diff --git a/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/backend/Req.ftl b/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/backend/Req.ftl new file mode 100644 index 0000000..8cc4dfa --- /dev/null +++ b/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/backend/Req.ftl @@ -0,0 +1,57 @@ +package ${packageName}.${subPackageName}; + +import java.io.Serial; +<#if hasTimeField> +import java.time.*; + +<#if hasBigDecimalField> +import java.math.BigDecimal; + + +<#if hasRequiredField> +import jakarta.validation.constraints.*; + + +import lombok.Data; + +import io.swagger.v3.oas.annotations.media.Schema; + +import org.hibernate.validator.constraints.Length; + +import top.continew.starter.extension.crud.model.req.BaseReq; + +/** + * 创建或修改${businessName}信息 + * + * @author ${author} + * @since ${datetime} + */ +@Data +@Schema(description = "创建或修改${businessName}信息") +public class ${className} extends BaseReq { + + @Serial + private static final long serialVersionUID = 1L; +<#if fieldConfigs??> + <#list fieldConfigs as fieldConfig> + <#if fieldConfig.showInForm> + + /** + * ${fieldConfig.comment} + */ + @Schema(description = "${fieldConfig.comment}") + <#if fieldConfig.isRequired> + <#if fieldConfig.fieldType = 'String'> + @NotBlank(message = "${fieldConfig.comment}不能为空") + <#else> + @NotNull(message = "${fieldConfig.comment}不能为空") + + + <#if fieldConfig.fieldType = 'String' && fieldConfig.columnSize??> + @Length(max = ${fieldConfig.columnSize}, message = "${fieldConfig.comment}长度不能超过 {max} 个字符") + + private ${fieldConfig.fieldType} ${fieldConfig.fieldName}; + + + +} \ No newline at end of file diff --git a/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/backend/Resp.ftl b/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/backend/Resp.ftl new file mode 100644 index 0000000..1c12bd9 --- /dev/null +++ b/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/backend/Resp.ftl @@ -0,0 +1,41 @@ +package ${packageName}.${subPackageName}; + +import java.io.Serial; +<#if hasTimeField> +import java.time.*; + +<#if hasBigDecimalField> +import java.math.BigDecimal; + + +import lombok.Data; + +import io.swagger.v3.oas.annotations.media.Schema; + +import top.continew.starter.extension.crud.model.resp.BaseResp; + +/** + * ${businessName}信息 + * + * @author ${author} + * @since ${datetime} + */ +@Data +@Schema(description = "${businessName}信息") +public class ${className} extends BaseResp { + + @Serial + private static final long serialVersionUID = 1L; +<#if fieldConfigs??> + <#list fieldConfigs as fieldConfig> + <#if fieldConfig.showInList> + + /** + * ${fieldConfig.comment} + */ + @Schema(description = "${fieldConfig.comment}") + private ${fieldConfig.fieldType} ${fieldConfig.fieldName}; + + + +} \ No newline at end of file diff --git a/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/backend/Service.ftl b/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/backend/Service.ftl new file mode 100644 index 0000000..dbfa162 --- /dev/null +++ b/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/backend/Service.ftl @@ -0,0 +1,15 @@ +package ${packageName}.${subPackageName}; + +import top.continew.starter.extension.crud.service.BaseService; +import ${packageName}.model.query.${classNamePrefix}Query; +import ${packageName}.model.req.${classNamePrefix}Req; +import ${packageName}.model.resp.${classNamePrefix}DetailResp; +import ${packageName}.model.resp.${classNamePrefix}Resp; + +/** + * ${businessName}业务接口 + * + * @author ${author} + * @since ${datetime} + */ +public interface ${className} extends BaseService<${classNamePrefix}Resp, ${classNamePrefix}DetailResp, ${classNamePrefix}Query, ${classNamePrefix}Req> {} \ No newline at end of file diff --git a/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/backend/ServiceImpl.ftl b/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/backend/ServiceImpl.ftl new file mode 100644 index 0000000..e578b7a --- /dev/null +++ b/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/backend/ServiceImpl.ftl @@ -0,0 +1,24 @@ +package ${packageName}.${subPackageName}; + +import lombok.RequiredArgsConstructor; + +import org.springframework.stereotype.Service; + +import top.continew.starter.extension.crud.service.impl.BaseServiceImpl; +import ${packageName}.mapper.${classNamePrefix}Mapper; +import ${packageName}.model.entity.${classNamePrefix}DO; +import ${packageName}.model.query.${classNamePrefix}Query; +import ${packageName}.model.req.${classNamePrefix}Req; +import ${packageName}.model.resp.${classNamePrefix}DetailResp; +import ${packageName}.model.resp.${classNamePrefix}Resp; +import ${packageName}.service.${classNamePrefix}Service; + +/** + * ${businessName}业务实现 + * + * @author ${author} + * @since ${datetime} + */ +@Service +@RequiredArgsConstructor +public class ${className} extends BaseServiceImpl<${classNamePrefix}Mapper, ${classNamePrefix}DO, ${classNamePrefix}Resp, ${classNamePrefix}DetailResp, ${classNamePrefix}Query, ${classNamePrefix}Req> implements ${classNamePrefix}Service {} \ No newline at end of file diff --git a/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/frontend/AddModal.ftl b/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/frontend/AddModal.ftl new file mode 100644 index 0000000..a74ab45 --- /dev/null +++ b/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/frontend/AddModal.ftl @@ -0,0 +1,129 @@ + + + diff --git a/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/frontend/DetailDrawer.ftl b/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/frontend/DetailDrawer.ftl new file mode 100644 index 0000000..e568350 --- /dev/null +++ b/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/frontend/DetailDrawer.ftl @@ -0,0 +1,41 @@ + + + + + diff --git a/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/frontend/api.ftl b/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/frontend/api.ftl new file mode 100644 index 0000000..b89e6ba --- /dev/null +++ b/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/frontend/api.ftl @@ -0,0 +1,65 @@ +import http from '@/utils/http' + +const BASE_URL = '/${apiModuleName}/${apiName}' + +export interface ${classNamePrefix}Resp { +<#if fieldConfigs??> +<#list fieldConfigs as fieldConfig> + <#if fieldConfig.showInList> + ${fieldConfig.fieldName}: string + + + createUserString: string + updateUserString: string + +} +export interface ${classNamePrefix}DetailResp { +<#if fieldConfigs??> +<#list fieldConfigs as fieldConfig> + ${fieldConfig.fieldName}: string + + createUserString: string + updateUserString: string + +} +export interface ${classNamePrefix}Query { +<#if fieldConfigs??> +<#list fieldConfigs as fieldConfig> + <#if fieldConfig.showInQuery> + ${fieldConfig.fieldName}: string + + + + sort: Array +} +export interface ${classNamePrefix}PageQuery extends ${classNamePrefix}Query, PageQuery {} + +/** @desc 查询${businessName}列表 */ +export function list${classNamePrefix}(query: ${classNamePrefix}PageQuery) { + return http.get>(`${'$'}{BASE_URL}`, query) +} + +/** @desc 查询${businessName}详情 */ +export function get${classNamePrefix}(id: string) { + return http.get<${classNamePrefix}DetailResp>(`${'$'}{BASE_URL}/${'$'}{id}`) +} + +/** @desc 新增${businessName} */ +export function add${classNamePrefix}(data: any) { + return http.post(`${'$'}{BASE_URL}`, data) +} + +/** @desc 修改${businessName} */ +export function update${classNamePrefix}(data: any, id: string) { + return http.put(`${'$'}{BASE_URL}/${'$'}{id}`, data) +} + +/** @desc 删除${businessName} */ +export function delete${classNamePrefix}(id: string) { + return http.del(`${'$'}{BASE_URL}/${'$'}{id}`) +} + +/** @desc 导出${businessName} */ +export function export${classNamePrefix}(query: ${classNamePrefix}Query) { + return http.download(`${'$'}{BASE_URL}/export`, query) +} diff --git a/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/frontend/index.ftl b/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/frontend/index.ftl new file mode 100644 index 0000000..3bdaeda --- /dev/null +++ b/continew-admin-plugins/continew-admin-generator/src/main/resources/templates/frontend/index.ftl @@ -0,0 +1,200 @@ + + + + + diff --git a/continew-admin-plugins/continew-admin-job/pom.xml b/continew-admin-plugins/continew-admin-job/pom.xml new file mode 100644 index 0000000..cc5f892 --- /dev/null +++ b/continew-admin-plugins/continew-admin-job/pom.xml @@ -0,0 +1,39 @@ + + 4.0.0 + + top.continew + continew-admin-plugins + ${revision} + + + continew-admin-job + 任务调度插件 + + + + + com.aizuda + snail-job-client-starter + + + com.aizuda + snail-job-client-retry-core + + + com.aizuda + snail-job-client-job-core + + + + + org.springframework + spring-webflux + + + + io.projectreactor.netty + reactor-netty-http + + + diff --git a/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/api/JobApi.java b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/api/JobApi.java new file mode 100644 index 0000000..cdbbfdb --- /dev/null +++ b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/api/JobApi.java @@ -0,0 +1,97 @@ +package top.continew.admin.job.api; + +import com.aizuda.snailjob.common.core.model.Result; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.service.annotation.*; +import top.continew.admin.job.model.JobPageResult; +import top.continew.admin.job.model.req.JobReq; +import top.continew.admin.job.model.req.JobStatusReq; +import top.continew.admin.job.model.resp.JobResp; + +import java.util.List; +import java.util.Set; + +/** + * 任务 REST API + * + * @author KAI + * @author Charles7c + * @since 2024/6/25 18:20 + */ +@HttpExchange(accept = MediaType.APPLICATION_JSON_VALUE) +public interface JobApi { + + /** + * 分页查询列表 + * + * @param groupName 任务组 + * @param jobName 任务名称 + * @param jobStatus 任务状态 + * @param page 页码 + * @param size 每页条数 + * @return 响应信息 + */ + @GetExchange("/job/page/list") + ResponseEntity>> page(@RequestParam(value = "groupName", required = false) String groupName, + @RequestParam(value = "jobName", required = false) String jobName, + @RequestParam(value = "jobStatus", required = false) Integer jobStatus, + @RequestParam("page") int page, + @RequestParam("size") int size); + + /** + * 新增 + * + * @param req 新增信息 + * @return 响应信息 + */ + @PostExchange("/job") + ResponseEntity> add(@RequestBody JobReq req); + + /** + * 修改 + * + * @param req 修改信息 + * @return 响应信息 + */ + @PutExchange("/job") + ResponseEntity> update(@RequestBody JobReq req); + + /** + * 修改状态 + * + * @param req 修改信息 + * @return 响应信息 + */ + @PutExchange("/job/status") + ResponseEntity> updateStatus(@RequestBody JobStatusReq req); + + /** + * 删除 + * + * @param ids ID 列表 + * @return 响应信息 + */ + @DeleteExchange("/job/ids") + ResponseEntity> delete(@RequestBody Set ids); + + /** + * 执行 + * + * @param id ID + * @return 响应信息 + */ + @PostExchange("/job/trigger/{id}") + ResponseEntity> trigger(@PathVariable("id") Long id); + + /** + * 查询分组列表 + * + * @return 响应信息 + */ + @GetExchange("/group/all/group-name/list") + ResponseEntity>> listGroup(); +} diff --git a/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/api/JobBatchApi.java b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/api/JobBatchApi.java new file mode 100644 index 0000000..d5df11c --- /dev/null +++ b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/api/JobBatchApi.java @@ -0,0 +1,98 @@ +package top.continew.admin.job.api; + +import com.aizuda.snailjob.common.core.model.Result; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.service.annotation.GetExchange; +import org.springframework.web.service.annotation.HttpExchange; +import org.springframework.web.service.annotation.PostExchange; +import top.continew.admin.job.model.JobInstanceLogPageResult; +import top.continew.admin.job.model.JobPageResult; +import top.continew.admin.job.model.resp.JobInstanceResp; +import top.continew.admin.job.model.resp.JobLogResp; + +import java.util.List; + +/** + * 任务批次 REST API + * + * @author KAI + * @author Charles7c + * @since 2024/6/27 23:03 + */ +@HttpExchange(value = "/job", accept = MediaType.APPLICATION_JSON_VALUE) +public interface JobBatchApi { + + /** + * 分页查询列表 + * + * @param jobId 任务 ID + * @param jobName 任务名称 + * @param groupName 组名称 + * @param taskBatchStatus 任务批次状态 + * @param datetimeRange 时间范围 + * @param page 页码 + * @param size 每页条数 + * @return 响应信息 + */ + @GetExchange("/batch/list") + ResponseEntity>> page(@RequestParam(value = "jobId", required = false) Long jobId, + @RequestParam(value = "jobName", required = false) String jobName, + @RequestParam(value = "groupName", required = false) String groupName, + @RequestParam(value = "taskBatchStatus", required = false) Integer taskBatchStatus, + @RequestParam(value = "datetimeRange", required = false) String[] datetimeRange, + @RequestParam(value = "page") Integer page, + @RequestParam(value = "size") Integer size); + + /** + * 停止 + * + * @param id ID + * @return 响应信息 + */ + @PostExchange("/batch/stop/{id}") + ResponseEntity> stop(@PathVariable("id") Long id); + + /** + * 重试 + * + * @param id ID + * @return 响应信息 + */ + @PostExchange("/batch/retry/{id}") + ResponseEntity> retry(@PathVariable("id") Long id); + + /** + * 分页查询任务实例列表 + * + * @param jobId 任务 ID + * @param taskBatchId 任务批次 ID + * @param page 页码 + * @param size 每页条数 + * @return 响应信息 + */ + @GetExchange("/task/list") + ResponseEntity>> pageTask(@RequestParam(value = "jobId", required = false) Long jobId, + @RequestParam(value = "taskBatchId") Long taskBatchId, + @RequestParam(value = "page") Integer page, + @RequestParam(value = "size") Integer size); + + /** + * 分页查询任务实例日志列表 + * + * @param jobId 任务 ID + * @param taskBatchId 任务批次 ID + * @param fromIndex 起始索引 + * @param size 每页条数 + * @return 响应信息 + */ + @GetExchange("/log/list") + ResponseEntity> pageLog(@RequestParam(value = "jobId", required = false) Long jobId, + @RequestParam(value = "taskBatchId") Long taskBatchId, + @RequestParam(value = "taskId") Long taskId, + @RequestParam(value = "startId") Integer startId, + @RequestParam(value = "fromIndex") Integer fromIndex, + @RequestParam(value = "size") Integer size); +} diff --git a/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/api/JobClient.java b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/api/JobClient.java new file mode 100644 index 0000000..b43dc8d --- /dev/null +++ b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/api/JobClient.java @@ -0,0 +1,141 @@ +package top.continew.admin.job.api; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONUtil; +import cn.hutool.jwt.JWTUtil; +import cn.hutool.jwt.RegisteredPayload; +import com.aizuda.snailjob.common.core.model.Result; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import top.continew.admin.job.constant.JobConstants; +import top.continew.admin.job.model.JobPageResult; +import top.continew.starter.cache.redisson.util.RedisUtils; +import top.continew.starter.extension.crud.model.resp.PageResp; + +import java.time.Duration; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + +/** + * 任务调度客户端 + * + * @author Charles7c + * @since 2024/7/4 23:07 + */ +@Slf4j +@Data +public class JobClient { + + public static final Integer STATUS_SUCCESS = 1; + private static final String AUTH_URL = "/auth/login"; + private final String url; + private final String username; + private final String password; + + public JobClient(String url, String username, String password) { + Assert.notBlank(url, "任务调度中心 URL 不能为空"); + Assert.notBlank(username, "任务调度中心用户名不能为空"); + Assert.notBlank(password, "任务调度中心密码不能为空"); + this.url = url; + this.username = username; + this.password = password; + } + + /** + * 请求 + * + * @param apiSupplier API 请求 + * @param 响应类型 + * @return 响应信息 + */ + public T request(Supplier>> apiSupplier) { + ResponseEntity> responseEntity = apiSupplier.get(); + this.checkResponse(responseEntity); + Result result = responseEntity.getBody(); + if (!STATUS_SUCCESS.equals(result.getStatus())) { + throw new IllegalStateException(result.getMessage()); + } + return result.getData(); + } + + /** + * 分页请求 + * + * @param apiSupplier API 请求 + * @param 响应类型 + * @return 分页列表信息 + */ + public PageResp requestPage(Supplier>>> apiSupplier) { + ResponseEntity>> responseEntity = apiSupplier.get(); + this.checkResponse(responseEntity); + JobPageResult> result = responseEntity.getBody(); + if (!STATUS_SUCCESS.equals(result.getStatus())) { + throw new IllegalStateException(result.getMessage()); + } + PageResp page = new PageResp<>(); + page.setList(result.getData()); + page.setTotal(result.getTotal()); + return page; + } + + /** + * 获取 Token + * + * @return Token + */ + public String getToken() { + String token = RedisUtils.get(JobConstants.AUTH_TOKEN_HEADER); + if (StrUtil.isBlank(token)) { + token = this.authenticate(); + Object expiresAtSeconds = JWTUtil.parseToken(token).getPayload(RegisteredPayload.EXPIRES_AT); + RedisUtils.set(JobConstants.AUTH_TOKEN_HEADER, token, Duration.ofSeconds(Convert + .toLong(expiresAtSeconds) - DateUtil.currentSeconds() - 60)); + } + return token; + } + + /** + * 密码认证 + * + * @return Token + */ + private String authenticate() { + Map paramMap = MapUtil.newHashMap(2); + paramMap.put("username", username); + paramMap.put("password", SecureUtil.md5(password)); + HttpRequest httpRequest = HttpUtil.createPost("%s%s".formatted(url, AUTH_URL)); + httpRequest.body(JSONUtil.toJsonStr(paramMap)); + HttpResponse response = httpRequest.execute(); + if (!response.isOk() || response.body() == null) { + throw new IllegalStateException("连接任务调度中心异常"); + } + Result result = JSONUtil.toBean(response.body(), Result.class); + if (!STATUS_SUCCESS.equals(result.getStatus())) { + log.warn("Password Authentication failed, expected a successful response. error msg: {}", result + .getMessage()); + throw new IllegalStateException(result.getMessage()); + } + return JSONUtil.parseObj(result.getData()).getStr("token"); + } + + /** + * 检查响应 + * + * @param responseEntity 响应信息 + */ + private void checkResponse(ResponseEntity responseEntity) { + if (!responseEntity.getStatusCode().is2xxSuccessful() || responseEntity.getBody() == null) { + throw new IllegalStateException("连接任务调度中心异常"); + } + } +} diff --git a/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/config/HttpExchangeConfiguration.java b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/config/HttpExchangeConfiguration.java new file mode 100644 index 0000000..ab0b786 --- /dev/null +++ b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/config/HttpExchangeConfiguration.java @@ -0,0 +1,118 @@ +package top.continew.admin.job.config; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.netty.channel.ChannelOption; +import io.netty.handler.timeout.ReadTimeoutHandler; +import io.netty.handler.timeout.WriteTimeoutHandler; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.reactive.ReactorClientHttpConnector; +import org.springframework.http.codec.json.Jackson2JsonDecoder; +import org.springframework.http.codec.json.Jackson2JsonEncoder; +import org.springframework.web.reactive.function.client.ClientRequest; +import org.springframework.web.reactive.function.client.ClientResponse; +import org.springframework.web.reactive.function.client.ExchangeFilterFunction; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.support.WebClientAdapter; +import org.springframework.web.service.invoker.HttpServiceProxyFactory; +import reactor.core.publisher.Mono; +import reactor.netty.http.client.HttpClient; +import top.continew.admin.job.api.JobApi; +import top.continew.admin.job.api.JobBatchApi; +import top.continew.admin.job.api.JobClient; +import top.continew.admin.job.constant.JobConstants; + +/** + * HTTP Exchange 配置 + * + * @author KAI + * @author Charles7c + * @since 2024/6/25 18:03 + */ +@Slf4j +@Configuration +@RequiredArgsConstructor +public class HttpExchangeConfiguration { + + private final ObjectMapper objectMapper; + @Value("${snail-job.namespace}") + private String namespace; + + @Value("${snail-job.server.api.url}") + private String baseUrl; + + @Value("${snail-job.server.api.username}") + private String username; + + @Value("${snail-job.server.api.password}") + private String password; + + @Bean + public JobApi jobApi() { + return httpServiceProxyFactory().createClient(JobApi.class); + } + + @Bean + public JobBatchApi jobBatchApi() { + return httpServiceProxyFactory().createClient(JobBatchApi.class); + } + + @Bean + public HttpServiceProxyFactory httpServiceProxyFactory() { + HttpClient httpClient = HttpClient.create() + .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 30000) + .doOnConnected(conn -> { + conn.addHandlerLast(new ReadTimeoutHandler(10)); + conn.addHandlerLast(new WriteTimeoutHandler(10)); + }) + .wiretap(true); + + WebClient webClient = WebClient.builder() + .codecs(config -> config.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(objectMapper))) + .codecs(config -> config.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(objectMapper))) + .clientConnector(new ReactorClientHttpConnector(httpClient)) + .filter(logRequest()) + .filter(logResponse()) + .filter((request, next) -> { + // 设置请求头 + ClientRequest filtered = ClientRequest.from(request) + .header(JobConstants.NAMESPACE_ID_HEADER, namespace) + .header(JobConstants.AUTH_TOKEN_HEADER, jobClient().getToken()) + .build(); + return next.exchange(filtered); + }) + .baseUrl(baseUrl) + .build(); + return HttpServiceProxyFactory.builderFor(WebClientAdapter.create(webClient)).build(); + } + + @Bean + public JobClient jobClient() { + return new JobClient(baseUrl, username, password); + } + + /** + * 打印请求日志 + */ + private ExchangeFilterFunction logRequest() { + return ExchangeFilterFunction.ofRequestProcessor(request -> { + log.info("---> {} {}", request.method(), request.url()); + return Mono.just(request); + }); + } + + /** + * 打印响应日志 + */ + private ExchangeFilterFunction logResponse() { + return ExchangeFilterFunction.ofResponseProcessor(response -> response.bodyToMono(String.class) + .flatMap(body -> { + log.info("<--- {}", response.statusCode()); + log.info(body); + return Mono.just(ClientResponse.from(response).body(body).build()); + })); + } +} diff --git a/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/config/SnailJobConfiguration.java b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/config/SnailJobConfiguration.java new file mode 100644 index 0000000..29640ed --- /dev/null +++ b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/config/SnailJobConfiguration.java @@ -0,0 +1,36 @@ +package top.continew.admin.job.config; + +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.ILoggingEvent; +import com.aizuda.snailjob.client.common.appender.SnailLogbackAppender; +import com.aizuda.snailjob.client.common.event.SnailClientStartingEvent; +import com.aizuda.snailjob.client.starter.EnableSnailJob; +import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.event.EventListener; + +/** + * Snail Job 配置 + * + * @author KAI + * @since 2024/6/26 9:19 + */ +@Configuration +@EnableSnailJob +@ConditionalOnProperty(prefix = "snail-job", value = "enabled", havingValue = "true", matchIfMissing = true) +public class SnailJobConfiguration { + + /** + * 日志上报 + */ + @EventListener(SnailClientStartingEvent.class) + public void onStarting() { + SnailLogbackAppender appender = new SnailLogbackAppender<>(); + appender.start(); + LoggerContext loggerContext = (LoggerContext)LoggerFactory.getILoggerFactory(); + Logger rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); + rootLogger.addAppender(appender); + } +} \ No newline at end of file diff --git a/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/constant/JobConstants.java b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/constant/JobConstants.java new file mode 100644 index 0000000..09962dd --- /dev/null +++ b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/constant/JobConstants.java @@ -0,0 +1,23 @@ +package top.continew.admin.job.constant; + +/** + * 任务调度常量 + * + * @author KAI + * @since 2024/6/26 9:19 + */ +public class JobConstants { + + /** + * 请求头:命名空间 ID + */ + public static final String NAMESPACE_ID_HEADER = "SNAIL-JOB-NAMESPACE-ID"; + + /** + * 请求头:认证令牌 + */ + public static final String AUTH_TOKEN_HEADER = "Snail-Job-Auth"; + + private JobConstants() { + } +} diff --git a/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/enums/JobBlockStrategyEnum.java b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/enums/JobBlockStrategyEnum.java new file mode 100644 index 0000000..f4cd2a2 --- /dev/null +++ b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/enums/JobBlockStrategyEnum.java @@ -0,0 +1,34 @@ +package top.continew.admin.job.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import top.continew.starter.core.enums.BaseEnum; + +/** + * 任务阻塞策略枚举 + * + * @author Charles7c + * @since 2024/7/11 22:28 + */ +@Getter +@RequiredArgsConstructor +public enum JobBlockStrategyEnum implements BaseEnum { + + /** + * 丢弃 + */ + DISCARD(1, "丢弃"), + + /** + * 覆盖 + */ + COVER(2, "覆盖"), + + /** + * 并行 + */ + PARALLEL(3, "并行"),; + + private final Integer value; + private final String description; +} diff --git a/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/enums/JobExecuteReasonEnum.java b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/enums/JobExecuteReasonEnum.java new file mode 100644 index 0000000..40a37e3 --- /dev/null +++ b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/enums/JobExecuteReasonEnum.java @@ -0,0 +1,119 @@ +package top.continew.admin.job.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import top.continew.starter.core.enums.BaseEnum; + +/** + * 任务执行原因枚举 + * + * @author Charles7c + * @since 2024/7/11 22:28 + */ +@Getter +@RequiredArgsConstructor +public enum JobExecuteReasonEnum implements BaseEnum { + + /** + * 无 + */ + NONE(0, "无"), + + /** + * 任务执行超时 + */ + TIME_OUT(1, "任务执行超时"), + + /** + * 无客户端节点 + */ + CLIENT_NOT_FOUND(2, "无客户端节点"), + + /** + * 任务已关闭 + */ + TASK_CLOSED(3, "任务已关闭"), + + /** + * 任务丢弃 + */ + TASK_DROPPED(4, "任务丢弃"), + + /** + * 任务被覆盖 + */ + TASK_COVERED(5, "任务被覆盖"), + + /** + * 无可执行任务项 + */ + TASK_NONE(6, "无可执行任务项"), + + /** + * 任务执行期间发生非预期异常 + */ + TASK_EXCEPTION(7, "任务执行期间发生非预期异常"), + + /** + * 手动停止 + */ + MANUAL_STOP(8, "手动停止"), + + /** + * 条件节点执行异常 + */ + NODE_EXCEPTION(9, "条件节点执行异常"), + + /** + * 任务中断 + */ + TASK_INTERRUPT(10, "任务中断"), + + /** + * 回调节点执行异常 + */ + CALLBACK_EXCEPTION(11, "回调节点执行异常"), + + /** + * 无需处理 + */ + NO_NEED_PROCESS(12, "无需处理"), + + /** + * 节点关闭跳过执行 + */ + NODE_SKIP(13, "节点关闭跳过执行"), + + /** + * 判定未通过 + */ + NOT_PASS(14, "判定未通过"), + + /** + * 任务已完成 + */ + TASK_FINISHED(15, "任务已完成"), + + /** + * 任务状态 + */ + TASK_RUNNING(16, "任务正在执行"), + + /** + * 任务等待执行 + */ + TASK_WAITING(17, "任务等待执行"), + + /** + * 任务执行失败 + */ + TASK_FAILED(18, "任务执行失败"), + + /** + * 任务执行成功 + */ + TASK_SUCCESS(19, "任务执行成功"),; + + private final Integer value; + private final String description; +} diff --git a/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/enums/JobExecuteStatusEnum.java b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/enums/JobExecuteStatusEnum.java new file mode 100644 index 0000000..571bbf7 --- /dev/null +++ b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/enums/JobExecuteStatusEnum.java @@ -0,0 +1,51 @@ +package top.continew.admin.job.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import top.continew.admin.common.constant.UiConstants; +import top.continew.starter.core.enums.BaseEnum; + +/** + * 任务执行状态枚举 + * + * @author Charles7c + * @since 2024/7/11 22:28 + */ +@Getter +@RequiredArgsConstructor +public enum JobExecuteStatusEnum implements BaseEnum { + + /** + * 待处理 + */ + WAITING(1, "待处理", UiConstants.COLOR_PRIMARY), + + /** + * 运行中 + */ + RUNNING(2, "运行中", UiConstants.COLOR_WARNING), + + /** + * 成功 + */ + SUCCEEDED(3, "成功", UiConstants.COLOR_SUCCESS), + + /** + * 已失败 + */ + FAILED(4, "已失败", UiConstants.COLOR_ERROR), + + /** + * 已停止 + */ + STOPPED(5, "已停止", UiConstants.COLOR_ERROR), + + /** + * 已取消 + */ + CANCELED(6, "已取消", UiConstants.COLOR_DEFAULT),; + + private final Integer value; + private final String description; + private final String color; +} diff --git a/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/enums/JobRouteStrategyEnum.java b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/enums/JobRouteStrategyEnum.java new file mode 100644 index 0000000..487cd4a --- /dev/null +++ b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/enums/JobRouteStrategyEnum.java @@ -0,0 +1,39 @@ +package top.continew.admin.job.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import top.continew.starter.core.enums.BaseEnum; + +/** + * 任务路由策略枚举 + * + * @author Charles7c + * @since 2024/7/11 22:28 + */ +@Getter +@RequiredArgsConstructor +public enum JobRouteStrategyEnum implements BaseEnum { + + /** + * 轮询 + */ + POLLING(4, "轮询"), + + /** + * 随机 + */ + RANDOM(2, "随机"), + + /** + * 一致性哈希 + */ + HASH(1, "一致性哈希"), + + /** + * LRU + */ + LRU(3, "LRU"),; + + private final Integer value; + private final String description; +} diff --git a/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/enums/JobStatusEnum.java b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/enums/JobStatusEnum.java new file mode 100644 index 0000000..0cc4e12 --- /dev/null +++ b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/enums/JobStatusEnum.java @@ -0,0 +1,31 @@ +package top.continew.admin.job.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import top.continew.admin.common.constant.UiConstants; +import top.continew.starter.core.enums.BaseEnum; + +/** + * 任务状态枚举 + * + * @author Charles7c + * @since 2024/7/11 22:28 + */ +@Getter +@RequiredArgsConstructor +public enum JobStatusEnum implements BaseEnum { + + /** + * 禁用 + */ + DISABLED(0, "禁用", UiConstants.COLOR_ERROR), + + /** + * 启用 + */ + ENABLED(1, "启用", UiConstants.COLOR_SUCCESS),; + + private final Integer value; + private final String description; + private final String color; +} diff --git a/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/enums/JobTaskTypeEnum.java b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/enums/JobTaskTypeEnum.java new file mode 100644 index 0000000..2cbb1a4 --- /dev/null +++ b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/enums/JobTaskTypeEnum.java @@ -0,0 +1,36 @@ +package top.continew.admin.job.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import top.continew.admin.common.constant.UiConstants; +import top.continew.starter.core.enums.BaseEnum; + +/** + * 任务类型枚举 + * + * @author Charles7c + * @since 2024/7/11 22:28 + */ +@Getter +@RequiredArgsConstructor +public enum JobTaskTypeEnum implements BaseEnum { + + /** + * 集群 + */ + CLUSTER(1, "集群", UiConstants.COLOR_PRIMARY), + + /** + * 广播 + */ + BROADCAST(2, "广播", UiConstants.COLOR_PRIMARY), + + /** + * 静态切片 + */ + SLICE(3, "静态切片", UiConstants.COLOR_PRIMARY),; + + private final Integer value; + private final String description; + private final String color; +} diff --git a/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/enums/JobTriggerTypeEnum.java b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/enums/JobTriggerTypeEnum.java new file mode 100644 index 0000000..b505ed1 --- /dev/null +++ b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/enums/JobTriggerTypeEnum.java @@ -0,0 +1,29 @@ +package top.continew.admin.job.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import top.continew.starter.core.enums.BaseEnum; + +/** + * 任务触发类型枚举 + * + * @author Charles7c + * @since 2024/7/11 22:28 + */ +@Getter +@RequiredArgsConstructor +public enum JobTriggerTypeEnum implements BaseEnum { + + /** + * 固定时间 + */ + FIXED_TIME(2, "固定时间"), + + /** + * CRON + */ + CRON(3, "CRON"); + + private final Integer value; + private final String description; +} diff --git a/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/JobInstanceLogPageResult.java b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/JobInstanceLogPageResult.java new file mode 100644 index 0000000..ab5f1dc --- /dev/null +++ b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/JobInstanceLogPageResult.java @@ -0,0 +1,52 @@ +package top.continew.admin.job.model; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import java.util.List; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 任务实例日志分页信息 + * + * @author Charles7c + * @since 2024/7/14 21:51 + */ +@Data +@Schema(description = "任务实例日志分页信息") +public class JobInstanceLogPageResult implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @Schema(description = "ID", example = "1") + private Long id; + + /** + * 日志详情 + */ + @Schema(description = "日志详情") + private List message; + + /** + * 是否结束 + */ + @Schema(description = "是否结束", example = "true") + private boolean isFinished; + + /** + * 起始索引 + */ + @Schema(description = "起始索引", example = "0") + private Integer fromIndex; + + /** + * 下一个开始 ID + */ + @Schema(description = "下一个开始ID", example = "9") + private Long nextStartId; +} diff --git a/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/JobPageResult.java b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/JobPageResult.java new file mode 100644 index 0000000..c6dbab5 --- /dev/null +++ b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/JobPageResult.java @@ -0,0 +1,30 @@ +package top.continew.admin.job.model; + +import com.aizuda.snailjob.common.core.model.Result; +import lombok.Data; + +/** + * 任务调度服务端分页返回对象 + * + * @author KAI + * @author Charles7c + * @since 2024/6/26 22:27 + */ +@Data +public class JobPageResult extends Result { + + /** + * 页码 + */ + private long page; + + /** + * 每页条数 + */ + private long size; + + /** + * 总条数 + */ + private long total; +} diff --git a/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/query/JobInstanceLogQuery.java b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/query/JobInstanceLogQuery.java new file mode 100644 index 0000000..89c80e8 --- /dev/null +++ b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/query/JobInstanceLogQuery.java @@ -0,0 +1,61 @@ +package top.continew.admin.job.model.query; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Min; +import lombok.Data; +import org.hibernate.validator.constraints.Range; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 任务实例日志查询条件 + * + * @author KAI + * @since 2024/6/28 16:58 + */ +@Data +@Schema(description = "任务实例日志查询条件") +public class JobInstanceLogQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 任务 ID + */ + @Schema(description = "任务ID", example = "1") + private Long jobId; + + /** + * 任务批次 ID + */ + @Schema(description = "任务批次ID", example = "1") + private Long taskBatchId; + + /** + * 任务实例 ID + */ + @Schema(description = "任务实例ID", example = "1") + private Long taskId; + + /** + * 开始 ID + */ + @Schema(description = "开始ID", example = "2850") + private Integer startId; + + /** + * 起始索引 + */ + @Schema(description = "起始索引", example = "0") + @Min(value = 0, message = "起始索引最小值为 {value}") + private Integer fromIndex = 0; + + /** + * 每页条数 + */ + @Schema(description = "每页条数", example = "50") + @Range(min = 1, max = 1000, message = "每页条数(取值范围 {min}-{max})") + private Integer size = 50; +} diff --git a/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/query/JobInstanceQuery.java b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/query/JobInstanceQuery.java new file mode 100644 index 0000000..c61f28b --- /dev/null +++ b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/query/JobInstanceQuery.java @@ -0,0 +1,33 @@ +package top.continew.admin.job.model.query; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 任务实例查询条件 + * + * @author KAI + * @since 2024/6/28 16:58 + */ +@Data +@Schema(description = "任务实例查询条件") +public class JobInstanceQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 任务 ID + */ + @Schema(description = "任务ID", example = "1") + private Long jobId; + + /** + * 任务批次 ID + */ + @Schema(description = "任务批次ID", example = "1") + private Long taskBatchId; +} diff --git a/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/query/JobLogQuery.java b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/query/JobLogQuery.java new file mode 100644 index 0000000..1c166b0 --- /dev/null +++ b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/query/JobLogQuery.java @@ -0,0 +1,74 @@ +package top.continew.admin.job.model.query; + +import cn.hutool.core.date.DatePattern; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.Size; +import lombok.Data; +import org.hibernate.validator.constraints.Range; +import org.springframework.format.annotation.DateTimeFormat; +import top.continew.admin.job.enums.JobExecuteStatusEnum; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 任务日志查询条件 + * + * @author KAI + * @since 2024/6/27 23:58 + */ +@Data +@Schema(description = "任务日志查询条件") +public class JobLogQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 任务 ID + */ + @Schema(description = "任务ID", example = "1") + private Long jobId; + + /** + * 任务组 + */ + @Schema(description = "任务组", example = "continew-admin") + private String groupName; + + /** + * 任务名称 + */ + @Schema(description = "任务名称", example = "定时任务1") + private String jobName; + + /** + * 任务批次状态 + */ + @Schema(description = "任务批次状态", example = "1") + private JobExecuteStatusEnum taskBatchStatus; + + /** + * 创建时间 + */ + @Schema(description = "创建时间", example = "2023-08-08 00:00:00,2023-08-08 23:59:59") + @DateTimeFormat(pattern = DatePattern.NORM_DATETIME_PATTERN) + @Size(max = 2, message = "创建时间必须是一个范围") + private LocalDateTime[] datetimeRange; + + /** + * 页码 + */ + @Schema(description = "页码", example = "1") + @Min(value = 1, message = "页码最小值为 {value}") + private Integer page = 1; + + /** + * 每页条数 + */ + @Schema(description = "每页条数", example = "10") + @Range(min = 1, max = 1000, message = "每页条数(取值范围 {min}-{max})") + private Integer size = 10; +} \ No newline at end of file diff --git a/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/query/JobQuery.java b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/query/JobQuery.java new file mode 100644 index 0000000..1dad130 --- /dev/null +++ b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/query/JobQuery.java @@ -0,0 +1,56 @@ +package top.continew.admin.job.model.query; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Min; +import lombok.Data; +import org.hibernate.validator.constraints.Range; +import top.continew.admin.job.enums.JobStatusEnum; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 任务查询条件 + * + * @author KAI + * @since 2024/6/25 16:43 + */ +@Data +@Schema(description = "任务查询条件") +public class JobQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 任务组 + */ + @Schema(description = "任务组", example = "continew-admin") + private String groupName; + + /** + * 任务名称 + */ + @Schema(description = "任务名称", example = "定时任务1") + private String jobName; + + /** + * 任务状态 + */ + @Schema(description = "任务状态", example = "1") + private JobStatusEnum jobStatus; + + /** + * 页码 + */ + @Schema(description = "页码", example = "1") + @Min(value = 1, message = "页码最小值为 {value}") + private Integer page = 1; + + /** + * 每页条数 + */ + @Schema(description = "每页条数", example = "10") + @Range(min = 1, max = 1000, message = "每页条数(取值范围 {min}-{max})") + private Integer size = 10; +} diff --git a/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/req/JobReq.java b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/req/JobReq.java new file mode 100644 index 0000000..49acfc5 --- /dev/null +++ b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/req/JobReq.java @@ -0,0 +1,147 @@ +package top.continew.admin.job.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.hibernate.validator.constraints.Length; +import top.continew.admin.job.enums.*; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 创建或修改任务信息 + * + * @author KAI + * @author Charles7c + * @since 2024/6/25 16:40 + */ +@Data +@Schema(description = "创建或修改任务信息") +public class JobReq implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 任务组 + */ + @Schema(description = "任务组", example = "continew-admin") + @NotBlank(message = "任务组不能为空") + private String groupName; + + /** + * 任务名称 + */ + @Schema(description = "任务名称", example = "定时任务1") + @NotBlank(message = "任务名称不能为空") + @Length(max = 64, message = "任务名称不能超过 {max} 个字符") + private String jobName; + + /** + * 描述 + */ + @Schema(description = "描述", example = "定时任务1的描述", defaultValue = "系统日志") + private String description; + + /** + * 触发类型 + */ + @Schema(description = "触发类型", example = "2") + @NotNull(message = "触发类型非法") + private JobTriggerTypeEnum triggerType; + + /** + * 间隔时长 + */ + @Schema(description = "间隔时长", example = "60") + @NotBlank(message = "间隔时长不能为空") + private String triggerInterval; + + /** + * 执行器类型 + */ + @Schema(description = "执行器类型", example = "1", defaultValue = "1") + private Integer executorType = 1; + + /** + * 任务类型 + */ + @Schema(description = "任务类型", example = "1") + @NotNull(message = "任务类型非法") + private JobTaskTypeEnum taskType; + + /** + * 执行器名称 + */ + @Schema(description = "执行器名称", example = "test") + @NotBlank(message = "执行器名称不能为空") + private String executorInfo; + + /** + * 任务参数 + */ + @Schema(description = "任务参数", example = "") + private String argsStr; + + /** + * 参数类型 + */ + @Schema(description = "参数类型", example = "1") + private Integer argsType; + + /** + * 路由策略 + */ + @Schema(description = "路由策略", example = "4") + @NotNull(message = "路由策略非法") + private JobRouteStrategyEnum routeKey; + + /** + * 阻塞策略 + */ + @Schema(description = "阻塞策略", example = "1") + @NotNull(message = "阻塞策略非法") + private JobBlockStrategyEnum blockStrategy; + + /** + * 超时时间(单位:秒) + */ + @Schema(description = "超时时间(单位:秒)", example = "60") + @NotNull(message = "超时时间不能为空") + private Integer executorTimeout; + + /** + * 最大重试次数 + */ + @Schema(description = "最大重试次数", example = "3") + @NotNull(message = "最大重试次数不能为空") + private Integer maxRetryTimes; + + /** + * 重试间隔(单位:秒) + */ + @Schema(description = "重试间隔(单位:秒)", example = "1") + @NotNull(message = "重试间隔不能为空") + private Integer retryInterval; + + /** + * 并行数 + */ + @Schema(description = "并行数", example = "1") + @NotNull(message = "并行数不能为空") + private Integer parallelNum; + + /** + * 任务状态 + */ + @Schema(description = "任务状态", example = "0", defaultValue = "0") + private JobStatusEnum jobStatus = JobStatusEnum.DISABLED; + + /** + * ID + */ + @Schema(hidden = true) + private Long id; +} diff --git a/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/req/JobStatusReq.java b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/req/JobStatusReq.java new file mode 100644 index 0000000..4dd038c --- /dev/null +++ b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/req/JobStatusReq.java @@ -0,0 +1,37 @@ +package top.continew.admin.job.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import top.continew.admin.job.enums.JobStatusEnum; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 修改任务状态信息 + * + * @author KAI + * @author Charles7c + * @since 2024/6/27 9:24 + */ +@Data +@Schema(description = "修改任务状态信息") +public class JobStatusReq implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 任务状态 + */ + @Schema(description = "任务状态", example = "1") + @NotNull(message = "任务状态非法") + private JobStatusEnum jobStatus; + + /** + * ID + */ + @Schema(hidden = true) + private Long id; +} diff --git a/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/resp/JobInstanceResp.java b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/resp/JobInstanceResp.java new file mode 100644 index 0000000..41b42d0 --- /dev/null +++ b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/resp/JobInstanceResp.java @@ -0,0 +1,70 @@ +package top.continew.admin.job.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 任务实例信息 + * + * @author KAI + * @author Charles7c + * @since 2024/6/28 16:58 + */ +@Data +@Schema(description = "任务实例信息") +public class JobInstanceResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @Schema(description = "ID", example = "1") + private Long id; + + /** + * 任务组 + */ + @Schema(description = "任务组", example = "continew-admin") + private String groupName; + + /** + * 任务 ID + */ + @Schema(description = "任务ID", example = "1") + private Long jobId; + + /** + * 任务批次 ID + */ + @Schema(description = "任务批次ID", example = "1") + private Long taskBatchId; + + /** + * 执行状态 + */ + @Schema(description = "执行状态", example = "1") + private Integer taskStatus; + + /** + * 重试次数 + */ + @Schema(description = "重试次数", example = "1") + private Integer retryCount; + + /** + * 执行结果 + */ + @Schema(description = "执行结果", example = "") + private String resultMessage; + + /** + * 客户端信息 + */ + @Schema(description = "客户端信息", example = "1812406095098114048@192.168.138.48:1789") + private String clientInfo; +} diff --git a/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/resp/JobLogResp.java b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/resp/JobLogResp.java new file mode 100644 index 0000000..10d4462 --- /dev/null +++ b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/resp/JobLogResp.java @@ -0,0 +1,85 @@ +package top.continew.admin.job.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.job.enums.JobExecuteReasonEnum; +import top.continew.admin.job.enums.JobExecuteStatusEnum; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 任务日志信息 + * + * @author KAI + * @author Charles7c + * @since 2024/6/27 22:50 + */ +@Data +@Schema(description = "任务日志信息") +public class JobLogResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @Schema(description = "ID", example = "1") + private Long id; + + /** + * 任务组 + */ + @Schema(description = "任务组", example = "continew-admin") + private String groupName; + + /** + * 任务名称 + */ + @Schema(description = "任务名称", example = "定时任务1") + private String jobName; + + /** + * 任务 ID + */ + @Schema(description = "任务ID", example = "1") + private Long jobId; + + /** + * 任务状态 + */ + @Schema(description = "任务状态", example = "3") + private JobExecuteStatusEnum taskBatchStatus; + + /** + * 操作原因 + */ + @Schema(description = "操作原因", example = "0") + private JobExecuteReasonEnum operationReason; + + /** + * 执行器类型 + */ + @Schema(description = "执行器类型", example = "1") + private Integer executorType; + + /** + * 执行器名称 + */ + @Schema(description = "执行器名称", example = "test") + private String executorInfo; + + /** + * 执行时间 + */ + @Schema(description = "执行时间", example = "2023-08-08 08:08:08", type = "string") + private LocalDateTime executionAt; + + /** + * 创建时间 + */ + @Schema(description = "创建时间", example = "2023-08-08 08:08:08", type = "string") + private LocalDateTime createDt; +} diff --git a/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/resp/JobResp.java b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/resp/JobResp.java new file mode 100644 index 0000000..d7313b3 --- /dev/null +++ b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/model/resp/JobResp.java @@ -0,0 +1,150 @@ +package top.continew.admin.job.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.job.enums.*; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 任务信息 + * + * @author KAI + * @author Charles7c + * @since 2024/6/25 17:15 + */ +@Data +@Schema(description = "任务信息") +public class JobResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @Schema(description = "ID", example = "1") + private Long id; + + /** + * 任务组 + */ + @Schema(description = "任务组", example = "continew-admin") + private String groupName; + + /** + * 任务名称 + */ + @Schema(description = "任务名称", example = "定时任务1") + private String jobName; + + /** + * 描述 + */ + @Schema(description = "描述", example = "定时任务1的描述") + private String description; + + /** + * 触发类型 + */ + @Schema(description = "触发类型", example = "2") + private JobTriggerTypeEnum triggerType; + + /** + * 间隔时长 + */ + @Schema(description = "间隔时长", example = "60") + private String triggerInterval; + + /** + * 执行器类型 + */ + @Schema(description = " 执行器类型", example = "1") + private Integer executorType; + + /** + * 任务类型 + */ + @Schema(description = "任务类型", example = "1") + private JobTaskTypeEnum taskType; + + /** + * 执行器名称 + */ + @Schema(description = "执行器名称", example = "test") + private String executorInfo; + + /** + * 任务参数 + */ + @Schema(description = "任务参数", example = "") + private String argsStr; + + /** + * 参数类型 + */ + @Schema(description = "参数类型", example = "1") + private String argsType; + + /** + * 路由策略 + */ + @Schema(description = "路由策略", example = "1") + private JobRouteStrategyEnum routeKey; + + /** + * 阻塞策略 + */ + @Schema(description = "阻塞策略", example = "1") + private JobBlockStrategyEnum blockStrategy; + + /** + * 超时时间(单位:秒) + */ + @Schema(description = "超时时间(单位:秒)", example = "60") + private Integer executorTimeout; + + /** + * 最大重试次数 + */ + @Schema(description = "最大重试次数", example = "3") + private Integer maxRetryTimes; + + /** + * 重试间隔(单位:秒) + */ + @Schema(description = "重试间隔", example = "1") + private Integer retryInterval; + + /** + * 并行数 + */ + @Schema(description = "并行数", example = "1") + private Integer parallelNum; + + /** + * 任务状态 + */ + @Schema(description = "任务状态", example = "1") + private JobStatusEnum jobStatus; + + /** + * 下次触发时间 + */ + @Schema(description = "下次触发时间", example = "2023-08-08 08:09:00", type = "string") + private LocalDateTime nextTriggerAt; + + /** + * 创建时间 + */ + @Schema(description = "创建时间", example = "2023-08-08 08:08:00", type = "string") + private LocalDateTime createDt; + + /** + * 修改时间 + */ + @Schema(description = "修改时间", example = "2023-08-08 08:08:00", type = "string") + private LocalDateTime updateDt; +} \ No newline at end of file diff --git a/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/service/JobLogService.java b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/service/JobLogService.java new file mode 100644 index 0000000..d8bbdb9 --- /dev/null +++ b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/service/JobLogService.java @@ -0,0 +1,61 @@ +package top.continew.admin.job.service; + +import top.continew.admin.job.model.JobInstanceLogPageResult; +import top.continew.admin.job.model.query.JobInstanceLogQuery; +import top.continew.admin.job.model.query.JobLogQuery; +import top.continew.admin.job.model.query.JobInstanceQuery; +import top.continew.admin.job.model.resp.JobLogResp; +import top.continew.admin.job.model.resp.JobInstanceResp; +import top.continew.starter.extension.crud.model.resp.PageResp; + +import java.util.List; + +/** + * 任务日志业务接口 + * + * @author KAI + * @author Charles7c + * @since 2024/6/27 22:52 + */ +public interface JobLogService { + + /** + * 分页查询列表 + * + * @param query 查询条件 + * @return 分页列表信息 + */ + PageResp page(JobLogQuery query); + + /** + * 停止 + * + * @param id ID + * @return 停止结果 + */ + boolean stop(Long id); + + /** + * 重试 + * + * @param id ID + * @return 重试结果 + */ + boolean retry(Long id); + + /** + * 查询任务实例列表 + * + * @param query 查询条件 + * @return 列表信息 + */ + List listInstance(JobInstanceQuery query); + + /** + * 分页查询任务实例日志列表 + * + * @param query 查询条件 + * @return 分页列表信息 + */ + JobInstanceLogPageResult pageInstanceLog(JobInstanceLogQuery query); +} diff --git a/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/service/JobService.java b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/service/JobService.java new file mode 100644 index 0000000..eae8acc --- /dev/null +++ b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/service/JobService.java @@ -0,0 +1,76 @@ +package top.continew.admin.job.service; + +import top.continew.admin.job.model.query.JobQuery; +import top.continew.admin.job.model.req.JobReq; +import top.continew.admin.job.model.req.JobStatusReq; +import top.continew.admin.job.model.resp.JobResp; +import top.continew.starter.extension.crud.model.resp.PageResp; + +import java.util.List; + +/** + * 任务业务接口 + * + * @author KAI + * @author Charles7c + * @since 2024/6/25 17:20 + */ +public interface JobService { + + /** + * 分页查询列表 + * + * @param query 查询条件 + * @return 分页列表信息 + */ + PageResp page(JobQuery query); + + /** + * 新增 + * + * @param req 创建信息 + * @return 新增结果 + */ + boolean add(JobReq req); + + /** + * 修改 + * + * @param req 修改信息 + * @param id ID + * @return 修改结果 + */ + boolean update(JobReq req, Long id); + + /** + * 修改状态 + * + * @param req 修改状态信息 + * @param id ID + * @return 修改状态结果 + */ + boolean updateStatus(JobStatusReq req, Long id); + + /** + * 删除 + * + * @param id ID + * @return 删除结果 + */ + boolean delete(Long id); + + /** + * 执行 + * + * @param id ID + * @return 执行结果 + */ + boolean trigger(Long id); + + /** + * 查询分组列表 + * + * @return 分组列表 + */ + List listGroup(); +} diff --git a/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/service/impl/JobLogServiceImpl.java b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/service/impl/JobLogServiceImpl.java new file mode 100644 index 0000000..cff8442 --- /dev/null +++ b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/service/impl/JobLogServiceImpl.java @@ -0,0 +1,67 @@ +package top.continew.admin.job.service.impl; + +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import top.continew.admin.job.api.JobBatchApi; +import top.continew.admin.job.api.JobClient; +import top.continew.admin.job.model.JobInstanceLogPageResult; +import top.continew.admin.job.model.query.JobInstanceLogQuery; +import top.continew.admin.job.model.query.JobInstanceQuery; +import top.continew.admin.job.model.query.JobLogQuery; +import top.continew.admin.job.model.resp.JobInstanceResp; +import top.continew.admin.job.model.resp.JobLogResp; +import top.continew.admin.job.service.JobLogService; +import top.continew.starter.extension.crud.model.resp.PageResp; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Objects; + +/** + * 任务日志业务实现 + * + * @author KAI + * @author Charles7c + * @since 2024/6/27 22:54 + */ +@Service +@RequiredArgsConstructor +public class JobLogServiceImpl implements JobLogService { + + private final JobClient jobClient; + private final JobBatchApi jobBatchApi; + + @Override + public PageResp page(JobLogQuery query) { + LocalDateTime[] datetimeRange = query.getDatetimeRange(); + return jobClient.requestPage(() -> jobBatchApi.page(query.getJobId(), query.getJobName(), query + .getGroupName(), query.getTaskBatchStatus() != null + ? query.getTaskBatchStatus().getValue() + : null, new String[] {DateUtil.format(datetimeRange[0], DatePattern.UTC_SIMPLE_PATTERN), DateUtil + .format(datetimeRange[1], DatePattern.UTC_SIMPLE_PATTERN)}, query.getPage(), query.getSize())); + } + + @Override + public boolean stop(Long id) { + return Boolean.TRUE.equals(jobClient.request(() -> jobBatchApi.stop(id))); + } + + @Override + public boolean retry(Long id) { + return Boolean.TRUE.equals(jobClient.request(() -> jobBatchApi.retry(id))); + } + + @Override + public List listInstance(JobInstanceQuery query) { + return jobClient.requestPage(() -> jobBatchApi.pageTask(query.getJobId(), query.getTaskBatchId(), 1, 100)) + .getList(); + } + + @Override + public JobInstanceLogPageResult pageInstanceLog(JobInstanceLogQuery query) { + return Objects.requireNonNull(jobBatchApi.pageLog(query.getJobId(), query.getTaskBatchId(), query + .getTaskId(), query.getStartId(), query.getFromIndex(), query.getSize()).getBody()).getData(); + } +} diff --git a/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/service/impl/JobServiceImpl.java b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/service/impl/JobServiceImpl.java new file mode 100644 index 0000000..d6ac7f8 --- /dev/null +++ b/continew-admin-plugins/continew-admin-job/src/main/java/top/continew/admin/job/service/impl/JobServiceImpl.java @@ -0,0 +1,68 @@ +package top.continew.admin.job.service.impl; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import top.continew.admin.job.api.JobApi; +import top.continew.admin.job.api.JobClient; +import top.continew.admin.job.model.query.JobQuery; +import top.continew.admin.job.model.req.JobReq; +import top.continew.admin.job.model.req.JobStatusReq; +import top.continew.admin.job.model.resp.JobResp; +import top.continew.admin.job.service.JobService; +import top.continew.starter.extension.crud.model.resp.PageResp; + +import java.util.Collections; +import java.util.List; + +/** + * 任务业务实现 + * + * @author KAI + * @author Charles7c + * @since 2024/6/25 17:25 + */ +@Service +@RequiredArgsConstructor +public class JobServiceImpl implements JobService { + + private final JobClient jobClient; + private final JobApi jobApi; + + @Override + public PageResp page(JobQuery query) { + return jobClient.requestPage(() -> jobApi.page(query.getGroupName(), query.getJobName(), query + .getJobStatus() != null ? query.getJobStatus().getValue() : null, query.getPage(), query.getSize())); + } + + @Override + public boolean add(JobReq req) { + return Boolean.TRUE.equals(jobClient.request(() -> jobApi.add(req))); + } + + @Override + public boolean update(JobReq req, Long id) { + req.setId(id); + return Boolean.TRUE.equals(jobClient.request(() -> jobApi.update(req))); + } + + @Override + public boolean updateStatus(JobStatusReq req, Long id) { + req.setId(id); + return Boolean.TRUE.equals(jobClient.request(() -> jobApi.updateStatus(req))); + } + + @Override + public boolean delete(Long id) { + return Boolean.TRUE.equals(jobClient.request(() -> jobApi.delete(Collections.singleton(id)))); + } + + @Override + public boolean trigger(Long id) { + return Boolean.TRUE.equals(jobClient.request(() -> jobApi.trigger(id))); + } + + @Override + public List listGroup() { + return jobClient.request(jobApi::listGroup); + } +} \ No newline at end of file diff --git a/continew-admin-plugins/pom.xml b/continew-admin-plugins/pom.xml new file mode 100644 index 0000000..db22cca --- /dev/null +++ b/continew-admin-plugins/pom.xml @@ -0,0 +1,28 @@ + + + 4.0.0 + + top.continew + continew-admin + ${revision} + + + continew-admin-plugins + pom + 插件模块(存放代码生成、任务调度等扩展模块) + + + continew-admin-generator + continew-admin-job + + + + + + top.continew + continew-admin-common + + + \ No newline at end of file diff --git a/continew-admin-system/pom.xml b/continew-admin-system/pom.xml new file mode 100644 index 0000000..6c39287 --- /dev/null +++ b/continew-admin-system/pom.xml @@ -0,0 +1,23 @@ + + + 4.0.0 + + top.continew + continew-admin + ${revision} + + + continew-admin-system + 系统管理模块(存放系统管理相关业务功能,例如:部门管理、角色管理、用户管理等) + + + + + top.continew + continew-admin-common + ${revision} + + + diff --git a/continew-admin-system/src/main/java/top/continew/admin/auth/model/query/OnlineUserQuery.java b/continew-admin-system/src/main/java/top/continew/admin/auth/model/query/OnlineUserQuery.java new file mode 100644 index 0000000..d4cc6aa --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/auth/model/query/OnlineUserQuery.java @@ -0,0 +1,50 @@ +package top.continew.admin.auth.model.query; + +import cn.hutool.core.date.DatePattern; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + * 在线用户查询条件 + * + * @author Charles7c + * @since 2023/1/20 23:07 + */ +@Data +@Schema(description = "在线用户查询条件") +public class OnlineUserQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户昵称 + */ + @Schema(description = "用户昵称", example = "张三") + private String nickname; + + /** + * 登录时间 + */ + @Schema(description = "登录时间", example = "2023-08-08 00:00:00,2023-08-08 23:59:59") + @DateTimeFormat(pattern = DatePattern.NORM_DATETIME_PATTERN) + private List loginTime; + + /** + * 用户 ID + */ + @Schema(hidden = true) + private Long userId; + + /** + * 角色 ID + */ + @Schema(hidden = true) + private Long roleId; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/auth/model/req/AccountLoginReq.java b/continew-admin-system/src/main/java/top/continew/admin/auth/model/req/AccountLoginReq.java new file mode 100644 index 0000000..6282e56 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/auth/model/req/AccountLoginReq.java @@ -0,0 +1,53 @@ +package top.continew.admin.auth.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 账号登录信息 + * + * @author Charles7c + * @since 2022/12/21 20:43 + */ +@Data +@Schema(description = "账号登录信息") +public class AccountLoginReq implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户名 + */ + @Schema(description = "用户名", example = "zhangsan") + @NotBlank(message = "用户名不能为空") + private String username; + + /** + * 密码(加密) + */ + @Schema(description = "密码(加密)", example = "HHwZoiBwCfh0xLdWOAd0bHOkEZlIMMOQKJyeFUw9T3ArrhL57od2i42s1o0sSXKkeHPJXvQsninhPFH2lArDDQ==") + @NotBlank(message = "密码不能为空") + private String password; + + /** + * 验证码 + */ + @Schema(description = "验证码", example = "ABCD") + @NotBlank(message = "验证码不能为空") + private String captcha; + + /** + * 验证码标识 + */ + @Schema(description = "验证码标识", example = "090b9a2c-1691-4fca-99db-e4ed0cff362f") + @NotBlank(message = "验证码标识不能为空") + private String uuid; + + @Schema(description = "学校", example = "") + private String school; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/auth/model/req/EmailLoginReq.java b/continew-admin-system/src/main/java/top/continew/admin/auth/model/req/EmailLoginReq.java new file mode 100644 index 0000000..1640a3a --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/auth/model/req/EmailLoginReq.java @@ -0,0 +1,41 @@ +package top.continew.admin.auth.model.req; + +import cn.hutool.core.lang.RegexPool; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 邮箱登录信息 + * + * @author Charles7c + * @since 2023/10/23 20:15 + */ +@Data +@Schema(description = "邮箱登录信息") +public class EmailLoginReq implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 邮箱 + */ + @Schema(description = "邮箱", example = "123456789@qq.com") + @NotBlank(message = "邮箱不能为空") + @Pattern(regexp = RegexPool.EMAIL, message = "邮箱格式错误") + private String email; + + /** + * 验证码 + */ + @Schema(description = "验证码", example = "888888") + @NotBlank(message = "验证码不能为空") + @Length(max = 6, message = "验证码非法") + private String captcha; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/auth/model/req/EmailRegisterReq.java b/continew-admin-system/src/main/java/top/continew/admin/auth/model/req/EmailRegisterReq.java new file mode 100644 index 0000000..480a16e --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/auth/model/req/EmailRegisterReq.java @@ -0,0 +1,80 @@ +package top.continew.admin.auth.model.req; + +import cn.hutool.core.lang.RegexPool; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import lombok.Data; +import org.hibernate.validator.constraints.Length; +import top.continew.admin.common.constant.RegexConstants; +import top.continew.admin.common.enums.GenderEnum; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 邮箱注册信息 + * + * @author Charles7c + * @since 2024/12/04 10:00 + */ +@Data +@Schema(description = "邮箱注册信息") +public class EmailRegisterReq implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 邮箱 + */ + @Schema(description = "邮箱", example = "123456789@qq.com") + @NotBlank(message = "邮箱不能为空") + @Pattern(regexp = RegexPool.EMAIL, message = "邮箱格式错误") + @Length(max = 255, message = "邮箱长度不能超过 {max} 个字符") + private String email; + + /** + * 昵称 + */ + @Schema(description = "昵称", example = "张三") + @NotBlank(message = "昵称不能为空") + @Pattern(regexp = RegexConstants.GENERAL_NAME, message = "昵称长度为 2-30 个字符,支持中文、字母、数字、下划线,短横线") + private String nickname; + + /** + * 密码(加密) + */ + @Schema(description = "密码(加密)", example = "E7c72TH+LDxKTwavjM99W1MdI9Lljh79aPKiv3XB9MXcplhm7qJ1BJCj28yaflbdVbfc366klMtjLIWQGqb0qw==") + @NotBlank(message = "密码不能为空") + private String password; + + /** + * 确认密码(加密) + */ + @Schema(description = "确认密码(加密)", example = "E7c72TH+LDxKTwavjM99W1MdI9Lljh79aPKiv3XB9MXcplhm7qJ1BJCj28yaflbdVbfc366klMtjLIWQGqb0qw==") + @NotBlank(message = "确认密码不能为空") + private String confirmPassword; + + /** + * 性别 + */ + @Schema(description = "性别", example = "1") + @NotNull(message = "性别非法") + private GenderEnum gender; + + /** + * 验证码 + */ + @Schema(description = "验证码", example = "888888") + @NotBlank(message = "验证码不能为空") + @Length(max = 6, message = "验证码非法") + private String captcha; + + /** + * 部门ID(可选,默认分配到默认部门) + */ + @Schema(description = "部门ID", example = "1") + private Long deptId; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/auth/model/req/PhoneLoginReq.java b/continew-admin-system/src/main/java/top/continew/admin/auth/model/req/PhoneLoginReq.java new file mode 100644 index 0000000..9c36a0f --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/auth/model/req/PhoneLoginReq.java @@ -0,0 +1,41 @@ +package top.continew.admin.auth.model.req; + +import cn.hutool.core.lang.RegexPool; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 手机号登录信息 + * + * @author Charles7c + * @since 2023/10/26 22:37 + */ +@Data +@Schema(description = "手机号登录信息") +public class PhoneLoginReq implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 手机号 + */ + @Schema(description = "手机号", example = "13811111111") + @NotBlank(message = "手机号不能为空") + @Pattern(regexp = RegexPool.MOBILE, message = "手机号格式错误") + private String phone; + + /** + * 验证码 + */ + @Schema(description = "验证码", example = "8888") + @NotBlank(message = "验证码不能为空") + @Length(max = 4, message = "验证码非法") + private String captcha; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/auth/model/req/PhoneRegisterReq.java b/continew-admin-system/src/main/java/top/continew/admin/auth/model/req/PhoneRegisterReq.java new file mode 100644 index 0000000..861b217 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/auth/model/req/PhoneRegisterReq.java @@ -0,0 +1,80 @@ +package top.continew.admin.auth.model.req; + +import cn.hutool.core.lang.RegexPool; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import lombok.Data; +import org.hibernate.validator.constraints.Length; +import top.continew.admin.common.constant.RegexConstants; +import top.continew.admin.common.enums.GenderEnum; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 手机号注册信息 + * + * @author Charles7c + * @since 2024/12/04 10:00 + */ +@Data +@Schema(description = "手机号注册信息") +public class PhoneRegisterReq implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 手机号码 + */ + @Schema(description = "手机号码", example = "13811111111") + @NotBlank(message = "手机号码不能为空") + @Pattern(regexp = RegexPool.MOBILE, message = "手机号码格式错误") + private String phone; + + /** + * 昵称 + */ + @Schema(description = "昵称", example = "张三") + @Pattern(regexp = RegexConstants.GENERAL_NAME, message = "昵称长度为 2-30 个字符,支持中文、字母、数字、下划线,短横线") + private String nickname; + + /** + * 密码(加密) + */ + @Schema(description = "密码(加密)", example = "E7c72TH+LDxKTwavjM99W1MdI9Lljh79aPKiv3XB9MXcplhm7qJ1BJCj28yaflbdVbfc366klMtjLIWQGqb0qw==") + private String password; + + /** + * 确认密码(加密) + */ + @Schema(description = "确认密码(加密)", example = "E7c72TH+LDxKTwavjM99W1MdI9Lljh79aPKiv3XB9MXcplhm7qJ1BJCj28yaflbdVbfc366klMtjLIWQGqb0qw==") + private String confirmPassword; + + /** + * 性别 + */ + @Schema(description = "性别", example = "1") + private GenderEnum gender; + + /** + * 验证码 + */ + @Schema(description = "验证码", example = "8888") + @Length(max = 4, message = "验证码非法") + private String captcha; + + /** + * 部门ID(可选,默认分配到默认部门) + */ + @Schema(description = "部门ID", example = "1") + private Long deptId; + + /** + * 邀请码 + */ + @Schema(description = "邀请码", example = "8888") + @Length(max = 4, message = "邀请码非法") + private String inviteCode; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/auth/model/req/UserRegisterReq.java b/continew-admin-system/src/main/java/top/continew/admin/auth/model/req/UserRegisterReq.java new file mode 100644 index 0000000..3fc47b4 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/auth/model/req/UserRegisterReq.java @@ -0,0 +1,100 @@ +package top.continew.admin.auth.model.req; + +import cn.hutool.core.lang.RegexPool; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import lombok.Data; +import org.hibernate.validator.constraints.Length; +import top.continew.admin.common.constant.RegexConstants; +import top.continew.admin.common.enums.GenderEnum; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 用户注册信息 + * + * @author Charles7c + * @since 2024/12/04 10:00 + */ +@Data +@Schema(description = "用户注册信息") +public class UserRegisterReq implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户名 + */ + @Schema(description = "用户名", example = "zhangsan") + @NotBlank(message = "用户名不能为空") + @Pattern(regexp = RegexConstants.USERNAME, message = "用户名长度为 4-64 个字符,支持大小写字母、数字、下划线") + private String username; + + /** + * 昵称 + */ + @Schema(description = "昵称", example = "张三") + @NotBlank(message = "昵称不能为空") + @Pattern(regexp = RegexConstants.GENERAL_NAME, message = "昵称长度为 2-30 个字符,支持中文、字母、数字、下划线,短横线") + private String nickname; + + /** + * 密码(加密) + */ + @Schema(description = "密码(加密)", example = "E7c72TH+LDxKTwavjM99W1MdI9Lljh79aPKiv3XB9MXcplhm7qJ1BJCj28yaflbdVbfc366klMtjLIWQGqb0qw==") + @NotBlank(message = "密码不能为空") + private String password; + + /** + * 确认密码(加密) + */ + @Schema(description = "确认密码(加密)", example = "E7c72TH+LDxKTwavjM99W1MdI9Lljh79aPKiv3XB9MXcplhm7qJ1BJCj28yaflbdVbfc366klMtjLIWQGqb0qw==") + @NotBlank(message = "确认密码不能为空") + private String confirmPassword; + + /** + * 邮箱 + */ + @Schema(description = "邮箱", example = "123456789@qq.com") + @Pattern(regexp = "^$|" + RegexPool.EMAIL, message = "邮箱格式错误") + @Length(max = 255, message = "邮箱长度不能超过 {max} 个字符") + private String email; + + /** + * 手机号码 + */ + @Schema(description = "手机号码", example = "13811111111") + @Pattern(regexp = "^$|" + RegexPool.MOBILE, message = "手机号码格式错误") + private String phone; + + /** + * 性别 + */ + @Schema(description = "性别", example = "1") + @NotNull(message = "性别非法") + private GenderEnum gender; + + /** + * 验证码 + */ + @Schema(description = "验证码", example = "ABCD") + @NotBlank(message = "验证码不能为空") + private String captcha; + + /** + * 验证码标识 + */ + @Schema(description = "验证码标识", example = "090b9a2c-1691-4fca-99db-e4ed0cff362f") + @NotBlank(message = "验证码标识不能为空") + private String uuid; + + /** + * 部门ID(可选,默认分配到默认部门) + */ + @Schema(description = "部门ID", example = "1") + private Long deptId; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/auth/model/resp/DeptInfo.java b/continew-admin-system/src/main/java/top/continew/admin/auth/model/resp/DeptInfo.java new file mode 100644 index 0000000..c749956 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/auth/model/resp/DeptInfo.java @@ -0,0 +1,16 @@ +package top.continew.admin.auth.model.resp; + +import lombok.Data; + +@Data +public class DeptInfo { + /** + * 部门ID + */ + private Long deptId; + + /** + * 部门名称 + */ + private String name; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/auth/model/resp/LoginResp.java b/continew-admin-system/src/main/java/top/continew/admin/auth/model/resp/LoginResp.java new file mode 100644 index 0000000..81711a2 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/auth/model/resp/LoginResp.java @@ -0,0 +1,47 @@ +package top.continew.admin.auth.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 令牌信息 + * + * @author Charles7c + * @since 2022/12/21 20:42 + */ +@Data +@Builder +@Schema(description = "令牌信息") +public class LoginResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 令牌 + */ + @Schema(description = "令牌", example = "eyJ0eXAiOiJlV1QiLCJhbGciqiJIUzI1NiJ9.eyJsb2dpblR5cGUiOiJsb29pbiIsImxvZ2luSWQiOjEsInJuU3RyIjoiSjd4SUljYnU5cmNwU09vQ3Uyc1ND1BYYTYycFRjcjAifQ.KUPOYm-2wfuLUSfEEAbpGE527fzmkAJG7sMNcQ0pUZ8") + private String token; + + /** + * AI Bot令牌 + */ + @Schema(description = "AI Bot令牌") + private String aiBotToken; + + /** + * SD Bot令牌 + */ + @Schema(description = "SD Bot令牌") + private String sdBotToken; + + /** + * idtoken + */ + @Schema(description = "generateToken") + private String generateToken; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/auth/model/resp/OnlineUserResp.java b/continew-admin-system/src/main/java/top/continew/admin/auth/model/resp/OnlineUserResp.java new file mode 100644 index 0000000..bd021b9 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/auth/model/resp/OnlineUserResp.java @@ -0,0 +1,87 @@ +package top.continew.admin.auth.model.resp; + +import cn.crane4j.annotation.*; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.auth.service.OnlineUserService; +import top.continew.admin.common.constant.ContainerConstants; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 在线用户信息 + * + * @author Charles7c + * @since 2023/1/20 21:54 + */ +@Data +@Schema(description = "在线用户信息") +public class OnlineUserResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @Schema(description = "ID", example = "1") + @Assemble(prop = ":nickname", container = ContainerConstants.USER_NICKNAME) + private Long id; + + /** + * 令牌 + */ + @Schema(description = "令牌", example = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpblR5cGUiOiJsb2dpbiIsImxvZ2luSWQiOjEsInJuU3RyIjoiTUd6djdyOVFoeHEwdVFqdFAzV3M5YjVJRzh4YjZPSEUifQ.7q7U3ouoN7WPhH2kUEM7vPe5KF3G_qavSG-vRgIxKvE") + @AssembleMethod(prop = ":lastActiveTime", targetType = OnlineUserService.class, method = @ContainerMethod(bindMethod = "getLastActiveTime", type = MappingType.ORDER_OF_KEYS)) + private String token; + + /** + * 用户名 + */ + @Schema(description = "用户名", example = "zhangsan") + private String username; + + /** + * 昵称 + */ + @Schema(description = "昵称", example = "张三") + private String nickname; + + /** + * 登录 IP + */ + @Schema(description = "登录 IP", example = "") + private String ip; + + /** + * 登录地点 + */ + @Schema(description = "登录地点", example = "中国北京北京市") + private String address; + + /** + * 浏览器 + */ + @Schema(description = "浏览器", example = "Chrome 115.0.0.0") + private String browser; + + /** + * 操作系统 + */ + @Schema(description = "操作系统", example = "Windows 10") + private String os; + + /** + * 登录时间 + */ + @Schema(description = "登录时间", example = "2023-08-08 08:08:08", type = "string") + private LocalDateTime loginTime; + + /** + * 最后活跃时间 + */ + @Schema(description = "最后活跃时间", example = "2023-08-08 08:08:08", type = "string") + private LocalDateTime lastActiveTime; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/auth/model/resp/RouteResp.java b/continew-admin-system/src/main/java/top/continew/admin/auth/model/resp/RouteResp.java new file mode 100644 index 0000000..a09caa7 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/auth/model/resp/RouteResp.java @@ -0,0 +1,114 @@ +package top.continew.admin.auth.model.resp; + +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 路由信息 + * + * @author Charles7c + * @since 2023/2/26 22:51 + */ +@Data +@Schema(description = "路由信息") +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class RouteResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @Schema(description = "ID", example = "1010") + private Long id; + + /** + * 上级菜单 ID + */ + @Schema(description = "上级菜单ID", example = "1000") + private Long parentId; + + /** + * 标题 + */ + @Schema(description = "标题", example = "用户管理") + private String title; + + /** + * 类型 + */ + @Schema(description = "类型", example = "2") + private Integer type; + + /** + * 路由地址 + */ + @Schema(description = "路由地址", example = "/system/user") + private String path; + + /** + * 组件名称 + */ + @Schema(description = "组件名称", example = "User") + private String name; + + /** + * 组件路径 + */ + @Schema(description = "组件路径", example = "/system/user/index") + private String component; + + /** + * 重定向地址 + */ + @Schema(description = "重定向地址") + private String redirect; + + /** + * 图标 + */ + @Schema(description = "图标", example = "user") + private String icon; + + /** + * 是否外链 + */ + @Schema(description = "是否外链", example = "false") + private Boolean isExternal; + + /** + * 是否缓存 + */ + @Schema(description = "是否缓存", example = "false") + private Boolean isCache; + + /** + * 是否隐藏 + */ + @Schema(description = "是否隐藏", example = "false") + private Boolean isHidden; + + /** + * 权限标识 + */ + @Schema(description = "权限标识", example = "system:user:list") + private String permission; + + /** + * 排序 + */ + @Schema(description = "排序", example = "1") + private Integer sort; + + /** + * 子路由列表 + */ + @Schema(description = "子路由列表") + private List children; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/auth/model/resp/SocialAuthAuthorizeResp.java b/continew-admin-system/src/main/java/top/continew/admin/auth/model/resp/SocialAuthAuthorizeResp.java new file mode 100644 index 0000000..8d2ecc2 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/auth/model/resp/SocialAuthAuthorizeResp.java @@ -0,0 +1,29 @@ +package top.continew.admin.auth.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 三方账号授权认证响应信息 + * + * @author Charles7c + * @since 2024/3/6 22:26 + */ +@Data +@Builder +@Schema(description = "三方账号授权认证响应信息") +public class SocialAuthAuthorizeResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 授权 URL + */ + @Schema(description = "授权 URL", example = "https://gitee.com/oauth/authorize?response_type=code&client_id=5d271b7f638941812aaf8bfc2e2f08f06d6235ef934e0e39537e2364eb8452c4&redirect_uri=http://localhost:5173/social/callback?source=gitee&state=d4ea7129e2531050210e9c918cc007d7&scope=user_info") + private String authorizeUrl; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/auth/model/resp/UserInfoResp.java b/continew-admin-system/src/main/java/top/continew/admin/auth/model/resp/UserInfoResp.java new file mode 100644 index 0000000..4f83a11 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/auth/model/resp/UserInfoResp.java @@ -0,0 +1,147 @@ +package top.continew.admin.auth.model.resp; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.common.enums.GenderEnum; +import top.continew.starter.security.mask.annotation.JsonMask; +import top.continew.starter.security.mask.enums.MaskType; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Set; + +/** + * 用户信息 + * + * @author Charles7c + * @since 2022/12/29 20:15 + */ +@Data +@Schema(description = "用户信息") +public class UserInfoResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @Schema(description = "ID", example = "1") + private Long id; + + /** + * 用户名 + */ + @Schema(description = "用户名", example = "zhangsan") + private String username; + + /** + * 昵称 + */ + @Schema(description = "昵称", example = "张三") + private String nickname; + + /** + * 性别 + */ + @Schema(description = "性别", example = "1") + private GenderEnum gender; + + /** + * 邮箱 + */ + @Schema(description = "邮箱", example = "c*******@126.com") + @JsonMask(MaskType.EMAIL) + private String email; + + /** + * 手机号码 + */ + @Schema(description = "手机号码", example = "188****8888") + @JsonMask(MaskType.MOBILE_PHONE) + private String phone; + + /** + * 头像地址 + */ + @Schema(description = "头像地址", example = "https://himg.bdimg.com/sys/portrait/item/public.1.81ac9a9e.rf1ix17UfughLQjNo7XQ_w.jpg") + private String avatar; + + /** + * 描述 + */ + @Schema(description = "描述", example = "张三描述信息") + private String description; + + /** + * 最后一次修改密码时间 + */ + @Schema(description = "最后一次修改密码时间", example = "2023-08-08 08:08:08", type = "string") + private LocalDateTime pwdResetTime; + + /** + * 密码是否已过期 + */ + @Schema(description = "密码是否已过期", example = "true") + private Boolean pwdExpired; + + /** + * 创建时间 + */ + @JsonIgnore + private LocalDateTime createTime; + + /** + * 注册日期 + */ + @Schema(description = "注册日期", example = "2023-08-08") + private LocalDate registrationDate; + + /** + * 部门 ID + */ + @Schema(description = "部门 ID", example = "1") + private Long deptId; + + /** + * 所属部门 + */ + @Schema(description = "所属部门", example = "测试部") + private String deptName; + + /** + * 所属部门集合 + */ + @Schema(description = "所属部门集合", example = "[{\"deptId\":1,\"name\":\"测试部\"},{\"deptId\":2,\"name\":\"研发部\"}]") + private DeptInfo[] deptInfos; + + /** + * 所属数据库 + */ + @Schema(description = "所属数据库", example = "kexue") + private String dbName; + /** + * 权限码集合 + */ + @Schema(description = "权限码集合", example = "[\"system:user:list\",\"system:user:add\"]") + private Set permissions; + + /** + * 角色编码集合 + */ + @Schema(description = "角色编码集合", example = "[\"test\"]") + private Set roles; + + /** + * 学院与每个页面的绑定 + */ + @Schema(description = "学院与每个页面的绑定", example = "学院与每个页面的绑定") + private String routers; + + public LocalDate getRegistrationDate() { + return createTime.toLocalDate(); + } +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/auth/service/LoginService.java b/continew-admin-system/src/main/java/top/continew/admin/auth/service/LoginService.java new file mode 100644 index 0000000..ffd5a9a --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/auth/service/LoginService.java @@ -0,0 +1,98 @@ +package top.continew.admin.auth.service; + +import jakarta.servlet.http.HttpServletRequest; +import me.zhyd.oauth.model.AuthUser; +import top.continew.admin.auth.model.req.EmailRegisterReq; +import top.continew.admin.auth.model.req.PhoneRegisterReq; +import top.continew.admin.auth.model.req.UserRegisterReq; +import top.continew.admin.auth.model.resp.RouteResp; +import top.continew.admin.common.model.dto.LoginUser; + +import java.util.List; + +/** + * 登录业务接口 + * + * @author Charles7c + * @since 2022/12/21 21:48 + */ +public interface LoginService { + + /** + * 账号登录 + * + * @param username 用户名 + * @param password 密码 + * @param request 请求对象 + * @return 令牌 + */ + LoginUser accountLogin(String username, String password, HttpServletRequest request); + + /** + * 手机号登录 + * + * @param phone 手机号 + * @return 令牌 + */ + LoginUser phoneLogin(String phone); + + /** + * 邮箱登录 + * + * @param email 邮箱 + * @return 令牌 + */ + LoginUser emailLogin(String email); + + /** + * 三方账号登录 + * + * @param authUser 三方账号信息 + * @return 令牌 + */ + LoginUser socialLogin(AuthUser authUser); + + /** + * 构建路由树 + * + * @param userId 用户 ID + * @return 路由树 + */ + List buildRouteTree(Long userId); + + /** + * 简单登录 + * + * @param userId 用户id + * @return LoginUser + */ + LoginUser simpleLogin(Long userId); + + /** + * 用户名密码注册 + * + * @param registerReq 注册信息 + * @param request 请求对象 + * @return 登录用户信息 + */ + LoginUser userRegister(UserRegisterReq registerReq, HttpServletRequest request); + + /** + * 手机号注册 + * + * @param registerReq 注册信息 + * @param request 请求对象 + * @return 登录用户信息 + */ + LoginUser phoneRegister(PhoneRegisterReq registerReq, HttpServletRequest request); + + /** + * 邮箱注册 + * + * @param registerReq 注册信息 + * @param request 请求对象 + * @return 登录用户信息 + */ + LoginUser emailRegister(EmailRegisterReq registerReq, HttpServletRequest request); + +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/auth/service/OnlineUserService.java b/continew-admin-system/src/main/java/top/continew/admin/auth/service/OnlineUserService.java new file mode 100644 index 0000000..aa54afc --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/auth/service/OnlineUserService.java @@ -0,0 +1,51 @@ +package top.continew.admin.auth.service; + +import top.continew.admin.auth.model.query.OnlineUserQuery; +import top.continew.admin.auth.model.resp.OnlineUserResp; +import top.continew.admin.common.model.dto.LoginUser; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 在线用户业务接口 + * + * @author Charles7c + * @since 2023/3/25 22:48 + */ +public interface OnlineUserService { + + /** + * 分页查询列表 + * + * @param query 查询条件 + * @param pageQuery 分页查询条件 + * @return 分页列表信息 + */ + PageResp page(OnlineUserQuery query, PageQuery pageQuery); + + /** + * 查询列表 + * + * @param query 查询条件 + * @return 列表信息 + */ + List list(OnlineUserQuery query); + + /** + * 查询 Token 最后活跃时间 + * + * @param token Token + * @return 最后活跃时间 + */ + LocalDateTime getLastActiveTime(String token); + + /** + * 踢出用户 + * + * @param userId 用户 ID + */ + void kickOut(Long userId); +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/auth/service/impl/LoginServiceImpl.java b/continew-admin-system/src/main/java/top/continew/admin/auth/service/impl/LoginServiceImpl.java new file mode 100644 index 0000000..ab6a990 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/auth/service/impl/LoginServiceImpl.java @@ -0,0 +1,626 @@ +package top.continew.admin.auth.service.impl; + +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.tree.Tree; +import cn.hutool.core.lang.tree.TreeNodeConfig; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.ReUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.servlet.JakartaServletUtil; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import me.zhyd.oauth.model.AuthUser; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import top.continew.admin.auth.model.req.EmailRegisterReq; +import top.continew.admin.auth.model.req.PhoneRegisterReq; +import top.continew.admin.auth.model.req.UserRegisterReq; +import top.continew.admin.auth.model.resp.RouteResp; +import top.continew.admin.auth.service.LoginService; +import top.continew.admin.common.constant.CacheConstants; +import top.continew.admin.common.constant.RegexConstants; +import top.continew.admin.common.constant.SysConstants; +import top.continew.admin.common.util.SecureUtils; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.admin.common.enums.GenderEnum; +import top.continew.admin.common.model.dto.LoginUser; +import top.continew.admin.common.model.dto.RoleDTO; +import top.continew.admin.common.util.helper.LoginHelper; +import top.continew.admin.system.enums.MenuTypeEnum; +import top.continew.admin.system.enums.MessageTemplateEnum; +import top.continew.admin.system.enums.MessageTypeEnum; +import top.continew.admin.system.enums.PasswordPolicyEnum; +import top.continew.admin.system.mapper.TaskPreDeductMapper; +import top.continew.admin.system.model.entity.DeptDO; +import top.continew.admin.system.model.entity.InviteCode; +import top.continew.admin.system.model.entity.RoleDO; +import top.continew.admin.system.model.entity.UserDO; +import top.continew.admin.system.model.entity.UserSocialDO; +import top.continew.admin.system.model.req.MessageReq; +import top.continew.admin.system.model.resp.MenuResp; +import top.continew.admin.system.service.*; +import top.continew.admin.system.mapper.InviteCodeMapper; +import top.continew.starter.cache.redisson.util.RedisUtils; +import top.continew.starter.core.autoconfigure.project.ProjectProperties; +import top.continew.starter.core.util.ExceptionUtils; +import top.continew.starter.core.util.validate.CheckUtils; +import top.continew.starter.core.util.validate.ValidationUtils; +import top.continew.starter.extension.crud.annotation.TreeField; +import top.continew.starter.extension.crud.util.TreeUtils; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import top.continew.starter.messaging.websocket.util.WebSocketUtils; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import static top.continew.admin.system.enums.PasswordPolicyEnum.PASSWORD_EXPIRATION_DAYS; + +/** + * 登录业务实现 + * + * @author Charles7c + * @since 2022/12/21 21:49 + */ +@Service +@RequiredArgsConstructor +public class LoginServiceImpl implements LoginService { + + private final ProjectProperties projectProperties; + private final ThreadPoolTaskExecutor threadPoolTaskExecutor; + private final UserService userService; + private final DeptService deptService; + private final RoleService roleService; + private final MenuService menuService; + private final UserRoleService userRoleService; + private final UserSocialService userSocialService; + private final OptionService optionService; + private final MessageService messageService; + private final TaskPreDeductMapper taskPreDeductMapper; + private final InviteCodeMapper inviteCodeMapper; + + @Override + //@DS("#school") + public LoginUser accountLogin(String username, String password, HttpServletRequest request) { + //todo + //CheckUtils.throwIf(!ContainerConstants.schools.contains(school), "学校不存在"); + UserDO user = userService.getByUsername(username); + boolean isError = ObjectUtil.isNull(user) || !ObjectUtil.equal(password, user.getPassword()); + this.checkUserLocked(username, request, isError); + CheckUtils.throwIf(isError, "用户名或密码错误"); + this.checkUserStatus(user); + return this.login(user); + } + + @Override + public LoginUser phoneLogin(String phone) { + UserDO user = userService.getByPhone(phone); + CheckUtils.throwIfNull(user, "此手机号未绑定本系统账号"); + this.checkUserStatus(user); + return this.login(user); + } + + @Override + public LoginUser emailLogin(String email) { + UserDO user = userService.getByEmail(email); + CheckUtils.throwIfNull(user, "此邮箱未绑定本系统账号"); + this.checkUserStatus(user); + return this.login(user); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public LoginUser socialLogin(AuthUser authUser) { + String source = authUser.getSource(); + String openId = authUser.getUuid(); + UserSocialDO userSocial = userSocialService.getBySourceAndOpenId(source, openId); + UserDO user; + if (null == userSocial) { + String username = authUser.getUsername(); + String nickname = authUser.getNickname(); + UserDO existsUser = userService.getByUsername(username); + String randomStr = RandomUtil.randomString(RandomUtil.BASE_CHAR, 5); + if (null != existsUser || !ReUtil.isMatch(RegexConstants.USERNAME, username)) { + username = randomStr + IdUtil.fastSimpleUUID(); + } + if (!ReUtil.isMatch(RegexConstants.GENERAL_NAME, nickname)) { + nickname = source.toLowerCase() + randomStr; + } + user = new UserDO(); + user.setUsername(username); + user.setNickname(nickname); + user.setGender(GenderEnum.valueOf(authUser.getGender().name())); + user.setAvatar(authUser.getAvatar()); + user.setDeptId(SysConstants.SUPER_DEPT_ID); + Long userId = userService.add(user); + RoleDO role = roleService.getByCode(SysConstants.ADMIN_ROLE_CODE); + userRoleService.add(Collections.singletonList(role.getId()), userId); + userSocial = new UserSocialDO(); + userSocial.setUserId(userId); + userSocial.setSource(source); + userSocial.setOpenId(openId); + this.sendSecurityMsg(user); + } else { + user = BeanUtil.copyProperties(userService.getById(userSocial.getUserId()), UserDO.class); + } + this.checkUserStatus(user); + userSocial.setMetaJson(JSONUtil.toJsonStr(authUser)); + userSocial.setLastLoginTime(LocalDateTime.now()); + userSocialService.saveOrUpdate(userSocial); + return this.login(user); + } + + @Override + public List buildRouteTree(Long userId) { + Set roleCodeSet = roleService.listCodeByUserId(userId); + if (CollUtil.isEmpty(roleCodeSet)) { + return new ArrayList<>(0); + } + // 查询菜单列表 + Set menuSet = new LinkedHashSet<>(); + if (roleCodeSet.contains(SysConstants.ADMIN_ROLE_CODE)) { + menuSet.addAll(menuService.listAll()); + } else { + roleCodeSet.forEach(roleCode -> menuSet.addAll(menuService.listByRoleCode(roleCode))); + } + List menuList = menuSet.stream().filter(m -> !MenuTypeEnum.BUTTON.equals(m.getType())).toList(); + // 构建路由树 + TreeField treeField = MenuResp.class.getDeclaredAnnotation(TreeField.class); + TreeNodeConfig treeNodeConfig = TreeUtils.genTreeNodeConfig(treeField); + List> treeList = TreeUtils.build(menuList, treeNodeConfig, (m, tree) -> { + tree.setId(m.getId()); + tree.setParentId(m.getParentId()); + tree.setName(m.getTitle()); + tree.setWeight(m.getSort()); + tree.putExtra("type", m.getType().getValue()); + tree.putExtra("path", m.getPath()); + tree.putExtra("name", m.getName()); + tree.putExtra("component", m.getComponent()); + tree.putExtra("redirect", m.getRedirect()); + tree.putExtra("icon", m.getIcon()); + tree.putExtra("isExternal", m.getIsExternal()); + tree.putExtra("isCache", m.getIsCache()); + tree.putExtra("isHidden", m.getIsHidden()); + tree.putExtra("permission", m.getPermission()); + }); + return BeanUtil.copyToList(treeList, RouteResp.class); + } + + /** + * 登录并缓存用户信息 + * + * @param user 用户信息 + * @return 令牌 + */ + private LoginUser login(UserDO user) { + Long userId = user.getId(); + + // 用户登录时重置预扣减余额为0 + try { + taskPreDeductMapper.resetPreDeductByUserId(userId); + } catch (Exception e) { + // 忽略异常,确保登录流程正常 + } + + CompletableFuture> permissionFuture = CompletableFuture.supplyAsync(() -> roleService + .listPermissionByUserId(userId), threadPoolTaskExecutor); + CompletableFuture> roleFuture = CompletableFuture.supplyAsync(() -> roleService + .listByUserId(userId), threadPoolTaskExecutor); + CompletableFuture passwordExpirationDaysFuture = CompletableFuture.supplyAsync(() -> optionService + .getValueByCode2Int(PASSWORD_EXPIRATION_DAYS.name()), threadPoolTaskExecutor); + CompletableFuture.allOf(permissionFuture, roleFuture, passwordExpirationDaysFuture); + LoginUser loginUser = new LoginUser(permissionFuture.join(), roleFuture.join(), passwordExpirationDaysFuture + .join()); + + if (loginUser.getRoleCodes() != null && !loginUser.getRoleCodes().isEmpty()) { + for (String roleCode : loginUser.getRoleCodes()) { + if ("teacher".equals(roleCode)) { + List deptIdS = deptService.listChildren(user.getDeptId()) + .stream() + .map(DeptDO::getId) + .toList(); + loginUser.setModelDeptIds(deptIdS); + } else if ("student".equals(roleCode)) { + DeptDO dept = deptService.getById(user.getDeptId()); + loginUser.setModelDeptIds(CollUtil.toList(dept.getParentId())); + } + } + } + BeanUtil.copyProperties(user, loginUser); + if (user.getDeptId() != null) { + DeptDO dept = deptService.getById(user.getDeptId()); + loginUser.setRouters(dept.getRouters()); + } + return LoginHelper.login(loginUser); + } + + /** + * 检查用户状态 + * + * @param user 用户信息 + */ + private void checkUserStatus(UserDO user) { + CheckUtils.throwIfEqual(DisEnableStatusEnum.DISABLE, user.getStatus(), "此账号已被禁用,如有疑问,请联系管理员"); + DeptDO dept = deptService.getById(user.getDeptId()); + CheckUtils.throwIfEqual(DisEnableStatusEnum.DISABLE, dept.getStatus(), "此账号所属部门已被禁用,如有疑问,请联系管理员"); + } + + /** + * 检测用户是否已被锁定 + * + * @param username 用户名 + * @param request 请求对象 + * @param isError 是否登录错误 + */ + private void checkUserLocked(String username, HttpServletRequest request, boolean isError) { + // 不锁定 + int maxErrorCount = optionService.getValueByCode2Int(PasswordPolicyEnum.PASSWORD_ERROR_LOCK_COUNT.name()); + if (maxErrorCount <= SysConstants.NO) { + return; + } + // 检测是否已被锁定 + String key = CacheConstants.USER_PASSWORD_ERROR_KEY_PREFIX + RedisUtils.formatKey(username, JakartaServletUtil + .getClientIP(request)); + int lockMinutes = optionService.getValueByCode2Int(PasswordPolicyEnum.PASSWORD_ERROR_LOCK_MINUTES.name()); + Integer currentErrorCount = ObjectUtil.defaultIfNull(RedisUtils.get(key), 0); + CheckUtils.throwIf(currentErrorCount >= maxErrorCount, "账号锁定 {} 分钟,请稍后再试", lockMinutes); + // 登录成功清除计数 + if (!isError) { + RedisUtils.delete(key); + return; + } + // 登录失败递增计数 + currentErrorCount++; + RedisUtils.set(key, currentErrorCount, Duration.ofMinutes(lockMinutes)); + CheckUtils.throwIf(currentErrorCount >= maxErrorCount, "密码错误已达 {} 次,账号锁定 {} 分钟", maxErrorCount, lockMinutes); + } + + /** + * 发送安全消息 + * + * @param user 用户信息 + */ + private void sendSecurityMsg(UserDO user) { + MessageReq req = new MessageReq(); + MessageTemplateEnum socialRegister = MessageTemplateEnum.SOCIAL_REGISTER; + req.setTitle(socialRegister.getTitle().formatted(projectProperties.getName())); + req.setContent(socialRegister.getContent().formatted(user.getNickname())); + req.setType(MessageTypeEnum.SECURITY); + messageService.add(req, CollUtil.toList(user.getId())); + List tokenList = StpUtil.getTokenValueListByLoginId(user.getId()); + for (String token : tokenList) { + WebSocketUtils.sendMessage(token, "1"); + } + } + + /** + * 简单登录 + * + * @param userId 用户id + */ + @Override + public LoginUser simpleLogin(Long userId) { + UserDO user = userService.getById(userId); + this.checkUserStatus(user); + return this.login(user); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public LoginUser userRegister(UserRegisterReq registerReq, HttpServletRequest request) { + // 验证验证码 + this.validateCaptcha(registerReq.getCaptcha(), registerReq.getUuid()); + + // 解密密码 + String rawPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(registerReq + .getPassword())); + ValidationUtils.throwIfBlank(rawPassword, "密码解密失败"); + String rawConfirmPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(registerReq + .getConfirmPassword())); + ValidationUtils.throwIfBlank(rawConfirmPassword, "确认密码解密失败"); + + // 验证密码一致性 + ValidationUtils.throwIfNotEqual(rawPassword, rawConfirmPassword, "两次输入的密码不一致"); + + // 验证密码强度 + ValidationUtils.throwIf(!ReUtil + .isMatch(RegexConstants.PASSWORD, rawPassword), "密码长度为 8-32 个字符,支持大小写字母、数字、特殊字符,至少包含字母和数字"); + + // 检查用户名唯一性 + CheckUtils.throwIfNotNull(userService.getByUsername(registerReq.getUsername()), "用户名 [{}] 已存在", registerReq + .getUsername()); + + // 检查邮箱唯一性(如果提供) + if (StrUtil.isNotBlank(registerReq.getEmail())) { + CheckUtils.throwIfNotNull(userService.getByEmail(registerReq.getEmail()), "邮箱 [{}] 已存在", registerReq + .getEmail()); + } + + // 检查手机号唯一性(如果提供) + if (StrUtil.isNotBlank(registerReq.getPhone())) { + CheckUtils.throwIfNotNull(userService.getByPhone(registerReq.getPhone()), "手机号 [{}] 已存在", registerReq + .getPhone()); + } + + // 创建用户 + UserDO user = this.createUser(registerReq.getUsername(), registerReq.getNickname(), rawPassword, registerReq + .getGender(), registerReq.getEmail(), registerReq.getPhone(), registerReq.getDeptId()); + + // 分配默认角色 + this.assignDefaultRole(user.getId()); + + // 发送注册成功消息 + this.sendRegisterSuccessMsg(user); + + return this.login(user); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public LoginUser phoneRegister(PhoneRegisterReq registerReq, HttpServletRequest request) { + // 验证手机验证码 + this.validateSmsCaptcha(registerReq.getCaptcha(), registerReq.getPhone()); + + // 验证邀请码 + this.validateInviteCode(registerReq.getInviteCode()); + + // 解密密码 + // String rawPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(registerReq.getPassword())); + // ValidationUtils.throwIfBlank(rawPassword, "密码解密失败"); + // String rawConfirmPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(registerReq.getConfirmPassword())); + // ValidationUtils.throwIfBlank(rawConfirmPassword, "确认密码解密失败"); + // + // // 验证密码一致性 + // ValidationUtils.throwIfNotEqual(rawPassword, rawConfirmPassword, "两次输入的密码不一致"); + // + // // 验证密码强度 + // ValidationUtils.throwIf(!ReUtil.isMatch(RegexConstants.PASSWORD, rawPassword), + // "密码长度为 8-32 个字符,支持大小写字母、数字、特殊字符,至少包含字母和数字"); + + // 检查手机号唯一性 + CheckUtils.throwIfNotNull(userService.getByPhone(registerReq.getPhone()), "手机号 [{}] 已存在", registerReq + .getPhone()); + + // 创建用户 + UserDO user = new UserDO(); + user.setPhone(registerReq.getPhone()); + user.setUsername(registerReq.getPhone()); + user.setPassword("123456"); + user.setNickname(registerReq.getPhone()); + user.setDeptId(1L); + user.setStatus(DisEnableStatusEnum.ENABLE); + user.setIsSystem(false); + user.setSysBeans(0); + user.setPayBeans(0); + user.setInviteBeans(0); + user.setPreDeductBeans(0); + Long userId = userService.add(user); + user.setId(userId); + + // 分配默认角色 + this.assignTeacherRole(user.getId()); + + // 发送注册成功消息 + this.sendRegisterSuccessMsg(user); + + // 更新邀请码状态 + if (StrUtil.isNotBlank(registerReq.getInviteCode())) { + InviteCode existingInviteCode = inviteCodeMapper.selectOne(new LambdaQueryWrapper() + .eq(InviteCode::getInviteCode, registerReq.getInviteCode()) + .eq(InviteCode::getIsUsed, 0)); + if (existingInviteCode != null) { + existingInviteCode.setIsUsed(1); + existingInviteCode.setUserId(userId); + existingInviteCode.setUsedTime(new Date()); + inviteCodeMapper.updateById(existingInviteCode); + } + } + return this.login(user); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public LoginUser emailRegister(EmailRegisterReq registerReq, HttpServletRequest request) { + // 验证邮箱验证码 + this.validateEmailCaptcha(registerReq.getCaptcha(), registerReq.getEmail()); + + // 解密密码 + String rawPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(registerReq + .getPassword())); + ValidationUtils.throwIfBlank(rawPassword, "密码解密失败"); + String rawConfirmPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(registerReq + .getConfirmPassword())); + ValidationUtils.throwIfBlank(rawConfirmPassword, "确认密码解密失败"); + + // 验证密码一致性 + ValidationUtils.throwIfNotEqual(rawPassword, rawConfirmPassword, "两次输入的密码不一致"); + + // 验证密码强度 + ValidationUtils.throwIf(!ReUtil + .isMatch(RegexConstants.PASSWORD, rawPassword), "密码长度为 8-32 个字符,支持大小写字母、数字、特殊字符,至少包含字母和数字"); + + // 检查邮箱唯一性 + CheckUtils.throwIfNotNull(userService.getByEmail(registerReq.getEmail()), "邮箱 [{}] 已存在", registerReq + .getEmail()); + + // 生成用户名(邮箱前缀作为用户名) + String username = StrUtil.subBefore(registerReq.getEmail(), "@", false); + // 如果用户名已存在,添加随机后缀 + if (userService.getByUsername(username) != null) { + username = username + RandomUtil.randomString(RandomUtil.BASE_CHAR, 5); + } + + // 创建用户 + UserDO user = this.createUser(username, registerReq.getNickname(), rawPassword, registerReq + .getGender(), registerReq.getEmail(), null, registerReq.getDeptId()); + + // 分配默认角色 + this.assignDefaultRole(user.getId()); + + // 发送注册成功消息 + this.sendRegisterSuccessMsg(user); + + return this.login(user); + } + + /** + * 验证邀请码 + * + * @param inviteCode 邀请码 + */ + private void validateInviteCode(String inviteCode) { + // 如果邀请码为空,直接返回(可选参数) + if (StrUtil.isBlank(inviteCode)) { + return; + } + + // 查询邀请码是否存在 + InviteCode existingInviteCode = inviteCodeMapper.selectOne(new LambdaQueryWrapper() + .eq(InviteCode::getInviteCode, inviteCode) + .eq(InviteCode::getIsUsed, 0)); + + CheckUtils.throwIfNull(existingInviteCode, "邀请码 [{}] 不存在或已失效", inviteCode); + } + + /** + * 验证图片验证码 + * + * @param captcha 验证码 + * @param uuid 验证码标识 + */ + private void validateCaptcha(String captcha, String uuid) { + String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + uuid; + String cachedCaptcha = RedisUtils.get(captchaKey); + ValidationUtils.throwIfBlank(cachedCaptcha, "验证码已过期"); + RedisUtils.delete(captchaKey); + ValidationUtils.throwIfNotEqualIgnoreCase(captcha, cachedCaptcha, "验证码错误"); + } + + /** + * 验证短信验证码 + * + * @param captcha 验证码 + * @param phone 手机号 + */ + private void validateSmsCaptcha(String captcha, String phone) { + String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + phone; + String cachedCaptcha = RedisUtils.get(captchaKey); + ValidationUtils.throwIfBlank(cachedCaptcha, "验证码已过期"); + RedisUtils.delete(captchaKey); + ValidationUtils.throwIfNotEqualIgnoreCase(captcha, cachedCaptcha, "验证码错误"); + } + + /** + * 验证邮箱验证码 + * + * @param captcha 验证码 + * @param email 邮箱 + */ + private void validateEmailCaptcha(String captcha, String email) { + String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + email; + String cachedCaptcha = RedisUtils.get(captchaKey); + ValidationUtils.throwIfBlank(cachedCaptcha, "验证码已过期"); + RedisUtils.delete(captchaKey); + ValidationUtils.throwIfNotEqualIgnoreCase(captcha, cachedCaptcha, "验证码错误"); + } + + /** + * 创建用户 + * + * @param username 用户名 + * @param nickname 昵称 + * @param password 密码 + * @param gender 性别 + * @param email 邮箱 + * @param phone 手机号 + * @param deptId 部门ID + * @return 用户信息 + */ + private UserDO createUser(String username, + String nickname, + String password, + GenderEnum gender, + String email, + String phone, + Long deptId) { + UserDO user = new UserDO(); + user.setUsername(username); + user.setNickname(nickname); + user.setPassword(password); + user.setGender(gender); + user.setEmail(email); + user.setPhone(phone); + user.setDeptId(ObjectUtil.defaultIfNull(deptId, SysConstants.SUPER_DEPT_ID)); + user.setStatus(DisEnableStatusEnum.ENABLE); + user.setIsSystem(false); + user.setSysBeans(0); + user.setPayBeans(0); + user.setInviteBeans(0); + user.setPreDeductBeans(0); + + Long userId = userService.add(user); + user.setId(userId); + return user; + } + + /** + * 分配teacher角色 + * + * @param userId 用户ID + */ + private void assignTeacherRole(Long userId) { + // 查找默认角色(这里可以根据业务需求调整) + RoleDO defaultRole = roleService.getByCode("teacher"); // 假设有一个默认的用户角色 + if (defaultRole == null) { + // 如果没有默认角色,可以分配管理员角色或创建一个默认角色 + defaultRole = roleService.getByCode(SysConstants.ADMIN_ROLE_CODE); + } + + if (defaultRole != null) { + userRoleService.add(Collections.singletonList(defaultRole.getId()), userId); + } + } + + /** + * 分配默认角色 + * + * @param userId 用户ID + */ + private void assignDefaultRole(Long userId) { + // 查找默认角色(这里可以根据业务需求调整) + RoleDO defaultRole = roleService.getByCode("student"); // 假设有一个默认的用户角色 + if (defaultRole == null) { + // 如果没有默认角色,可以分配管理员角色或创建一个默认角色 + defaultRole = roleService.getByCode(SysConstants.ADMIN_ROLE_CODE); + } + + if (defaultRole != null) { + userRoleService.add(Collections.singletonList(defaultRole.getId()), userId); + } + } + + /** + * 发送注册成功消息 + * + * @param user 用户信息 + */ + private void sendRegisterSuccessMsg(UserDO user) { + MessageReq req = new MessageReq(); + req.setTitle("注册成功通知"); + req.setContent("恭喜您成功注册 " + projectProperties.getName() + " 账号!用户名:" + user.getUsername()); + req.setType(MessageTypeEnum.SECURITY); + messageService.add(req, CollUtil.toList(user.getId())); + } +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/auth/service/impl/OnlineUserServiceImpl.java b/continew-admin-system/src/main/java/top/continew/admin/auth/service/impl/OnlineUserServiceImpl.java new file mode 100644 index 0000000..0c275f3 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/auth/service/impl/OnlineUserServiceImpl.java @@ -0,0 +1,113 @@ +package top.continew.admin.auth.service.impl; + +import cn.crane4j.annotation.AutoOperate; +import cn.dev33.satoken.dao.SaTokenDao; +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import top.continew.admin.auth.model.query.OnlineUserQuery; +import top.continew.admin.auth.model.resp.OnlineUserResp; +import top.continew.admin.auth.service.OnlineUserService; +import top.continew.admin.common.model.dto.LoginUser; +import top.continew.admin.common.util.helper.LoginHelper; +import top.continew.starter.core.constant.StringConstants; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Date; +import java.util.List; + +/** + * 在线用户业务实现 + * + * @author Charles7c + * @since 2023/3/25 22:49 + */ +@Service +@RequiredArgsConstructor +public class OnlineUserServiceImpl implements OnlineUserService { + + @Override + @AutoOperate(type = OnlineUserResp.class, on = "list") + public PageResp page(OnlineUserQuery query, PageQuery pageQuery) { + List loginUserList = this.list(query); + List list = BeanUtil.copyToList(loginUserList, OnlineUserResp.class); + return PageResp.build(pageQuery.getPage(), pageQuery.getSize(), list); + } + + @Override + public List list(OnlineUserQuery query) { + List loginUserList = new ArrayList<>(); + // 查询所有登录用户 + List tokenKeyList = StpUtil.searchTokenValue(StringConstants.EMPTY, 0, -1, false); + tokenKeyList.parallelStream().forEach(tokenKey -> { + String token = StrUtil.subAfter(tokenKey, StringConstants.COLON, true); + // 忽略已过期或失效 Token + if (StpUtil.stpLogic.getTokenActiveTimeoutByToken(token) < SaTokenDao.NEVER_EXPIRE) { + return; + } + // 检查是否符合查询条件 + LoginUser loginUser = LoginHelper.getLoginUser(token); + if (this.isMatchQuery(query, loginUser)) { + loginUserList.add(loginUser); + } + }); + // 设置排序 + CollUtil.sort(loginUserList, Comparator.comparing(LoginUser::getLoginTime).reversed()); + return loginUserList; + } + + @Override + public LocalDateTime getLastActiveTime(String token) { + long lastActiveTime = StpUtil.getStpLogic().getTokenLastActiveTime(token); + return lastActiveTime == SaTokenDao.NOT_VALUE_EXPIRE ? null : DateUtil.date(lastActiveTime).toLocalDateTime(); + } + + @Override + public void kickOut(Long userId) { + if (!StpUtil.isLogin(userId)) { + return; + } + StpUtil.logout(userId); + } + + /** + * 是否符合查询条件 + * + * @param query 查询条件 + * @param loginUser 登录用户信息 + * @return 是否符合查询条件 + */ + private boolean isMatchQuery(OnlineUserQuery query, LoginUser loginUser) { + boolean flag1 = true; + String nickname = query.getNickname(); + if (StrUtil.isNotBlank(nickname)) { + flag1 = StrUtil.contains(loginUser.getUsername(), nickname) || StrUtil.contains(LoginHelper + .getNickname(loginUser.getId()), nickname); + } + boolean flag2 = true; + List loginTime = query.getLoginTime(); + if (CollUtil.isNotEmpty(loginTime)) { + flag2 = DateUtil.isIn(DateUtil.date(loginUser.getLoginTime()).toJdkDate(), loginTime.get(0), loginTime + .get(1)); + } + boolean flag3 = true; + Long userId = query.getUserId(); + if (null != userId) { + flag3 = userId.equals(loginUser.getId()); + } + boolean flag4 = true; + Long roleId = query.getRoleId(); + if (null != roleId) { + flag4 = loginUser.getRoles().stream().anyMatch(r -> r.getId().equals(roleId)); + } + return flag1 && flag2 && flag3 && flag4; + } +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/config/file/FileRecorderImpl.java b/continew-admin-system/src/main/java/top/continew/admin/system/config/file/FileRecorderImpl.java new file mode 100644 index 0000000..725d392 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/config/file/FileRecorderImpl.java @@ -0,0 +1,114 @@ +package top.continew.admin.system.config.file; + +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ClassUtil; +import cn.hutool.core.util.EscapeUtil; +import cn.hutool.core.util.StrUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.x.file.storage.core.FileInfo; +import org.dromara.x.file.storage.core.recorder.FileRecorder; +import org.dromara.x.file.storage.core.upload.FilePartInfo; +import org.springframework.stereotype.Component; +import top.continew.admin.common.util.helper.LoginHelper; +import top.continew.admin.system.enums.FileTypeEnum; +import top.continew.admin.system.mapper.FileMapper; +import top.continew.admin.system.mapper.StorageMapper; +import top.continew.admin.system.model.entity.FileDO; +import top.continew.admin.system.model.entity.StorageDO; +import top.continew.admin.system.model.entity.UserDO; +import top.continew.admin.system.service.UserService; +import top.continew.starter.core.constant.StringConstants; + +import java.util.Optional; + +/** + * 文件记录实现类 + * + * @author Charles7c + * @since 2023/12/24 22:31 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class FileRecorderImpl implements FileRecorder { + + private final FileMapper fileMapper; + private final StorageMapper storageMapper; + private final UserService userService; + + @Override + public boolean save(FileInfo fileInfo) { + FileDO file = new FileDO(); + String originalFilename = EscapeUtil.unescape(fileInfo.getOriginalFilename()); + file.setName(StrUtil.contains(originalFilename, StringConstants.DOT) + ? StrUtil.subBefore(originalFilename, StringConstants.DOT, true) + : originalFilename); + file.setUrl(fileInfo.getUrl()); + file.setSize(fileInfo.getSize()); + file.setExtension(fileInfo.getExt()); + file.setType(FileTypeEnum.getByExtension(file.getExtension())); + file.setThumbnailUrl(fileInfo.getThUrl()); + file.setThumbnailSize(fileInfo.getThSize()); + StorageDO storage = (StorageDO)fileInfo.getAttr().get(ClassUtil.getClassName(StorageDO.class, false)); + file.setStorageId(storage.getId()); + if (LoginHelper.getLoginUserSafely() == null) { + UserDO user = userService.getById(StpUtil.getLoginIdAsLong()); + file.setDeptId(user.getDeptId()); + } else { + file.setDeptId(LoginHelper.getLoginUserSafely().getDeptId()); + } + + file.setCreateUser(StpUtil.getLoginIdAsLong()); + file.setCreateTime(DateUtil.toLocalDateTime(fileInfo.getCreateTime())); + file.setUpdateUser(StpUtil.getLoginIdAsLong()); + file.setUpdateTime(file.getCreateTime()); + fileMapper.insert(file); + return true; + } + + @Override + public FileInfo getByUrl(String url) { + FileDO file = this.getFileByUrl(url); + if (null == file) { + return null; + } + return file.toFileInfo(storageMapper.lambdaQuery().eq(StorageDO::getId, file.getStorageId()).one().getCode()); + } + + @Override + public boolean delete(String url) { + FileDO file = this.getFileByUrl(url); + return fileMapper.lambdaUpdate().eq(FileDO::getUrl, file.getUrl()).remove(); + } + + @Override + public void update(FileInfo fileInfo) { + /* 不使用分片功能则无需重写 */ + } + + @Override + public void saveFilePart(FilePartInfo filePartInfo) { + /* 不使用分片功能则无需重写 */ + } + + @Override + public void deleteFilePartByUploadId(String s) { + /* 不使用分片功能则无需重写 */ + } + + /** + * 根据 URL 查询文件 + * + * @param url URL + * @return 文件信息 + */ + private FileDO getFileByUrl(String url) { + Optional fileOptional = fileMapper.lambdaQuery().eq(FileDO::getUrl, url).oneOpt(); + return fileOptional.orElseGet(() -> fileMapper.lambdaQuery() + .likeLeft(FileDO::getUrl, StrUtil.subAfter(url, StringConstants.SLASH, true)) + .oneOpt() + .orElse(null)); + } +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/config/file/FileStorageConfigLoader.java b/continew-admin-system/src/main/java/top/continew/admin/system/config/file/FileStorageConfigLoader.java new file mode 100644 index 0000000..d4e928d --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/config/file/FileStorageConfigLoader.java @@ -0,0 +1,41 @@ +package top.continew.admin.system.config.file; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.stereotype.Component; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.admin.system.model.query.StorageQuery; +import top.continew.admin.system.model.req.StorageReq; +import top.continew.admin.system.model.resp.StorageResp; +import top.continew.admin.system.service.StorageService; + +import java.util.List; + +/** + * 文件存储配置加载器 + * + * @author Charles7c + * @since 2023/12/24 22:31 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class FileStorageConfigLoader implements ApplicationRunner { + + private final StorageService storageService; + + @Override + public void run(ApplicationArguments args) { + StorageQuery query = new StorageQuery(); + query.setStatus(DisEnableStatusEnum.ENABLE); + List storageList = storageService.list(query, null); + if (CollUtil.isEmpty(storageList)) { + return; + } + storageList.forEach(s -> storageService.load(BeanUtil.copyProperties(s, StorageReq.class))); + } +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/config/mail/MailConfigurerImpl.java b/continew-admin-system/src/main/java/top/continew/admin/system/config/mail/MailConfigurerImpl.java new file mode 100644 index 0000000..c042557 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/config/mail/MailConfigurerImpl.java @@ -0,0 +1,44 @@ +package top.continew.admin.system.config.mail; + +import cn.hutool.core.map.MapUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import top.continew.admin.common.constant.SysConstants; +import top.continew.admin.system.service.OptionService; +import top.continew.starter.messaging.mail.core.MailConfig; +import top.continew.starter.messaging.mail.core.MailConfigurer; + +import java.util.Map; + +/** + * 邮件配置实现 + * + * @author Charles7c + * @since 2024/5/30 22:32 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class MailConfigurerImpl implements MailConfigurer { + + private final OptionService optionService; + + @Override + public MailConfig getMailConfig() { + // 查询邮件配置 + Map map = optionService.getByCategory("MAIL"); + // 封装邮件配置 + MailConfig mailConfig = new MailConfig(); + mailConfig.setProtocol(MapUtil.getStr(map, "MAIL_PROTOCOL")); + mailConfig.setHost(MapUtil.getStr(map, "MAIL_HOST")); + mailConfig.setPort(MapUtil.getInt(map, "MAIL_PORT")); + mailConfig.setUsername(MapUtil.getStr(map, "MAIL_USERNAME")); + mailConfig.setPassword(MapUtil.getStr(map, "MAIL_PASSWORD")); + mailConfig.setSslEnabled(SysConstants.YES.equals(MapUtil.getInt(map, "MAIL_SSL_ENABLED"))); + if (mailConfig.isSslEnabled()) { + mailConfig.setSslPort(MapUtil.getInt(map, "MAIL_SSL_PORT")); + } + return mailConfig; + } +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/enums/FileTypeEnum.java b/continew-admin-system/src/main/java/top/continew/admin/system/enums/FileTypeEnum.java new file mode 100644 index 0000000..ef644e8 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/enums/FileTypeEnum.java @@ -0,0 +1,64 @@ +package top.continew.admin.system.enums; + +import cn.hutool.core.util.StrUtil; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import top.continew.starter.core.enums.BaseEnum; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * 文件类型枚举 + * + * @author Charles7c + * @since 2023/12/23 13:38 + */ +@Getter +@RequiredArgsConstructor +public enum FileTypeEnum implements BaseEnum { + + /** + * 其他 + */ + UNKNOWN(1, "其他", Collections.emptyList()), + + /** + * 图片 + */ + IMAGE(2, "图片", List + .of("jpg", "jpeg", "png", "gif", "bmp", "webp", "ico", "psd", "tiff", "dwg", "jxr", "apng", "xcf")), + + /** + * 文档 + */ + DOC(3, "文档", List.of("txt", "pdf", "doc", "xls", "ppt", "docx", "xlsx", "pptx")), + + /** + * 视频 + */ + VIDEO(4, "视频", List.of("mp4", "avi", "mkv", "flv", "webm", "wmv", "m4v", "mov", "mpg", "rmvb", "3gp")), + + /** + * 音频 + */ + AUDIO(5, "音频", List.of("mp3", "flac", "wav", "ogg", "midi", "m4a", "aac", "amr", "ac3", "aiff")),; + + private final Integer value; + private final String description; + private final List extensions; + + /** + * 根据扩展名查询 + * + * @param extension 扩展名 + * @return 文件类型 + */ + public static FileTypeEnum getByExtension(String extension) { + return Arrays.stream(FileTypeEnum.values()) + .filter(t -> t.getExtensions().contains(StrUtil.emptyIfNull(extension).toLowerCase())) + .findFirst() + .orElse(FileTypeEnum.UNKNOWN); + } +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/enums/ImportPolicyEnum.java b/continew-admin-system/src/main/java/top/continew/admin/system/enums/ImportPolicyEnum.java new file mode 100644 index 0000000..7b496ab --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/enums/ImportPolicyEnum.java @@ -0,0 +1,41 @@ +package top.continew.admin.system.enums; + +import cn.hutool.core.collection.CollUtil; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import top.continew.starter.core.enums.BaseEnum; + +import java.util.List; + +/** + * 数据导入策略 + * + * @author Kils + * @since 2024-06-17 18:33 + */ +@Getter +@RequiredArgsConstructor +public enum ImportPolicyEnum implements BaseEnum { + + /** + * 跳过该行 + */ + SKIP(1, "跳过该行"), + + /** + * 修改数据 + */ + UPDATE(2, "修改数据"), + + /** + * 停止导入 + */ + EXIT(3, "停止导入"); + + private final Integer value; + private final String description; + + public boolean validate(ImportPolicyEnum importPolicy, String data, List existList) { + return this == importPolicy && CollUtil.isNotEmpty(existList) && existList.contains(data); + } +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/enums/LogStatusEnum.java b/continew-admin-system/src/main/java/top/continew/admin/system/enums/LogStatusEnum.java new file mode 100644 index 0000000..0ec9e60 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/enums/LogStatusEnum.java @@ -0,0 +1,29 @@ +package top.continew.admin.system.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import top.continew.starter.core.enums.BaseEnum; + +/** + * 操作状态枚举 + * + * @author Charles7c + * @since 2022/12/25 9:09 + */ +@Getter +@RequiredArgsConstructor +public enum LogStatusEnum implements BaseEnum { + + /** + * 成功 + */ + SUCCESS(1, "成功"), + + /** + * 失败 + */ + FAILURE(2, "失败"),; + + private final Integer value; + private final String description; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/enums/MenuTypeEnum.java b/continew-admin-system/src/main/java/top/continew/admin/system/enums/MenuTypeEnum.java new file mode 100644 index 0000000..280e87f --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/enums/MenuTypeEnum.java @@ -0,0 +1,34 @@ +package top.continew.admin.system.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import top.continew.starter.core.enums.BaseEnum; + +/** + * 菜单类型枚举 + * + * @author Charles7c + * @since 2023/2/15 20:12 + */ +@Getter +@RequiredArgsConstructor +public enum MenuTypeEnum implements BaseEnum { + + /** + * 目录 + */ + DIR(1, "目录"), + + /** + * 菜单 + */ + MENU(2, "菜单"), + + /** + * 按钮 + */ + BUTTON(3, "按钮"),; + + private final Integer value; + private final String description; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/enums/MessageTemplateEnum.java b/continew-admin-system/src/main/java/top/continew/admin/system/enums/MessageTemplateEnum.java new file mode 100644 index 0000000..4f7bc99 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/enums/MessageTemplateEnum.java @@ -0,0 +1,25 @@ +package top.continew.admin.system.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * 消息模板枚举 + * + * @author Bull-BCLS + * @since 2023/10/15 19:51 + */ +@Getter +@RequiredArgsConstructor +public enum MessageTemplateEnum { + + /** + * 第三方登录 + */ + SOCIAL_REGISTER("欢迎注册 %s", "尊敬的 %s,欢迎注册使用,请及时配置您的密码。"), + + SOCIAL_COUR("新课程:%s", "你好:%s,你有新课程:%s"); + + private final String title; + private final String content; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/enums/MessageTypeEnum.java b/continew-admin-system/src/main/java/top/continew/admin/system/enums/MessageTypeEnum.java new file mode 100644 index 0000000..3c3c70a --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/enums/MessageTypeEnum.java @@ -0,0 +1,26 @@ +package top.continew.admin.system.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import top.continew.admin.common.constant.UiConstants; +import top.continew.starter.core.enums.BaseEnum; + +/** + * 消息类型枚举 + * + * @author Charles7c + * @since 2023/11/2 20:08 + */ +@Getter +@RequiredArgsConstructor +public enum MessageTypeEnum implements BaseEnum { + + /** + * 安全消息 + */ + SECURITY(1, "安全消息", UiConstants.COLOR_PRIMARY),; + + private final Integer value; + private final String description; + private final String color; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/enums/NoticeStatusEnum.java b/continew-admin-system/src/main/java/top/continew/admin/system/enums/NoticeStatusEnum.java new file mode 100644 index 0000000..7d633bd --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/enums/NoticeStatusEnum.java @@ -0,0 +1,56 @@ +package top.continew.admin.system.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import top.continew.admin.common.constant.UiConstants; +import top.continew.starter.core.enums.BaseEnum; + +import java.time.LocalDateTime; + +/** + * 公告状态枚举 + * + * @author Charles7c + * @since 2023/8/20 10:55 + */ +@Getter +@RequiredArgsConstructor +public enum NoticeStatusEnum implements BaseEnum { + + /** + * 待发布 + */ + PENDING_RELEASE(1, "待发布", UiConstants.COLOR_PRIMARY), + + /** + * 已发布 + */ + PUBLISHED(2, "已发布", UiConstants.COLOR_SUCCESS), + + /** + * 已过期 + */ + EXPIRED(3, "已过期", UiConstants.COLOR_ERROR),; + + private final Integer value; + private final String description; + private final String color; + + /** + * 获取公告状态 + * + * @param effectiveTime 生效时间 + * @param terminateTime 终止时间 + * @return 公告状态 + */ + public static NoticeStatusEnum getStatus(LocalDateTime effectiveTime, LocalDateTime terminateTime) { + LocalDateTime now = LocalDateTime.now(); + if (effectiveTime != null && effectiveTime.isAfter(now)) { + return PENDING_RELEASE; + } + if (terminateTime != null && terminateTime.isBefore(now)) { + return EXPIRED; + } + return PUBLISHED; + } +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/enums/PasswordPolicyEnum.java b/continew-admin-system/src/main/java/top/continew/admin/system/enums/PasswordPolicyEnum.java new file mode 100644 index 0000000..837f786 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/enums/PasswordPolicyEnum.java @@ -0,0 +1,180 @@ +package top.continew.admin.system.enums; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.ReUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import top.continew.admin.common.constant.RegexConstants; +import top.continew.admin.common.constant.SysConstants; +import top.continew.admin.system.model.entity.UserDO; +import top.continew.admin.system.service.OptionService; +import top.continew.admin.system.service.UserPasswordHistoryService; +import top.continew.starter.core.util.validate.ValidationUtils; + +import java.util.Map; + +/** + * 密码策略枚举 + * + * @author Kils + * @author Charles7c + * @since 2024/5/9 11:25 + */ +@Getter +@RequiredArgsConstructor +public enum PasswordPolicyEnum { + + /** + * 登录密码错误锁定账号的次数 + */ + PASSWORD_ERROR_LOCK_COUNT("登录密码错误锁定账号的次数取值范围为 %d-%d", SysConstants.NO, 10, null), + + /** + * 登录密码错误锁定账号的时间(min) + */ + PASSWORD_ERROR_LOCK_MINUTES("登录密码错误锁定账号的时间取值范围为 %d-%d 分钟", 1, 1440, null), + + /** + * 密码有效期(天) + */ + PASSWORD_EXPIRATION_DAYS("密码有效期取值范围为 %d-%d 天", SysConstants.NO, 999, null), + + /** + * 密码到期提前提示(天) + */ + PASSWORD_EXPIRATION_WARNING_DAYS("密码到期提前提示取值范围为 %d-%d 天", SysConstants.NO, 998, null) { + @Override + public void validateRange(int value, Map policyMap) { + if (CollUtil.isEmpty(policyMap)) { + super.validateRange(value, policyMap); + return; + } + Integer passwordExpirationDays = ObjUtil.defaultIfNull(Convert.toInt(policyMap.get(PASSWORD_EXPIRATION_DAYS + .name())), SpringUtil.getBean(OptionService.class).getValueByCode2Int(PASSWORD_EXPIRATION_DAYS.name())); + if (passwordExpirationDays > SysConstants.NO) { + ValidationUtils.throwIf(value >= passwordExpirationDays, "密码到期前的提示时间应小于密码有效期"); + return; + } + super.validateRange(value, policyMap); + } + }, + + /** + * 密码最小长度 + */ + PASSWORD_MIN_LENGTH("密码最小长度取值范围为 %d-%d", 8, 32, "密码最小长度为 %d 个字符") { + @Override + public void validate(String password, int value, UserDO user) { + // 最小长度校验 + ValidationUtils.throwIf(StrUtil.length(password) < value, this.getMsg().formatted(value)); + // 完整校验 + int passwordMaxLength = this.getMax(); + ValidationUtils.throwIf(!ReUtil.isMatch(RegexConstants.PASSWORD_TEMPLATE + .formatted(value, passwordMaxLength), password), "密码长度为 {}-{} 个字符,支持大小写字母、数字、特殊字符,至少包含字母和数字", value, passwordMaxLength); + } + }, + + /** + * 密码是否必须包含特殊字符 + */ + PASSWORD_REQUIRE_SYMBOLS("密码是否必须包含特殊字符取值只能为是(%d)或否(%d)", SysConstants.NO, SysConstants.YES, "密码必须包含特殊字符") { + @Override + public void validateRange(int value, Map policyMap) { + ValidationUtils.throwIf(value != SysConstants.YES && value != SysConstants.NO, this.getDescription() + .formatted(SysConstants.YES, SysConstants.NO)); + } + + @Override + public void validate(String password, int value, UserDO user) { + ValidationUtils.throwIf(value == SysConstants.YES && !ReUtil + .isMatch(RegexConstants.SPECIAL_CHARACTER, password), this.getMsg()); + } + }, + + /** + * 密码是否允许包含正反序账号名 + */ + PASSWORD_ALLOW_CONTAIN_USERNAME("密码是否允许包含正反序账号名取值只能为是(%d)或否(%d)", SysConstants.NO, SysConstants.YES, "密码不允许包含正反序账号名") { + @Override + public void validateRange(int value, Map policyMap) { + ValidationUtils.throwIf(value != SysConstants.YES && value != SysConstants.NO, this.getDescription() + .formatted(SysConstants.YES, SysConstants.NO)); + } + + @Override + public void validate(String password, int value, UserDO user) { + if (value <= SysConstants.NO) { + String username = user.getUsername(); + ValidationUtils.throwIf(StrUtil.containsAnyIgnoreCase(password, username, StrUtil + .reverse(username)), this.getMsg()); + } + } + }, + + /** + * 密码重复使用次数 + */ + PASSWORD_REPETITION_TIMES("密码重复使用规则取值范围为 %d-%d", 3, 32, "新密码不得与历史前 %d 次密码重复") { + @Override + public void validate(String password, int value, UserDO user) { + UserPasswordHistoryService userPasswordHistoryService = SpringUtil + .getBean(UserPasswordHistoryService.class); + ValidationUtils.throwIf(userPasswordHistoryService.isPasswordReused(user.getId(), password, value), this + .getMsg() + .formatted(value)); + } + },; + + /** + * 描述 + */ + private final String description; + + /** + * 最小值 + */ + private final Integer min; + + /** + * 最大值 + */ + private final Integer max; + + /** + * 提示信息 + */ + private final String msg; + + /** + * 策略类别 + */ + public static final String CATEGORY = "PASSWORD"; + + /** + * 校验取值范围 + * + * @param value 值 + * @param policyMap 策略集合 + */ + public void validateRange(int value, Map policyMap) { + Integer minValue = this.getMin(); + Integer maxValue = this.getMax(); + ValidationUtils.throwIf(value < minValue || value > maxValue, this.getDescription() + .formatted(minValue, maxValue)); + } + + /** + * 校验 + * + * @param password 密码 + * @param value 策略值 + * @param user 用户信息 + */ + public void validate(String password, int value, UserDO user) { + // 无需校验 + } +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/enums/SocialSourceEnum.java b/continew-admin-system/src/main/java/top/continew/admin/system/enums/SocialSourceEnum.java new file mode 100644 index 0000000..9ec6510 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/enums/SocialSourceEnum.java @@ -0,0 +1,27 @@ +package top.continew.admin.system.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * 第三方账号平台枚举 + * + * @author Charles7c + * @since 2023/10/19 21:22 + */ +@Getter +@RequiredArgsConstructor +public enum SocialSourceEnum { + + /** + * 码云 + */ + GITEE("码云"), + + /** + * GitHub + */ + GITHUB("GitHub"),; + + private final String description; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/enums/StorageTypeEnum.java b/continew-admin-system/src/main/java/top/continew/admin/system/enums/StorageTypeEnum.java new file mode 100644 index 0000000..5d84a18 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/enums/StorageTypeEnum.java @@ -0,0 +1,29 @@ +package top.continew.admin.system.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import top.continew.starter.core.enums.BaseEnum; + +/** + * 存储类型枚举 + * + * @author Charles7c + * @since 2023/12/27 21:45 + */ +@Getter +@RequiredArgsConstructor +public enum StorageTypeEnum implements BaseEnum { + + /** + * 兼容S3协议存储 + */ + S3(1, "兼容S3协议存储"), + + /** + * 本地存储 + */ + LOCAL(2, "本地存储"),; + + private final Integer value; + private final String description; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/mapper/DeptMapper.java b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/DeptMapper.java new file mode 100644 index 0000000..fd0bb25 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/DeptMapper.java @@ -0,0 +1,17 @@ +package top.continew.admin.system.mapper; + +import top.continew.admin.system.model.entity.DeptDO; +import top.continew.starter.data.mp.base.BaseMapper; + +/** + * 部门 Mapper + * + * @author Charles7c + * @since 2023/1/22 17:56 + */ +public interface DeptMapper extends BaseMapper { + + DeptDO selectDoById(Long id); + + DeptDO selectParentsById(Long id); +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/mapper/DictItemMapper.java b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/DictItemMapper.java new file mode 100644 index 0000000..f826271 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/DictItemMapper.java @@ -0,0 +1,25 @@ +package top.continew.admin.system.mapper; + +import org.apache.ibatis.annotations.Param; +import top.continew.admin.system.model.entity.DictItemDO; +import top.continew.starter.data.mp.base.BaseMapper; +import top.continew.starter.extension.crud.model.resp.LabelValueResp; + +import java.util.List; + +/** + * 字典项 Mapper + * + * @author Charles7c + * @since 2023/9/11 21:29 + */ +public interface DictItemMapper extends BaseMapper { + + /** + * 根据字典编码查询 + * + * @param dictCode 字典编码 + * @return 字典项列表 + */ + List listByDictCode(@Param("dictCode") String dictCode); +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/mapper/DictMapper.java b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/DictMapper.java new file mode 100644 index 0000000..5634e88 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/DictMapper.java @@ -0,0 +1,13 @@ +package top.continew.admin.system.mapper; + +import top.continew.admin.system.model.entity.DictDO; +import top.continew.starter.data.mp.base.BaseMapper; + +/** + * 字典 Mapper + * + * @author Charles7c + * @since 2023/9/11 21:29 + */ +public interface DictMapper extends BaseMapper { +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/mapper/FileMapper.java b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/FileMapper.java new file mode 100644 index 0000000..8cd4aa1 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/FileMapper.java @@ -0,0 +1,39 @@ +package top.continew.admin.system.mapper; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import top.continew.admin.system.model.entity.FileDO; +import top.continew.admin.system.model.query.FileQuery; +import top.continew.admin.system.model.resp.FileResp; +import top.continew.admin.system.model.resp.FileStatisticsResp; +import top.continew.starter.data.mp.base.BaseMapper; + +import java.util.List; + +/** + * 文件 Mapper + * + * @author Charles7c + * @since 2023/12/23 10:38 + */ +public interface FileMapper extends BaseMapper { + + /** + * 查询文件资源统计信息 + * + * @return 文件资源统计信息 + */ + @Select("SELECT type, COUNT(1) number, SUM(size) size FROM sys_file GROUP BY type") + List statistics(); + + /** + * 分页查询列表 + * + * @param page 分页查询条件 + * @param query 查询条件 + * @return 分页信息 + */ + IPage selectFilesPage(@Param("page") IPage page, @Param("query") FileQuery query); + +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/mapper/InviteCodeMapper.java b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/InviteCodeMapper.java new file mode 100644 index 0000000..f261e0b --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/InviteCodeMapper.java @@ -0,0 +1,13 @@ +package top.continew.admin.system.mapper; + +import top.continew.admin.system.model.entity.InviteCode; +import top.continew.starter.data.mp.base.BaseMapper; + +/** + * 邀请码 Mapper + * + * @author Charles7c + * @since 2022/12/21 20:42 + */ +public interface InviteCodeMapper extends BaseMapper { +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/mapper/LogMapper.java b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/LogMapper.java new file mode 100644 index 0000000..16c79a7 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/LogMapper.java @@ -0,0 +1,71 @@ +package top.continew.admin.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Param; +import top.continew.admin.system.model.entity.LogDO; +import top.continew.admin.system.model.resp.DashboardAccessTrendResp; +import top.continew.admin.system.model.resp.DashboardPopularModuleResp; +import top.continew.admin.system.model.resp.DashboardTotalResp; +import top.continew.admin.system.model.resp.log.LogResp; +import top.continew.starter.data.mp.base.BaseMapper; + +import java.util.List; +import java.util.Map; + +/** + * 系统日志 Mapper + * + * @author Charles7c + * @since 2022/12/22 21:47 + */ +public interface LogMapper extends BaseMapper { + + /** + * 分页查询列表 + * + * @param page 分页条件 + * @param queryWrapper 查询条件 + * @return 分页列表信息 + */ + IPage selectLogPage(@Param("page") IPage page, + @Param(Constants.WRAPPER) QueryWrapper queryWrapper); + + /** + * 查询列表 + * + * @param queryWrapper 查询条件 + * @return 列表信息 + */ + List selectLogList(@Param(Constants.WRAPPER) QueryWrapper queryWrapper); + + /** + * 查询仪表盘总计信息 + * + * @return 仪表盘总计信息 + */ + DashboardTotalResp selectDashboardTotal(); + + /** + * 查询仪表盘访问趋势信息 + * + * @param days 日期数 + * @return 仪表盘访问趋势信息 + */ + List selectListDashboardAccessTrend(@Param("days") Integer days); + + /** + * 查询仪表盘热门模块列表 + * + * @return 仪表盘热门模块列表 + */ + List selectListDashboardPopularModule(); + + /** + * 查询仪表盘访客地域分布信息 + * + * @return 仪表盘访客地域分布信息 + */ + List> selectListDashboardGeoDistribution(); +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/mapper/MenuMapper.java b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/MenuMapper.java new file mode 100644 index 0000000..da771fd --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/MenuMapper.java @@ -0,0 +1,33 @@ +package top.continew.admin.system.mapper; + +import org.apache.ibatis.annotations.Param; +import top.continew.admin.system.model.entity.MenuDO; +import top.continew.starter.data.mp.base.BaseMapper; + +import java.util.List; +import java.util.Set; + +/** + * 菜单 Mapper + * + * @author Charles7c + * @since 2023/2/15 20:30 + */ +public interface MenuMapper extends BaseMapper { + + /** + * 根据用户 ID 查询权限码 + * + * @param userId 用户 ID + * @return 权限码集合 + */ + Set selectPermissionByUserId(@Param("userId") Long userId); + + /** + * 根据角色编码查询 + * + * @param roleCode 角色编码 + * @return 菜单列表 + */ + List selectListByRoleCode(@Param("roleCode") String roleCode); +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/mapper/MessageMapper.java b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/MessageMapper.java new file mode 100644 index 0000000..81cc929 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/MessageMapper.java @@ -0,0 +1,28 @@ +package top.continew.admin.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Param; +import top.continew.admin.system.model.entity.MessageDO; +import top.continew.admin.system.model.resp.MessageResp; +import top.continew.starter.data.mp.base.BaseMapper; + +/** + * 消息 Mapper + * + * @author Bull-BCLS + * @since 2023/10/15 19:05 + */ +public interface MessageMapper extends BaseMapper { + + /** + * 分页查询列表 + * + * @param page 分页查询条件 + * @param queryWrapper 查询条件 + * @return 分页信息 + */ + IPage selectPageByUserId(@Param("page") IPage page, + @Param(Constants.WRAPPER) QueryWrapper queryWrapper); +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/mapper/MessageUserMapper.java b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/MessageUserMapper.java new file mode 100644 index 0000000..5930a5f --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/MessageUserMapper.java @@ -0,0 +1,23 @@ +package top.continew.admin.system.mapper; + +import org.apache.ibatis.annotations.Param; +import top.continew.admin.system.model.entity.MessageUserDO; +import top.continew.starter.data.mp.base.BaseMapper; + +/** + * 消息和用户 Mapper + * + * @author Bull-BCLS + * @since 2023/10/15 20:25 + */ +public interface MessageUserMapper extends BaseMapper { + + /** + * 根据用户 ID 和消息类型查询未读消息数量 + * + * @param userId 用户 ID + * @param type 消息类型 + * @return 未读消息信息 + */ + Long selectUnreadCountByUserIdAndType(@Param("userId") Long userId, @Param("type") Integer type); +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/mapper/NoticeMapper.java b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/NoticeMapper.java new file mode 100644 index 0000000..60c8560 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/NoticeMapper.java @@ -0,0 +1,23 @@ +package top.continew.admin.system.mapper; + +import top.continew.admin.system.model.entity.NoticeDO; +import top.continew.admin.system.model.resp.DashboardNoticeResp; +import top.continew.starter.data.mp.base.BaseMapper; + +import java.util.List; + +/** + * 公告 Mapper + * + * @author Charles7c + * @since 2023/8/20 10:55 + */ +public interface NoticeMapper extends BaseMapper { + + /** + * 查询仪表盘公告列表 + * + * @return 仪表盘公告列表 + */ + List selectDashboardList(); +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/mapper/OptionMapper.java b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/OptionMapper.java new file mode 100644 index 0000000..46617f0 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/OptionMapper.java @@ -0,0 +1,26 @@ +package top.continew.admin.system.mapper; + +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import top.continew.admin.system.model.entity.OptionDO; +import top.continew.starter.data.mp.base.BaseMapper; + +import java.util.List; + +/** + * 参数 Mapper + * + * @author Bull-BCLS + * @since 2023/8/26 19:38 + */ +public interface OptionMapper extends BaseMapper { + + /** + * 根据类别查询 + * + * @param category 类别 + * @return 列表 + */ + @Select("SELECT code, value, default_value FROM sys_option WHERE category = #{category}") + List selectByCategory(@Param("category") String category); +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/mapper/RoleDeptMapper.java b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/RoleDeptMapper.java new file mode 100644 index 0000000..7624f22 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/RoleDeptMapper.java @@ -0,0 +1,26 @@ +package top.continew.admin.system.mapper; + +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import top.continew.admin.system.model.entity.RoleDeptDO; +import top.continew.starter.data.mp.base.BaseMapper; + +import java.util.List; + +/** + * 角色和部门 Mapper + * + * @author Charles7c + * @since 2023/2/18 21:57 + */ +public interface RoleDeptMapper extends BaseMapper { + + /** + * 根据角色 ID 查询 + * + * @param roleId 角色 ID + * @return 部门 ID 列表 + */ + @Select("SELECT dept_id FROM sys_role_dept WHERE role_id = #{roleId}") + List selectDeptIdByRoleId(@Param("roleId") Long roleId); +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/mapper/RoleMapper.java b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/RoleMapper.java new file mode 100644 index 0000000..66f1912 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/RoleMapper.java @@ -0,0 +1,13 @@ +package top.continew.admin.system.mapper; + +import top.continew.admin.system.model.entity.RoleDO; +import top.continew.starter.data.mp.base.BaseMapper; + +/** + * 角色 Mapper + * + * @author Charles7c + * @since 2023/2/8 23:17 + */ +public interface RoleMapper extends BaseMapper { +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/mapper/RoleMenuMapper.java b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/RoleMenuMapper.java new file mode 100644 index 0000000..b8dc8be --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/RoleMenuMapper.java @@ -0,0 +1,23 @@ +package top.continew.admin.system.mapper; + +import top.continew.admin.system.model.entity.RoleMenuDO; +import top.continew.starter.data.mp.base.BaseMapper; + +import java.util.List; + +/** + * 角色和菜单 Mapper + * + * @author Charles7c + * @since 2023/2/15 20:30 + */ +public interface RoleMenuMapper extends BaseMapper { + + /** + * 根据角色 ID 列表查询 + * + * @param roleIds 角色 ID 列表 + * @return 菜单 ID 列表 + */ + List selectMenuIdByRoleIds(List roleIds); +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/mapper/StorageMapper.java b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/StorageMapper.java new file mode 100644 index 0000000..af9bb1b --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/StorageMapper.java @@ -0,0 +1,13 @@ +package top.continew.admin.system.mapper; + +import top.continew.admin.system.model.entity.StorageDO; +import top.continew.starter.data.mp.base.BaseMapper; + +/** + * 存储 Mapper + * + * @author Charles7c + * @since 2023/12/26 22:09 + */ +public interface StorageMapper extends BaseMapper { +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/mapper/TaskPreDeductMapper.java b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/TaskPreDeductMapper.java new file mode 100644 index 0000000..6054ab5 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/TaskPreDeductMapper.java @@ -0,0 +1,24 @@ +package top.continew.admin.system.mapper; + +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Update; +import top.continew.admin.system.model.entity.TaskPreDeductDO; +import top.continew.starter.data.mp.base.BaseMapper; + +/** + * 任务预扣减记录 Mapper + * + * @author jiay + * @since 2025/08/28 00:00 + */ +public interface TaskPreDeductMapper extends BaseMapper { + + /** + * 重置用户预扣减余额为0(用户登录时调用) + * + * @param userId 用户ID + * @return 影响行数 + */ + @Update("UPDATE t_task_pre_deduct SET pre_deduct_amount = 0 WHERE user_id = #{userId}") + int resetPreDeductByUserId(@Param("userId") Long userId); +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/mapper/UserMapper.java b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/UserMapper.java new file mode 100644 index 0000000..c31bbd8 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/UserMapper.java @@ -0,0 +1,137 @@ +package top.continew.admin.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; +import top.continew.admin.common.config.mybatis.DataPermissionMapper; +import top.continew.admin.system.model.entity.UserDO; +import top.continew.admin.system.model.resp.UserDetailResp; +import top.continew.admin.system.model.resp.UserTreeResp; +import top.continew.starter.data.mp.datapermission.DataPermission; +import top.continew.starter.security.crypto.annotation.FieldEncrypt; + +import java.util.List; + +/** + * 用户 Mapper + * + * @author Charles7c + * @since 2022/12/22 21:47 + */ +public interface UserMapper extends DataPermissionMapper { + + /** + * 分页查询列表 + * + * @param page 分页条件 + * @param queryWrapper 查询条件 + * @return 分页列表信息 + */ + @DataPermission(tableAlias = "t1") + IPage selectUserPage(@Param("page") IPage page, + @Param(Constants.WRAPPER) QueryWrapper queryWrapper); + + /** + * 查询列表 + * + * @param queryWrapper 查询条件 + * @return 列表信息 + */ + @DataPermission(tableAlias = "t1") + List selectUserList(@Param(Constants.WRAPPER) QueryWrapper queryWrapper); + + /** + * 根据用户名查询 + * + * @param username 用户名 + * @return 用户信息 + */ + @Select("SELECT * FROM sys_user WHERE username = #{username}") + UserDO selectByUsername(@Param("username") String username); + + /** + * 根据手机号查询 + * + * @param phone 手机号 + * @return 用户信息 + */ + @Select("SELECT * FROM sys_user WHERE phone = #{phone}") + UserDO selectByPhone(@FieldEncrypt @Param("phone") String phone); + + /** + * 根据邮箱查询 + * + * @param email 邮箱 + * @return 用户信息 + */ + @Select("SELECT * FROM sys_user WHERE email = #{email}") + UserDO selectByEmail(@FieldEncrypt @Param("email") String email); + + /** + * 根据 ID 查询昵称 + * + * @param id ID + * @return 昵称 + */ + @Select("SELECT nickname FROM sys_user WHERE id = #{id}") + String selectNicknameById(@Param("id") Long id); + + /** + * 根据邮箱查询数量 + * + * @param email 邮箱 + * @return 用户数量 + */ + Long selectCountByEmail(@FieldEncrypt @Param("email") String email, @Param("id") Long id); + + /** + * 根据手机号查询数量 + * + * @param phone 手机号 + * @return 用户数量 + */ + Long selectCountByPhone(@FieldEncrypt @Param("phone") String phone, @Param("id") Long id); + + List selectTeacherById(@FieldEncrypt @Param("deptId") Long deptId); + + /** + * 扣减消费余额 + * + * @param id ID + */ + @Update("update sys_user set sys_beans = sys_beans - #{chargePoints} WHERE id = #{id}") + int updateSysBeansById(@Param("id") Long id, @Param("chargePoints") Long chargePoints); + + /** + * 增加预扣减金额(任务开始前) + * + * @param id ID + * @param preDeductAmount 预扣减金额 + */ + @Update("update sys_user set pre_deduct_beans = pre_deduct_beans + #{preDeductAmount} WHERE id = #{id}") + int addPreDeductBeansById(@Param("id") Long id, @Param("preDeductAmount") Long preDeductAmount); + + /** + * 减少预扣减金额并扣减实际余额(任务完成后) + * + * @param id ID + * @param preDeductAmount 预扣减金额(分) + * @param actualAmount 实际扣减金额(分) + */ + @Update("update sys_user set pre_deduct_beans = pre_deduct_beans - #{preDeductAmount}, sys_beans = sys_beans - #{actualAmount} WHERE id = #{id}") + int deductActualAndClearPreDeduct(@Param("id") Long id, + @Param("preDeductAmount") Long preDeductAmount, + @Param("actualAmount") Long actualAmount); + + /** + * 清零预扣减金额(任务失败或取消时) + * + * @param id ID + * @param preDeductAmount 预扣减金额(分) + */ + @Update("update sys_user set pre_deduct_beans = pre_deduct_beans - #{preDeductAmount} WHERE id = #{id}") + int clearPreDeductBeansById(@Param("id") Long id, @Param("preDeductAmount") Long preDeductAmount); +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/mapper/UserPasswordHistoryMapper.java b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/UserPasswordHistoryMapper.java new file mode 100644 index 0000000..1af4448 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/UserPasswordHistoryMapper.java @@ -0,0 +1,22 @@ +package top.continew.admin.system.mapper; + +import org.apache.ibatis.annotations.Param; +import top.continew.starter.data.mp.base.BaseMapper; +import top.continew.admin.system.model.entity.UserPasswordHistoryDO; + +/** + * 用户历史密码 Mapper + * + * @author Charles7c + * @since 2024/5/16 21:58 + */ +public interface UserPasswordHistoryMapper extends BaseMapper { + + /** + * 删除过期历史密码 + * + * @param userId 用户 ID + * @param count 保留 N 个历史 + */ + void deleteExpired(@Param("userId") Long userId, @Param("count") int count); +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/mapper/UserRoleMapper.java b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/UserRoleMapper.java new file mode 100644 index 0000000..7540593 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/UserRoleMapper.java @@ -0,0 +1,26 @@ +package top.continew.admin.system.mapper; + +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import top.continew.admin.system.model.entity.UserRoleDO; +import top.continew.starter.data.mp.base.BaseMapper; + +import java.util.List; + +/** + * 用户和角色 Mapper + * + * @author Charles7c + * @since 2023/2/13 23:13 + */ +public interface UserRoleMapper extends BaseMapper { + + /** + * 根据用户 ID 查询 + * + * @param userId 用户 ID + * @return 角色 ID 列表 + */ + @Select("SELECT role_id FROM sys_user_role WHERE user_id = #{userId}") + List selectRoleIdByUserId(@Param("userId") Long userId); +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/mapper/UserSocialMapper.java b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/UserSocialMapper.java new file mode 100644 index 0000000..a4a0123 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/mapper/UserSocialMapper.java @@ -0,0 +1,23 @@ +package top.continew.admin.system.mapper; + +import org.apache.ibatis.annotations.Param; +import top.continew.admin.system.model.entity.UserSocialDO; +import top.continew.starter.data.mp.base.BaseMapper; + +/** + * 用户社会化关联 Mapper + * + * @author Charles7c + * @since 2023/10/11 22:10 + */ +public interface UserSocialMapper extends BaseMapper { + + /** + * 根据来源和开放 ID 查询 + * + * @param source 来源 + * @param openId 开放 ID + * @return 用户社会化关联信息 + */ + UserSocialDO selectBySourceAndOpenId(@Param("source") String source, @Param("openId") String openId); +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/DeptDO.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/DeptDO.java new file mode 100644 index 0000000..b95d644 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/DeptDO.java @@ -0,0 +1,66 @@ +package top.continew.admin.system.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.starter.extension.crud.model.entity.BaseDO; + +import java.io.Serial; + +/** + * 部门实体 + * + * @author Charles7c + * @since 2023/1/22 13:50 + */ +@Data +@TableName("sys_dept") +public class DeptDO extends BaseDO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 名称 + */ + private String name; + /** + * 数据库名称 + */ + private String dbName; + + /** + * 上级部门 ID + */ + private Long parentId; + + /** + * 祖级列表 + */ + private String ancestors; + + /** + * 描述 + */ + private String description; + + /** + * 排序 + */ + private Integer sort; + + /** + * 状态 + */ + private DisEnableStatusEnum status; + + /** + * 是否为系统内置数据 + */ + private Boolean isSystem; + + /** + * 学院与每个页面的绑定 + */ + private String routers; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/DictDO.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/DictDO.java new file mode 100644 index 0000000..edaf9ad --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/DictDO.java @@ -0,0 +1,43 @@ +package top.continew.admin.system.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import top.continew.starter.extension.crud.annotation.DictField; +import top.continew.starter.extension.crud.model.entity.BaseDO; + +import java.io.Serial; + +/** + * 字典实体 + * + * @author Charles7c + * @since 2023/9/11 21:29 + */ +@Data +@DictField(valueKey = "code") +@TableName("sys_dict") +public class DictDO extends BaseDO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 名称 + */ + private String name; + + /** + * 编码 + */ + private String code; + + /** + * 描述 + */ + private String description; + + /** + * 是否为系统内置数据 + */ + private Boolean isSystem; +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/DictItemDO.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/DictItemDO.java new file mode 100644 index 0000000..e9391b7 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/DictItemDO.java @@ -0,0 +1,57 @@ +package top.continew.admin.system.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.starter.extension.crud.model.entity.BaseDO; + +import java.io.Serial; + +/** + * 字典项实体 + * + * @author Charles7c + * @since 2023/9/11 21:29 + */ +@Data +@TableName("sys_dict_item") +public class DictItemDO extends BaseDO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 标签 + */ + private String label; + + /** + * 值 + */ + private String value; + + /** + * 标签颜色 + */ + private String color; + + /** + * 排序 + */ + private Integer sort; + + /** + * 描述 + */ + private String description; + + /** + * 状态 + */ + private DisEnableStatusEnum status; + + /** + * 字典ID + */ + private Long dictId; +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/FileDO.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/FileDO.java new file mode 100644 index 0000000..6d594a6 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/FileDO.java @@ -0,0 +1,97 @@ +package top.continew.admin.system.model.entity; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import org.dromara.x.file.storage.core.FileInfo; +import top.continew.admin.system.enums.FileTypeEnum; +import top.continew.starter.core.constant.StringConstants; +import top.continew.starter.core.util.StrUtils; +import top.continew.starter.extension.crud.model.entity.BaseDO; + +import java.io.Serial; + +/** + * 文件实体 + * + * @author Charles7c + * @since 2023/12/23 10:38 + */ +@Data +@TableName("sys_file") +public class FileDO extends BaseDO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 名称 + */ + private String name; + + /** + * 大小(字节) + */ + private Long size; + + /** + * URL + */ + private String url; + + /** + * 扩展名 + */ + private String extension; + + /** + * 类型 + */ + private FileTypeEnum type; + + /** + * 缩略图大小(字节) + */ + private Long thumbnailSize; + + /** + * 缩略图URL + */ + private String thumbnailUrl; + + /** + * 存储 ID + */ + private Long storageId; + /** + * 部门 ID + */ + private Long deptId; + + /** + * 转换为 X-File-Storage 文件信息对象 + * + * @param storageCode 存储编码 + * @return X-File-Storage 文件信息对象 + */ + public FileInfo toFileInfo(String storageCode) { + FileInfo fileInfo = new FileInfo(); + fileInfo.setUrl(this.url); + fileInfo.setSize(this.size); + fileInfo.setFilename(StrUtil.contains(this.url, StringConstants.SLASH) + ? StrUtil.subAfter(this.url, StringConstants.SLASH, true) + : this.url); + fileInfo.setOriginalFilename(StrUtils + .blankToDefault(this.extension, this.name, ex -> this.name + StringConstants.DOT + ex)); + fileInfo.setBasePath(StringConstants.EMPTY); + fileInfo.setPath(StrUtil.subBefore(this.url, StringConstants.SLASH, true) + StringConstants.SLASH); + fileInfo.setExt(this.extension); + fileInfo.setPlatform(storageCode); + fileInfo.setThUrl(this.thumbnailUrl); + fileInfo.setThFilename(StrUtil.contains(this.thumbnailUrl, StringConstants.SLASH) + ? StrUtil.subAfter(this.thumbnailUrl, StringConstants.SLASH, true) + : this.thumbnailUrl); + fileInfo.setThSize(this.thumbnailSize); + return fileInfo; + } +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/InviteCode.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/InviteCode.java new file mode 100644 index 0000000..0a28b6d --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/InviteCode.java @@ -0,0 +1,44 @@ +package top.continew.admin.system.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.NoArgsConstructor; +import top.continew.starter.extension.crud.model.entity.BaseIdDO; + +import java.util.Date; + +/** + * 用户实体 + * + * @author Charles7c + * @since 2022/12/21 20:42 + */ +@Data +@NoArgsConstructor +@TableName("t_invite_code") +public class InviteCode extends BaseIdDO { + + /** + * 用户 ID + */ + private Long userId; + + /** + * 角色 ID + */ + private String inviteCode; + + private String prefix; + + private String codeNumber; + + /** + * 是否已使用:0-未使用,1-已使用 + */ + private int isUsed; + + private Date usedTime; + + private Date createdTime; + +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/LogDO.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/LogDO.java new file mode 100644 index 0000000..6db20b3 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/LogDO.java @@ -0,0 +1,125 @@ +package top.continew.admin.system.model.entity; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import top.continew.admin.system.enums.LogStatusEnum; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 系统日志实体 + * + * @author Charles7c + * @since 2022/12/25 9:11 + */ +@Data +@TableName("sys_log") +public class LogDO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId + private Long id; + + /** + * 链路 ID + */ + private String traceId; + + /** + * 日志描述 + */ + private String description; + + /** + * 所属模块 + */ + private String module; + + /** + * 请求 URL + */ + private String requestUrl; + + /** + * 请求方式 + */ + private String requestMethod; + + /** + * 请求头 + */ + private String requestHeaders; + + /** + * 请求体 + */ + private String requestBody; + + /** + * 状态码 + */ + private Integer statusCode; + + /** + * 响应头 + */ + private String responseHeaders; + + /** + * 响应体 + */ + private String responseBody; + + /** + * 耗时(ms) + */ + private Long timeTaken; + + /** + * IP + */ + private String ip; + + /** + * IP 归属地 + */ + private String address; + + /** + * 浏览器 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 状态 + */ + private LogStatusEnum status; + + /** + * 错误信息 + */ + private String errorMsg; + + /** + * 创建人 + */ + private Long createUser; + + /** + * 创建时间 + */ + private LocalDateTime createTime; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/MenuDO.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/MenuDO.java new file mode 100644 index 0000000..97e2e2f --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/MenuDO.java @@ -0,0 +1,93 @@ +package top.continew.admin.system.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.admin.system.enums.MenuTypeEnum; +import top.continew.starter.extension.crud.model.entity.BaseDO; + +import java.io.Serial; + +/** + * 菜单实体 + * + * @author Charles7c + * @since 2023/2/15 20:14 + */ +@Data +@TableName("sys_menu") +public class MenuDO extends BaseDO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 标题 + */ + private String title; + + /** + * 上级菜单 ID + */ + private Long parentId; + + /** + * 类型 + */ + private MenuTypeEnum type; + + /** + * 路由地址 + */ + private String path; + + /** + * 组件名称 + */ + private String name; + + /** + * 组件路径 + */ + private String component; + + /** + * 重定向地址 + */ + private String redirect; + + /** + * 图标 + */ + private String icon; + + /** + * 是否外链 + */ + private Boolean isExternal; + + /** + * 是否缓存 + */ + private Boolean isCache; + + /** + * 是否隐藏 + */ + private Boolean isHidden; + + /** + * 权限标识 + */ + private String permission; + + /** + * 排序 + */ + private Integer sort; + + /** + * 状态 + */ + private DisEnableStatusEnum status; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/MessageDO.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/MessageDO.java new file mode 100644 index 0000000..83423af --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/MessageDO.java @@ -0,0 +1,59 @@ +package top.continew.admin.system.model.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import top.continew.admin.system.enums.MessageTypeEnum; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 消息实体 + * + * @author Bull-BCLS + * @since 2023/10/15 19:05 + */ +@Data +@TableName("sys_message") +public class MessageDO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId + private Long id; + + /** + * 标题 + */ + private String title; + + /** + * 内容 + */ + private String content; + + /** + * 类型(1:系统消息) + */ + private MessageTypeEnum type; + + /** + * 创建人 + */ + @TableField(fill = FieldFill.INSERT) + private Long createUser; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/MessageUserDO.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/MessageUserDO.java new file mode 100644 index 0000000..f811d61 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/MessageUserDO.java @@ -0,0 +1,42 @@ +package top.continew.admin.system.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 消息和用户关联实体 + * + * @author Bull-BCLS + * @since 2023/10/15 20:25 + */ +@Data +@TableName("sys_message_user") +public class MessageUserDO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 消息 ID + */ + private Long messageId; + + /** + * 用户 ID + */ + private Long userId; + + /** + * 是否已读 + */ + private Boolean isRead; + + /** + * 读取时间 + */ + private LocalDateTime readTime; +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/NoticeDO.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/NoticeDO.java new file mode 100644 index 0000000..2915b6b --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/NoticeDO.java @@ -0,0 +1,47 @@ +package top.continew.admin.system.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import top.continew.starter.extension.crud.model.entity.BaseDO; + +import java.io.Serial; +import java.time.LocalDateTime; + +/** + * 公告实体 + * + * @author Charles7c + * @since 2023/8/20 10:55 + */ +@Data +@TableName("sys_notice") +public class NoticeDO extends BaseDO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 标题 + */ + private String title; + + /** + * 内容 + */ + private String content; + + /** + * 类型 + */ + private String type; + + /** + * 生效时间 + */ + private LocalDateTime effectiveTime; + + /** + * 终止时间 + */ + private LocalDateTime terminateTime; +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/OptionDO.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/OptionDO.java new file mode 100644 index 0000000..5d569df --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/OptionDO.java @@ -0,0 +1,51 @@ +package top.continew.admin.system.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import top.continew.starter.extension.crud.model.entity.BaseUpdateDO; + +import java.io.Serial; + +/** + * 参数实体 + * + * @author Bull-BCLS + * @since 2023/8/26 19:20 + */ +@Data +@TableName("sys_option") +public class OptionDO extends BaseUpdateDO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 类别 + */ + private String category; + + /** + * 名称 + */ + private String name; + + /** + * 键 + */ + private String code; + + /** + * 值 + */ + private String value; + + /** + * 默认值 + */ + private String defaultValue; + + /** + * 描述 + */ + private String description; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/RoleDO.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/RoleDO.java new file mode 100644 index 0000000..a4cfdf6 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/RoleDO.java @@ -0,0 +1,64 @@ +package top.continew.admin.system.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import top.continew.admin.common.enums.DataScopeEnum; +import top.continew.starter.extension.crud.annotation.DictField; +import top.continew.starter.extension.crud.model.entity.BaseDO; + +import java.io.Serial; + +/** + * 角色实体 + * + * @author Charles7c + * @since 2023/2/8 22:54 + */ +@Data +@DictField +@TableName("sys_role") +public class RoleDO extends BaseDO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 名称 + */ + private String name; + + /** + * 编码 + */ + private String code; + + /** + * 数据权限 + */ + private DataScopeEnum dataScope; + + /** + * 描述 + */ + private String description; + + /** + * 排序 + */ + private Integer sort; + + /** + * 是否为系统内置数据 + */ + private Boolean isSystem; + + /** + * 菜单选择是否父子节点关联 + */ + private Boolean menuCheckStrictly; + + /** + * 部门选择是否父子节点关联 + */ + private Boolean deptCheckStrictly; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/RoleDeptDO.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/RoleDeptDO.java new file mode 100644 index 0000000..bf24b89 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/RoleDeptDO.java @@ -0,0 +1,38 @@ +package top.continew.admin.system.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 角色和部门实体 + * + * @author Charles7c + * @since 2023/2/18 21:57 + */ +@Data +@NoArgsConstructor +@TableName("sys_role_dept") +public class RoleDeptDO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 角色 ID + */ + private Long roleId; + + /** + * 部门 ID + */ + private Long deptId; + + public RoleDeptDO(Long roleId, Long deptId) { + this.roleId = roleId; + this.deptId = deptId; + } +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/RoleMenuDO.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/RoleMenuDO.java new file mode 100644 index 0000000..3b969fb --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/RoleMenuDO.java @@ -0,0 +1,38 @@ +package top.continew.admin.system.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 角色和菜单实体 + * + * @author Charles7c + * @since 2023/2/15 20:20 + */ +@Data +@NoArgsConstructor +@TableName("sys_role_menu") +public class RoleMenuDO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 角色 ID + */ + private Long roleId; + + /** + * 菜单 ID + */ + private Long menuId; + + public RoleMenuDO(Long roleId, Long menuId) { + this.roleId = roleId; + this.menuId = menuId; + } +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/StorageDO.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/StorageDO.java new file mode 100644 index 0000000..96bee26 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/StorageDO.java @@ -0,0 +1,86 @@ +package top.continew.admin.system.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.admin.system.enums.StorageTypeEnum; +import top.continew.starter.extension.crud.model.entity.BaseDO; +import top.continew.starter.security.crypto.annotation.FieldEncrypt; + +import java.io.Serial; + +/** + * 存储实体 + * + * @author Charles7c + * @since 2023/12/26 22:09 + */ +@Data +@TableName("sys_storage") +public class StorageDO extends BaseDO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 名称 + */ + private String name; + + /** + * 编码 + */ + private String code; + + /** + * 类型 + */ + private StorageTypeEnum type; + + /** + * Access Key(访问密钥) + */ + @FieldEncrypt + private String accessKey; + + /** + * Secret Key(私有密钥) + */ + @FieldEncrypt + private String secretKey; + + /** + * Endpoint(终端节点) + */ + private String endpoint; + + /** + * 桶名称 + */ + private String bucketName; + + /** + * 域名 + */ + private String domain; + + /** + * 描述 + */ + private String description; + + /** + * 是否为默认存储 + */ + private Boolean isDefault; + + /** + * 排序 + */ + private Integer sort; + + /** + * 状态 + */ + private DisEnableStatusEnum status; +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/TaskPreDeductDO.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/TaskPreDeductDO.java new file mode 100644 index 0000000..13ffba0 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/TaskPreDeductDO.java @@ -0,0 +1,57 @@ +package top.continew.admin.system.model.entity; + +import lombok.Data; + +import com.baomidou.mybatisplus.annotation.TableName; +import top.continew.starter.extension.crud.model.entity.BaseDO; + +import java.io.Serial; + +/** + * 任务预扣减记录实体 + * + * @author jiay + * @since 2025/08/28 00:00 + */ +@Data +@TableName("t_task_pre_deduct") +public class TaskPreDeductDO extends BaseDO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + private Long userId; + + /** + * 任务订单号 + */ + private String taskId; + + /** + * 生成平台 + */ + private Long platformId; + + /** + * 任务类型 (token:0, 文生图:1,图生图:2,音效生成:3,参考生视频:4, 白膜贴图:5) + */ + private Integer chargeType; + + /** + * 预扣减金额(分) + */ + private Long preDeductAmount; + + /** + * 扣减来源 (main_account: 主账户, sub_account: 子账户) + */ + private String deductSource; + + /** + * 任务状态 (0:进行中, 1:已完成, 2:已失败, 3:已取消) + */ + private Integer status = 0; +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/UserDO.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/UserDO.java new file mode 100644 index 0000000..c59f950 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/UserDO.java @@ -0,0 +1,110 @@ +package top.continew.admin.system.model.entity; + +import com.baomidou.mybatisplus.annotation.FieldStrategy; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.admin.common.enums.GenderEnum; +import top.continew.starter.extension.crud.model.entity.BaseDO; +import top.continew.starter.security.crypto.annotation.FieldEncrypt; + +import java.io.Serial; +import java.time.LocalDateTime; + +/** + * 用户实体 + * + * @author Charles7c + * @since 2022/12/21 20:42 + */ +@Data +@TableName("sys_user") +public class UserDO extends BaseDO { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户名 + */ + private String username; + + /** + * 昵称 + */ + private String nickname; + + /** + * 密码 + */ + // @FieldEncrypt(encryptor = BCryptEncryptor.class) + private String password; + + /** + * 性别 + */ + private GenderEnum gender; + + /** + * 邮箱 + */ + @FieldEncrypt + @TableField(insertStrategy = FieldStrategy.NOT_EMPTY) + private String email; + + /** + * 手机号码 + */ + @FieldEncrypt + @TableField(insertStrategy = FieldStrategy.NOT_EMPTY) + private String phone; + + /** + * 头像地址 + */ + private String avatar; + + /** + * 描述 + */ + private String description; + + /** + * 状态 + */ + private DisEnableStatusEnum status; + + /** + * 是否为系统内置数据 + */ + private Boolean isSystem; + + /** + * 最后一次修改密码时间 + */ + private LocalDateTime pwdResetTime; + + /** + * 部门 ID + */ + private Long deptId; + /** + * 余额 + */ + private Integer sysBeans; + + /** + * 余额 备用 + */ + private Integer payBeans; + /** + * 消耗点数 + */ + private Integer inviteBeans; + + /** + * 预扣减金额(正在进行的任务消耗) + */ + private Integer preDeductBeans = 0; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/UserPasswordHistoryDO.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/UserPasswordHistoryDO.java new file mode 100644 index 0000000..3c107fa --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/UserPasswordHistoryDO.java @@ -0,0 +1,51 @@ +package top.continew.admin.system.model.entity; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 用户历史密码实体 + * + * @author Charles7c + * @since 2024/5/16 21:58 + */ +@Data +@NoArgsConstructor +@TableName("sys_user_password_history") +public class UserPasswordHistoryDO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(type = IdType.ASSIGN_ID) + private Long id; + + /** + * 用户 ID + */ + private Long userId; + + /** + * 密码 + */ + private String password; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + public UserPasswordHistoryDO(Long userId, String password) { + this.userId = userId; + this.password = password; + } +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/UserRoleDO.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/UserRoleDO.java new file mode 100644 index 0000000..f489937 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/UserRoleDO.java @@ -0,0 +1,38 @@ +package top.continew.admin.system.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 用户和角色实体 + * + * @author Charles7c + * @since 2023/2/13 23:13 + */ +@Data +@NoArgsConstructor +@TableName("sys_user_role") +public class UserRoleDO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户 ID + */ + private Long userId; + + /** + * 角色 ID + */ + private Long roleId; + + public UserRoleDO(Long userId, Long roleId) { + this.userId = userId; + this.roleId = roleId; + } +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/UserSocialDO.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/UserSocialDO.java new file mode 100644 index 0000000..53d60b6 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/entity/UserSocialDO.java @@ -0,0 +1,55 @@ +package top.continew.admin.system.model.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 用户社会化关联实体 + * + * @author Charles7c + * @since 2023/10/11 22:10 + */ +@Data +@TableName("sys_user_social") +public class UserSocialDO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户 ID + */ + private Long userId; + + /** + * 来源 + */ + private String source; + + /** + * 开放 ID + */ + private String openId; + + /** + * 附加信息 + */ + private String metaJson; + + /** + * 最后登录时间 + */ + private LocalDateTime lastLoginTime; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/query/DeptQuery.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/query/DeptQuery.java new file mode 100644 index 0000000..6fa2d24 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/query/DeptQuery.java @@ -0,0 +1,44 @@ +package top.continew.admin.system.model.query; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.starter.data.core.annotation.Query; +import top.continew.starter.data.core.enums.QueryType; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 部门查询条件 + * + * @author Charles7c + * @since 2023/1/22 17:52 + */ +@Data +@Schema(description = "部门查询条件") +public class DeptQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 关键词 + */ + @Schema(description = "关键词", example = "测试部") + @Query(columns = {"name", "description"}, type = QueryType.LIKE) + private String description; + + /** + * 状态 + */ + @Schema(description = "状态", example = "1") + private DisEnableStatusEnum status; + + /** + * 学院与每个页面的绑定 + */ + @Schema(description = "学院与每个页面的绑定", example = "学校信息") + @Query(columns = {"name", "routers"}, type = QueryType.LIKE) + private String routers; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/query/DictItemQuery.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/query/DictItemQuery.java new file mode 100644 index 0000000..d7227eb --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/query/DictItemQuery.java @@ -0,0 +1,43 @@ +package top.continew.admin.system.model.query; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.starter.data.core.annotation.Query; +import top.continew.starter.data.core.enums.QueryType; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 字典项查询条件 + * + * @author Charles7c + * @since 2023/9/11 21:29 + */ +@Data +@Schema(description = "字典项查询条件") +public class DictItemQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 关键词 + */ + @Schema(description = "关键词") + @Query(columns = {"label", "description"}, type = QueryType.LIKE) + private String description; + + /** + * 状态 + */ + @Schema(description = "状态", example = "1") + private DisEnableStatusEnum status; + + /** + * 字典 ID + */ + @Schema(description = "字典 ID") + private Long dictId; +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/query/DictQuery.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/query/DictQuery.java new file mode 100644 index 0000000..94af7fe --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/query/DictQuery.java @@ -0,0 +1,30 @@ +package top.continew.admin.system.model.query; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.starter.data.core.annotation.Query; +import top.continew.starter.data.core.enums.QueryType; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 字典查询条件 + * + * @author Charles7c + * @since 2023/9/11 21:29 + */ +@Data +@Schema(description = "字典查询条件") +public class DictQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 关键词 + */ + @Schema(description = "关键词") + @Query(columns = {"name", "code", "description"}, type = QueryType.LIKE) + private String description; +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/query/FileQuery.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/query/FileQuery.java new file mode 100644 index 0000000..2d4feff --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/query/FileQuery.java @@ -0,0 +1,42 @@ +package top.continew.admin.system.model.query; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.system.enums.FileTypeEnum; +import top.continew.starter.data.core.annotation.Query; +import top.continew.starter.data.core.enums.QueryType; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 文件查询条件 + * + * @author Charles7c + * @since 2023/12/23 10:38 + */ +@Data +@Schema(description = "文件查询条件") +public class FileQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 名称 + */ + @Schema(description = "名称", example = "图片") + @Query(type = QueryType.LIKE) + private String name; + + /** + * 类型 + */ + @Schema(description = "类型", example = "2") + private FileTypeEnum type; + /** + * 用户id + */ + @Schema(description = "用户id", example = "123") + private String createUser; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/query/LogQuery.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/query/LogQuery.java new file mode 100644 index 0000000..131db07 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/query/LogQuery.java @@ -0,0 +1,65 @@ +package top.continew.admin.system.model.query; + +import cn.hutool.core.date.DatePattern; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Size; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; +import top.continew.admin.common.enums.DisEnableStatusEnum; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + * 日志查询条件 + * + * @author Charles7c + * @since 2023/1/15 11:43 + */ +@Data +@Schema(description = "日志查询条件") +public class LogQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 日志描述 + */ + @Schema(description = "日志描述", example = "新增数据") + private String description; + + /** + * 所属模块 + */ + @Schema(description = "所属模块", example = "所属模块") + private String module; + + /** + * IP + */ + @Schema(description = "IP", example = "") + private String ip; + + /** + * 操作人 + */ + @Schema(description = "操作人", example = "admin") + private String createUserString; + + /** + * 操作时间 + */ + @Schema(description = "操作时间", example = "2023-08-08 00:00:00,2023-08-08 23:59:59") + @DateTimeFormat(pattern = DatePattern.NORM_DATETIME_PATTERN) + @Size(max = 2, message = "操作时间必须是一个范围") + private List createTime; + + /** + * 状态 + */ + @Schema(description = "状态", example = "1") + private DisEnableStatusEnum status; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/query/MenuQuery.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/query/MenuQuery.java new file mode 100644 index 0000000..3a08cc5 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/query/MenuQuery.java @@ -0,0 +1,43 @@ +package top.continew.admin.system.model.query; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.NoArgsConstructor; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.starter.data.core.annotation.Query; +import top.continew.starter.data.core.enums.QueryType; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 菜单查询条件 + * + * @author Charles7c + * @since 2023/2/15 20:21 + */ +@Data +@NoArgsConstructor +@Schema(description = "菜单查询条件") +public class MenuQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 标题 + */ + @Schema(description = "标题", example = "用户管理") + @Query(type = QueryType.LIKE) + private String title; + + /** + * 状态 + */ + @Schema(description = "状态", example = "1") + private DisEnableStatusEnum status; + + public MenuQuery(DisEnableStatusEnum status) { + this.status = status; + } +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/query/MessageQuery.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/query/MessageQuery.java new file mode 100644 index 0000000..d04286b --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/query/MessageQuery.java @@ -0,0 +1,58 @@ +package top.continew.admin.system.model.query; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.system.enums.MessageTypeEnum; +import top.continew.starter.data.core.annotation.Query; +import top.continew.starter.data.core.annotation.QueryIgnore; +import top.continew.starter.data.core.enums.QueryType; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 消息查询条件 + * + * @author Bull-BCLS + * @since 2023/10/15 19:05 + */ +@Data +@Schema(description = "消息查询条件") +public class MessageQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @Schema(description = "ID", example = "1") + private Long id; + + /** + * 标题 + */ + @Schema(description = "标题", example = "欢迎注册 xxx") + @Query(type = QueryType.LIKE) + private String title; + + /** + * 类型 + */ + @Schema(description = "类型", example = "1") + private MessageTypeEnum type; + + /** + * 是否已读 + */ + @Schema(description = "是否已读", example = "true") + @QueryIgnore + private Boolean isRead; + + /** + * 用户 ID + */ + @Schema(hidden = true) + @QueryIgnore + private Long userId; +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/query/NoticeQuery.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/query/NoticeQuery.java new file mode 100644 index 0000000..8765330 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/query/NoticeQuery.java @@ -0,0 +1,36 @@ +package top.continew.admin.system.model.query; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.starter.data.core.annotation.Query; +import top.continew.starter.data.core.enums.QueryType; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 公告查询条件 + * + * @author Charles7c + * @since 2023/8/20 10:55 + */ +@Data +@Schema(description = "公告查询条件") +public class NoticeQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 标题 + */ + @Schema(description = "标题", example = "这是公告标题") + @Query(type = QueryType.LIKE) + private String title; + + /** + * 类型 + */ + @Schema(description = "类型", example = "1") + private String type; +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/query/OptionQuery.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/query/OptionQuery.java new file mode 100644 index 0000000..092f113 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/query/OptionQuery.java @@ -0,0 +1,37 @@ +package top.continew.admin.system.model.query; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.starter.data.core.annotation.Query; +import top.continew.starter.data.core.enums.QueryType; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 参数查询条件 + * + * @author Bull-BCLS + * @since 2023/8/26 19:38 + */ +@Data +@Schema(description = "参数查询条件") +public class OptionQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 键列表 + */ + @Schema(description = "键列表", example = "SITE_TITLE,SITE_COPYRIGHT") + @Query(type = QueryType.IN) + private List code; + + /** + * 类别 + */ + @Schema(description = "类别", example = "SITE") + private String category; +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/query/RoleQuery.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/query/RoleQuery.java new file mode 100644 index 0000000..0e7db95 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/query/RoleQuery.java @@ -0,0 +1,30 @@ +package top.continew.admin.system.model.query; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.starter.data.core.annotation.Query; +import top.continew.starter.data.core.enums.QueryType; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 角色查询条件 + * + * @author Charles7c + * @since 2023/2/8 23:04 + */ +@Data +@Schema(description = "角色查询条件") +public class RoleQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 关键词 + */ + @Schema(description = "关键词", example = "测试人员") + @Query(columns = {"name", "code", "description"}, type = QueryType.LIKE) + private String description; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/query/StorageQuery.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/query/StorageQuery.java new file mode 100644 index 0000000..d844e30 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/query/StorageQuery.java @@ -0,0 +1,37 @@ +package top.continew.admin.system.model.query; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.starter.data.core.annotation.Query; +import top.continew.starter.data.core.enums.QueryType; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 存储查询条件 + * + * @author Charles7c + * @since 2023/12/26 22:09 + */ +@Data +@Schema(description = "存储查询条件") +public class StorageQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 关键词 + */ + @Schema(description = "关键词", example = "本地存储") + @Query(columns = {"name", "code", "description"}, type = QueryType.LIKE) + private String description; + + /** + * 状态 + */ + @Schema(description = "状态", example = "1") + private DisEnableStatusEnum status; +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/query/UserBalanceQuery.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/query/UserBalanceQuery.java new file mode 100644 index 0000000..be2cd9c --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/query/UserBalanceQuery.java @@ -0,0 +1,32 @@ +package top.continew.admin.system.model.query; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 用户余额查询条件 + * + * @author Charles7c + * @since 2023/2/20 21:01 + */ +@Data +@Schema(description = "用户查询条件") +public class UserBalanceQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * id + */ + @Schema(description = "用户id", example = "1111") + private Long id; + /** + * 消耗点数 + */ + @Schema(description = "消耗点数", example = "12") + private Integer inviteBeans; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/query/UserQuery.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/query/UserQuery.java new file mode 100644 index 0000000..02b626d --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/query/UserQuery.java @@ -0,0 +1,60 @@ +package top.continew.admin.system.model.query; + +import cn.hutool.core.date.DatePattern; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Size; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; +import top.continew.admin.common.enums.DisEnableStatusEnum; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + * 用户查询条件 + * + * @author Charles7c + * @since 2023/2/20 21:01 + */ +@Data +@Schema(description = "用户查询条件") +public class UserQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 关键词 + */ + @Schema(description = "关键词", example = "zhangsan") + private String description; + + /** + * 状态 + */ + @Schema(description = "状态", example = "1") + private DisEnableStatusEnum status; + + /** + * 创建时间 + */ + @Schema(description = "创建时间", example = "2023-08-08 00:00:00,2023-08-08 23:59:59") + @DateTimeFormat(pattern = DatePattern.NORM_DATETIME_PATTERN) + @Size(max = 2, message = "创建时间必须是一个范围") + private List createTime; + + /** + * 部门 ID + */ + @Schema(description = "部门 ID", example = "1") + private Long deptId; + + /** + * 部门 ID列表 + */ + @Schema(description = "部门 ID列表", example = "[1, 2]") + private List deptIds; + +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/DeptReq.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/DeptReq.java new file mode 100644 index 0000000..6b6086f --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/DeptReq.java @@ -0,0 +1,80 @@ +package top.continew.admin.system.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.hibernate.validator.constraints.Length; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.starter.extension.crud.model.req.BaseReq; + +import java.io.Serial; + +/** + * 创建或修改部门信息 + * + * @author Charles7c + * @since 2023/1/24 00:21 + */ +@Data +@Schema(description = "创建或修改部门信息") +public class DeptReq extends BaseReq { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 上级部门 ID + */ + @Schema(description = "上级部门 ID", example = "2") + @NotNull(message = "上级部门不能为空") + private Long parentId; + + /** + * 名称 + */ + @Schema(description = "名称", example = "测试部") + @NotBlank(message = "名称不能为空") + @Length(max = 30, message = "名称长度不能超过 {max} 个字符") + private String name; + /** + * 数据库名称 + */ + @Schema(description = "数据库名称", example = "测试部") + @NotBlank(message = "名称不能为空") + @Length(max = 30, message = "名称长度不能超过 {max} 个字符") + private String dbName; + + /** + * 排序 + */ + @Schema(description = "排序", example = "1") + @Min(value = 1, message = "排序最小值为 {value}") + private Integer sort; + + /** + * 描述 + */ + @Schema(description = "描述", example = "测试部描述信息") + @Length(max = 200, message = "描述长度不能超过 {max} 个字符") + private String description; + + /** + * 状态 + */ + @Schema(description = "状态", example = "1") + private DisEnableStatusEnum status; + + /** + * 祖级列表 + */ + @Schema(hidden = true) + private String ancestors; + + /** + * 学院与每个页面的绑定 + */ + @Schema(description = "学院与每个页面的绑定", example = "学校信息") + private String routers; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/DictItemReq.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/DictItemReq.java new file mode 100644 index 0000000..b93b117 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/DictItemReq.java @@ -0,0 +1,76 @@ +package top.continew.admin.system.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.hibernate.validator.constraints.Length; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.starter.extension.crud.model.req.BaseReq; + +import java.io.Serial; + +/** + * 创建或修改字典项信息 + * + * @author Charles7c + * @since 2023/9/11 21:29 + */ +@Data +@Schema(description = "创建或修改字典项信息") +public class DictItemReq extends BaseReq { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 标签 + */ + @Schema(description = "标签", example = "通知") + @NotBlank(message = "标签不能为空") + @Length(max = 30, message = "标签长度不能超过 {max} 个字符") + private String label; + + /** + * 值 + */ + @Schema(description = "值", example = "1") + @NotBlank(message = "值不能为空") + @Length(max = 30, message = "值长度不能超过 {max} 个字符") + private String value; + + /** + * 标签颜色 + */ + @Schema(description = "标签颜色", example = "blue") + @Length(max = 30, message = "标签颜色长度不能超过 {max} 个字符") + private String color; + + /** + * 排序 + */ + @Schema(description = "排序", example = "1") + @Min(value = 1, message = "排序最小值为 {value}") + private Integer sort; + + /** + * 描述 + */ + @Schema(description = "描述", example = "通知描述信息") + @Length(max = 200, message = "描述长度不能超过 {max} 个字符") + private String description; + + /** + * 状态 + */ + @Schema(description = "状态", example = "1") + private DisEnableStatusEnum status; + + /** + * 所属字典 + */ + @Schema(description = "所属字典", example = "1") + @NotNull(message = "所属字典不能为空") + private Long dictId; +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/DictReq.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/DictReq.java new file mode 100644 index 0000000..0de0328 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/DictReq.java @@ -0,0 +1,48 @@ +package top.continew.admin.system.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import lombok.Data; +import org.hibernate.validator.constraints.Length; +import top.continew.admin.common.constant.RegexConstants; +import top.continew.starter.extension.crud.model.req.BaseReq; + +import java.io.Serial; + +/** + * 创建或修改字典信息 + * + * @author Charles7c + * @since 2023/9/11 21:29 + */ +@Data +@Schema(description = "创建或修改字典信息") +public class DictReq extends BaseReq { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 名称 + */ + @Schema(description = "名称", example = "公告类型") + @NotBlank(message = "名称不能为空") + @Pattern(regexp = RegexConstants.GENERAL_NAME, message = "名称长度为 2-30 个字符,支持中文、字母、数字、下划线,短横线") + private String name; + + /** + * 编码 + */ + @Schema(description = "编码", example = "notice_type") + @NotBlank(message = "编码不能为空") + @Pattern(regexp = RegexConstants.GENERAL_CODE, message = "编码长度为 2-30 个字符,支持大小写字母、数字、下划线,以字母开头") + private String code; + + /** + * 描述 + */ + @Schema(description = "描述", example = "公告类型描述信息") + @Length(max = 200, message = "描述长度不能超过 {max} 个字符") + private String description; +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/FileReq.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/FileReq.java new file mode 100644 index 0000000..d2cfd65 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/FileReq.java @@ -0,0 +1,31 @@ +package top.continew.admin.system.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import org.hibernate.validator.constraints.Length; +import top.continew.starter.extension.crud.model.req.BaseReq; + +import java.io.Serial; + +/** + * 修改文件信息 + * + * @author Charles7c + * @since 2023/12/23 10:38 + */ +@Data +@Schema(description = "修改文件信息") +public class FileReq extends BaseReq { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 名称 + */ + @Schema(description = "名称", example = "test123") + @NotBlank(message = "文件名称不能为空") + @Length(max = 255, message = "文件名称长度不能超过 {max} 个字符") + private String name; +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/MenuReq.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/MenuReq.java new file mode 100644 index 0000000..cb3e8dd --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/MenuReq.java @@ -0,0 +1,122 @@ +package top.continew.admin.system.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.hibernate.validator.constraints.Length; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.admin.system.enums.MenuTypeEnum; +import top.continew.starter.extension.crud.model.req.BaseReq; + +import java.io.Serial; + +/** + * 创建或修改菜单信息 + * + * @author Charles7c + * @since 2023/2/15 20:21 + */ +@Data +@Schema(description = "创建或修改菜单信息") +public class MenuReq extends BaseReq { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 类型 + */ + @Schema(description = "类型", example = "2") + @NotNull(message = "类型非法") + private MenuTypeEnum type; + + /** + * 图标 + */ + @Schema(description = "图标", example = "user") + @Length(max = 50, message = "图标长度不能超过 {max} 个字符") + private String icon; + + /** + * 标题 + */ + @Schema(description = "标题", example = "用户管理") + @NotBlank(message = "标题不能为空") + @Length(max = 30, message = "标题长度不能超过 {max} 个字符") + private String title; + + /** + * 排序 + */ + @Schema(description = "排序", example = "1") + @NotNull(message = "排序不能为空") + @Min(value = 1, message = "排序最小值为 {value}") + private Integer sort; + + /** + * 权限标识 + */ + @Schema(description = "权限标识", example = "system:user:list") + @Length(max = 100, message = "权限标识长度不能超过 {max} 个字符") + private String permission; + + /** + * 路由地址 + */ + @Schema(description = "路由地址", example = "/system/user") + @Length(max = 255, message = "路由地址长度不能超过 {max} 个字符") + private String path; + + /** + * 组件名称 + */ + @Schema(description = "组件名称", example = "User") + @Length(max = 50, message = "组件名称长度不能超过 {max} 个字符") + private String name; + + /** + * 组件路径 + */ + @Schema(description = "组件路径", example = "/system/user/index") + @Length(max = 255, message = "组件路径长度不能超过 {max} 个字符") + private String component; + + /** + * 重定向地址 + */ + @Schema(description = "重定向地址") + private String redirect; + + /** + * 是否外链 + */ + @Schema(description = "是否外链", example = "false") + private Boolean isExternal; + + /** + * 是否缓存 + */ + @Schema(description = "是否缓存", example = "false") + private Boolean isCache; + + /** + * 是否隐藏 + */ + @Schema(description = "是否隐藏", example = "false") + private Boolean isHidden; + + /** + * 上级菜单 ID + */ + @Schema(description = "上级菜单 ID", example = "1000") + private Long parentId; + + /** + * 状态 + */ + @Schema(description = "状态", example = "1") + @NotNull(message = "状态非法") + private DisEnableStatusEnum status; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/MessageReq.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/MessageReq.java new file mode 100644 index 0000000..0f76c31 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/MessageReq.java @@ -0,0 +1,48 @@ +package top.continew.admin.system.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.hibernate.validator.constraints.Length; +import top.continew.admin.system.enums.MessageTypeEnum; +import top.continew.starter.extension.crud.model.req.BaseReq; + +import java.io.Serial; + +/** + * 创建消息信息 + * + * @author Bull-BCLS + * @since 2023/10/15 19:05 + */ +@Data +@Schema(description = "创建消息信息") +public class MessageReq extends BaseReq { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 标题 + */ + @Schema(description = "标题", example = "欢迎注册 xxx") + @NotBlank(message = "标题不能为空") + @Length(max = 50, message = "标题长度不能超过 {max} 个字符") + private String title; + + /** + * 内容 + */ + @Schema(description = "内容", example = "尊敬的 xx,欢迎注册使用,请及时配置您的密码。") + @NotBlank(message = "内容不能为空") + @Length(max = 255, message = "内容长度不能超过 {max} 个字符") + private String content; + + /** + * 类型 + */ + @Schema(description = "类型(1:系统消息)", example = "1") + @NotNull(message = "类型非法") + private MessageTypeEnum type; +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/NoticeReq.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/NoticeReq.java new file mode 100644 index 0000000..14d45cc --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/NoticeReq.java @@ -0,0 +1,61 @@ +package top.continew.admin.system.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Future; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import org.hibernate.validator.constraints.Length; +import top.continew.starter.extension.crud.model.req.BaseReq; + +import java.io.Serial; +import java.time.LocalDateTime; + +/** + * 创建或修改公告信息 + * + * @author Charles7c + * @since 2023/8/20 10:55 + */ +@Data +@Schema(description = "创建或修改公告信息") +public class NoticeReq extends BaseReq { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 标题 + */ + @Schema(description = "标题", example = "这是公告标题") + @NotBlank(message = "标题不能为空") + @Length(max = 150, message = "标题长度不能超过 {max} 个字符") + private String title; + + /** + * 内容 + */ + @Schema(description = "内容", example = "这是公告内容") + @NotBlank(message = "内容不能为空") + private String content; + + /** + * 类型(取值于字典 notice_type) + */ + @Schema(description = "类型(取值于字典 notice_type)", example = "1") + @NotBlank(message = "类型不能为空") + @Length(max = 30, message = "类型长度不能超过 {max} 个字符") + private String type; + + /** + * 生效时间 + */ + @Schema(description = "生效时间", example = "2023-08-08 00:00:00", type = "string") + private LocalDateTime effectiveTime; + + /** + * 终止时间 + */ + @Schema(description = "终止时间", example = "2023-08-08 23:59:59", type = "string") + @Future(message = "终止时间必须是未来时间") + private LocalDateTime terminateTime; +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/OptionReq.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/OptionReq.java new file mode 100644 index 0000000..96ef4de --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/OptionReq.java @@ -0,0 +1,46 @@ +package top.continew.admin.system.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.hibernate.validator.constraints.Length; +import top.continew.starter.extension.crud.model.req.BaseReq; + +import java.io.Serial; + +/** + * 修改参数信息 + * + * @author Bull-BCLS + * @since 2023/8/26 19:38 + */ +@Data +@Schema(description = "修改参数信息") +public class OptionReq extends BaseReq { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @Schema(description = "ID", example = "1") + @NotNull(message = "ID不能为空") + private Long id; + + /** + * 键 + */ + @Schema(description = "键", example = "site_title") + @NotBlank(message = "键不能为空") + @Length(max = 100, message = "键长度不能超过 {max} 个字符") + private String code; + + /** + * 值 + */ + @Schema(description = "值", example = "ContiNew Admin") + @NotBlank(message = "值不能为空") + private String value; +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/OptionResetValueReq.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/OptionResetValueReq.java new file mode 100644 index 0000000..b389221 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/OptionResetValueReq.java @@ -0,0 +1,34 @@ +package top.continew.admin.system.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 参数重置信息 + * + * @author Bull-BCLS + * @since 2023/9/21 23:10 + */ +@Data +@Schema(description = "参数重置信息") +public class OptionResetValueReq implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 键列表 + */ + @Schema(description = "键列表", example = "SITE_TITLE,SITE_COPYRIGHT") + private List code; + + /** + * 类别 + */ + @Schema(description = "类别", example = "SITE") + private String category; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/RoleReq.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/RoleReq.java new file mode 100644 index 0000000..89cb873 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/RoleReq.java @@ -0,0 +1,89 @@ +package top.continew.admin.system.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import lombok.Data; +import org.hibernate.validator.constraints.Length; +import top.continew.admin.common.constant.RegexConstants; +import top.continew.admin.common.enums.DataScopeEnum; +import top.continew.starter.extension.crud.model.req.BaseReq; + +import java.io.Serial; +import java.util.ArrayList; +import java.util.List; + +/** + * 创建或修改角色信息 + * + * @author Charles7c + * @since 2023/2/8 23:12 + */ +@Data +@Schema(description = "创建或修改角色信息") +public class RoleReq extends BaseReq { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 名称 + */ + @Schema(description = "名称", example = "测试人员") + @NotBlank(message = "名称不能为空") + @Pattern(regexp = RegexConstants.GENERAL_NAME, message = "名称长度为 2-30 个字符,支持中文、字母、数字、下划线,短横线") + private String name; + + /** + * 编码 + */ + @Schema(description = "编码", example = "test") + @NotBlank(message = "编码不能为空") + @Pattern(regexp = RegexConstants.GENERAL_CODE, message = "编码长度为 2-30 个字符,支持大小写字母、数字、下划线,以字母开头") + private String code; + + /** + * 排序 + */ + @Schema(description = "排序", example = "1") + @Min(value = 1, message = "排序最小值为 {value}") + private Integer sort; + + /** + * 描述 + */ + @Schema(description = "描述", example = "测试人员描述信息") + @Length(max = 200, message = "描述长度不能超过 {max} 个字符") + private String description; + + /** + * 功能权限:菜单 ID 列表 + */ + @Schema(description = "功能权限:菜单 ID 列表", example = "1000,1010,1011,1012,1013,1014") + private List menuIds = new ArrayList<>(); + + /** + * 数据权限 + */ + @Schema(description = "数据权限", example = "5") + private DataScopeEnum dataScope; + + /** + * 权限范围:部门 ID 列表 + */ + @Schema(description = "权限范围:部门 ID 列表", example = "5") + private List deptIds = new ArrayList<>(); + + /** + * 菜单选择是否父子节点关联 + */ + @Schema(description = "菜单选择是否父子节点关联", example = "false") + private Boolean menuCheckStrictly; + + /** + * 部门选择是否父子节点关联 + */ + @Schema(description = "部门选择是否父子节点关联", example = "false") + private Boolean deptCheckStrictly; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/StorageReq.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/StorageReq.java new file mode 100644 index 0000000..9d05391 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/StorageReq.java @@ -0,0 +1,118 @@ +package top.continew.admin.system.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import lombok.Data; +import org.hibernate.validator.constraints.Length; +import top.continew.admin.common.constant.RegexConstants; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.admin.system.enums.StorageTypeEnum; +import top.continew.admin.system.util.ValidateGroup; +import top.continew.starter.extension.crud.model.req.BaseReq; + +import java.io.Serial; + +/** + * 存储请求信息 + * + * @author Charles7c + * @since 2023/12/26 22:09 + */ +@Data +@Schema(description = "存储请求信息") +public class StorageReq extends BaseReq { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 名称 + */ + @Schema(description = "名称", example = "存储1") + @NotBlank(message = "名称不能为空") + @Length(max = 100, message = "名称长度不能超过 {max} 个字符") + private String name; + + /** + * 编码 + */ + @Schema(description = "编码", example = "local") + @NotBlank(message = "编码不能为空") + @Pattern(regexp = RegexConstants.GENERAL_CODE, message = "编码长度为 2-30 个字符,支持大小写字母、数字、下划线,以字母开头") + private String code; + + /** + * 类型 + */ + @Schema(description = "类型", example = "2") + @NotNull(message = "类型非法") + private StorageTypeEnum type; + + /** + * 访问密钥 + */ + @Schema(description = "访问密钥", example = "") + @Length(max = 255, message = "访问密钥长度不能超过 {max} 个字符") + @NotBlank(message = "访问密钥不能为空", groups = ValidateGroup.Storage.S3.class) + private String accessKey; + + /** + * 私有密钥 + */ + @Schema(description = "私有密钥", example = "") + @NotBlank(message = "私有密钥不能为空", groups = ValidateGroup.Storage.S3.class) + private String secretKey; + + /** + * 终端节点 + */ + @Schema(description = "终端节点", example = "") + @Length(max = 255, message = "终端节点长度不能超过 {max} 个字符") + @NotBlank(message = "终端节点不能为空", groups = ValidateGroup.Storage.S3.class) + private String endpoint; + + /** + * 桶名称 + */ + @Schema(description = "桶名称", example = "C:/continew-admin/data/file/") + @Length(max = 255, message = "桶名称长度不能超过 {max} 个字符") + @NotBlank(message = "桶名称不能为空", groups = ValidateGroup.Storage.S3.class) + @NotBlank(message = "存储路径不能为空", groups = ValidateGroup.Storage.Local.class) + private String bucketName; + + /** + * 域名 + */ + @Schema(description = "域名", example = "http://localhost:8000/file") + @Length(max = 255, message = "域名长度不能超过 {max} 个字符") + @NotBlank(message = "域名不能为空") + private String domain; + + /** + * 排序 + */ + @Schema(description = "排序", example = "1") + private Integer sort; + + /** + * 描述 + */ + @Schema(description = "描述", example = "存储描述") + @Length(max = 200, message = "描述长度不能超过 {max} 个字符") + private String description; + + /** + * 是否为默认存储 + */ + @Schema(description = "是否为默认存储", example = "true") + @NotNull(message = "是否为默认存储不能为空") + private Boolean isDefault; + + /** + * 状态 + */ + @Schema(description = "状态", example = "1") + private DisEnableStatusEnum status; +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserBasicInfoUpdateReq.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserBasicInfoUpdateReq.java new file mode 100644 index 0000000..5e74995 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserBasicInfoUpdateReq.java @@ -0,0 +1,41 @@ +package top.continew.admin.system.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import lombok.Data; +import top.continew.admin.common.constant.RegexConstants; +import top.continew.admin.common.enums.GenderEnum; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 用户基础信息修改信息 + * + * @author Charles7c + * @since 2023/1/7 23:08 + */ +@Data +@Schema(description = "用户基础信息修改信息") +public class UserBasicInfoUpdateReq implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 昵称 + */ + @Schema(description = "昵称", example = "张三") + @NotBlank(message = "昵称不能为空") + @Pattern(regexp = RegexConstants.GENERAL_NAME, message = "昵称长度为 2-30 个字符,支持中文、字母、数字、下划线,短横线") + private String nickname; + + /** + * 性别 + */ + @Schema(description = "性别", example = "1") + @NotNull(message = "性别非法") + private GenderEnum gender; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserEmailUpdateRequest.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserEmailUpdateRequest.java new file mode 100644 index 0000000..fdbe52a --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserEmailUpdateRequest.java @@ -0,0 +1,48 @@ +package top.continew.admin.system.model.req; + +import cn.hutool.core.lang.RegexPool; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 用户邮箱修改信息 + * + * @author Charles7c + * @since 2023/1/12 20:18 + */ +@Data +@Schema(description = "用户邮箱修改信息") +public class UserEmailUpdateRequest implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 新邮箱 + */ + @Schema(description = "新邮箱", example = "123456789@qq.com") + @NotBlank(message = "新邮箱不能为空") + @Pattern(regexp = RegexPool.EMAIL, message = "邮箱格式错误") + private String email; + + /** + * 验证码 + */ + @Schema(description = "验证码", example = "888888") + @NotBlank(message = "验证码不能为空") + @Length(max = 6, message = "验证码非法") + private String captcha; + + /** + * 当前密码(加密) + */ + @Schema(description = "当前密码(加密)", example = "SYRLSszQGcMv4kP2Yolou9zf28B9GDakR9u91khxmR7V++i5A384kwnNZxqgvT6bjT4zqpIDuMFLWSt92hQJJA==") + @NotBlank(message = "当前密码不能为空") + private String oldPassword; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserImportReq.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserImportReq.java new file mode 100644 index 0000000..539118a --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserImportReq.java @@ -0,0 +1,59 @@ +package top.continew.admin.system.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.admin.system.enums.ImportPolicyEnum; +import top.continew.starter.extension.crud.model.req.BaseReq; + +import java.io.Serial; + +/** + * 用户导入参数 + * + * @author Kils + * @since 2024-6-17 16:42 + */ +@Data +@Schema(description = "用户导入参数") +public class UserImportReq extends BaseReq { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 导入会话KEY + */ + @Schema(description = "导入会话KEY", example = "1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed") + @NotBlank(message = "导入已过期,请重新上传") + private String importKey; + + /** + * 用户重复策略 + */ + @Schema(description = "重复用户策略", example = "1") + @NotNull(message = "重复用户策略不能为空") + private ImportPolicyEnum duplicateUser; + + /** + * 重复邮箱策略 + */ + @Schema(description = "重复邮箱策略", example = "1") + @NotNull(message = "重复邮箱策略不能为空") + private ImportPolicyEnum duplicateEmail; + + /** + * 重复手机策略 + */ + @Schema(description = "重复手机策略", example = "1") + @NotNull(message = "重复手机策略不能为空") + private ImportPolicyEnum duplicatePhone; + + /** + * 默认状态 + */ + @Schema(description = "默认状态", example = "1") + private DisEnableStatusEnum defaultStatus; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserImportRowReq.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserImportRowReq.java new file mode 100644 index 0000000..94d24e0 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserImportRowReq.java @@ -0,0 +1,83 @@ +package top.continew.admin.system.model.req; + +import cn.hutool.core.lang.RegexPool; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import lombok.Data; +import org.hibernate.validator.constraints.Length; +import top.continew.admin.common.constant.RegexConstants; +import top.continew.starter.extension.crud.model.req.BaseReq; +import top.continew.starter.extension.crud.util.ValidateGroup; + +import java.io.Serial; + +/** + * 用户导入行数据 + * + * @author Kils + * @since 2024-6-17 16:42 + */ +@Data +@Schema(description = "用户导入行数据") +public class UserImportRowReq extends BaseReq { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户名 + */ + @NotBlank(message = "用户名不能为空") + @Pattern(regexp = RegexConstants.USERNAME, message = "用户名长度为 4-64 个字符,支持大小写字母、数字、下划线,以字母开头") + private String username; + + /** + * 昵称 + */ + @NotBlank(message = "昵称不能为空") + @Pattern(regexp = RegexConstants.GENERAL_NAME, message = "昵称长度为 2-30 个字符,支持中文、字母、数字、下划线,短横线") + private String nickname; + + /** + * 密码 + */ + @NotBlank(message = "密码不能为空", groups = ValidateGroup.Crud.Add.class) + private String password; + + /** + * 部门名称 + */ + @NotNull(message = "所属部门不能为空") + private String deptName; + + /** + * 角色 + */ + @NotBlank(message = "所属角色不能为空") + private String roleName; + + /** + * 性别 + */ + private String gender; + + /** + * 邮箱 + */ + @Pattern(regexp = "^$|" + RegexPool.EMAIL, message = "邮箱格式错误") + @Length(max = 255, message = "邮箱长度不能超过 {max} 个字符") + private String email; + + /** + * 手机号码 + */ + @Pattern(regexp = "^$|" + RegexPool.MOBILE, message = "手机号码格式错误") + private String phone; + + /** + * 描述 + */ + private String description; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserPasswordResetReq.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserPasswordResetReq.java new file mode 100644 index 0000000..0af2db4 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserPasswordResetReq.java @@ -0,0 +1,29 @@ +package top.continew.admin.system.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 用户密码重置信息 + * + * @author Charles7c + * @since 2024/2/2 22:50 + */ +@Data +@Schema(description = "用户密码重置信息") +public class UserPasswordResetReq implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 新密码(加密) + */ + @Schema(description = "新密码(加密)", example = "Gzc78825P5baH190lRuZFb9KJxRt/psN2jiyOMPoc5WRcCvneCwqDm3Q33BZY56EzyyVy7vQu7jQwYTK4j1+5w==") + @NotBlank(message = "新密码不能为空") + private String newPassword; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserPasswordUpdateReq.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserPasswordUpdateReq.java new file mode 100644 index 0000000..a9746dc --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserPasswordUpdateReq.java @@ -0,0 +1,35 @@ +package top.continew.admin.system.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 用户密码修改信息 + * + * @author Charles7c + * @since 2023/1/9 23:28 + */ +@Data +@Schema(description = "用户密码修改信息") +public class UserPasswordUpdateReq implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 当前密码(加密) + */ + @Schema(description = "当前密码(加密)", example = "E7c72TH+LDxKTwavjM99W1MdI9Lljh79aPKiv3XB9MXcplhm7qJ1BJCj28yaflbdVbfc366klMtjLIWQGqb0qw==") + private String oldPassword; + + /** + * 新密码(加密) + */ + @Schema(description = "新密码(加密)", example = "Gzc78825P5baH190lRuZFb9KJxRt/psN2jiyOMPoc5WRcCvneCwqDm3Q33BZY56EzyyVy7vQu7jQwYTK4j1+5w==") + @NotBlank(message = "新密码不能为空") + private String newPassword; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserPhoneUpdateReq.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserPhoneUpdateReq.java new file mode 100644 index 0000000..0492b01 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserPhoneUpdateReq.java @@ -0,0 +1,48 @@ +package top.continew.admin.system.model.req; + +import cn.hutool.core.lang.RegexPool; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 用户手机号修改信息 + * + * @author Charles7c + * @since 2023/10/27 20:11 + */ +@Data +@Schema(description = "用户手机号修改信息") +public class UserPhoneUpdateReq implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 新手机号 + */ + @Schema(description = "新手机号", example = "13811111111") + @NotBlank(message = "新手机号不能为空") + @Pattern(regexp = RegexPool.MOBILE, message = "手机号格式错误") + private String phone; + + /** + * 验证码 + */ + @Schema(description = "验证码", example = "8888") + @NotBlank(message = "验证码不能为空") + @Length(max = 4, message = "验证码非法") + private String captcha; + + /** + * 当前密码(加密) + */ + @Schema(description = "当前密码(加密)", example = "SYRLSszQGcMv4kP2Yolou9zf28B9GDakR9u91khxmR7V++i5A384kwnNZxqgvT6bjT4zqpIDuMFLWSt92hQJJA==") + @NotBlank(message = "当前密码不能为空") + private String oldPassword; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserReq.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserReq.java new file mode 100644 index 0000000..52e2a2a --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserReq.java @@ -0,0 +1,104 @@ +package top.continew.admin.system.model.req; + +import cn.hutool.core.lang.RegexPool; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import lombok.Data; +import org.hibernate.validator.constraints.Length; +import top.continew.admin.common.constant.RegexConstants; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.admin.common.enums.GenderEnum; +import top.continew.starter.extension.crud.model.req.BaseReq; +import top.continew.starter.extension.crud.util.ValidateGroup; + +import java.io.Serial; +import java.util.List; + +/** + * 创建或修改用户信息 + * + * @author Charles7c + * @since 2023/2/20 21:03 + */ +@Data +@Schema(description = "创建或修改用户信息") +public class UserReq extends BaseReq { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户名 + */ + @Schema(description = "用户名", example = "zhangsan") + @NotBlank(message = "用户名不能为空") + @Pattern(regexp = RegexConstants.USERNAME, message = "用户名长度为 4-64 个字符,支持大小写字母、数字、下划线") + private String username; + + /** + * 昵称 + */ + @Schema(description = "昵称", example = "张三") + @NotBlank(message = "昵称不能为空") + @Pattern(regexp = RegexConstants.GENERAL_NAME, message = "昵称长度为 2-30 个字符,支持中文、字母、数字、下划线,短横线") + private String nickname; + + /** + * 密码(加密) + */ + @Schema(description = "密码(加密)", example = "E7c72TH+LDxKTwavjM99W1MdI9Lljh79aPKiv3XB9MXcplhm7qJ1BJCj28yaflbdVbfc366klMtjLIWQGqb0qw==") + @NotBlank(message = "密码不能为空", groups = ValidateGroup.Crud.Add.class) + private String password; + + /** + * 邮箱 + */ + @Schema(description = "邮箱", example = "123456789@qq.com") + @Pattern(regexp = "^$|" + RegexPool.EMAIL, message = "邮箱格式错误") + @Length(max = 255, message = "邮箱长度不能超过 {max} 个字符") + private String email; + + /** + * 手机号码 + */ + @Schema(description = "手机号码", example = "13811111111") + @Pattern(regexp = "^$|" + RegexPool.MOBILE, message = "手机号码格式错误") + private String phone; + + /** + * 性别 + */ + @Schema(description = "性别", example = "1") + @NotNull(message = "性别非法") + private GenderEnum gender; + + /** + * 所属部门 + */ + @Schema(description = "所属部门", example = "5") + @NotNull(message = "所属部门不能为空") + private Long deptId; + + /** + * 所属角色 + */ + @Schema(description = "所属角色", example = "2") + @NotEmpty(message = "所属角色不能为空") + private List roleIds; + + /** + * 描述 + */ + @Schema(description = "描述", example = "张三描述信息") + @Length(max = 200, message = "描述长度不能超过 {max} 个字符") + private String description; + + /** + * 状态 + */ + @Schema(description = "状态", example = "1") + private DisEnableStatusEnum status; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserRoleUpdateReq.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserRoleUpdateReq.java new file mode 100644 index 0000000..73a70c1 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/req/UserRoleUpdateReq.java @@ -0,0 +1,30 @@ +package top.continew.admin.system.model.req; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 用户角色修改信息 + * + * @author Charles7c + * @since 2023/2/24 23:05 + */ +@Data +@Schema(description = "用户角色修改信息") +public class UserRoleUpdateReq implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 角色 ID 列表 + */ + @Schema(description = "所属角色", example = "1,2") + @NotEmpty(message = "所属角色不能为空") + private List roleIds; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/AvatarResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/AvatarResp.java new file mode 100644 index 0000000..79348a4 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/AvatarResp.java @@ -0,0 +1,29 @@ +package top.continew.admin.system.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 头像信息 + * + * @author Charles7c + * @since 2023/1/2 16:29 + */ +@Data +@Builder +@Schema(description = "头像信息") +public class AvatarResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 头像地址 + */ + @Schema(description = "头像地址", example = "https://himg.bdimg.com/sys/portrait/item/public.1.81ac9a9e.rf1ix17UfughLQjNo7XQ_w.jpg") + private String avatar; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/DashboardAccessTrendResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/DashboardAccessTrendResp.java new file mode 100644 index 0000000..f12ceda --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/DashboardAccessTrendResp.java @@ -0,0 +1,39 @@ +package top.continew.admin.system.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 仪表盘-访问趋势信息 + * + * @author Charles7c + * @since 2023/9/9 20:20 + */ +@Data +@Schema(description = "仪表盘-访问趋势信息") +public class DashboardAccessTrendResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 日期 + */ + @Schema(description = "日期", example = "2023-08-08") + private String date; + + /** + * 浏览量(PV) + */ + @Schema(description = "浏览量(PV)", example = "1000") + private Long pvCount; + + /** + * IP 数 + */ + @Schema(description = "IP 数", example = "500") + private Long ipCount; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/DashboardGeoDistributionResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/DashboardGeoDistributionResp.java new file mode 100644 index 0000000..3b2cd57 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/DashboardGeoDistributionResp.java @@ -0,0 +1,35 @@ +package top.continew.admin.system.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +/** + * 仪表盘-访客地域分布信息 + * + * @author Charles7c + * @since 2023/9/9 12:07 + */ +@Data +@Schema(description = "仪表盘-访客地域分布信息") +public class DashboardGeoDistributionResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 地点列表 + */ + @Schema(description = "地点列表", example = "[\"中国北京北京市\",\"中国广东省深圳市\"]") + private List locations; + + /** + * 地点 IP 统计信息 + */ + @Schema(description = "地点 IP 统计信息", example = "[{\"name\":\"中国北京北京市\",\"value\":1000},{\"name\":\"中国广东省深圳市\",\"value\": 500}]") + private List> locationIpStatistics; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/DashboardNoticeResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/DashboardNoticeResp.java new file mode 100644 index 0000000..fc8a001 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/DashboardNoticeResp.java @@ -0,0 +1,39 @@ +package top.continew.admin.system.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 仪表盘-公告信息 + * + * @author Charles7c + * @since 2023/8/20 10:55 + */ +@Data +@Schema(description = "仪表盘-公告信息") +public class DashboardNoticeResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @Schema(description = "ID", example = "1") + private Long id; + + /** + * 标题 + */ + @Schema(description = "标题", example = "这是公告标题") + private String title; + + /** + * 类型(取值于字典 notice_type) + */ + @Schema(description = "类型(取值于字典 notice_type)", example = "1") + private String type; +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/DashboardPopularModuleResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/DashboardPopularModuleResp.java new file mode 100644 index 0000000..22e8dc4 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/DashboardPopularModuleResp.java @@ -0,0 +1,53 @@ +package top.continew.admin.system.model.resp; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 仪表盘-热门模块信息 + * + * @author Charles7c + * @since 2023/9/9 9:52 + */ +@Data +@Schema(description = "仪表盘-热门模块信息") +public class DashboardPopularModuleResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 模块 + */ + @Schema(description = "模块", example = "角色管理") + private String module; + + /** + * 浏览量(PV) + */ + @Schema(description = "浏览量(PV)", example = "1234") + private Long pvCount; + + /** + * 较昨日新增 PV(百分比) + */ + @Schema(description = "较昨日新增(百分比)", example = "23.4") + private BigDecimal newPvFromYesterday; + + /** + * 今日浏览量(PV) + */ + @JsonIgnore + private Long todayPvCount; + + /** + * 昨日浏览量(PV) + */ + @JsonIgnore + private Long yesterdayPvCount; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/DashboardTotalResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/DashboardTotalResp.java new file mode 100644 index 0000000..0eab7b5 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/DashboardTotalResp.java @@ -0,0 +1,53 @@ +package top.continew.admin.system.model.resp; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 仪表盘-总计信息 + * + * @author Charles7c + * @since 2023/9/8 21:32 + */ +@Data +@Schema(description = "仪表盘-总计信息") +public class DashboardTotalResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 浏览量(PV) + */ + @Schema(description = "浏览量(PV)", example = "88888") + private Long pvCount; + + /** + * IP 数 + */ + @Schema(description = "IP 数", example = "66666") + private Long ipCount; + + /** + * 今日浏览量(PV) + */ + @Schema(description = "今日浏览量(PV)", example = "1234") + private Long todayPvCount; + + /** + * 较昨日新增 PV(百分比) + */ + @Schema(description = "较昨日新增(百分比)", example = "23.4") + private BigDecimal newPvFromYesterday; + + /** + * 昨日浏览量(PV) + */ + @JsonIgnore + private Long yesterdayPvCount; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/DeptChildResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/DeptChildResp.java new file mode 100644 index 0000000..3855074 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/DeptChildResp.java @@ -0,0 +1,80 @@ +package top.continew.admin.system.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.common.enums.DisEnableStatusEnum; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 部门信息 + * + * @author Charles7c + * @since 2023/1/22 13:53 + */ +@Data +@Schema(description = "部门信息") +public class DeptChildResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + /** + * ID + */ + @Schema(description = " ID", example = "2") + private Long id; + private Long key; + + /** + * 名称 + */ + @Schema(description = "名称", example = "测试部") + private String title; + /** + * 数据库名称 + */ + @Schema(description = "数据库名称", example = "数据库名称") + private String dbName; + /** + * 祖级列表 + */ + private String ancestors; + /** + * 上级部门 ID + */ + @Schema(description = "上级部门 ID", example = "2") + private Long parentId; + + /** + * 状态 + */ + @Schema(description = "状态", example = "1") + private DisEnableStatusEnum status; + + /** + * 排序 + */ + @Schema(description = "排序", example = "3") + private Integer sort; + + /** + * 是否为系统内置数据 + */ + @Schema(description = "是否为系统内置数据", example = "false") + private Boolean isSystem; + + /** + * 描述 + */ + @Schema(description = "描述", example = "测试部描述信息") + private String description; + + private List children; + /** + * 老师信息 + */ + private List teacherChildren; + +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/DeptResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/DeptResp.java new file mode 100644 index 0000000..55069f6 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/DeptResp.java @@ -0,0 +1,83 @@ +package top.continew.admin.system.model.resp; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.starter.extension.crud.annotation.TreeField; +import top.continew.starter.file.excel.converter.ExcelBaseEnumConverter; +import top.continew.starter.extension.crud.model.resp.BaseDetailResp; + +import java.io.Serial; + +/** + * 部门信息 + * + * @author Charles7c + * @since 2023/1/22 13:53 + */ +@Data +@ExcelIgnoreUnannotated +@TreeField(value = "id", nameKey = "name") +@Schema(description = "部门信息") +public class DeptResp extends BaseDetailResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 名称 + */ + @Schema(description = "名称", example = "测试部") + @ExcelProperty(value = "名称", order = 2) + private String name; + /** + * 数据库名称 + */ + @Schema(description = "数据库名称", example = "数据库名称") + @ExcelProperty(value = "数据库名称", order = 2) + private String dbName; + + /** + * 上级部门 ID + */ + @Schema(description = "上级部门 ID", example = "2") + @ExcelProperty(value = "上级部门 ID", order = 3) + private Long parentId; + + /** + * 状态 + */ + @Schema(description = "状态", example = "1") + @ExcelProperty(value = "状态", converter = ExcelBaseEnumConverter.class, order = 5) + private DisEnableStatusEnum status; + + /** + * 排序 + */ + @Schema(description = "排序", example = "3") + @ExcelProperty(value = "排序", order = 6) + private Integer sort; + + /** + * 是否为系统内置数据 + */ + @Schema(description = "是否为系统内置数据", example = "false") + @ExcelProperty(value = "系统内置", order = 7) + private Boolean isSystem; + + /** + * 描述 + */ + @Schema(description = "描述", example = "测试部描述信息") + @ExcelProperty(value = "描述", order = 8) + private String description; + + /** + * 学院与每个页面的绑定 + */ + @Schema(description = "描述", example = "测试部描述信息") + @ExcelProperty(value = "学院与每个页面的绑定", order = 8) + private String routers; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/DictItemResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/DictItemResp.java new file mode 100644 index 0000000..731e732 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/DictItemResp.java @@ -0,0 +1,67 @@ +package top.continew.admin.system.model.resp; + +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.starter.file.excel.converter.ExcelBaseEnumConverter; +import top.continew.starter.extension.crud.model.resp.BaseDetailResp; + +import java.io.Serial; + +/** + * 字典项信息 + * + * @author Charles7c + * @since 2023/9/11 21:29 + */ +@Data +@Schema(description = "字典项信息") +public class DictItemResp extends BaseDetailResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 标签 + */ + @Schema(description = "标签", example = "通知") + private String label; + + /** + * 值 + */ + @Schema(description = "值", example = "1") + private String value; + + /** + * 标签颜色 + */ + @Schema(description = "标签颜色", example = "blue") + private String color; + + /** + * 状态 + */ + @Schema(description = "状态", example = "1") + @ExcelProperty(value = "状态", converter = ExcelBaseEnumConverter.class) + private DisEnableStatusEnum status; + + /** + * 排序 + */ + @Schema(description = "排序", example = "1") + private Integer sort; + + /** + * 描述 + */ + @Schema(description = "描述", example = "通知描述信息") + private String description; + + /** + * 字典 ID + */ + @Schema(description = "字典 ID", example = "1") + private Long dictId; +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/DictResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/DictResp.java new file mode 100644 index 0000000..e0a8404 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/DictResp.java @@ -0,0 +1,45 @@ +package top.continew.admin.system.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.starter.extension.crud.model.resp.BaseDetailResp; + +import java.io.Serial; + +/** + * 字典信息 + * + * @author Charles7c + * @since 2023/9/11 21:29 + */ +@Data +@Schema(description = "字典信息") +public class DictResp extends BaseDetailResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 名称 + */ + @Schema(description = "名称", example = "公告类型") + private String name; + + /** + * 编码 + */ + @Schema(description = "编码", example = "notice_type") + private String code; + + /** + * 描述 + */ + @Schema(description = "描述", example = "公告类型描述信息") + private String description; + + /** + * 是否为系统内置数据 + */ + @Schema(description = "是否为系统内置数据", example = "true") + private Boolean isSystem; +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/FileResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/FileResp.java new file mode 100644 index 0000000..66de085 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/FileResp.java @@ -0,0 +1,78 @@ +package top.continew.admin.system.model.resp; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.system.enums.FileTypeEnum; +import top.continew.starter.extension.crud.model.resp.BaseDetailResp; + +import java.io.Serial; + +/** + * 文件信息 + * + * @author Charles7c + * @since 2023/12/23 10:38 + */ +@Data +@ExcelIgnoreUnannotated +@Schema(description = "文件详情信息") +public class FileResp extends BaseDetailResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 名称 + */ + @Schema(description = "名称", example = "example") + private String name; + + /** + * 大小(字节) + */ + @Schema(description = "大小(字节)", example = "4096") + private Long size; + + /** + * URL + */ + @Schema(description = "URL", example = "https://examplebucket.oss-cn-hangzhou.aliyuncs.com/example/example.jpg") + private String url; + + /** + * 扩展名 + */ + @Schema(description = "扩展名", example = "jpg") + private String extension; + + /** + * 类型 + */ + @Schema(description = "类型", example = "2") + private FileTypeEnum type; + + /** + * 缩略图大小(字节) + */ + @Schema(description = "缩略图大小(字节)", example = "1024") + private Long thumbnailSize; + + /** + * 缩略图 URL + */ + @Schema(description = "缩略图 URL", example = "https://examplebucket.oss-cn-hangzhou.aliyuncs.com/example/example.jpg.min.jpg") + private String thumbnailUrl; + + /** + * 存储 ID + */ + @Schema(description = "存储 ID", example = "1") + private Long storageId; + + /** + * 存储名称 + */ + @Schema(description = "存储名称", example = "MinIO") + private String storageName; +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/FileStatisticsResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/FileStatisticsResp.java new file mode 100644 index 0000000..33cff76 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/FileStatisticsResp.java @@ -0,0 +1,49 @@ +package top.continew.admin.system.model.resp; + +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.system.enums.FileTypeEnum; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 文件资源统计信息 + * + * @author Kils + * @since 2024/4/30 14:30 + */ +@Data +@Schema(description = "文件资源统计信息") +@JsonInclude(JsonInclude.Include.NON_NULL) +public class FileStatisticsResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 文件类型 + */ + @Schema(description = "类型", example = "2") + private FileTypeEnum type; + + /** + * 大小(字节) + */ + @Schema(description = "大小(字节)", example = "4096") + private Long size; + + /** + * 数量 + */ + @Schema(description = "数量", example = "1000") + private Long number; + + /** + * 分类数据 + */ + @Schema(description = "分类数据") + private List data; +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/FileUploadResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/FileUploadResp.java new file mode 100644 index 0000000..eb3b0d4 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/FileUploadResp.java @@ -0,0 +1,29 @@ +package top.continew.admin.system.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 文件上传响应信息 + * + * @author Charles7c + * @since 2024/3/6 22:26 + */ +@Data +@Builder +@Schema(description = "文件上传响应信息") +public class FileUploadResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 文件 URL + */ + @Schema(description = "文件 URL", example = "http://localhost:8000/file/65e87dc3fb377a6fb58bdece.jpg") + private String url; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/MenuResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/MenuResp.java new file mode 100644 index 0000000..bbfdced --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/MenuResp.java @@ -0,0 +1,109 @@ +package top.continew.admin.system.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.admin.system.enums.MenuTypeEnum; +import top.continew.starter.extension.crud.annotation.TreeField; +import top.continew.starter.extension.crud.model.resp.BaseResp; + +import java.io.Serial; + +/** + * 菜单信息 + * + * @author Charles7c + * @since 2023/2/15 20:23 + */ +@Data +@TreeField(value = "id") +@Schema(description = "菜单信息") +public class MenuResp extends BaseResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 标题 + */ + @Schema(description = "标题", example = "用户管理") + private String title; + + /** + * 上级菜单 ID + */ + @Schema(description = "上级菜单 ID", example = "1000") + private Long parentId; + + /** + * 类型 + */ + @Schema(description = "类型", example = "2") + private MenuTypeEnum type; + + /** + * 路由地址 + */ + @Schema(description = "路由地址", example = "/system/user") + private String path; + + /** + * 组件名称 + */ + @Schema(description = "组件名称", example = "User") + private String name; + + /** + * 组件路径 + */ + @Schema(description = "组件路径", example = "/system/user/index") + private String component; + + /** + * 重定向地址 + */ + @Schema(description = "重定向地址") + private String redirect; + + /** + * 图标 + */ + @Schema(description = "图标", example = "user") + private String icon; + + /** + * 是否外链 + */ + @Schema(description = "是否外链", example = "false") + private Boolean isExternal; + + /** + * 是否缓存 + */ + @Schema(description = "是否缓存", example = "false") + private Boolean isCache; + + /** + * 是否隐藏 + */ + @Schema(description = "是否隐藏", example = "false") + private Boolean isHidden; + + /** + * 权限标识 + */ + @Schema(description = "权限标识", example = "system:user:list") + private String permission; + + /** + * 排序 + */ + @Schema(description = "排序", example = "1") + private Integer sort; + + /** + * 状态 + */ + @Schema(description = "状态", example = "1") + private DisEnableStatusEnum status; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/MessageResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/MessageResp.java new file mode 100644 index 0000000..7ea9dff --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/MessageResp.java @@ -0,0 +1,81 @@ +package top.continew.admin.system.model.resp; + +import cn.crane4j.annotation.Assemble; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.common.constant.ContainerConstants; +import top.continew.admin.system.enums.MessageTypeEnum; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 消息信息 + * + * @author Bull-BCLS + * @since 2023/10/15 19:05 + */ +@Data +@Schema(description = "消息信息") +public class MessageResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @Schema(description = "ID", example = "1") + private Long id; + + /** + * 标题 + */ + @Schema(description = "标题", example = "欢迎注册 xxx") + private String title; + + /** + * 内容 + */ + @Schema(description = "内容", example = "尊敬的 xx,欢迎注册使用,请及时配置您的密码。") + private String content; + + /** + * 类型 + */ + @Schema(description = "类型(1:系统消息)", example = "1") + private MessageTypeEnum type; + + /** + * 是否已读 + */ + @Schema(description = "是否已读", example = "true") + private Boolean isRead; + + /** + * 读取时间 + */ + @Schema(description = "读取时间", example = "2023-08-08 23:59:59", type = "string") + private LocalDateTime readTime; + + /** + * 创建人 + */ + @JsonIgnore + @Assemble(prop = ":createUserString", container = ContainerConstants.USER_NICKNAME) + private Long createUser; + + /** + * 创建人 + */ + @Schema(description = "创建人", example = "超级管理员") + private String createUserString; + + /** + * 创建时间 + */ + @Schema(description = "创建时间", example = "2023-08-08 08:08:08", type = "string") + private LocalDateTime createTime; +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/MessageTypeUnreadResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/MessageTypeUnreadResp.java new file mode 100644 index 0000000..767aa55 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/MessageTypeUnreadResp.java @@ -0,0 +1,34 @@ +package top.continew.admin.system.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.system.enums.MessageTypeEnum; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 各类型未读消息信息 + * + * @author Charles7c + * @since 2023/11/2 23:00 + */ +@Data +@Schema(description = "各类型未读消息信息") +public class MessageTypeUnreadResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 类型 + */ + @Schema(description = "类型(1:系统消息)", example = "1") + private MessageTypeEnum type; + + /** + * 数量 + */ + @Schema(description = "数量", example = "10") + private Long count; +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/MessageUnreadResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/MessageUnreadResp.java new file mode 100644 index 0000000..25e1a97 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/MessageUnreadResp.java @@ -0,0 +1,36 @@ +package top.continew.admin.system.model.resp; + +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 未读消息信息 + * + * @author Charles7c + * @since 2023/11/2 23:00 + */ +@Data +@Schema(description = "未读消息信息") +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class MessageUnreadResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 未读消息数量 + */ + @Schema(description = "未读消息数量", example = "20") + private Long total; + + /** + * 各类型未读消息数量 + */ + @Schema(description = "各类型未读消息数量") + private List details; +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/NoticeDetailResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/NoticeDetailResp.java new file mode 100644 index 0000000..9125c42 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/NoticeDetailResp.java @@ -0,0 +1,60 @@ +package top.continew.admin.system.model.resp; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.starter.extension.crud.model.resp.BaseDetailResp; + +import java.io.Serial; +import java.time.LocalDateTime; + +/** + * 公告详情信息 + * + * @author Charles7c + * @since 2023/8/20 10:55 + */ +@Data +@ExcelIgnoreUnannotated +@Schema(description = "公告详情信息") +public class NoticeDetailResp extends BaseDetailResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 标题 + */ + @Schema(description = "标题", example = "这是公告标题") + @ExcelProperty(value = "标题") + private String title; + + /** + * 内容 + */ + @Schema(description = "内容", example = "这是公告内容") + @ExcelProperty(value = "内容") + private String content; + + /** + * 类型(取值于字典 notice_type) + */ + @Schema(description = "类型(取值于字典 notice_type)", example = "1") + @ExcelProperty(value = "类型") + private String type; + + /** + * 生效时间 + */ + @Schema(description = "生效时间", example = "2023-08-08 00:00:00", type = "string") + @ExcelProperty(value = "生效时间") + private LocalDateTime effectiveTime; + + /** + * 终止时间 + */ + @Schema(description = "终止时间", example = "2023-08-08 23:59:59", type = "string") + @ExcelProperty(value = "终止时间") + private LocalDateTime terminateTime; +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/NoticeResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/NoticeResp.java new file mode 100644 index 0000000..0d71bbd --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/NoticeResp.java @@ -0,0 +1,57 @@ +package top.continew.admin.system.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.system.enums.NoticeStatusEnum; +import top.continew.starter.extension.crud.model.resp.BaseResp; + +import java.io.Serial; +import java.time.LocalDateTime; + +/** + * 公告信息 + * + * @author Charles7c + * @since 2023/8/20 10:55 + */ +@Data +@Schema(description = "公告信息") +public class NoticeResp extends BaseResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 标题 + */ + @Schema(description = "标题", example = "这是公告标题") + private String title; + + /** + * 类型(取值于字典 notice_type) + */ + @Schema(description = "类型(取值于字典 notice_type)", example = "1") + private String type; + + /** + * 生效时间 + */ + @Schema(description = "生效时间", example = "2023-08-08 00:00:00", type = "string") + private LocalDateTime effectiveTime; + + /** + * 终止时间 + */ + @Schema(description = "终止时间", example = "2023-08-08 23:59:59", type = "string") + private LocalDateTime terminateTime; + + /** + * 状态 + * + * @return 公告状态 + */ + @Schema(description = "状态", example = "1") + public NoticeStatusEnum getStatus() { + return NoticeStatusEnum.getStatus(effectiveTime, terminateTime); + } +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/OptionResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/OptionResp.java new file mode 100644 index 0000000..d18e0f2 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/OptionResp.java @@ -0,0 +1,63 @@ +package top.continew.admin.system.model.resp; + +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 参数信息 + * + * @author Bull-BCLS + * @since 2023/8/26 19:38 + */ +@Data +@Schema(description = "参数信息") +public class OptionResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @Schema(description = "ID", example = "1") + private Long id; + + /** + * 名称 + */ + @Schema(description = "名称", example = "系统标题") + private String name; + + /** + * 键 + */ + @Schema(description = "键", example = "site_title") + private String code; + + /** + * 值 + */ + @Schema(description = "值", example = "ContiNew Admin") + private String value; + + /** + * 默认值 + */ + @JsonIgnore + private String defaultValue; + + /** + * 描述 + */ + @Schema(description = "描述", example = "用于显示登录页面的系统标题。") + private String description; + + public String getValue() { + return StrUtil.nullToDefault(value, defaultValue); + } +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/RoleDetailResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/RoleDetailResp.java new file mode 100644 index 0000000..9ffdaa9 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/RoleDetailResp.java @@ -0,0 +1,103 @@ +package top.continew.admin.system.model.resp; + +import cn.crane4j.annotation.AssembleMethod; +import cn.crane4j.annotation.ContainerMethod; +import cn.crane4j.annotation.MappingType; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.common.enums.DataScopeEnum; +import top.continew.admin.system.service.RoleDeptService; +import top.continew.starter.file.excel.converter.ExcelBaseEnumConverter; +import top.continew.starter.extension.crud.model.resp.BaseDetailResp; + +import java.io.Serial; +import java.util.List; + +/** + * 角色详情信息 + * + * @author Charles7c + * @since 2023/2/1 22:19 + */ +@Data +@ExcelIgnoreUnannotated +@Schema(description = "角色详情信息") +@AssembleMethod(key = "id", prop = ":deptIds", targetType = RoleDeptService.class, method = @ContainerMethod(bindMethod = "listDeptIdByRoleId", type = MappingType.ORDER_OF_KEYS)) +public class RoleDetailResp extends BaseDetailResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 名称 + */ + @Schema(description = "名称", example = "测试人员") + @ExcelProperty(value = "名称") + private String name; + + /** + * 编码 + */ + @Schema(description = "编码", example = "test") + @ExcelProperty(value = "编码") + private String code; + + /** + * 数据权限 + */ + @Schema(description = "数据权限", example = "5") + @ExcelProperty(value = "数据权限", converter = ExcelBaseEnumConverter.class) + private DataScopeEnum dataScope; + + /** + * 排序 + */ + @Schema(description = "排序", example = "1") + @ExcelProperty(value = "排序") + private Integer sort; + + /** + * 是否为系统内置数据 + */ + @Schema(description = "是否为系统内置数据", example = "false") + @ExcelProperty(value = "系统内置") + private Boolean isSystem; + + /** + * 菜单选择是否父子节点关联 + */ + @Schema(description = "菜单选择是否父子节点关联", example = "false") + private Boolean menuCheckStrictly; + + /** + * 部门选择是否父子节点关联 + */ + @Schema(description = "部门选择是否父子节点关联", example = "false") + private Boolean deptCheckStrictly; + + /** + * 描述 + */ + @Schema(description = "描述", example = "测试人员描述信息") + @ExcelProperty(value = "描述") + private String description; + + /** + * 功能权限:菜单 ID 列表 + */ + @Schema(description = "功能权限:菜单 ID 列表", example = "1000,1010,1011,1012,1013,1014") + private List menuIds; + + /** + * 权限范围:部门 ID 列表 + */ + @Schema(description = "权限范围:部门 ID 列表", example = "5") + private List deptIds; + + @Override + public Boolean getDisabled() { + return this.getIsSystem(); + } +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/RoleResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/RoleResp.java new file mode 100644 index 0000000..281e9c8 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/RoleResp.java @@ -0,0 +1,63 @@ +package top.continew.admin.system.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.common.enums.DataScopeEnum; +import top.continew.starter.extension.crud.model.resp.BaseDetailResp; + +import java.io.Serial; + +/** + * 角色信息 + * + * @author Charles7c + * @since 2023/2/8 23:05 + */ +@Data +@Schema(description = "角色信息") +public class RoleResp extends BaseDetailResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 名称 + */ + @Schema(description = "名称", example = "测试人员") + private String name; + + /** + * 编码 + */ + @Schema(description = "编码", example = "test") + private String code; + + /** + * 数据权限 + */ + @Schema(description = "数据权限", example = "5") + private DataScopeEnum dataScope; + + /** + * 排序 + */ + @Schema(description = "排序", example = "1") + private Integer sort; + + /** + * 是否为系统内置数据 + */ + @Schema(description = "是否为系统内置数据", example = "false") + private Boolean isSystem; + + /** + * 描述 + */ + @Schema(description = "描述", example = "测试人员描述信息") + private String description; + + @Override + public Boolean getDisabled() { + return this.getIsSystem(); + } +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/SimpleUserInfoResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/SimpleUserInfoResp.java new file mode 100644 index 0000000..f9e8a82 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/SimpleUserInfoResp.java @@ -0,0 +1,45 @@ +package top.continew.admin.system.model.resp; + +import cn.crane4j.annotation.Assemble; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.common.constant.ContainerConstants; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 用户信息 + * + * @author Charles7c + * @since 2023/2/20 21:08 + */ +@Data +@Schema(description = "用户信息") +@Assemble(key = "id", prop = ":roleIds", sort = 0, container = ContainerConstants.USER_ROLE_ID_LIST) +public class SimpleUserInfoResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @Schema(description = "ID", example = "1") + @ExcelProperty(value = "ID", order = 1) + private Long id; + + /** + * 用户名 + */ + @Schema(description = "用户名", example = "zhangsan") + private String username; + + /** + * 昵称 + */ + @Schema(description = "昵称", example = "张三") + private String nickname; + +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/StorageResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/StorageResp.java new file mode 100644 index 0000000..fad7dd7 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/StorageResp.java @@ -0,0 +1,103 @@ +package top.continew.admin.system.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.admin.system.enums.StorageTypeEnum; +import top.continew.starter.extension.crud.model.resp.BaseDetailResp; +import top.continew.starter.security.mask.annotation.JsonMask; + +import java.io.Serial; + +/** + * 存储响应信息 + * + * @author Charles7c + * @since 2023/12/26 22:09 + */ +@Data +@Schema(description = "存储响应信息") +public class StorageResp extends BaseDetailResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 名称 + */ + @Schema(description = "名称", example = "存储1") + private String name; + + /** + * 编码 + */ + @Schema(description = "编码", example = "local") + private String code; + + /** + * 状态 + */ + @Schema(description = "状态", example = "1") + private DisEnableStatusEnum status; + + /** + * 类型 + */ + @Schema(description = "类型", example = "2") + private StorageTypeEnum type; + + /** + * 访问密钥 + */ + @Schema(description = "访问密钥", example = "") + private String accessKey; + + /** + * 私有密钥 + */ + @Schema(description = "私有密钥", example = "") + @JsonMask(left = 4, right = 3) + private String secretKey; + + /** + * 终端节点 + */ + @Schema(description = "终端节点", example = "") + private String endpoint; + + /** + * 桶名称 + */ + @Schema(description = "桶名称", example = "C:/continew-admin/data/file/") + private String bucketName; + + /** + * 域名 + */ + @Schema(description = "域名", example = "http://localhost:8000/file") + private String domain; + + /** + * 描述 + */ + @Schema(description = "描述", example = "存储描述") + private String description; + + /** + * 是否为默认存储 + */ + @Schema(description = "是否为默认存储", example = "true") + private Boolean isDefault; + + /** + * 排序 + */ + @Schema(description = "排序", example = "1") + private Integer sort; + + @Override + public Boolean getDisabled() { + return this.getIsDefault(); + } + +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/UserDetailResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/UserDetailResp.java new file mode 100644 index 0000000..72445f4 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/UserDetailResp.java @@ -0,0 +1,149 @@ +package top.continew.admin.system.model.resp; + +import cn.crane4j.annotation.Assemble; +import cn.crane4j.annotation.AssembleMethod; +import cn.crane4j.annotation.ContainerMethod; +import cn.crane4j.annotation.Mapping; +import cn.crane4j.annotation.condition.ConditionOnExpression; +import cn.crane4j.core.executor.handler.ManyToManyAssembleOperationHandler; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.common.constant.ContainerConstants; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.admin.common.enums.GenderEnum; +import top.continew.admin.common.util.helper.LoginHelper; +import top.continew.admin.system.service.DeptService; +import top.continew.starter.file.excel.converter.ExcelBaseEnumConverter; +import top.continew.starter.extension.crud.model.resp.BaseDetailResp; +import top.continew.starter.file.excel.converter.ExcelListConverter; +import top.continew.starter.security.crypto.annotation.FieldEncrypt; + +import java.io.Serial; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Objects; + +/** + * 用户详情信息 + * + * @author Charles7c + * @since 2023/2/20 21:11 + */ +@Data +@ExcelIgnoreUnannotated +@Schema(description = "用户详情信息") +@Assemble(key = "id", prop = ":roleIds", sort = 0, container = ContainerConstants.USER_ROLE_ID_LIST) +public class UserDetailResp extends BaseDetailResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户名 + */ + @Schema(description = "用户名", example = "zhangsan") + @ExcelProperty(value = "用户名", order = 2) + private String username; + + /** + * 昵称 + */ + @Schema(description = "昵称", example = "张三") + @ExcelProperty(value = "昵称", order = 3) + private String nickname; + + /** + * 状态 + */ + @Schema(description = "状态", example = "1") + @ExcelProperty(value = "状态", converter = ExcelBaseEnumConverter.class, order = 4) + private DisEnableStatusEnum status; + + /** + * 性别 + */ + @Schema(description = "性别", example = "1") + @ExcelProperty(value = "性别", converter = ExcelBaseEnumConverter.class, order = 5) + private GenderEnum gender; + + /** + * 部门 ID + */ + @Schema(description = "部门 ID", example = "5") + @ConditionOnExpression("#target.deptName == null") + @AssembleMethod(props = @Mapping(src = "name", ref = "deptName"), targetType = DeptService.class, method = @ContainerMethod(bindMethod = "get", resultType = DeptResp.class)) + @ExcelProperty(value = "部门 ID", order = 6) + private Long deptId; + + /** + * 所属部门 + */ + @Schema(description = "所属部门", example = "测试部") + @ExcelProperty(value = "所属部门", order = 7) + private String deptName; + + /** + * 角色 ID 列表 + */ + @Schema(description = "角色 ID 列表", example = "2") + @Assemble(prop = ":roleNames", container = ContainerConstants.USER_ROLE_NAME_LIST, handlerType = ManyToManyAssembleOperationHandler.class) + @ExcelProperty(value = "角色 ID 列表", converter = ExcelListConverter.class, order = 8) + private List roleIds; + + /** + * 角色名称列表 + */ + @Schema(description = "角色名称列表", example = "测试人员") + @ExcelProperty(value = "角色", converter = ExcelListConverter.class, order = 9) + private List roleNames; + + /** + * 手机号码 + */ + @Schema(description = "手机号码", example = "13811111111") + @ExcelProperty(value = "手机号码", order = 10) + @FieldEncrypt + private String phone; + + /** + * 邮箱 + */ + @Schema(description = "邮箱", example = "123456789@qq.com") + @ExcelProperty(value = "邮箱", order = 11) + @FieldEncrypt + private String email; + + /** + * 是否为系统内置数据 + */ + @Schema(description = "系统内置", example = "false") + @ExcelProperty(value = "系统内置", order = 12) + private Boolean isSystem; + + /** + * 描述 + */ + @Schema(description = "描述", example = "张三描述信息") + @ExcelProperty(value = "描述", order = 13) + private String description; + + /** + * 头像地址 + */ + @Schema(description = "头像地址", example = "https://himg.bdimg.com/sys/portrait/item/public.1.81ac9a9e.rf1ix17UfughLQjNo7XQ_w.jpg") + @ExcelProperty(value = "头像地址", order = 14) + private String avatar; + + /** + * 最后一次修改密码时间 + */ + @Schema(description = "最后一次修改密码时间", example = "2023-08-08 08:08:08", type = "string") + private LocalDateTime pwdResetTime; + + @Override + public Boolean getDisabled() { + return this.getIsSystem() || Objects.equals(this.getId(), LoginHelper.getUserId()); + } +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/UserImportParseResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/UserImportParseResp.java new file mode 100644 index 0000000..c8696a3 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/UserImportParseResp.java @@ -0,0 +1,57 @@ +package top.continew.admin.system.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 用户导入结果 + * + * @author kils + * @since 2024-06-18 14:37 + */ +@Data +@Schema(description = "用户导入结果") +@AllArgsConstructor +@NoArgsConstructor +public class UserImportParseResp { + + private static final long serialVersionUID = 1L; + + /** + * 导入会话KEY + */ + @Schema(description = "导入会话KEY", example = "1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed") + private String importKey; + + /** + * 总计行数 + */ + @Schema(description = "总计行数", example = "100") + private Integer totalRows; + + /** + * 有效行数 + */ + @Schema(description = "有效行数", example = "100") + private Integer validRows; + + /** + * 用户重复行数 + */ + @Schema(description = "用户重复行数", example = "100") + private Integer duplicateUserRows; + + /** + * 重复邮箱行数 + */ + @Schema(description = "重复邮箱行数", example = "100") + private Integer duplicateEmailRows; + + /** + * 重复手机行数 + */ + @Schema(description = "重复手机行数", example = "100") + private Integer duplicatePhoneRows; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/UserImportResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/UserImportResp.java new file mode 100644 index 0000000..5d447eb --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/UserImportResp.java @@ -0,0 +1,39 @@ +package top.continew.admin.system.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 用户导入结果 + * + * @author kils + * @since 2024-06-18 14:37 + */ +@Data +@Schema(description = "用户导入结果") +@AllArgsConstructor +@NoArgsConstructor +public class UserImportResp { + + private static final long serialVersionUID = 1L; + + /** + * 总计行数 + */ + @Schema(description = "总计行数", example = "100") + private Integer totalRows; + + /** + * 新增行数 + */ + @Schema(description = "新增行数", example = "100") + private Integer insertRows; + + /** + * 修改行数 + */ + @Schema(description = "修改行数", example = "100") + private Integer updateRows; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/UserResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/UserResp.java new file mode 100644 index 0000000..6f2e6bf --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/UserResp.java @@ -0,0 +1,118 @@ +package top.continew.admin.system.model.resp; + +import cn.crane4j.annotation.Assemble; +import cn.crane4j.core.executor.handler.ManyToManyAssembleOperationHandler; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.common.constant.ContainerConstants; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.admin.common.enums.GenderEnum; +import top.continew.admin.common.util.helper.LoginHelper; +import top.continew.starter.extension.crud.model.resp.BaseDetailResp; +import top.continew.starter.security.mask.annotation.JsonMask; +import top.continew.starter.security.mask.enums.MaskType; + +import java.io.Serial; +import java.util.List; +import java.util.Objects; + +/** + * 用户信息 + * + * @author Charles7c + * @since 2023/2/20 21:08 + */ +@Data +@Schema(description = "用户信息") +@Assemble(key = "id", prop = ":roleIds", sort = 0, container = ContainerConstants.USER_ROLE_ID_LIST) +public class UserResp extends BaseDetailResp { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户名 + */ + @Schema(description = "用户名", example = "zhangsan") + private String username; + + /** + * 昵称 + */ + @Schema(description = "昵称", example = "张三") + private String nickname; + + /** + * 性别 + */ + @Schema(description = "性别", example = "1") + private GenderEnum gender; + + /** + * 头像地址 + */ + @Schema(description = "头像地址", example = "https://himg.bdimg.com/sys/portrait/item/public.1.81ac9a9e.rf1ix17UfughLQjNo7XQ_w.jpg") + private String avatar; + + /** + * 邮箱 + */ + @Schema(description = "邮箱", example = "c*******@126.com") + @JsonMask(MaskType.EMAIL) + private String email; + + /** + * 手机号码 + */ + @Schema(description = "手机号码", example = "188****8888") + @JsonMask(MaskType.MOBILE_PHONE) + private String phone; + + /** + * 状态 + */ + @Schema(description = "状态", example = "1") + private DisEnableStatusEnum status; + + /** + * 是否为系统内置数据 + */ + @Schema(description = "是否为系统内置数据", example = "false") + private Boolean isSystem; + + /** + * 描述 + */ + @Schema(description = "描述", example = "张三描述信息") + private String description; + + /** + * 部门 ID + */ + @Schema(description = "部门 ID", example = "5") + private Long deptId; + + /** + * 所属部门 + */ + @Schema(description = "所属部门", example = "测试部") + private String deptName; + + /** + * 角色 ID 列表 + */ + @Schema(description = "角色 ID 列表", example = "2") + @Assemble(prop = ":roleNames", container = ContainerConstants.USER_ROLE_NAME_LIST, handlerType = ManyToManyAssembleOperationHandler.class) + private List roleIds; + + /** + * 角色名称列表 + */ + @Schema(description = "角色名称列表", example = "测试人员") + private List roleNames; + + @Override + public Boolean getDisabled() { + return this.getIsSystem() || Objects.equals(this.getId(), LoginHelper.getUserId()); + } +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/UserSocialBindResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/UserSocialBindResp.java new file mode 100644 index 0000000..a2770ea --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/UserSocialBindResp.java @@ -0,0 +1,33 @@ +package top.continew.admin.system.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 第三方账号绑定信息 + * + * @author Charles7c + * @since 2023/10/19 21:29 + */ +@Data +@Schema(description = "第三方账号绑定信息") +public class UserSocialBindResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 来源 + */ + @Schema(description = "来源", example = "GITEE") + private String source; + + /** + * 描述 + */ + @Schema(description = "描述", example = "码云") + private String description; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/UserTreeResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/UserTreeResp.java new file mode 100644 index 0000000..297aa08 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/UserTreeResp.java @@ -0,0 +1,97 @@ +package top.continew.admin.system.model.resp; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.admin.common.enums.GenderEnum; +import top.continew.starter.security.mask.annotation.JsonMask; +import top.continew.starter.security.mask.enums.MaskType; + +import java.io.Serial; +import java.util.List; + +/** + * 用户信息 + * + * @author Charles7c + * @since 2023/2/20 21:08 + */ +@Data +@Schema(description = "用户信息") +public class UserTreeResp { + + @Serial + private static final long serialVersionUID = 1L; + private Long id; + private Long key; + /** + * 用户名 + */ + @Schema(description = "用户名", example = "zhangsan") + private String username; + + /** + * 昵称 + */ + @Schema(description = "昵称", example = "张三") + private String title; + + /** + * 性别 + */ + @Schema(description = "性别", example = "1") + private GenderEnum gender; + + /** + * 头像地址 + */ + @Schema(description = "头像地址", example = "https://himg.bdimg.com/sys/portrait/item/public.1.81ac9a9e.rf1ix17UfughLQjNo7XQ_w.jpg") + private String avatar; + + /** + * 邮箱 + */ + @Schema(description = "邮箱", example = "c*******@126.com") + @JsonMask(MaskType.EMAIL) + private String email; + + /** + * 手机号码 + */ + @Schema(description = "手机号码", example = "188****8888") + @JsonMask(MaskType.MOBILE_PHONE) + private String phone; + + /** + * 状态 + */ + @Schema(description = "状态", example = "1") + private DisEnableStatusEnum status; + + /** + * 是否为系统内置数据 + */ + @Schema(description = "是否为系统内置数据", example = "false") + private Boolean isSystem; + + /** + * 描述 + */ + @Schema(description = "描述", example = "张三描述信息") + private String description; + + /** + * 部门 ID + */ + @Schema(description = "部门 ID", example = "5") + private Long deptId; + + /** + * 所属部门 + */ + @Schema(description = "所属部门", example = "测试部") + private String deptName; + + private List teacherChildren; + +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/log/LogDetailResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/log/LogDetailResp.java new file mode 100644 index 0000000..cd37ccd --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/log/LogDetailResp.java @@ -0,0 +1,155 @@ +package top.continew.admin.system.model.resp.log; + +import cn.crane4j.annotation.Assemble; +import cn.crane4j.annotation.condition.ConditionOnPropertyNotNull; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.common.constant.ContainerConstants; +import top.continew.admin.system.enums.LogStatusEnum; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 日志详情信息 + * + * @author Charles7c + * @since 2023/1/18 20:19 + */ +@Data +@Schema(description = "日志详情信息") +public class LogDetailResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @Schema(description = "ID", example = "1") + private Long id; + + /** + * 链路 ID + */ + @Schema(description = "链路 ID", example = "904846526308876288") + private String traceId; + + /** + * 日志描述 + */ + @Schema(description = "日志描述", example = "新增数据") + private String description; + + /** + * 所属模块 + */ + @Schema(description = "所属模块", example = "部门管理") + private String module; + + /** + * 请求 URL + */ + @Schema(description = "请求 URL", example = "http://api.continew.top/system/dept") + private String requestUrl; + + /** + * 请求方式 + */ + @Schema(description = "请求方式", example = "POST") + private String requestMethod; + + /** + * 请求头 + */ + @Schema(description = "请求头", example = "{\"Origin\": [\"https://admin.continew.top\"],...}") + private String requestHeaders; + + /** + * 请求体 + */ + @Schema(description = "请求体", example = "{\"name\": \"测试部\",...}") + private String requestBody; + + /** + * 状态码 + */ + @Schema(description = "状态码", example = "200") + private Integer statusCode; + + /** + * 响应头 + */ + @Schema(description = "响应头", example = "{\"Content-Type\": [\"application/json\"],...}") + private String responseHeaders; + + /** + * 响应体 + */ + @Schema(description = "响应体", example = "{\"success\":true},...") + private String responseBody; + + /** + * 耗时(ms) + */ + @Schema(description = "耗时(ms)", example = "58") + private Long timeTaken; + + /** + * IP + */ + @Schema(description = "IP", example = "") + private String ip; + + /** + * IP 归属地 + */ + @Schema(description = "IP 归属地", example = "中国北京北京市") + private String address; + + /** + * 浏览器 + */ + @Schema(description = "浏览器", example = "Chrome 115.0.0.0") + private String browser; + + /** + * 操作系统 + */ + @Schema(description = "操作系统", example = "Windows 10") + private String os; + + /** + * 状态 + */ + @Schema(description = "状态", example = "1") + private LogStatusEnum status; + + /** + * 错误信息 + */ + @Schema(description = "错误信息") + private String errorMsg; + + /** + * 创建人 + */ + @JsonIgnore + @ConditionOnPropertyNotNull + @Assemble(prop = ":createUserString", container = ContainerConstants.USER_NICKNAME) + private Long createUser; + + /** + * 创建人 + */ + @Schema(description = "创建人", example = "张三") + private String createUserString; + + /** + * 创建时间 + */ + @Schema(description = "创建时间", example = "2023-08-08 08:08:08", type = "string") + private LocalDateTime createTime; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/log/LogResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/log/LogResp.java new file mode 100644 index 0000000..21b97d1 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/log/LogResp.java @@ -0,0 +1,102 @@ +package top.continew.admin.system.model.resp.log; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.system.enums.LogStatusEnum; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 日志信息 + * + * @author Charles7c + * @since 2023/1/14 18:27 + */ +@Data +@Schema(description = "日志信息") +public class LogResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @Schema(description = "ID", example = "1") + private Long id; + + /** + * 日志描述 + */ + @Schema(description = "日志描述", example = "新增数据") + private String description; + + /** + * 所属模块 + */ + @Schema(description = "所属模块", example = "部门管理") + private String module; + + /** + * 耗时(ms) + */ + @Schema(description = "耗时(ms)", example = "58") + private Long timeTaken; + + /** + * IP + */ + @Schema(description = "IP", example = "") + private String ip; + + /** + * IP 归属地 + */ + @Schema(description = "IP 归属地", example = "中国北京北京市") + private String address; + + /** + * 浏览器 + */ + @Schema(description = "浏览器", example = "Chrome 115.0.0.0") + private String browser; + + /** + * 操作系统 + */ + @Schema(description = "操作系统", example = "Windows 10") + private String os; + + /** + * 状态 + */ + @Schema(description = "状态", example = "1") + private LogStatusEnum status; + + /** + * 错误信息 + */ + @Schema(description = "错误信息") + private String errorMsg; + + /** + * 创建人 + */ + @JsonIgnore + private Long createUser; + + /** + * 创建人 + */ + @Schema(description = "创建人", example = "张三") + private String createUserString; + + /** + * 创建时间 + */ + @Schema(description = "创建时间", example = "2023-08-08 08:08:08", type = "string") + private LocalDateTime createTime; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/log/LoginLogExportResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/log/LoginLogExportResp.java new file mode 100644 index 0000000..55fdcb6 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/log/LoginLogExportResp.java @@ -0,0 +1,90 @@ +package top.continew.admin.system.model.resp.log; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.system.enums.LogStatusEnum; +import top.continew.starter.file.excel.converter.ExcelBaseEnumConverter; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 登录日志导出信息 + * + * @author Charles7c + * @since 2023/1/14 18:27 + */ +@Data +@ExcelIgnoreUnannotated +@Schema(description = "登录日志导出信息") +public class LoginLogExportResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @Schema(description = "ID", example = "1") + @ExcelProperty(value = "ID") + private Long id; + + /** + * 登录时间 + */ + @Schema(description = "登录时间", example = "2023-08-08 08:08:08", type = "string") + @ExcelProperty(value = "登录时间") + private LocalDateTime createTime; + + /** + * 用户昵称 + */ + @Schema(description = "用户昵称", example = "张三") + @ExcelProperty(value = "用户昵称") + private String createUserString; + + /** + * 登录行为 + */ + @Schema(description = "登录行为", example = "账号登录") + @ExcelProperty(value = "登录行为") + private String description; + + /** + * 状态 + */ + @Schema(description = "状态", example = "1") + @ExcelProperty(value = "状态", converter = ExcelBaseEnumConverter.class) + private LogStatusEnum status; + + /** + * 登录 IP + */ + @Schema(description = "登录 IP", example = "") + @ExcelProperty(value = "登录 IP") + private String ip; + + /** + * 登录地点 + */ + @Schema(description = "登录地点", example = "中国北京北京市") + @ExcelProperty(value = "登录地点") + private String address; + + /** + * 浏览器 + */ + @Schema(description = "浏览器", example = "Chrome 115.0.0.0") + @ExcelProperty(value = "浏览器") + private String browser; + + /** + * 终端系统 + */ + @Schema(description = "终端系统", example = "Windows 10") + @ExcelProperty(value = "终端系统") + private String os; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/log/OperationLogExportResp.java b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/log/OperationLogExportResp.java new file mode 100644 index 0000000..997f20f --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/model/resp/log/OperationLogExportResp.java @@ -0,0 +1,104 @@ +package top.continew.admin.system.model.resp.log; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.continew.admin.system.enums.LogStatusEnum; +import top.continew.starter.file.excel.converter.ExcelBaseEnumConverter; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 操作日志导出信息 + * + * @author Charles7c + * @since 2023/1/14 18:27 + */ +@Data +@ExcelIgnoreUnannotated +@Schema(description = "操作日志导出信息") +public class OperationLogExportResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @Schema(description = "ID", example = "1") + @ExcelProperty(value = "ID") + private Long id; + + /** + * 操作时间 + */ + @Schema(description = "操作时间", example = "2023-08-08 08:08:08", type = "string") + @ExcelProperty(value = "操作时间") + private LocalDateTime createTime; + + /** + * 操作人 + */ + @Schema(description = "操作人", example = "张三") + @ExcelProperty(value = "操作人") + private String createUserString; + + /** + * 操作内容 + */ + @Schema(description = "操作内容", example = "账号登录") + @ExcelProperty(value = "操作内容") + private String description; + + /** + * 所属模块 + */ + @Schema(description = "所属模块", example = "部门管理") + @ExcelProperty(value = "所属模块") + private String module; + + /** + * 状态 + */ + @Schema(description = "状态", example = "1") + @ExcelProperty(value = "状态", converter = ExcelBaseEnumConverter.class) + private LogStatusEnum status; + + /** + * 操作 IP + */ + @Schema(description = "操作 IP", example = "") + @ExcelProperty(value = "操作 IP") + private String ip; + + /** + * 操作地点 + */ + @Schema(description = "操作地点", example = "中国北京北京市") + @ExcelProperty(value = "操作地点") + private String address; + + /** + * 耗时(ms) + */ + @Schema(description = "耗时(ms)", example = "58") + @ExcelProperty(value = "耗时(ms)") + private Long timeTaken; + + /** + * 浏览器 + */ + @Schema(description = "浏览器", example = "Chrome 115.0.0.0") + @ExcelProperty(value = "浏览器") + private String browser; + + /** + * 终端系统 + */ + @Schema(description = "终端系统", example = "Windows 10") + @ExcelProperty(value = "终端系统") + private String os; +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/DashboardService.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/DashboardService.java new file mode 100644 index 0000000..cb134b3 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/DashboardService.java @@ -0,0 +1,50 @@ +package top.continew.admin.system.service; + +import top.continew.admin.system.model.resp.*; + +import java.util.List; + +/** + * 仪表盘业务接口 + * + * @author Charles7c + * @since 2023/9/8 21:32 + */ +public interface DashboardService { + + /** + * 查询总计信息 + * + * @return 总计信息 + */ + DashboardTotalResp getTotal(); + + /** + * 查询访问趋势信息 + * + * @param days 日期数 + * @return 访问趋势信息 + */ + List listAccessTrend(Integer days); + + /** + * 查询热门模块列表 + * + * @return 热门模块列表 + */ + List listPopularModule(); + + /** + * 查询访客地域分布信息 + * + * @return 访客地域分布信息 + */ + DashboardGeoDistributionResp getGeoDistribution(); + + /** + * 查询公告列表 + * + * @return 公告列表 + */ + List listNotice(); +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/DeptService.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/DeptService.java new file mode 100644 index 0000000..9811b15 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/DeptService.java @@ -0,0 +1,50 @@ +package top.continew.admin.system.service; + +import top.continew.admin.system.model.entity.DeptDO; +import top.continew.admin.system.model.query.DeptQuery; +import top.continew.admin.system.model.req.DeptReq; +import top.continew.admin.system.model.resp.DeptChildResp; +import top.continew.admin.system.model.resp.DeptResp; +import top.continew.starter.data.mp.service.IService; +import top.continew.starter.extension.crud.service.BaseService; + +import java.util.List; + +/** + * 部门业务接口 + * + * @author Charles7c + * @since 2023/1/22 17:54 + */ +public interface DeptService extends BaseService, IService { + + /** + * 查询子部门列表 + * + * @param id ID + * @return 子部门列表 + */ + List listChildren(Long id); + + /** + * 通过名称查询部门 + * + * @param list 名称列表 + * @return 部门列表 + */ + List listByNames(List list); + + /** + * 通过名称查询部门数量 + * + * @param deptNames 名称列表 + * @return 部门数量 + */ + int countByNames(List deptNames); + + DeptDO selectDoById(Long id); + + DeptDO selectParentsById(Long id); + + List getDeptResp(DeptQuery query); +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/DictItemService.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/DictItemService.java new file mode 100644 index 0000000..4abfa02 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/DictItemService.java @@ -0,0 +1,42 @@ +package top.continew.admin.system.service; + +import top.continew.admin.system.model.entity.DictItemDO; +import top.continew.admin.system.model.query.DictItemQuery; +import top.continew.admin.system.model.req.DictItemReq; +import top.continew.admin.system.model.resp.DictItemResp; +import top.continew.starter.data.mp.service.IService; +import top.continew.starter.extension.crud.model.resp.LabelValueResp; +import top.continew.starter.extension.crud.service.BaseService; + +import java.util.List; + +/** + * 字典项业务接口 + * + * @author Charles7c + * @since 2023/9/11 21:29 + */ +public interface DictItemService extends BaseService, IService { + + /** + * 根据字典编码查询 + * + * @param dictCode 字典编码 + * @return 字典项列表 + */ + List listByDictCode(String dictCode); + + /** + * 根据字典 ID 列表删除 + * + * @param dictIds 字典 ID 列表 + */ + void deleteByDictIds(List dictIds); + + /** + * 查询枚举字典名称列表 + * + * @return 枚举字典名称列表 + */ + List listEnumDictNames(); +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/DictService.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/DictService.java new file mode 100644 index 0000000..4775f4c --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/DictService.java @@ -0,0 +1,27 @@ +package top.continew.admin.system.service; + +import top.continew.admin.system.model.entity.DictDO; +import top.continew.admin.system.model.query.DictQuery; +import top.continew.admin.system.model.req.DictReq; +import top.continew.admin.system.model.resp.DictResp; +import top.continew.starter.data.mp.service.IService; +import top.continew.starter.extension.crud.model.resp.LabelValueResp; +import top.continew.starter.extension.crud.service.BaseService; + +import java.util.List; + +/** + * 字典业务接口 + * + * @author Charles7c + * @since 2023/9/11 21:29 + */ +public interface DictService extends BaseService, IService { + + /** + * 查询枚举字典 + * + * @return 枚举字典列表 + */ + List listEnumDict(); +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/FileService.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/FileService.java new file mode 100644 index 0000000..370b412 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/FileService.java @@ -0,0 +1,77 @@ +package top.continew.admin.system.service; + +import org.dromara.x.file.storage.core.FileInfo; +import org.springframework.web.multipart.MultipartFile; +import top.continew.admin.system.model.entity.FileDO; +import top.continew.admin.system.model.query.FileQuery; +import top.continew.admin.system.model.req.FileReq; +import top.continew.admin.system.model.resp.FileResp; +import top.continew.admin.system.model.resp.FileStatisticsResp; +import top.continew.starter.data.mp.service.IService; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.extension.crud.service.BaseService; + +import java.util.List; + +/** + * 文件业务接口 + * + * @author Charles7c + * @since 2023/12/23 10:38 + */ +public interface FileService extends BaseService, IService { + + /** + * 上传到默认存储 + * + * @param file 文件信息 + * @return 文件信息 + */ + default FileInfo upload(MultipartFile file) { + return upload(file, null); + } + + /** + * 上传到指定存储 + * + * @param file 文件信息 + * @param storageCode 存储编码 + * @return 文件信息 + */ + FileInfo upload(MultipartFile file, String storageCode); + + /** + * 上传文件测试 + * + * @param file + * @param storageCode + * @return + */ + FileInfo uploadFile(MultipartFile file, String storageCode); + + /** + * 根据存储 ID 列表查询 + * + * @param storageIds 存储 ID 列表 + * @return 文件数量 + */ + Long countByStorageIds(List storageIds); + + /** + * 查询文件资源统计信息 + * + * @return 资源统计信息 + */ + FileStatisticsResp statistics(); + + /** + * 获取文件列表 + * + * @param query 查询条件 + * @param pageQuery 分页条件 + * @return 文件列表 + */ + PageResp getFilesPage(FileQuery query, PageQuery pageQuery); + +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/LogService.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/LogService.java new file mode 100644 index 0000000..a63ed1e --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/LogService.java @@ -0,0 +1,88 @@ +package top.continew.admin.system.service; + +import jakarta.servlet.http.HttpServletResponse; +import top.continew.admin.system.model.query.LogQuery; +import top.continew.admin.system.model.resp.DashboardAccessTrendResp; +import top.continew.admin.system.model.resp.DashboardPopularModuleResp; +import top.continew.admin.system.model.resp.DashboardTotalResp; +import top.continew.admin.system.model.resp.log.LogDetailResp; +import top.continew.admin.system.model.resp.log.LogResp; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.query.SortQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; + +import java.util.List; +import java.util.Map; + +/** + * 系统日志业务接口 + * + * @author Charles7c + * @since 2022/12/23 20:12 + */ +public interface LogService { + + /** + * 分页查询列表 + * + * @param query 查询条件 + * @param pageQuery 分页查询条件 + * @return 分页列表信息 + */ + PageResp page(LogQuery query, PageQuery pageQuery); + + /** + * 查询详情 + * + * @param id ID + * @return 详情信息 + */ + LogDetailResp get(Long id); + + /** + * 导出登录日志 + * + * @param query 查询条件 + * @param sortQuery 排序查询条件 + * @param response 响应对象 + */ + void exportLoginLog(LogQuery query, SortQuery sortQuery, HttpServletResponse response); + + /** + * 导出操作日志 + * + * @param query 查询条件 + * @param sortQuery 排序查询条件 + * @param response 响应对象 + */ + void exportOperationLog(LogQuery query, SortQuery sortQuery, HttpServletResponse response); + + /** + * 查询仪表盘总计信息 + * + * @return 仪表盘总计信息 + */ + DashboardTotalResp getDashboardTotal(); + + /** + * 查询仪表盘访问趋势信息 + * + * @param days 日期数 + * @return 仪表盘访问趋势信息 + */ + List listDashboardAccessTrend(Integer days); + + /** + * 查询仪表盘热门模块列表 + * + * @return 仪表盘热门模块列表 + */ + List listDashboardPopularModule(); + + /** + * 查询仪表盘访客地域分布信息 + * + * @return 仪表盘访客地域分布信息 + */ + List> listDashboardGeoDistribution(); +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/MenuService.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/MenuService.java new file mode 100644 index 0000000..9e68591 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/MenuService.java @@ -0,0 +1,43 @@ +package top.continew.admin.system.service; + +import top.continew.admin.system.model.entity.MenuDO; +import top.continew.admin.system.model.query.MenuQuery; +import top.continew.admin.system.model.req.MenuReq; +import top.continew.admin.system.model.resp.MenuResp; +import top.continew.starter.extension.crud.service.BaseService; +import top.continew.starter.data.mp.service.IService; + +import java.util.List; +import java.util.Set; + +/** + * 菜单业务接口 + * + * @author Charles7c + * @since 2023/2/15 20:30 + */ +public interface MenuService extends BaseService, IService { + + /** + * 查询全部菜单 + * + * @return 菜单列表 + */ + List listAll(); + + /** + * 根据用户 ID 查询 + * + * @param userId 用户 ID + * @return 权限码集合 + */ + Set listPermissionByUserId(Long userId); + + /** + * 根据角色编码查询 + * + * @param roleCode 角色编码 + * @return 菜单列表 + */ + List listByRoleCode(String roleCode); +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/MessageService.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/MessageService.java new file mode 100644 index 0000000..35f0bfc --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/MessageService.java @@ -0,0 +1,42 @@ +package top.continew.admin.system.service; + +import java.util.List; + +import top.continew.admin.system.model.query.MessageQuery; +import top.continew.admin.system.model.req.MessageReq; +import top.continew.admin.system.model.resp.MessageResp; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; + +/** + * 消息业务接口 + * + * @author Bull-BCLS + * @since 2023/10/15 19:05 + */ +public interface MessageService { + + /** + * 分页查询列表 + * + * @param query 查询条件 + * @param pageQuery 分页查询条件 + * @return 分页列表信息 + */ + PageResp page(MessageQuery query, PageQuery pageQuery); + + /** + * 新增 + * + * @param req 新增信息 + * @param userIdList 接收人列表 + */ + void add(MessageReq req, List userIdList); + + /** + * 删除 + * + * @param ids ID 列表 + */ + void delete(List ids); +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/MessageUserService.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/MessageUserService.java new file mode 100644 index 0000000..25940f2 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/MessageUserService.java @@ -0,0 +1,45 @@ +package top.continew.admin.system.service; + +import java.util.List; + +import top.continew.admin.system.model.resp.MessageUnreadResp; + +/** + * 消息和用户关联业务接口 + * + * @author Bull-BCLS + * @since 2023/10/15 19:05 + */ +public interface MessageUserService { + + /** + * 根据用户 ID 查询未读消息数量 + * + * @param userId 用户 ID + * @param isDetail 是否查询详情 + * @return 未读消息信息 + */ + MessageUnreadResp countUnreadMessageByUserId(Long userId, Boolean isDetail); + + /** + * 新增 + * + * @param messageId 消息 ID + * @param userIdList 用户 ID 列表 + */ + void add(Long messageId, List userIdList); + + /** + * 将消息标记已读 + * + * @param ids 消息ID(为空则将所有消息标记已读) + */ + void readMessage(List ids); + + /** + * 根据消息 ID 删除 + * + * @param messageIds 消息 ID 列表 + */ + void deleteByMessageIds(List messageIds); +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/NoticeService.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/NoticeService.java new file mode 100644 index 0000000..3a6749e --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/NoticeService.java @@ -0,0 +1,28 @@ +package top.continew.admin.system.service; + +import top.continew.admin.system.model.entity.NoticeDO; +import top.continew.admin.system.model.query.NoticeQuery; +import top.continew.admin.system.model.req.NoticeReq; +import top.continew.admin.system.model.resp.NoticeDetailResp; +import top.continew.admin.system.model.resp.NoticeResp; +import top.continew.admin.system.model.resp.DashboardNoticeResp; +import top.continew.starter.extension.crud.service.BaseService; +import top.continew.starter.data.mp.service.IService; + +import java.util.List; + +/** + * 公告业务接口 + * + * @author Charles7c + * @since 2023/8/20 10:55 + */ +public interface NoticeService extends BaseService, IService { + + /** + * 查询仪表盘公告列表 + * + * @return 仪表盘公告列表 + */ + List listDashboard(); +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/OptionService.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/OptionService.java new file mode 100644 index 0000000..be6c9eb --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/OptionService.java @@ -0,0 +1,66 @@ +package top.continew.admin.system.service; + +import top.continew.admin.system.model.query.OptionQuery; +import top.continew.admin.system.model.req.OptionReq; +import top.continew.admin.system.model.req.OptionResetValueReq; +import top.continew.admin.system.model.resp.OptionResp; + +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +/** + * 参数业务接口 + * + * @author Bull-BCLS + * @since 2023/8/26 19:38 + */ +public interface OptionService { + + /** + * 查询列表 + * + * @param query 查询条件 + * @return 列表信息 + */ + List list(OptionQuery query); + + /** + * 根据类别查询 + * + * @param category 类别 + * @return 参数信息 + */ + Map getByCategory(String category); + + /** + * 修改参数 + * + * @param options 参数列表 + */ + void update(List options); + + /** + * 重置参数 + * + * @param req 重置信息 + */ + void resetValue(OptionResetValueReq req); + + /** + * 根据编码查询参数值 + * + * @param code 编码 + * @return 参数值(自动转换为 int 类型) + */ + int getValueByCode2Int(String code); + + /** + * 根据编码查询参数值 + * + * @param code 编码 + * @param mapper 转换方法 e.g.:value -> Integer.parseInt(value) + * @return 参数值 + */ + T getValueByCode(String code, Function mapper); +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/RoleDeptService.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/RoleDeptService.java new file mode 100644 index 0000000..eac93b7 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/RoleDeptService.java @@ -0,0 +1,43 @@ +package top.continew.admin.system.service; + +import java.util.List; + +/** + * 角色和部门业务接口 + * + * @author Charles7c + * @since 2023/2/19 10:40 + */ +public interface RoleDeptService { + + /** + * 新增 + * + * @param deptIds 部门 ID 列表 + * @param roleId 角色 ID + * @return 是否新增成功(true:成功;false:无变更/失败) + */ + boolean add(List deptIds, Long roleId); + + /** + * 根据角色 ID 删除 + * + * @param roleIds 角色 ID 列表 + */ + void deleteByRoleIds(List roleIds); + + /** + * 根据部门 ID 删除 + * + * @param deptIds 部门 ID 列表 + */ + void deleteByDeptIds(List deptIds); + + /** + * 根据角色 ID 查询 + * + * @param roleId 角色 ID + * @return 部门 ID 列表 + */ + List listDeptIdByRoleId(Long roleId); +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/RoleMenuService.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/RoleMenuService.java new file mode 100644 index 0000000..3b520ed --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/RoleMenuService.java @@ -0,0 +1,36 @@ +package top.continew.admin.system.service; + +import java.util.List; + +/** + * 角色和菜单业务接口 + * + * @author Charles7c + * @since 2023/2/19 10:40 + */ +public interface RoleMenuService { + + /** + * 新增 + * + * @param menuIds 菜单 ID 列表 + * @param roleId 角色 ID + * @return 是否新增成功(true:成功;false:无变更/失败) + */ + boolean add(List menuIds, Long roleId); + + /** + * 根据角色 ID 删除 + * + * @param roleIds 角色 ID 列表 + */ + void deleteByRoleIds(List roleIds); + + /** + * 根据角色 ID 查询 + * + * @param roleIds 角色 ID 列表 + * @return 菜单 ID 列表 + */ + List listMenuIdByRoleIds(List roleIds); +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/RoleService.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/RoleService.java new file mode 100644 index 0000000..cad4b4a --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/RoleService.java @@ -0,0 +1,78 @@ +package top.continew.admin.system.service; + +import top.continew.admin.common.model.dto.RoleDTO; +import top.continew.admin.system.model.entity.RoleDO; +import top.continew.admin.system.model.query.RoleQuery; +import top.continew.admin.system.model.req.RoleReq; +import top.continew.admin.system.model.resp.RoleDetailResp; +import top.continew.admin.system.model.resp.RoleResp; +import top.continew.starter.data.mp.service.IService; +import top.continew.starter.extension.crud.service.BaseService; + +import java.util.List; +import java.util.Set; + +/** + * 角色业务接口 + * + * @author Charles7c + * @since 2023/2/8 23:15 + */ +public interface RoleService extends BaseService, IService { + + /** + * 根据用户 ID 查询权限码 + * + * @param userId 用户 ID + * @return 权限码集合 + */ + Set listPermissionByUserId(Long userId); + + /** + * 根据 ID 列表查询 + * + * @param ids ID 列表 + * @return 名称列表 + */ + List listNameByIds(List ids); + + /** + * 根据用户 ID 查询角色编码 + * + * @param userId 用户 ID + * @return 角色编码集合 + */ + Set listCodeByUserId(Long userId); + + /** + * 根据用户 ID 查询角色 + * + * @param userId 用户 ID + * @return 角色集合 + */ + Set listByUserId(Long userId); + + /** + * 根据角色编码查询 + * + * @param code 角色编码 + * @return 角色信息 + */ + RoleDO getByCode(String code); + + /** + * 根据角色名称查询 + * + * @param list 名称列表 + * @return 角色列表 + */ + List listByNames(List list); + + /** + * 根据角色名称查询数量 + * + * @param roleNames 名称列表 + * @return 角色数量 + */ + int countByNames(List roleNames); +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/StorageService.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/StorageService.java new file mode 100644 index 0000000..c67595b --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/StorageService.java @@ -0,0 +1,46 @@ +package top.continew.admin.system.service; + +import top.continew.admin.system.model.entity.StorageDO; +import top.continew.admin.system.model.query.StorageQuery; +import top.continew.admin.system.model.req.StorageReq; +import top.continew.admin.system.model.resp.StorageResp; +import top.continew.starter.data.mp.service.IService; +import top.continew.starter.extension.crud.service.BaseService; + +/** + * 存储业务接口 + * + * @author Charles7c + * @since 2023/12/26 22:09 + */ +public interface StorageService extends BaseService, IService { + + /** + * 查询默认存储 + * + * @return 存储信息 + */ + StorageDO getDefaultStorage(); + + /** + * 根据编码查询 + * + * @param code 编码 + * @return 存储信息 + */ + StorageDO getByCode(String code); + + /** + * 加载存储 + * + * @param req 存储信息 + */ + void load(StorageReq req); + + /** + * 卸载存储 + * + * @param req 存储信息 + */ + void unload(StorageReq req); +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/UserPasswordHistoryService.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/UserPasswordHistoryService.java new file mode 100644 index 0000000..e8ab386 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/UserPasswordHistoryService.java @@ -0,0 +1,38 @@ +package top.continew.admin.system.service; + +import java.util.List; + +/** + * 用户历史密码业务接口 + * + * @author Charles7c + * @since 2024/5/16 21:58 + */ +public interface UserPasswordHistoryService { + + /** + * 新增 + * + * @param userId 用户 ID + * @param password 密码 + * @param count 保留 N 个历史 + */ + void add(Long userId, String password, int count); + + /** + * 根据用户 ID 删除 + * + * @param userIds 用户 ID 列表 + */ + void deleteByUserIds(List userIds); + + /** + * 密码是否为重复使用 + * + * @param userId 用户 ID + * @param password 密码 + * @param count 最近 N 次 + * @return 是否为重复使用 + */ + boolean isPasswordReused(Long userId, String password, int count); +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/UserRoleService.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/UserRoleService.java new file mode 100644 index 0000000..cf14862 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/UserRoleService.java @@ -0,0 +1,53 @@ +package top.continew.admin.system.service; + +import top.continew.admin.system.model.entity.UserRoleDO; + +import java.util.List; + +/** + * 用户和角色业务接口 + * + * @author Charles7c + * @since 2023/2/20 21:30 + */ +public interface UserRoleService { + + /** + * 新增 + * + * @param roleIds 角色 ID 列表 + * @param userId 用户 ID + * @return 是否新增成功(true:成功;false:无变更/失败) + */ + boolean add(List roleIds, Long userId); + + /** + * 根据用户 ID 删除 + * + * @param userIds 用户 ID 列表 + */ + void deleteByUserIds(List userIds); + + /** + * 批量插入 + * + * @param list 数据集 + */ + void saveBatch(List list); + + /** + * 根据用户 ID 查询 + * + * @param userId 用户 ID + * @return 角色 ID 列表 + */ + List listRoleIdByUserId(Long userId); + + /** + * 根据角色 ID 判断是否已被用户关联 + * + * @param roleIds 角色 ID 列表 + * @return 是否已关联(true:已关联;false:未关联) + */ + boolean isRoleIdExists(List roleIds); +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/UserService.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/UserService.java new file mode 100644 index 0000000..8edd269 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/UserService.java @@ -0,0 +1,152 @@ +package top.continew.admin.system.service; + +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.multipart.MultipartFile; +import top.continew.admin.system.model.entity.UserDO; +import top.continew.admin.system.model.query.UserBalanceQuery; +import top.continew.admin.system.model.query.UserQuery; +import top.continew.admin.system.model.req.*; +import top.continew.admin.system.model.resp.*; +import top.continew.starter.data.mp.service.IService; +import top.continew.starter.extension.crud.service.BaseService; + +import java.io.IOException; +import java.util.List; + +/** + * 用户业务接口 + * + * @author Charles7c + * @since 2022/12/21 21:48 + */ +public interface UserService extends BaseService, IService { + + /** + * 新增 + * + * @param user 用户信息 + * @return ID + */ + Long add(UserDO user); + + /** + * 重置密码 + * + * @param req 重置信息 + * @param id ID + */ + void resetPassword(UserPasswordResetReq req, Long id); + + /** + * 修改角色 + * + * @param updateReq 修改信息 + * @param id ID + */ + void updateRole(UserRoleUpdateReq updateReq, Long id); + + /** + * 上传头像 + * + * @param avatar 头像文件 + * @param id ID + * @return 新头像路径 + */ + String updateAvatar(MultipartFile avatar, Long id) throws IOException; + + /** + * 修改基础信息 + * + * @param req 修改信息 + * @param id ID + */ + void updateBasicInfo(UserBasicInfoUpdateReq req, Long id); + + /** + * 修改密码 + * + * @param oldPassword 当前密码 + * @param newPassword 新密码 + * @param id ID + */ + void updatePassword(String oldPassword, String newPassword, Long id); + + /** + * 修改手机号 + * + * @param newPhone 新手机号 + * @param oldPassword 当前密码 + * @param id ID + */ + void updatePhone(String newPhone, String oldPassword, Long id); + + /** + * 修改邮箱 + * + * @param newEmail 新邮箱 + * @param oldPassword 当前密码 + * @param id ID + */ + void updateEmail(String newEmail, String oldPassword, Long id); + + /** + * 根据用户名查询 + * + * @param username 用户名 + * @return 用户信息 + */ + UserDO getByUsername(String username); + + /** + * 通过用户id、消耗点数查询余额是否充足 + * + * @param query + * @return + */ + boolean getUserBalance(UserBalanceQuery query); + + /** + * 根据手机号查询 + * + * @param phone 手机号 + * @return 用户信息 + */ + UserDO getByPhone(String phone); + + /** + * 根据邮箱查询 + * + * @param email 邮箱 + * @return 用户信息 + */ + UserDO getByEmail(String email); + + /** + * 根据部门 ID 列表查询 + * + * @param deptIds 部门 ID 列表 + * @return 用户数量 + */ + Long countByDeptIds(List deptIds); + + /** + * 下载用户导入模板 + */ + void downloadImportUserTemplate(HttpServletResponse response) throws IOException; + + /** + * 导入用户 + * + */ + UserImportResp importUser(UserImportReq req); + + /** + * 解析用户导入数据 + * + * @param file 导入用户文件 + * @return 解析结果 + */ + UserImportParseResp parseImportUser(MultipartFile file); + + List selectTeacherById(Long deptId); +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/UserSocialService.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/UserSocialService.java new file mode 100644 index 0000000..999002e --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/UserSocialService.java @@ -0,0 +1,56 @@ +package top.continew.admin.system.service; + +import java.util.List; + +import top.continew.admin.system.model.entity.UserSocialDO; + +import me.zhyd.oauth.model.AuthUser; + +/** + * 用户社会化关联业务接口 + * + * @author Charles7c + * @since 2023/10/11 22:10 + */ +public interface UserSocialService { + + /** + * 根据来源和开放 ID 查询 + * + * @param source 来源 + * @param openId 开放 ID + * @return 用户社会化关联信息 + */ + UserSocialDO getBySourceAndOpenId(String source, String openId); + + /** + * 保存 + * + * @param userSocial 用户社会化关联信息 + */ + void saveOrUpdate(UserSocialDO userSocial); + + /** + * 根据用户 ID 查询 + * + * @param userId 用户 ID + * @return 用户社会化关联信息 + */ + List listByUserId(Long userId); + + /** + * 绑定 + * + * @param authUser 三方账号信息 + * @param userId 用户 ID + */ + void bind(AuthUser authUser, Long userId); + + /** + * 根据来源和用户 ID 删除 + * + * @param source 来源 + * @param userId 用户 ID + */ + void deleteBySourceAndUserId(String source, Long userId); +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/DashboardServiceImpl.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/DashboardServiceImpl.java new file mode 100644 index 0000000..711800b --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/DashboardServiceImpl.java @@ -0,0 +1,75 @@ +package top.continew.admin.system.service.impl; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.NumberUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import top.continew.admin.system.service.NoticeService; +import top.continew.admin.system.service.DashboardService; +import top.continew.admin.system.service.LogService; +import top.continew.admin.system.model.resp.*; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Map; + +/** + * 仪表盘业务实现 + * + * @author Charles7c + * @since 2023/9/8 21:32 + */ +@Service +@RequiredArgsConstructor +public class DashboardServiceImpl implements DashboardService { + + private final LogService logService; + private final NoticeService noticeService; + + @Override + public DashboardTotalResp getTotal() { + DashboardTotalResp totalResp = logService.getDashboardTotal(); + Long todayPvCount = totalResp.getTodayPvCount(); + Long yesterdayPvCount = totalResp.getYesterdayPvCount(); + BigDecimal newPvCountFromYesterday = NumberUtil.sub(todayPvCount, yesterdayPvCount); + BigDecimal newPvFromYesterday = (0 == yesterdayPvCount) + ? BigDecimal.valueOf(100) + : NumberUtil.round(NumberUtil.mul(NumberUtil.div(newPvCountFromYesterday, yesterdayPvCount), 100), 1); + totalResp.setNewPvFromYesterday(newPvFromYesterday); + return totalResp; + } + + @Override + public List listAccessTrend(Integer days) { + return logService.listDashboardAccessTrend(days); + } + + @Override + public List listPopularModule() { + List popularModuleList = logService.listDashboardPopularModule(); + for (DashboardPopularModuleResp popularModule : popularModuleList) { + Long todayPvCount = popularModule.getTodayPvCount(); + Long yesterdayPvCount = popularModule.getYesterdayPvCount(); + BigDecimal newPvCountFromYesterday = NumberUtil.sub(todayPvCount, yesterdayPvCount); + BigDecimal newPvFromYesterday = (0 == yesterdayPvCount) + ? BigDecimal.valueOf(100) + : NumberUtil.round(NumberUtil.mul(NumberUtil.div(newPvCountFromYesterday, yesterdayPvCount), 100), 1); + popularModule.setNewPvFromYesterday(newPvFromYesterday); + } + return popularModuleList; + } + + @Override + public DashboardGeoDistributionResp getGeoDistribution() { + List> locationIpStatistics = logService.listDashboardGeoDistribution(); + DashboardGeoDistributionResp geoDistribution = new DashboardGeoDistributionResp(); + geoDistribution.setLocationIpStatistics(locationIpStatistics); + geoDistribution.setLocations(locationIpStatistics.stream().map(m -> Convert.toStr(m.get("name"))).toList()); + return geoDistribution; + } + + @Override + public List listNotice() { + return noticeService.listDashboard(); + } +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/DeptServiceImpl.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/DeptServiceImpl.java new file mode 100644 index 0000000..b9985c8 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/DeptServiceImpl.java @@ -0,0 +1,303 @@ +package top.continew.admin.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.baomidou.dynamic.datasource.DynamicRoutingDataSource; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.admin.system.mapper.DeptMapper; +import top.continew.admin.system.model.entity.DeptDO; +import top.continew.admin.system.model.query.DeptQuery; +import top.continew.admin.system.model.req.DeptReq; +import top.continew.admin.system.model.resp.DeptChildResp; +import top.continew.admin.system.model.resp.DeptResp; +import top.continew.admin.system.model.resp.UserTreeResp; +import top.continew.admin.system.service.DeptService; +import top.continew.admin.system.service.RoleDeptService; +import top.continew.admin.system.service.UserService; +import top.continew.starter.core.util.validate.CheckUtils; +import top.continew.starter.data.core.enums.DatabaseType; +import top.continew.starter.data.core.util.MetaUtils; +import top.continew.starter.extension.crud.service.impl.BaseServiceImpl; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * 部门业务实现 + * + * @author Charles7c + * @since 2023/1/22 17:55 + */ +@Service +@RequiredArgsConstructor +public class DeptServiceImpl extends BaseServiceImpl implements DeptService { + + private final UserService userService; + private final RoleDeptService roleDeptService; + + @Override + public List listChildren(Long id) { + DatabaseType databaseType = MetaUtils.getDatabaseTypeOrDefault(SpringUtil + .getBean(DynamicRoutingDataSource.class), DatabaseType.MYSQL); + return baseMapper.lambdaQuery().apply(databaseType.findInSet(id, "ancestors")).list(); + } + + @Override + public List listByNames(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + return this.list(Wrappers.lambdaQuery().in(DeptDO::getName, list)); + } + + @Override + public int countByNames(List deptNames) { + if (CollUtil.isEmpty(deptNames)) { + return 0; + } + return (int)this.count(Wrappers.lambdaQuery().in(DeptDO::getName, deptNames)); + } + + @Override + public DeptDO selectDoById(Long id) { + return baseMapper.selectDoById(id); + } + + @Override + public DeptDO selectParentsById(Long id) { + return baseMapper.selectParentsById(id); + } + + /*** + * 获取文件管理左边的部门管理树形结构 + * + * @param query + * @return + */ + @Override + public List getDeptResp(DeptQuery query) { + QueryWrapper wrapper = new QueryWrapper<>(); + List deptDOS = this.list(wrapper); + List list = deptDOS.stream().map(x -> { + DeptChildResp resp = new DeptChildResp(); + BeanUtils.copyProperties(x, resp); + resp.setTitle(x.getName()); + resp.setKey(x.getId()); + return resp; + }).toList(); + list = streamToTree(list, 0L); + return list; + } + + /** + * 构建树形结构数据 + * + * @author hzy + * @param treeList + * @param parentId + * @return + */ + public List streamToTreelist(List treeList, Long parentId) { + List list = treeList.stream() + // 过滤父节点 + .filter(parent -> parent.getParentId().equals(parentId)) + // 把父节点children递归赋值成为子节点 + .map(child -> { + List list1 = streamToTree(treeList, child.getId()); + + if (child.getParentId() != 0) { + List teachers = userService.selectTeacherById(child.getId()); + if (teachers.size() > 0) { + DeptChildResp resp = new DeptChildResp(); + resp.setId(0L); + resp.setTitle("老师"); + resp.setTeacherChildren(teachers); + list1.add(resp); + } + } + child.setChildren(list1); + return child; + }) + .collect(Collectors.toList()); + return list; + } + + public List streamToTree(List treeList, Long parentId) { + List list = treeList.stream() + // 过滤父节点 + .filter(parent -> parent.getParentId().equals(parentId)) + // 把父节点children递归赋值成为子节点 + .map(child -> { + List children = streamToTree(treeList, child.getId()); + //获取第第二节点 + String ancestors = child.getAncestors(); + String[] array = ancestors.split(","); + if (child.getParentId() != 0 && array.length == 2) { + List teachers = userService.selectTeacherById(child.getId()); + List childResps = teachers.stream().map(x -> { + DeptChildResp resp = new DeptChildResp(); + BeanUtils.copyProperties(x, resp); + resp.setKey(x.getId()); + return resp; + }).toList(); + if (teachers.size() > 0) { + DeptChildResp resp = new DeptChildResp(); + resp.setId(0L); + resp.setTitle("老师"); + resp.setChildren(childResps); + //resp.setTeacherChildren(teachers); + children.add(resp); + } + } + child.setChildren(children); + return child; + }) + .collect(Collectors.toList()); + return list; + } + + @Override + protected void beforeAdd(DeptReq req) { + String name = req.getName(); + boolean isExists = this.isNameExists(name, req.getParentId(), null); + CheckUtils.throwIf(isExists, "新增失败,[{}] 已存在", name); + req.setAncestors(this.getAncestors(req.getParentId())); + } + + @Override + protected void beforeUpdate(DeptReq req, Long id) { + String name = req.getName(); + boolean isExists = this.isNameExists(name, req.getParentId(), id); + CheckUtils.throwIf(isExists, "修改失败,[{}] 已存在", name); + DeptDO oldDept = super.getById(id); + String oldName = oldDept.getName(); + DisEnableStatusEnum newStatus = req.getStatus(); + Long oldParentId = oldDept.getParentId(); + if (Boolean.TRUE.equals(oldDept.getIsSystem())) { + CheckUtils.throwIfEqual(DisEnableStatusEnum.DISABLE, newStatus, "[{}] 是系统内置部门,不允许禁用", oldName); + CheckUtils.throwIfNotEqual(req.getParentId(), oldParentId, "[{}] 是系统内置部门,不允许变更上级部门", oldName); + } + // 启用/禁用部门 + if (ObjectUtil.notEqual(newStatus, oldDept.getStatus())) { + List children = this.listChildren(id); + long enabledChildrenCount = children.stream() + .filter(d -> DisEnableStatusEnum.ENABLE.equals(d.getStatus())) + .count(); + CheckUtils.throwIf(DisEnableStatusEnum.DISABLE + .equals(newStatus) && enabledChildrenCount > 0, "禁用 [{}] 前,请先禁用其所有下级部门", oldName); + DeptDO oldParentDept = this.getByParentId(oldParentId); + CheckUtils.throwIf(DisEnableStatusEnum.ENABLE.equals(newStatus) && DisEnableStatusEnum.DISABLE + .equals(oldParentDept.getStatus()), "启用 [{}] 前,请先启用其所有上级部门", oldName); + } + // 变更上级部门 + if (ObjectUtil.notEqual(req.getParentId(), oldParentId)) { + // 更新祖级列表 + String newAncestors = this.getAncestors(req.getParentId()); + req.setAncestors(newAncestors); + // 更新子级的祖级列表 + this.updateChildrenAncestors(newAncestors, oldDept.getAncestors(), id); + } + } + + @Override + protected void beforeDelete(List ids) { + List list = baseMapper.lambdaQuery() + .select(DeptDO::getName, DeptDO::getIsSystem) + .in(DeptDO::getId, ids) + .list(); + Optional isSystemData = list.stream().filter(DeptDO::getIsSystem).findFirst(); + CheckUtils.throwIf(isSystemData::isPresent, "所选部门 [{}] 是系统内置部门,不允许删除", isSystemData.orElseGet(DeptDO::new) + .getName()); + CheckUtils.throwIf(this.countChildren(ids) > 0, "所选部门存在下级部门,不允许删除"); + CheckUtils.throwIf(userService.countByDeptIds(ids) > 0, "所选部门存在用户关联,请解除关联后重试"); + // 删除角色和部门关联 + roleDeptService.deleteByDeptIds(ids); + } + + /** + * 名称是否存在 + * + * @param name 名称 + * @param parentId 上级 ID + * @param id ID + * @return 是否存在 + */ + private boolean isNameExists(String name, Long parentId, Long id) { + return baseMapper.lambdaQuery() + .eq(DeptDO::getName, name) + .eq(DeptDO::getParentId, parentId) + .ne(null != id, DeptDO::getId, id) + .exists(); + } + + /** + * 获取祖级列表 + * + * @param parentId 上级部门 + * @return 祖级列表 + */ + private String getAncestors(Long parentId) { + DeptDO parentDept = this.getByParentId(parentId); + return "%s,%s".formatted(parentDept.getAncestors(), parentId); + } + + /** + * 根据上级部门 ID 查询 + * + * @param parentId 上级部门 ID + * @return 上级部门信息 + */ + private DeptDO getByParentId(Long parentId) { + DeptDO parentDept = baseMapper.selectById(parentId); + CheckUtils.throwIfNull(parentDept, "上级部门不存在"); + return parentDept; + } + + /** + * 查询子部门数量 + * + * @param ids ID 列表 + * @return 子部门数量 + */ + private Long countChildren(List ids) { + if (CollUtil.isEmpty(ids)) { + return 0L; + } + DatabaseType databaseType = MetaUtils.getDatabaseTypeOrDefault(SpringUtil + .getBean(DynamicRoutingDataSource.class), DatabaseType.MYSQL); + return ids.stream() + .mapToLong(id -> baseMapper.lambdaQuery().apply(databaseType.findInSet(id, "ancestors")).count()) + .sum(); + } + + /** + * 更新子部门祖级列表 + * + * @param newAncestors 新祖级列表 + * @param oldAncestors 原祖级列表 + * @param id ID + */ + private void updateChildrenAncestors(String newAncestors, String oldAncestors, Long id) { + List children = this.listChildren(id); + if (CollUtil.isEmpty(children)) { + return; + } + List list = new ArrayList<>(children.size()); + for (DeptDO child : children) { + DeptDO dept = new DeptDO(); + dept.setId(child.getId()); + dept.setAncestors(child.getAncestors().replaceFirst(oldAncestors, newAncestors)); + list.add(dept); + } + baseMapper.updateBatchById(list); + } +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/DictItemServiceImpl.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/DictItemServiceImpl.java new file mode 100644 index 0000000..68eac16 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/DictItemServiceImpl.java @@ -0,0 +1,113 @@ +package top.continew.admin.system.service.impl; + +import cn.hutool.core.util.ClassUtil; +import cn.hutool.core.util.StrUtil; +import com.alicp.jetcache.anno.Cached; +import jakarta.annotation.PostConstruct; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import top.continew.admin.common.constant.CacheConstants; +import top.continew.admin.system.mapper.DictItemMapper; +import top.continew.admin.system.model.entity.DictItemDO; +import top.continew.admin.system.model.query.DictItemQuery; +import top.continew.admin.system.model.req.DictItemReq; +import top.continew.admin.system.model.resp.DictItemResp; +import top.continew.admin.system.service.DictItemService; +import top.continew.starter.cache.redisson.util.RedisUtils; +import top.continew.starter.core.autoconfigure.project.ProjectProperties; +import top.continew.starter.core.constant.StringConstants; +import top.continew.starter.core.enums.BaseEnum; +import top.continew.starter.core.util.validate.CheckUtils; +import top.continew.starter.extension.crud.model.resp.LabelValueResp; +import top.continew.starter.extension.crud.service.impl.BaseServiceImpl; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * 字典项业务实现 + * + * @author Charles7c + * @since 2023/9/11 21:29 + */ +@Service +@RequiredArgsConstructor +public class DictItemServiceImpl extends BaseServiceImpl implements DictItemService { + + private final ProjectProperties projectProperties; + private static final Map> ENUM_DICT_CACHE = new ConcurrentHashMap<>(); + + @Override + protected void beforeAdd(DictItemReq req) { + String value = req.getValue(); + CheckUtils.throwIf(this.isValueExists(value, null, req.getDictId()), "新增失败,字典值 [{}] 已存在", value); + RedisUtils.deleteByPattern(CacheConstants.DICT_KEY_PREFIX + StringConstants.ASTERISK); + } + + @Override + protected void beforeUpdate(DictItemReq req, Long id) { + String value = req.getValue(); + CheckUtils.throwIf(this.isValueExists(value, id, req.getDictId()), "修改失败,字典值 [{}] 已存在", value); + RedisUtils.deleteByPattern(CacheConstants.DICT_KEY_PREFIX + StringConstants.ASTERISK); + } + + @Override + @Cached(key = "#dictCode", name = CacheConstants.DICT_KEY_PREFIX) + public List listByDictCode(String dictCode) { + return Optional.ofNullable(ENUM_DICT_CACHE.get(dictCode.toLowerCase())) + .orElseGet(() -> baseMapper.listByDictCode(dictCode)); + } + + @Override + public void deleteByDictIds(List dictIds) { + baseMapper.lambdaUpdate().in(DictItemDO::getDictId, dictIds).remove(); + RedisUtils.deleteByPattern(CacheConstants.DICT_KEY_PREFIX + StringConstants.ASTERISK); + } + + @Override + public List listEnumDictNames() { + return ENUM_DICT_CACHE.keySet().stream().toList(); + } + + /** + * 字典值是否存在 + * + * @param value 字典值 + * @param id ID + * @param dictId 字典 ID + * @return 是否存在 + */ + private boolean isValueExists(String value, Long id, Long dictId) { + return baseMapper.lambdaQuery() + .eq(DictItemDO::getValue, value) + .eq(DictItemDO::getDictId, dictId) + .ne(null != id, DictItemDO::getId, id) + .exists(); + } + + /** + * 将枚举转换为枚举字典 + * + * @param enumClass 枚举类型 + * @return 枚举字典 + */ + private List toEnumDict(Class enumClass) { + Object[] enumConstants = enumClass.getEnumConstants(); + return Arrays.stream(enumConstants).map(e -> { + BaseEnum baseEnum = (BaseEnum)e; + return new LabelValueResp(baseEnum.getDescription(), baseEnum.getValue(), baseEnum.getColor()); + }).toList(); + } + + /** + * 缓存枚举字典 + */ + @PostConstruct + public void init() { + Set> classSet = ClassUtil.scanPackageBySuper(projectProperties.getBasePackage(), BaseEnum.class); + ENUM_DICT_CACHE.putAll(classSet.stream() + .collect(Collectors.toMap(cls -> StrUtil.toUnderlineCase(cls.getSimpleName()) + .toLowerCase(), this::toEnumDict))); + } +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/DictServiceImpl.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/DictServiceImpl.java new file mode 100644 index 0000000..720315e --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/DictServiceImpl.java @@ -0,0 +1,86 @@ +package top.continew.admin.system.service.impl; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import top.continew.admin.system.mapper.DictMapper; +import top.continew.admin.system.model.entity.DictDO; +import top.continew.admin.system.model.query.DictQuery; +import top.continew.admin.system.model.req.DictReq; +import top.continew.admin.system.model.resp.DictResp; +import top.continew.admin.system.service.DictItemService; +import top.continew.admin.system.service.DictService; +import top.continew.starter.core.util.validate.CheckUtils; +import top.continew.starter.extension.crud.model.resp.LabelValueResp; +import top.continew.starter.extension.crud.service.impl.BaseServiceImpl; + +import java.util.List; +import java.util.Optional; + +/** + * 字典业务实现 + * + * @author Charles7c + * @since 2023/9/11 21:29 + */ +@Service +@RequiredArgsConstructor +public class DictServiceImpl extends BaseServiceImpl implements DictService { + + private final DictItemService dictItemService; + + @Override + protected void beforeAdd(DictReq req) { + String name = req.getName(); + CheckUtils.throwIf(this.isNameExists(name, null), "新增失败,[{}] 已存在", name); + String code = req.getCode(); + CheckUtils.throwIf(this.isCodeExists(code, null), "新增失败,[{}] 已存在", code); + } + + @Override + protected void beforeUpdate(DictReq req, Long id) { + String name = req.getName(); + CheckUtils.throwIf(this.isNameExists(name, id), "修改失败,[{}] 已存在", name); + DictDO oldDict = super.getById(id); + CheckUtils.throwIfNotEqual(req.getCode(), oldDict.getCode(), "不允许修改字典编码"); + } + + @Override + protected void beforeDelete(List ids) { + List list = baseMapper.lambdaQuery() + .select(DictDO::getName, DictDO::getIsSystem) + .in(DictDO::getId, ids) + .list(); + Optional isSystemData = list.stream().filter(DictDO::getIsSystem).findFirst(); + CheckUtils.throwIf(isSystemData::isPresent, "所选字典 [{}] 是系统内置字典,不允许删除", isSystemData.orElseGet(DictDO::new) + .getName()); + dictItemService.deleteByDictIds(ids); + } + + @Override + public List listEnumDict() { + List enumDictNameList = dictItemService.listEnumDictNames(); + return enumDictNameList.stream().map(name -> new LabelValueResp(name, name)).toList(); + } + + /** + * 名称是否存在 + * + * @param name 名称 + * @param id ID + * @return 是否存在 + */ + private boolean isNameExists(String name, Long id) { + return baseMapper.lambdaQuery().eq(DictDO::getName, name).ne(null != id, DictDO::getId, id).exists(); + } + + /** + * 编码是否存在 + * + * @param code 编码 + * @param id ID + * @return 是否存在 + */ + private boolean isCodeExists(String code, Long id) { + return baseMapper.lambdaQuery().eq(DictDO::getCode, code).ne(null != id, DictDO::getId, id).exists(); + } +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/FileServiceImpl.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/FileServiceImpl.java new file mode 100644 index 0000000..dc7e2d5 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/FileServiceImpl.java @@ -0,0 +1,218 @@ +package top.continew.admin.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.io.file.FileNameUtil; +import cn.hutool.core.util.ClassUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.URLUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import jakarta.annotation.Resource; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.x.file.storage.core.FileInfo; +import org.dromara.x.file.storage.core.FileStorageService; +import org.dromara.x.file.storage.core.ProgressListener; +import org.dromara.x.file.storage.core.upload.UploadPretreatment; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import top.continew.admin.common.model.dto.LoginUser; +import top.continew.admin.common.util.helper.LoginHelper; +import top.continew.admin.system.enums.FileTypeEnum; +import top.continew.admin.system.mapper.FileMapper; +import top.continew.admin.system.model.entity.FileDO; +import top.continew.admin.system.model.entity.StorageDO; +import top.continew.admin.system.model.query.FileQuery; +import top.continew.admin.system.model.req.FileReq; +import top.continew.admin.system.model.resp.FileResp; +import top.continew.admin.system.model.resp.FileStatisticsResp; +import top.continew.admin.system.service.FileService; +import top.continew.admin.system.service.StorageService; +import top.continew.starter.core.constant.StringConstants; +import top.continew.starter.core.util.StrUtils; +import top.continew.starter.core.util.URLUtils; +import top.continew.starter.core.util.validate.CheckUtils; +import top.continew.starter.data.mp.util.QueryWrapperHelper; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.extension.crud.service.impl.BaseServiceImpl; + +import java.time.LocalDate; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 文件业务实现 + * + * @author Charles7c + * @since 2023/12/23 10:38 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class FileServiceImpl extends BaseServiceImpl implements FileService { + + private final FileStorageService fileStorageService; + @Resource + private StorageService storageService; + + @Override + protected void beforeDelete(List ids) { + List fileList = baseMapper.lambdaQuery().in(FileDO::getId, ids).list(); + Map> fileListGroup = fileList.stream().collect(Collectors.groupingBy(FileDO::getStorageId)); + for (Map.Entry> entry : fileListGroup.entrySet()) { + StorageDO storage = storageService.getById(entry.getKey()); + for (FileDO file : entry.getValue()) { + FileInfo fileInfo = file.toFileInfo(storage.getCode()); + fileStorageService.delete(fileInfo); + } + } + } + + @Override + public FileInfo upload(MultipartFile file, String storageCode) { + StorageDO storage; + if (StrUtil.isBlank(storageCode)) { + storage = storageService.getDefaultStorage(); + CheckUtils.throwIfNull(storage, "请先指定默认存储"); + } else { + storage = storageService.getByCode(storageCode); + CheckUtils.throwIfNotExists(storage, "StorageDO", "Code", storageCode); + } + LocalDate today = LocalDate.now(); + String path = today.getYear() + StringConstants.SLASH + today.getMonthValue() + StringConstants.SLASH + today + .getDayOfMonth() + StringConstants.SLASH; + UploadPretreatment uploadPretreatment = fileStorageService.of(file) + .setPlatform(storage.getCode()) + .putAttr(ClassUtil.getClassName(StorageDO.class, false), storage) + .setPath(path); + // 图片文件生成缩略图 + if (FileTypeEnum.IMAGE.getExtensions().contains(FileNameUtil.extName(file.getOriginalFilename()))) { + uploadPretreatment.thumbnail(img -> img.size(100, 100)); + } + uploadPretreatment.setProgressMonitor(new ProgressListener() { + @Override + public void start() { + log.info("开始上传"); + } + + @Override + public void progress(long progressSize, Long allSize) { + log.info("已上传 [{}],总大小 [{}]", progressSize, allSize); + } + + @Override + public void finish() { + log.info("上传结束"); + } + }); + // 处理本地存储文件 URL + FileInfo fileInfo = uploadPretreatment.upload(); + String domain = StrUtil.appendIfMissing(storage.getDomain(), StringConstants.SLASH); + fileInfo.setUrl(URLUtil.normalize(domain + fileInfo.getPath() + fileInfo.getFilename())); + return fileInfo; + } + + /** + * hzy + * 上传 + * + * @param file + * @param storageCode + * @return + */ + @Override + public FileInfo uploadFile(MultipartFile file, String storageCode) { + StorageDO storage; + if (StrUtil.isBlank(storageCode)) { + storage = storageService.getDefaultStorage(); + CheckUtils.throwIfNull(storage, "请先指定默认存储"); + } else { + storage = storageService.getByCode(storageCode); + CheckUtils.throwIfNotExists(storage, "StorageDO", "Code", storageCode); + } + LocalDate today = LocalDate.now(); + String str = "武汉纺织大学,计算机学院,老师,课程封面图,"; + List list = Arrays.asList(str.split(",")); + String path = today.getYear() + StringConstants.SLASH; + for (String s : list) { + path += s + StringConstants.SLASH; + } + UploadPretreatment uploadPretreatment = fileStorageService.of(file) + .setPlatform(storage.getCode()) + .putAttr(ClassUtil.getClassName(StorageDO.class, false), storage) + .setPath(path); + // 图片文件生成缩略图 + if (FileTypeEnum.IMAGE.getExtensions().contains(FileNameUtil.extName(file.getOriginalFilename()))) { + uploadPretreatment.thumbnail(img -> img.size(100, 100)); + } + uploadPretreatment.setProgressMonitor(new ProgressListener() { + @Override + public void start() { + log.info("开始上传"); + } + + @Override + public void progress(long progressSize, Long allSize) { + log.info("已上传 [{}],总大小 [{}]", progressSize, allSize); + } + + @Override + public void finish() { + log.info("上传结束"); + } + }); + // 处理本地存储文件 URL + FileInfo fileInfo = uploadPretreatment.upload(); + String domain = StrUtil.appendIfMissing(storage.getDomain(), StringConstants.SLASH); + fileInfo.setUrl(URLUtil.normalize(domain + fileInfo.getPath() + fileInfo.getFilename())); + return fileInfo; + } + + @Override + public Long countByStorageIds(List storageIds) { + return baseMapper.lambdaQuery().in(FileDO::getStorageId, storageIds).count(); + } + + @Override + public FileStatisticsResp statistics() { + FileStatisticsResp resp = new FileStatisticsResp(); + List statisticsList = baseMapper.statistics(); + if (CollUtil.isEmpty(statisticsList)) { + return resp; + } + resp.setData(statisticsList); + resp.setSize(statisticsList.stream().mapToLong(FileStatisticsResp::getSize).sum()); + resp.setNumber(statisticsList.stream().mapToLong(FileStatisticsResp::getNumber).sum()); + return resp; + } + + @Override + public PageResp getFilesPage(FileQuery query, PageQuery pageQuery) { + // 获取当前登录用户信息 + LoginUser loginUser = LoginHelper.getLoginUser(); + List deptIds = loginUser.getModelDeptIds(); + QueryWrapper queryWrapper = QueryWrapperHelper.build(query, pageQuery.getSort()); + queryWrapper.apply(null != query.getCreateUser(), "create_user={0}", query.getCreateUser()); + IPage page = baseMapper.selectFilesPage(new Page<>(pageQuery.getPage(), pageQuery.getSize()), query); + return PageResp.build(page); + } + + @Override + protected void fill(Object obj) { + super.fill(obj); + if (obj instanceof FileResp fileResp && !URLUtils.isHttpUrl(fileResp.getUrl())) { + StorageDO storage = storageService.getById(fileResp.getStorageId()); + String prefix = StrUtil.appendIfMissing(storage.getDomain(), StringConstants.SLASH); + String url = URLUtil.normalize(prefix + fileResp.getUrl()); + fileResp.setUrl(url); + String thumbnailUrl = StrUtils.blankToDefault(fileResp.getThumbnailUrl(), url, thUrl -> URLUtil + .normalize(prefix + thUrl)); + fileResp.setThumbnailUrl(thumbnailUrl); + fileResp.setStorageName("%s (%s)".formatted(storage.getName(), storage.getCode())); + } + } +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/LogServiceImpl.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/LogServiceImpl.java new file mode 100644 index 0000000..9f4a6b2 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/LogServiceImpl.java @@ -0,0 +1,154 @@ +package top.continew.admin.system.service.impl; + +import cn.crane4j.annotation.AutoOperate; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.text.CharSequenceUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.admin.system.mapper.LogMapper; +import top.continew.admin.system.model.entity.LogDO; +import top.continew.admin.system.model.query.LogQuery; +import top.continew.admin.system.model.resp.DashboardAccessTrendResp; +import top.continew.admin.system.model.resp.DashboardPopularModuleResp; +import top.continew.admin.system.model.resp.DashboardTotalResp; +import top.continew.admin.system.model.resp.log.LogDetailResp; +import top.continew.admin.system.model.resp.log.LogResp; +import top.continew.admin.system.model.resp.log.LoginLogExportResp; +import top.continew.admin.system.model.resp.log.OperationLogExportResp; +import top.continew.admin.system.service.LogService; +import top.continew.starter.core.util.validate.CheckUtils; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.query.SortQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.file.excel.util.ExcelUtils; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 系统日志业务实现 + * + * @author Charles7c + * @since 2022/12/23 20:12 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class LogServiceImpl implements LogService { + + private final LogMapper baseMapper; + + @Override + public PageResp page(LogQuery query, PageQuery pageQuery) { + QueryWrapper queryWrapper = this.buildQueryWrapper(query); + IPage page = baseMapper.selectLogPage(new Page<>(pageQuery.getPage(), pageQuery + .getSize()), queryWrapper); + return PageResp.build(page); + } + + @Override + @AutoOperate(type = LogDetailResp.class) + public LogDetailResp get(Long id) { + LogDO logDO = baseMapper.selectById(id); + CheckUtils.throwIfNotExists(logDO, "LogDO", "ID", id); + return BeanUtil.copyProperties(logDO, LogDetailResp.class); + } + + @Override + public void exportLoginLog(LogQuery query, SortQuery sortQuery, HttpServletResponse response) { + List list = BeanUtil.copyToList(this.list(query, sortQuery), LoginLogExportResp.class); + ExcelUtils.export(list, "导出登录日志数据", LoginLogExportResp.class, response); + } + + @Override + public void exportOperationLog(LogQuery query, SortQuery sortQuery, HttpServletResponse response) { + List list = BeanUtil.copyToList(this + .list(query, sortQuery), OperationLogExportResp.class); + ExcelUtils.export(list, "导出操作日志数据", OperationLogExportResp.class, response); + } + + @Override + public DashboardTotalResp getDashboardTotal() { + return baseMapper.selectDashboardTotal(); + } + + @Override + public List listDashboardAccessTrend(Integer days) { + return baseMapper.selectListDashboardAccessTrend(days); + } + + @Override + public List listDashboardPopularModule() { + return baseMapper.selectListDashboardPopularModule(); + } + + @Override + public List> listDashboardGeoDistribution() { + return baseMapper.selectListDashboardGeoDistribution(); + } + + /** + * 查询列表 + * + * @param query 查询条件 + * @param sortQuery 排序查询条件 + * @return 列表信息 + */ + private List list(LogQuery query, SortQuery sortQuery) { + QueryWrapper queryWrapper = this.buildQueryWrapper(query); + this.sort(queryWrapper, sortQuery); + return baseMapper.selectLogList(queryWrapper); + } + + /** + * 设置排序 + * + * @param queryWrapper 查询条件封装对象 + * @param sortQuery 排序查询条件 + */ + private void sort(QueryWrapper queryWrapper, SortQuery sortQuery) { + if (sortQuery == null || sortQuery.getSort().isUnsorted()) { + return; + } + for (Sort.Order order : sortQuery.getSort()) { + String property = order.getProperty(); + queryWrapper.orderBy(true, order.isAscending(), CharSequenceUtil.toUnderlineCase(property)); + } + } + + /** + * 构建 QueryWrapper + * + * @param query 查询条件 + * @return QueryWrapper + */ + private QueryWrapper buildQueryWrapper(LogQuery query) { + String description = query.getDescription(); + String module = query.getModule(); + String ip = query.getIp(); + String createUserString = query.getCreateUserString(); + DisEnableStatusEnum status = query.getStatus(); + List createTimeList = query.getCreateTime(); + return new QueryWrapper().and(StrUtil.isNotBlank(description), q -> q.like("t1.description", description) + .or() + .like("t1.module", description)) + .eq(StrUtil.isNotBlank(module), "t1.module", module) + .and(StrUtil.isNotBlank(ip), q -> q.like("t1.ip", ip).or().like("t1.address", ip)) + .and(StrUtil.isNotBlank(createUserString), q -> q.like("t2.username", createUserString) + .or() + .like("t2.nickname", createUserString)) + .eq(null != status, "t1.status", status) + .between(CollUtil.isNotEmpty(createTimeList), "t1.create_time", CollUtil.getFirst(createTimeList), CollUtil + .getLast(createTimeList)); + } +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/MenuServiceImpl.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/MenuServiceImpl.java new file mode 100644 index 0000000..29c5805 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/MenuServiceImpl.java @@ -0,0 +1,126 @@ +package top.continew.admin.system.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.StrUtil; +import com.alicp.jetcache.anno.Cached; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import top.continew.admin.common.constant.CacheConstants; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.admin.system.enums.MenuTypeEnum; +import top.continew.admin.system.mapper.MenuMapper; +import top.continew.admin.system.model.entity.MenuDO; +import top.continew.admin.system.model.query.MenuQuery; +import top.continew.admin.system.model.req.MenuReq; +import top.continew.admin.system.model.resp.MenuResp; +import top.continew.admin.system.service.MenuService; +import top.continew.starter.cache.redisson.util.RedisUtils; +import top.continew.starter.core.constant.StringConstants; +import top.continew.starter.core.util.validate.CheckUtils; +import top.continew.starter.extension.crud.service.impl.BaseServiceImpl; + +import java.util.List; +import java.util.Set; + +/** + * 菜单业务实现 + * + * @author Charles7c + * @since 2023/2/15 20:30 + */ +@Service +@RequiredArgsConstructor +public class MenuServiceImpl extends BaseServiceImpl implements MenuService { + + @Override + public Long add(MenuReq req) { + String title = req.getTitle(); + CheckUtils.throwIf(this.isTitleExists(title, req.getParentId(), null), "新增失败,标题 [{}] 已存在", title); + // 目录和菜单的组件名称不能重复 + if (!MenuTypeEnum.BUTTON.equals(req.getType())) { + String name = req.getName(); + CheckUtils.throwIf(this.isNameExists(name, null), "新增失败,组件名称 [{}] 已存在", name); + } + // 目录类型菜单,默认为 Layout + if (MenuTypeEnum.DIR.equals(req.getType())) { + req.setComponent(StrUtil.blankToDefault(req.getComponent(), "Layout")); + } + RedisUtils.deleteByPattern(CacheConstants.MENU_KEY_PREFIX + StringConstants.ASTERISK); + return super.add(req); + } + + @Override + public void update(MenuReq req, Long id) { + String title = req.getTitle(); + CheckUtils.throwIf(this.isTitleExists(title, req.getParentId(), id), "修改失败,标题 [{}] 已存在", title); + // 目录和菜单的组件名称不能重复 + if (!MenuTypeEnum.BUTTON.equals(req.getType())) { + String name = req.getName(); + CheckUtils.throwIf(this.isNameExists(name, id), "修改失败,组件名称 [{}] 已存在", name); + } + MenuDO oldMenu = super.getById(id); + CheckUtils.throwIfNotEqual(req.getType(), oldMenu.getType(), "不允许修改菜单类型"); + super.update(req, id); + RedisUtils.deleteByPattern(CacheConstants.MENU_KEY_PREFIX + StringConstants.ASTERISK); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(List ids) { + baseMapper.lambdaUpdate().in(MenuDO::getParentId, ids).remove(); + super.delete(ids); + RedisUtils.deleteByPattern(CacheConstants.MENU_KEY_PREFIX + StringConstants.ASTERISK); + } + + @Override + @Cached(key = "'ALL'", name = CacheConstants.MENU_KEY_PREFIX) + public List listAll() { + return super.list(new MenuQuery(DisEnableStatusEnum.ENABLE), null); + } + + @Override + public Set listPermissionByUserId(Long userId) { + return baseMapper.selectPermissionByUserId(userId); + } + + @Override + @Cached(key = "#roleCode", name = CacheConstants.MENU_KEY_PREFIX) + public List listByRoleCode(String roleCode) { + List menuList = baseMapper.selectListByRoleCode(roleCode); + List list = BeanUtil.copyToList(menuList, MenuResp.class); + list.forEach(this::fill); + return list; + } + + /** + * 标题是否存在 + * + * @param title 标题 + * @param parentId 上级 ID + * @param id ID + * @return true:存在;false:不存在 + */ + private boolean isTitleExists(String title, Long parentId, Long id) { + return baseMapper.lambdaQuery() + .eq(MenuDO::getTitle, title) + .eq(MenuDO::getParentId, parentId) + .ne(null != id, MenuDO::getId, id) + .exists(); + } + + /** + * 名称是否存在 + * + * @param name 标题 + * @param id ID + * @return true:存在;false:不存在 + */ + private boolean isNameExists(String name, Long id) { + return baseMapper.lambdaQuery() + .eq(MenuDO::getName, name) + .ne(MenuDO::getType, MenuTypeEnum.BUTTON) + .ne(null != id, MenuDO::getId, id) + .exists(); + } +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/MessageServiceImpl.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/MessageServiceImpl.java new file mode 100644 index 0000000..5638daa --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/MessageServiceImpl.java @@ -0,0 +1,65 @@ +package top.continew.admin.system.service.impl; + +import cn.crane4j.annotation.AutoOperate; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import top.continew.admin.system.mapper.MessageMapper; +import top.continew.admin.system.model.entity.MessageDO; +import top.continew.admin.system.model.query.MessageQuery; +import top.continew.admin.system.model.req.MessageReq; +import top.continew.admin.system.model.resp.MessageResp; +import top.continew.admin.system.service.MessageService; +import top.continew.admin.system.service.MessageUserService; +import top.continew.starter.core.util.validate.CheckUtils; +import top.continew.starter.data.mp.util.QueryWrapperHelper; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; + +import java.util.List; + +/** + * 消息业务实现 + * + * @author Bull-BCLS + * @since 2023/10/15 19:05 + */ +@Service +@RequiredArgsConstructor +public class MessageServiceImpl implements MessageService { + + private final MessageMapper baseMapper; + private final MessageUserService messageUserService; + + @Override + @AutoOperate(type = MessageResp.class, on = "list") + public PageResp page(MessageQuery query, PageQuery pageQuery) { + QueryWrapper queryWrapper = QueryWrapperHelper.build(query, pageQuery.getSort()); + queryWrapper.apply(null != query.getUserId(), "t2.user_id={0}", query.getUserId()) + .apply(null != query.getIsRead(), "t2.is_read={0}", query.getIsRead()); + IPage page = baseMapper.selectPageByUserId(new Page<>(pageQuery.getPage(), pageQuery + .getSize()), queryWrapper); + return PageResp.build(page); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void add(MessageReq req, List userIdList) { + CheckUtils.throwIf(() -> CollUtil.isEmpty(userIdList), "消息接收人不能为空"); + MessageDO message = BeanUtil.copyProperties(req, MessageDO.class); + baseMapper.insert(message); + messageUserService.add(message.getId(), userIdList); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(List ids) { + baseMapper.deleteBatchIds(ids); + messageUserService.deleteByMessageIds(ids); + } +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/MessageUserServiceImpl.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/MessageUserServiceImpl.java new file mode 100644 index 0000000..02c9b8a --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/MessageUserServiceImpl.java @@ -0,0 +1,79 @@ +package top.continew.admin.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import top.continew.admin.system.enums.MessageTypeEnum; +import top.continew.admin.system.mapper.MessageUserMapper; +import top.continew.admin.system.model.entity.MessageUserDO; +import top.continew.admin.system.model.resp.MessageTypeUnreadResp; +import top.continew.admin.system.model.resp.MessageUnreadResp; +import top.continew.admin.system.service.MessageUserService; +import top.continew.starter.core.util.validate.CheckUtils; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +/** + * 消息和用户关联业务实现 + * + * @author Bull-BCLS + * @since 2023/10/15 19:05 + */ +@Service +@RequiredArgsConstructor +public class MessageUserServiceImpl implements MessageUserService { + + private final MessageUserMapper baseMapper; + + @Override + public MessageUnreadResp countUnreadMessageByUserId(Long userId, Boolean isDetail) { + MessageUnreadResp result = new MessageUnreadResp(); + Long total = 0L; + if (Boolean.TRUE.equals(isDetail)) { + List detailList = new ArrayList<>(); + for (MessageTypeEnum messageType : MessageTypeEnum.values()) { + MessageTypeUnreadResp resp = new MessageTypeUnreadResp(); + resp.setType(messageType); + Long count = baseMapper.selectUnreadCountByUserIdAndType(userId, messageType.getValue()); + resp.setCount(count); + detailList.add(resp); + total += count; + } + result.setDetails(detailList); + } else { + total = baseMapper.selectUnreadCountByUserIdAndType(userId, null); + } + result.setTotal(total); + return result; + } + + @Override + public void add(Long messageId, List userIdList) { + CheckUtils.throwIfEmpty(userIdList, "消息接收人不能为空"); + List messageUserList = userIdList.stream().map(userId -> { + MessageUserDO messageUser = new MessageUserDO(); + messageUser.setUserId(userId); + messageUser.setMessageId(messageId); + messageUser.setIsRead(false); + return messageUser; + }).toList(); + baseMapper.insertBatch(messageUserList); + } + + @Override + public void readMessage(List ids) { + baseMapper.lambdaUpdate() + .set(MessageUserDO::getIsRead, true) + .set(MessageUserDO::getReadTime, LocalDateTime.now()) + .eq(MessageUserDO::getIsRead, false) + .in(CollUtil.isNotEmpty(ids), MessageUserDO::getMessageId, ids) + .update(); + } + + @Override + public void deleteByMessageIds(List messageIds) { + baseMapper.lambdaUpdate().in(MessageUserDO::getMessageId, messageIds).remove(); + } +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/NoticeServiceImpl.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/NoticeServiceImpl.java new file mode 100644 index 0000000..4230018 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/NoticeServiceImpl.java @@ -0,0 +1,31 @@ +package top.continew.admin.system.service.impl; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import top.continew.admin.system.mapper.NoticeMapper; +import top.continew.admin.system.model.entity.NoticeDO; +import top.continew.admin.system.model.query.NoticeQuery; +import top.continew.admin.system.model.req.NoticeReq; +import top.continew.admin.system.model.resp.DashboardNoticeResp; +import top.continew.admin.system.model.resp.NoticeDetailResp; +import top.continew.admin.system.model.resp.NoticeResp; +import top.continew.admin.system.service.NoticeService; +import top.continew.starter.extension.crud.service.impl.BaseServiceImpl; + +import java.util.List; + +/** + * 公告业务实现 + * + * @author Charles7c + * @since 2023/8/20 10:55 + */ +@Service +@RequiredArgsConstructor +public class NoticeServiceImpl extends BaseServiceImpl implements NoticeService { + + @Override + public List listDashboard() { + return baseMapper.selectDashboardList(); + } +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/OptionServiceImpl.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/OptionServiceImpl.java new file mode 100644 index 0000000..d79a97c --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/OptionServiceImpl.java @@ -0,0 +1,112 @@ +package top.continew.admin.system.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.alicp.jetcache.anno.Cached; +import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import top.continew.admin.common.constant.CacheConstants; +import top.continew.admin.system.enums.PasswordPolicyEnum; +import top.continew.admin.system.mapper.OptionMapper; +import top.continew.admin.system.model.entity.OptionDO; +import top.continew.admin.system.model.query.OptionQuery; +import top.continew.admin.system.model.req.OptionReq; +import top.continew.admin.system.model.req.OptionResetValueReq; +import top.continew.admin.system.model.resp.OptionResp; +import top.continew.admin.system.service.OptionService; +import top.continew.starter.cache.redisson.util.RedisUtils; +import top.continew.starter.core.constant.StringConstants; +import top.continew.starter.core.util.validate.CheckUtils; +import top.continew.starter.core.util.validate.ValidationUtils; +import top.continew.starter.data.mp.util.QueryWrapperHelper; + +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 参数业务实现 + * + * @author Bull-BCLS + * @since 2023/8/26 19:38 + */ +@Service +@RequiredArgsConstructor +public class OptionServiceImpl implements OptionService { + + private final OptionMapper baseMapper; + + @Override + public List list(OptionQuery query) { + return BeanUtil.copyToList(baseMapper.selectList(QueryWrapperHelper.build(query)), OptionResp.class); + } + + @Override + @Cached(key = "#category", name = CacheConstants.OPTION_KEY_PREFIX + "MAP:") + public Map getByCategory(String category) { + return baseMapper.selectByCategory(category) + .stream() + .collect(Collectors.toMap(OptionDO::getCode, o -> StrUtil.emptyIfNull(ObjectUtil.defaultIfNull(o + .getValue(), o.getDefaultValue())), (oldVal, newVal) -> oldVal)); + } + + @Override + public void update(List options) { + Map passwordPolicyOptionMap = options.stream() + .filter(option -> StrUtil.startWith(option + .getCode(), PasswordPolicyEnum.CATEGORY + StringConstants.UNDERLINE)) + .collect(Collectors.toMap(OptionReq::getCode, OptionReq::getValue, (oldVal, newVal) -> oldVal)); + // 校验密码策略参数取值范围 + for (Map.Entry passwordPolicyOptionEntry : passwordPolicyOptionMap.entrySet()) { + String code = passwordPolicyOptionEntry.getKey(); + String value = passwordPolicyOptionEntry.getValue(); + ValidationUtils.throwIf(!NumberUtil.isNumber(value), "参数 [%s] 的值必须为数字", code); + PasswordPolicyEnum passwordPolicy = PasswordPolicyEnum.valueOf(code); + passwordPolicy.validateRange(Integer.parseInt(value), passwordPolicyOptionMap); + } + RedisUtils.deleteByPattern(CacheConstants.OPTION_KEY_PREFIX + StringConstants.ASTERISK); + baseMapper.updateBatchById(BeanUtil.copyToList(options, OptionDO.class)); + } + + @Override + public void resetValue(OptionResetValueReq req) { + RedisUtils.deleteByPattern(CacheConstants.OPTION_KEY_PREFIX + StringConstants.ASTERISK); + String category = req.getCategory(); + List codeList = req.getCode(); + ValidationUtils.throwIf(StrUtil.isBlank(category) && CollUtil.isEmpty(codeList), "键列表不能为空"); + LambdaUpdateChainWrapper chainWrapper = baseMapper.lambdaUpdate().set(OptionDO::getValue, null); + if (StrUtil.isNotBlank(category)) { + chainWrapper.eq(OptionDO::getCategory, category); + } else { + chainWrapper.in(OptionDO::getCode, req.getCode()); + } + chainWrapper.update(); + } + + @Override + public int getValueByCode2Int(String code) { + return this.getValueByCode(code, Integer::parseInt); + } + + @Override + public T getValueByCode(String code, Function mapper) { + String value = RedisUtils.get(CacheConstants.OPTION_KEY_PREFIX + code); + if (StrUtil.isNotBlank(value)) { + return mapper.apply(value); + } + OptionDO option = baseMapper.lambdaQuery() + .eq(OptionDO::getCode, code) + .select(OptionDO::getValue, OptionDO::getDefaultValue) + .one(); + CheckUtils.throwIfNull(option, "参数 [{}] 不存在", code); + value = StrUtil.nullToDefault(option.getValue(), option.getDefaultValue()); + CheckUtils.throwIfBlank(value, "参数 [{}] 数据错误", code); + RedisUtils.set(CacheConstants.OPTION_KEY_PREFIX + code, value); + return mapper.apply(value); + } +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/RoleDeptServiceImpl.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/RoleDeptServiceImpl.java new file mode 100644 index 0000000..853f7f1 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/RoleDeptServiceImpl.java @@ -0,0 +1,62 @@ +package top.continew.admin.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import top.continew.admin.system.mapper.RoleDeptMapper; +import top.continew.admin.system.model.entity.RoleDeptDO; +import top.continew.admin.system.service.RoleDeptService; + +import java.util.List; + +/** + * 角色和部门业务实现 + * + * @author Charles7c + * @since 2023/2/19 10:47 + */ +@Service +@RequiredArgsConstructor +public class RoleDeptServiceImpl implements RoleDeptService { + + private final RoleDeptMapper baseMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean add(List deptIds, Long roleId) { + // 检查是否有变更 + List oldDeptIdList = baseMapper.lambdaQuery() + .select(RoleDeptDO::getDeptId) + .eq(RoleDeptDO::getRoleId, roleId) + .list() + .stream() + .map(RoleDeptDO::getDeptId) + .toList(); + if (CollUtil.isEmpty(CollUtil.disjunction(deptIds, oldDeptIdList))) { + return false; + } + // 删除原有关联 + baseMapper.lambdaUpdate().eq(RoleDeptDO::getRoleId, roleId).remove(); + // 保存最新关联 + List roleDeptList = deptIds.stream().map(deptId -> new RoleDeptDO(roleId, deptId)).toList(); + return baseMapper.insertBatch(roleDeptList); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteByRoleIds(List roleIds) { + baseMapper.lambdaUpdate().in(RoleDeptDO::getRoleId, roleIds).remove(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteByDeptIds(List deptIds) { + baseMapper.lambdaUpdate().in(RoleDeptDO::getDeptId, deptIds).remove(); + } + + @Override + public List listDeptIdByRoleId(Long roleId) { + return baseMapper.selectDeptIdByRoleId(roleId); + } +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/RoleMenuServiceImpl.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/RoleMenuServiceImpl.java new file mode 100644 index 0000000..9c239df --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/RoleMenuServiceImpl.java @@ -0,0 +1,61 @@ +package top.continew.admin.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import top.continew.admin.system.mapper.RoleMenuMapper; +import top.continew.admin.system.model.entity.RoleMenuDO; +import top.continew.admin.system.service.RoleMenuService; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 角色和菜单业务实现 + * + * @author Charles7c + * @since 2023/2/19 10:43 + */ +@Service +@RequiredArgsConstructor +public class RoleMenuServiceImpl implements RoleMenuService { + + private final RoleMenuMapper baseMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean add(List menuIds, Long roleId) { + // 检查是否有变更 + List oldMenuIdList = baseMapper.lambdaQuery() + .select(RoleMenuDO::getMenuId) + .eq(RoleMenuDO::getRoleId, roleId) + .list() + .stream() + .map(RoleMenuDO::getMenuId) + .collect(Collectors.toList()); + if (CollUtil.isEmpty(CollUtil.disjunction(menuIds, oldMenuIdList))) { + return false; + } + // 删除原有关联 + baseMapper.lambdaUpdate().eq(RoleMenuDO::getRoleId, roleId).remove(); + // 保存最新关联 + List roleMenuList = menuIds.stream().map(menuId -> new RoleMenuDO(roleId, menuId)).toList(); + return baseMapper.insertBatch(roleMenuList); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteByRoleIds(List roleIds) { + baseMapper.lambdaUpdate().in(RoleMenuDO::getRoleId, roleIds).remove(); + } + + @Override + public List listMenuIdByRoleIds(List roleIds) { + if (CollUtil.isEmpty(roleIds)) { + return new ArrayList<>(0); + } + return baseMapper.selectMenuIdByRoleIds(roleIds); + } +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/RoleServiceImpl.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/RoleServiceImpl.java new file mode 100644 index 0000000..689688d --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/RoleServiceImpl.java @@ -0,0 +1,212 @@ +package top.continew.admin.system.service.impl; + +import cn.crane4j.annotation.ContainerMethod; +import cn.crane4j.annotation.MappingType; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import com.alicp.jetcache.anno.CacheInvalidate; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import top.continew.admin.auth.model.query.OnlineUserQuery; +import top.continew.admin.auth.service.OnlineUserService; +import top.continew.admin.common.constant.CacheConstants; +import top.continew.admin.common.constant.ContainerConstants; +import top.continew.admin.common.constant.SysConstants; +import top.continew.admin.common.enums.DataScopeEnum; +import top.continew.admin.common.model.dto.LoginUser; +import top.continew.admin.common.model.dto.RoleDTO; +import top.continew.admin.common.util.helper.LoginHelper; +import top.continew.admin.system.mapper.RoleMapper; +import top.continew.admin.system.model.entity.RoleDO; +import top.continew.admin.system.model.query.RoleQuery; +import top.continew.admin.system.model.req.RoleReq; +import top.continew.admin.system.model.resp.MenuResp; +import top.continew.admin.system.model.resp.RoleDetailResp; +import top.continew.admin.system.model.resp.RoleResp; +import top.continew.admin.system.service.*; +import top.continew.starter.core.util.validate.CheckUtils; +import top.continew.starter.extension.crud.service.impl.BaseServiceImpl; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 角色业务实现 + * + * @author Charles7c + * @since 2023/2/8 23:17 + */ +@Service +@RequiredArgsConstructor +public class RoleServiceImpl extends BaseServiceImpl implements RoleService { + + private final MenuService menuService; + private final RoleMenuService roleMenuService; + private final RoleDeptService roleDeptService; + private final UserRoleService userRoleService; + private final OnlineUserService onlineUserService; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long add(RoleReq req) { + String name = req.getName(); + CheckUtils.throwIf(this.isNameExists(name, null), "新增失败,[{}] 已存在", name); + String code = req.getCode(); + CheckUtils.throwIf(this.isCodeExists(code, null), "新增失败,[{}] 已存在", code); + // 新增信息 + Long roleId = super.add(req); + // 保存角色和菜单关联 + roleMenuService.add(req.getMenuIds(), roleId); + // 保存角色和部门关联 + roleDeptService.add(req.getDeptIds(), roleId); + return roleId; + } + + @Override + @Transactional(rollbackFor = Exception.class) + @CacheInvalidate(key = "#req.code == 'admin' ? 'ALL' : #req.code", name = CacheConstants.MENU_KEY_PREFIX) + public void update(RoleReq req, Long id) { + String name = req.getName(); + CheckUtils.throwIf(this.isNameExists(name, id), "修改失败,[{}] 已存在", name); + RoleDO oldRole = super.getById(id); + CheckUtils.throwIfNotEqual(req.getCode(), oldRole.getCode(), "角色编码不允许修改", oldRole.getName()); + DataScopeEnum oldDataScope = oldRole.getDataScope(); + if (Boolean.TRUE.equals(oldRole.getIsSystem())) { + CheckUtils.throwIfNotEqual(req.getDataScope(), oldDataScope, "[{}] 是系统内置角色,不允许修改角色数据权限", oldRole.getName()); + } + // 更新信息 + super.update(req, id); + if (SysConstants.ADMIN_ROLE_CODE.equals(req.getCode())) { + return; + } + // 保存角色和菜单关联 + boolean isSaveMenuSuccess = roleMenuService.add(req.getMenuIds(), id); + // 保存角色和部门关联 + boolean isSaveDeptSuccess = roleDeptService.add(req.getDeptIds(), id); + // 如果功能权限或数据权限有变更,则更新在线用户权限信息 + if (ObjectUtil.notEqual(req.getDataScope(), oldDataScope) || isSaveMenuSuccess || isSaveDeptSuccess) { + OnlineUserQuery query = new OnlineUserQuery(); + query.setRoleId(id); + List loginUserList = onlineUserService.list(query); + loginUserList.parallelStream().forEach(loginUser -> { + loginUser.setRoles(this.listByUserId(loginUser.getId())); + loginUser.setPermissions(this.listPermissionByUserId(loginUser.getId())); + LoginHelper.updateLoginUser(loginUser, loginUser.getToken()); + }); + } + } + + @Override + protected void beforeDelete(List ids) { + List list = baseMapper.lambdaQuery() + .select(RoleDO::getName, RoleDO::getIsSystem) + .in(RoleDO::getId, ids) + .list(); + Optional isSystemData = list.stream().filter(RoleDO::getIsSystem).findFirst(); + CheckUtils.throwIf(isSystemData::isPresent, "所选角色 [{}] 是系统内置角色,不允许删除", isSystemData.orElseGet(RoleDO::new) + .getName()); + CheckUtils.throwIf(userRoleService.isRoleIdExists(ids), "所选角色存在用户关联,请解除关联后重试"); + // 删除角色和菜单关联 + roleMenuService.deleteByRoleIds(ids); + // 删除角色和部门关联 + roleDeptService.deleteByRoleIds(ids); + } + + @Override + protected void fill(Object obj) { + super.fill(obj); + if (obj instanceof RoleDetailResp detail) { + Long roleId = detail.getId(); + if (SysConstants.ADMIN_ROLE_CODE.equals(detail.getCode())) { + List list = menuService.listAll(); + List menuIds = list.stream().map(MenuResp::getId).toList(); + detail.setMenuIds(menuIds); + } else { + detail.setMenuIds(roleMenuService.listMenuIdByRoleIds(CollUtil.newArrayList(roleId))); + } + } + } + + @Override + public Set listPermissionByUserId(Long userId) { + Set roleCodeSet = this.listCodeByUserId(userId); + if (roleCodeSet == null || roleCodeSet.isEmpty()) + return null; + // 超级管理员赋予全部权限 + if (roleCodeSet.contains(SysConstants.ADMIN_ROLE_CODE)) { + return CollUtil.newHashSet(SysConstants.ALL_PERMISSION); + } + return menuService.listPermissionByUserId(userId); + } + + @Override + @ContainerMethod(namespace = ContainerConstants.USER_ROLE_NAME_LIST, type = MappingType.ORDER_OF_KEYS) + public List listNameByIds(List ids) { + List roleList = baseMapper.lambdaQuery().select(RoleDO::getName).in(RoleDO::getId, ids).list(); + return roleList.stream().map(RoleDO::getName).toList(); + } + + @Override + public Set listCodeByUserId(Long userId) { + List roleIdList = userRoleService.listRoleIdByUserId(userId); + if (roleIdList == null || roleIdList.isEmpty()) + return null; + List roleList = baseMapper.lambdaQuery().select(RoleDO::getCode).in(RoleDO::getId, roleIdList).list(); + return roleList.stream().map(RoleDO::getCode).collect(Collectors.toSet()); + } + + @Override + public Set listByUserId(Long userId) { + List roleIdList = userRoleService.listRoleIdByUserId(userId); + if (roleIdList == null || roleIdList.isEmpty()) + return null; + List roleList = baseMapper.lambdaQuery().in(RoleDO::getId, roleIdList).list(); + return new HashSet<>(BeanUtil.copyToList(roleList, RoleDTO.class)); + } + + @Override + public RoleDO getByCode(String code) { + return baseMapper.lambdaQuery().eq(RoleDO::getCode, code).one(); + } + + @Override + public List listByNames(List list) { + if (CollUtil.isEmpty(list)) { + return Collections.emptyList(); + } + return this.list(Wrappers.lambdaQuery().in(RoleDO::getName, list)); + } + + @Override + public int countByNames(List roleNames) { + if (CollUtil.isEmpty(roleNames)) { + return 0; + } + return (int)this.count(Wrappers.lambdaQuery().in(RoleDO::getName, roleNames)); + } + + /** + * 名称是否存在 + * + * @param name 名称 + * @param id ID + * @return 是否存在 + */ + private boolean isNameExists(String name, Long id) { + return baseMapper.lambdaQuery().eq(RoleDO::getName, name).ne(null != id, RoleDO::getId, id).exists(); + } + + /** + * 编码是否存在 + * + * @param code 编码 + * @param id ID + * @return 是否存在 + */ + private boolean isCodeExists(String code, Long id) { + return baseMapper.lambdaQuery().eq(RoleDO::getCode, code).ne(null != id, RoleDO::getId, id).exists(); + } +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/StorageServiceImpl.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/StorageServiceImpl.java new file mode 100644 index 0000000..c34e52a --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/StorageServiceImpl.java @@ -0,0 +1,192 @@ +package top.continew.admin.system.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.URLUtil; +import jakarta.annotation.Resource; +import lombok.RequiredArgsConstructor; +import org.dromara.x.file.storage.core.FileStorageProperties; +import org.dromara.x.file.storage.core.FileStorageService; +import org.dromara.x.file.storage.core.FileStorageServiceBuilder; +import org.dromara.x.file.storage.core.platform.FileStorage; +import org.springframework.stereotype.Service; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.admin.common.util.SecureUtils; +import top.continew.admin.system.enums.StorageTypeEnum; +import top.continew.admin.system.mapper.StorageMapper; +import top.continew.admin.system.model.entity.StorageDO; +import top.continew.admin.system.model.query.StorageQuery; +import top.continew.admin.system.model.req.StorageReq; +import top.continew.admin.system.model.resp.StorageResp; +import top.continew.admin.system.service.FileService; +import top.continew.admin.system.service.StorageService; +import top.continew.admin.system.util.ValidateGroup; +import top.continew.starter.core.constant.StringConstants; +import top.continew.starter.core.util.ExceptionUtils; +import top.continew.starter.core.util.URLUtils; +import top.continew.starter.core.util.validate.CheckUtils; +import top.continew.starter.core.util.validate.ValidationUtils; +import top.continew.starter.extension.crud.service.impl.BaseServiceImpl; +import top.continew.starter.web.util.SpringWebUtils; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * 存储业务实现 + * + * @author Charles7c + * @since 2023/12/26 22:09 + */ +@Service +@RequiredArgsConstructor +public class StorageServiceImpl extends BaseServiceImpl implements StorageService { + + private final FileStorageService fileStorageService; + @Resource + private FileService fileService; + + @Override + protected void beforeAdd(StorageReq req) { + this.decodeSecretKey(req, null); + CheckUtils.throwIf(Boolean.TRUE.equals(req.getIsDefault()) && this.isDefaultExists(null), "请先取消原有默认存储"); + String code = req.getCode(); + CheckUtils.throwIf(this.isCodeExists(code, null), "新增失败,[{}] 已存在", code); + this.load(req); + } + + @Override + protected void beforeUpdate(StorageReq req, Long id) { + StorageDO oldStorage = super.getById(id); + CheckUtils.throwIfNotEqual(req.getCode(), oldStorage.getCode(), "不允许修改存储编码"); + CheckUtils.throwIfNotEqual(req.getType(), oldStorage.getType(), "不允许修改存储类型"); + DisEnableStatusEnum newStatus = req.getStatus(); + CheckUtils.throwIf(Boolean.TRUE.equals(oldStorage.getIsDefault()) && DisEnableStatusEnum.DISABLE + .equals(newStatus), "[{}] 是默认存储,不允许禁用", oldStorage.getName()); + this.decodeSecretKey(req, oldStorage); + DisEnableStatusEnum oldStatus = oldStorage.getStatus(); + // 先卸载 + if (DisEnableStatusEnum.ENABLE.equals(oldStatus)) { + this.unload(BeanUtil.copyProperties(oldStorage, StorageReq.class)); + } + // 再加载 + if (DisEnableStatusEnum.ENABLE.equals(newStatus)) { + this.load(req); + } + if (Boolean.TRUE.equals(req.getIsDefault())) { + CheckUtils.throwIf(!DisEnableStatusEnum.ENABLE.equals(oldStatus), "请先启用该存储"); + CheckUtils.throwIf(this.isDefaultExists(id), "请先取消原有默认存储"); + } + } + + @Override + protected void beforeDelete(List ids) { + CheckUtils.throwIf(fileService.countByStorageIds(ids) > 0, "所选存储存在文件关联,请删除文件后重试"); + List storageList = baseMapper.lambdaQuery().in(StorageDO::getId, ids).list(); + storageList.forEach(s -> { + CheckUtils.throwIfEqual(Boolean.TRUE, s.getIsDefault(), "[{}] 是默认存储,不允许禁用", s.getName()); + // 卸载启用状态的存储 + if (DisEnableStatusEnum.ENABLE.equals(s.getStatus())) { + this.unload(BeanUtil.copyProperties(s, StorageReq.class)); + } + }); + } + + @Override + public StorageDO getDefaultStorage() { + return baseMapper.lambdaQuery().eq(StorageDO::getIsDefault, true).one(); + } + + @Override + public StorageDO getByCode(String code) { + return baseMapper.lambdaQuery().eq(StorageDO::getCode, code).one(); + } + + @Override + public void load(StorageReq req) { + CopyOnWriteArrayList fileStorageList = fileStorageService.getFileStorageList(); + String domain = req.getDomain(); + ValidationUtils.throwIf(!URLUtils.isHttpUrl(domain), "域名格式错误"); + String bucketName = req.getBucketName(); + StorageTypeEnum type = req.getType(); + if (StorageTypeEnum.LOCAL.equals(type)) { + ValidationUtils.validate(req, ValidateGroup.Storage.Local.class); + req.setBucketName(StrUtil.appendIfMissing(bucketName + .replace(StringConstants.BACKSLASH, StringConstants.SLASH), StringConstants.SLASH)); + FileStorageProperties.LocalPlusConfig config = new FileStorageProperties.LocalPlusConfig(); + config.setPlatform(req.getCode()); + config.setStoragePath(bucketName); + fileStorageList.addAll(FileStorageServiceBuilder.buildLocalPlusFileStorage(Collections + .singletonList(config))); + SpringWebUtils.registerResourceHandler(MapUtil.of(URLUtil.url(req.getDomain()).getPath(), bucketName)); + } else if (StorageTypeEnum.S3.equals(type)) { + ValidationUtils.validate(req, ValidateGroup.Storage.S3.class); + FileStorageProperties.AmazonS3Config config = new FileStorageProperties.AmazonS3Config(); + config.setPlatform(req.getCode()); + config.setAccessKey(req.getAccessKey()); + config.setSecretKey(req.getSecretKey()); + config.setEndPoint(req.getEndpoint()); + config.setBucketName(bucketName); + config.setDomain(domain); + fileStorageList.addAll(FileStorageServiceBuilder.buildAmazonS3FileStorage(Collections + .singletonList(config), null)); + } + } + + @Override + public void unload(StorageReq req) { + CopyOnWriteArrayList fileStorageList = fileStorageService.getFileStorageList(); + FileStorage fileStorage = fileStorageService.getFileStorage(req.getCode()); + fileStorageList.remove(fileStorage); + fileStorage.close(); + SpringWebUtils.deRegisterResourceHandler(MapUtil.of(URLUtil.url(req.getDomain()).getPath(), req + .getBucketName())); + } + + /** + * 解密 SecretKey + * + * @param req 请求参数 + * @param storage 存储信息 + */ + private void decodeSecretKey(StorageReq req, StorageDO storage) { + if (!StorageTypeEnum.S3.equals(req.getType())) { + return; + } + // 修改时,如果 SecretKey 不修改,需要手动修正 + String newSecretKey = req.getSecretKey(); + boolean isSecretKeyNotUpdate = StrUtil.isBlank(newSecretKey) || newSecretKey.contains(StringConstants.ASTERISK); + if (null != storage && isSecretKeyNotUpdate) { + req.setSecretKey(storage.getSecretKey()); + return; + } + // 新增时或修改了 SecretKey + String secretKey = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(newSecretKey)); + ValidationUtils.throwIfNull(secretKey, "私有密钥解密失败"); + ValidationUtils.throwIf(secretKey.length() > 255, "私有密钥长度不能超过 255 个字符"); + req.setSecretKey(secretKey); + } + + /** + * 默认存储是否存在 + * + * @param id ID + * @return 是否存在 + */ + private boolean isDefaultExists(Long id) { + return baseMapper.lambdaQuery().eq(StorageDO::getIsDefault, true).ne(null != id, StorageDO::getId, id).exists(); + } + + /** + * 编码是否存在 + * + * @param code 编码 + * @param id ID + * @return 是否存在 + */ + private boolean isCodeExists(String code, Long id) { + return baseMapper.lambdaQuery().eq(StorageDO::getCode, code).ne(null != id, StorageDO::getId, id).exists(); + } +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/UserPasswordHistoryServiceImpl.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/UserPasswordHistoryServiceImpl.java new file mode 100644 index 0000000..7e32087 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/UserPasswordHistoryServiceImpl.java @@ -0,0 +1,59 @@ +package top.continew.admin.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import top.continew.admin.system.mapper.UserPasswordHistoryMapper; +import top.continew.admin.system.model.entity.UserPasswordHistoryDO; +import top.continew.admin.system.service.UserPasswordHistoryService; + +import java.util.List; + +/** + * 用户历史密码业务实现 + * + * @author Charles7c + * @since 2024/5/16 21:58 + */ +@Service +@RequiredArgsConstructor +public class UserPasswordHistoryServiceImpl implements UserPasswordHistoryService { + + private final UserPasswordHistoryMapper baseMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public void add(Long userId, String password, int count) { + if (StrUtil.isBlank(password)) { + return; + } + baseMapper.insert(new UserPasswordHistoryDO(userId, password)); + // 删除过期历史密码 + baseMapper.deleteExpired(userId, count); + } + + @Override + public void deleteByUserIds(List userIds) { + baseMapper.lambdaUpdate().in(UserPasswordHistoryDO::getUserId, userIds).remove(); + } + + @Override + public boolean isPasswordReused(Long userId, String password, int count) { + // 查询近 N 个历史密码 + List list = baseMapper.lambdaQuery() + .select(UserPasswordHistoryDO::getPassword) + .eq(UserPasswordHistoryDO::getUserId, userId) + .orderByDesc(UserPasswordHistoryDO::getCreateTime) + .last("LIMIT %s".formatted(count)) + .list(); + if (CollUtil.isEmpty(list)) { + return false; + } + // 校验是否重复使用历史密码 + List passwordList = list.stream().map(UserPasswordHistoryDO::getPassword).toList(); + return passwordList.stream().anyMatch(p -> ObjectUtil.equal(password, p)); + } +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/UserRoleServiceImpl.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/UserRoleServiceImpl.java new file mode 100644 index 0000000..3e045ca --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/UserRoleServiceImpl.java @@ -0,0 +1,69 @@ +package top.continew.admin.system.service.impl; + +import cn.crane4j.annotation.ContainerMethod; +import cn.crane4j.annotation.MappingType; +import cn.hutool.core.collection.CollUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import top.continew.admin.common.constant.ContainerConstants; +import top.continew.admin.system.mapper.UserRoleMapper; +import top.continew.admin.system.model.entity.UserRoleDO; +import top.continew.admin.system.service.UserRoleService; + +import java.util.List; + +/** + * 用户和角色业务实现 + * + * @author Charles7c + * @since 2023/2/20 21:30 + */ +@Service +@RequiredArgsConstructor +public class UserRoleServiceImpl implements UserRoleService { + + private final UserRoleMapper baseMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean add(List roleIds, Long userId) { + // 检查是否有变更 + List oldRoleIdList = baseMapper.lambdaQuery() + .select(UserRoleDO::getRoleId) + .eq(UserRoleDO::getUserId, userId) + .list() + .stream() + .map(UserRoleDO::getRoleId) + .toList(); + if (CollUtil.isEmpty(CollUtil.disjunction(roleIds, oldRoleIdList))) { + return false; + } + // 删除原有关联 + baseMapper.lambdaUpdate().eq(UserRoleDO::getUserId, userId).remove(); + // 保存最新关联 + List userRoleList = roleIds.stream().map(roleId -> new UserRoleDO(userId, roleId)).toList(); + return baseMapper.insertBatch(userRoleList); + } + + @Override + public void deleteByUserIds(List userIds) { + baseMapper.lambdaUpdate().in(UserRoleDO::getUserId, userIds).remove(); + } + + @Override + public void saveBatch(List list) { + baseMapper.insertBatch(list); + } + + @Override + @ContainerMethod(namespace = ContainerConstants.USER_ROLE_ID_LIST, type = MappingType.ORDER_OF_KEYS) + public List listRoleIdByUserId(Long userId) { + return baseMapper.selectRoleIdByUserId(userId); + } + + @Override + public boolean isRoleIdExists(List roleIds) { + return baseMapper.lambdaQuery().in(UserRoleDO::getRoleId, roleIds).exists(); + } +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/UserServiceImpl.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..ebb5b43 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/UserServiceImpl.java @@ -0,0 +1,720 @@ +package top.continew.admin.system.service.impl; + +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.img.ImgUtil; +import cn.hutool.core.io.file.FileNameUtil; +import cn.hutool.core.io.resource.ResourceUtil; +import cn.hutool.core.lang.UUID; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.CharsetUtil; +import cn.hutool.core.util.EnumUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.validation.ValidationUtil; +import cn.hutool.http.ContentType; +import cn.hutool.json.JSONUtil; +import com.alibaba.excel.EasyExcel; +import com.alicp.jetcache.anno.CacheInvalidate; +import com.alicp.jetcache.anno.CacheType; +import com.alicp.jetcache.anno.CacheUpdate; +import com.alicp.jetcache.anno.Cached; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.core.toolkit.support.SFunction; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import me.ahoo.cosid.IdGenerator; +import me.ahoo.cosid.provider.DefaultIdGeneratorProvider; +import net.dreamlu.mica.core.result.R; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; +import top.continew.admin.auth.model.query.OnlineUserQuery; +import top.continew.admin.auth.service.OnlineUserService; +import top.continew.admin.common.constant.CacheConstants; +import top.continew.admin.common.constant.SysConstants; +import top.continew.admin.common.enums.DisEnableStatusEnum; +import top.continew.admin.common.enums.GenderEnum; +import top.continew.admin.common.model.dto.LoginUser; +import top.continew.admin.common.util.AiBotUtil; +import top.continew.admin.common.util.SecureUtils; +import top.continew.admin.common.util.helper.LoginHelper; +import top.continew.admin.system.mapper.UserMapper; +import top.continew.admin.system.model.entity.DeptDO; +import top.continew.admin.system.model.entity.RoleDO; +import top.continew.admin.system.model.entity.UserDO; +import top.continew.admin.system.model.entity.UserRoleDO; +import top.continew.admin.system.model.query.UserBalanceQuery; +import top.continew.admin.system.model.query.UserQuery; +import top.continew.admin.system.model.req.*; +import top.continew.admin.system.model.resp.*; +import top.continew.admin.system.service.*; +import top.continew.starter.cache.redisson.util.RedisUtils; +import top.continew.starter.core.constant.StringConstants; +import top.continew.starter.core.exception.BusinessException; +import top.continew.starter.core.util.validate.CheckUtils; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.query.SortQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.extension.crud.service.CommonUserService; +import top.continew.starter.extension.crud.service.impl.BaseServiceImpl; +import top.continew.starter.web.util.FileUploadUtils; + +import java.io.IOException; +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static top.continew.admin.system.enums.ImportPolicyEnum.*; +import static top.continew.admin.system.enums.PasswordPolicyEnum.*; + +/** + * 用户业务实现 + * + * @author Charles7c + * @since 2022/12/21 21:49 + */ +@Service +@RequiredArgsConstructor +public class UserServiceImpl extends BaseServiceImpl implements UserService, CommonUserService { + private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class); + private final UserPasswordHistoryService userPasswordHistoryService; + private final OnlineUserService onlineUserService; + private final OptionService optionService; + private final UserRoleService userRoleService; + private final RoleService roleService; + + @Resource + private DeptService deptService; + @Value("${avatar.support-suffix}") + private String[] avatarSupportSuffix; + + @Override + public PageResp page(UserQuery query, PageQuery pageQuery) { + QueryWrapper queryWrapper = this.buildQueryWrapper(query); + super.sort(queryWrapper, pageQuery); + IPage page = baseMapper.selectUserPage(new Page<>(pageQuery.getPage(), pageQuery + .getSize()), queryWrapper); + PageResp pageResp = PageResp.build(page, super.getListClass()); + pageResp.getList().forEach(this::fill); + return pageResp; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Long add(UserDO user) { + user.setStatus(DisEnableStatusEnum.ENABLE); + baseMapper.insert(user); + + // 注册AI Bot + AiBotUtil.usersRegister(user.getUsername(), user.getPassword()); + + return user.getId(); + } + + @Override + public void downloadImportUserTemplate(HttpServletResponse response) throws IOException { + try { + FileUploadUtils.download(response, ResourceUtil + .getStream("templates/import/userImportTemplate.xlsx"), "用户导入模板.xlsx"); + } catch (Exception e) { + log.error("下载用户导入模板失败:", e); + response.setCharacterEncoding(CharsetUtil.UTF_8); + response.setContentType(ContentType.JSON.toString()); + response.getWriter().write(JSONUtil.toJsonStr(R.fail("下载用户导入模板失败"))); + } + } + + @Override + public UserImportParseResp parseImportUser(MultipartFile file) { + UserImportParseResp userImportResp = new UserImportParseResp(); + List userRowList; + // 读取表格数据 + try { + userRowList = EasyExcel.read(file.getInputStream()) + .head(UserImportRowReq.class) + .sheet() + .headRowNumber(1) + .doReadSync(); + } catch (Exception e) { + log.error("用户导入数据文件解析异常:", e); + throw new BusinessException("数据文件解析异常"); + } + userImportResp.setTotalRows(userRowList.size()); + if (CollUtil.isEmpty(userRowList)) { + throw new BusinessException("数据文件格式错误"); + } + + // 过滤无效数据 + List validUserRowList = filterErrorUserImportData(userRowList); + userImportResp.setValidRows(validUserRowList.size()); + if (CollUtil.isEmpty(validUserRowList)) { + throw new BusinessException("数据文件格式错误"); + } + + // 检测表格内数据是否合法 + Set seenEmails = new HashSet<>(); + boolean hasDuplicateEmail = validUserRowList.stream() + .map(UserImportRowReq::getEmail) + .anyMatch(email -> email != null && !seenEmails.add(email)); + CheckUtils.throwIf(hasDuplicateEmail, "存在重复邮箱,请检测数据"); + Set seenPhones = new HashSet<>(); + boolean hasDuplicatePhone = validUserRowList.stream() + .map(UserImportRowReq::getPhone) + .anyMatch(phone -> phone != null && !seenPhones.add(phone)); + CheckUtils.throwIf(hasDuplicatePhone, "存在重复手机,请检测数据"); + + // 校验是否存在错误角色 + List roleNames = validUserRowList.stream().map(UserImportRowReq::getRoleName).distinct().toList(); + int existRoleCount = roleService.countByNames(roleNames); + CheckUtils.throwIf(existRoleCount < roleNames.size(), "存在错误角色,请检查数据"); + // 校验是否存在错误部门 + List deptNames = validUserRowList.stream().map(UserImportRowReq::getDeptName).distinct().toList(); + int existDeptCount = deptService.countByNames(deptNames); + CheckUtils.throwIf(existDeptCount < deptNames.size(), "存在错误部门,请检查数据"); + + // 查询重复用户 + userImportResp + .setDuplicateUserRows(countExistByField(validUserRowList, UserImportRowReq::getUsername, UserDO::getUsername, false)); + // 查询重复邮箱 + userImportResp + .setDuplicateEmailRows(countExistByField(validUserRowList, UserImportRowReq::getEmail, UserDO::getEmail, true)); + // 查询重复手机 + userImportResp + .setDuplicatePhoneRows(countExistByField(validUserRowList, UserImportRowReq::getPhone, UserDO::getPhone, true)); + + // 设置导入会话并缓存数据,有效期10分钟 + String importKey = UUID.fastUUID().toString(true); + RedisUtils.set(CacheConstants.DATA_IMPORT_KEY + importKey, JSONUtil.toJsonStr(validUserRowList), Duration + .ofMinutes(10)); + userImportResp.setImportKey(importKey); + + return userImportResp; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public UserImportResp importUser(UserImportReq req) { + // 校验导入会话是否过期 + List importUserList; + try { + String data = RedisUtils.get(CacheConstants.DATA_IMPORT_KEY + req.getImportKey()); + importUserList = JSONUtil.toList(data, UserImportRowReq.class); + CheckUtils.throwIf(CollUtil.isEmpty(importUserList), "导入已过期,请重新上传"); + } catch (Exception e) { + log.error("导入异常:", e); + throw new BusinessException("导入已过期,请重新上传"); + } + // 已存在数据查询 + List existEmails = listExistByField(importUserList, UserImportRowReq::getEmail, UserDO::getEmail); + List existPhones = listExistByField(importUserList, UserImportRowReq::getPhone, UserDO::getPhone); + List existUserList = listByUsernames(importUserList.stream() + .map(UserImportRowReq::getUsername) + .filter(Objects::nonNull) + .toList()); + List existUsernames = existUserList.stream().map(UserDO::getUsername).toList(); + CheckUtils + .throwIf(isExitImportUser(req, importUserList, existUsernames, existEmails, existPhones), "数据不符合导入策略,已退出导入"); + + // 基础数据准备 + Map userMap = existUserList.stream() + .collect(Collectors.toMap(UserDO::getUsername, UserDO::getId)); + List roleList = roleService.listByNames(importUserList.stream() + .map(UserImportRowReq::getRoleName) + .distinct() + .toList()); + Map roleMap = roleList.stream().collect(Collectors.toMap(RoleDO::getName, RoleDO::getId)); + List deptList = deptService.listByNames(importUserList.stream() + .map(UserImportRowReq::getDeptName) + .distinct() + .toList()); + Map deptMap = deptList.stream().collect(Collectors.toMap(DeptDO::getName, DeptDO::getId)); + + // 批量操作数据库集合 + List insertList = new ArrayList<>(); + List updateList = new ArrayList<>(); + List userRoleDOList = new ArrayList<>(); + // ID生成器 + IdGenerator idGenerator = DefaultIdGeneratorProvider.INSTANCE.getShare(); + for (UserImportRowReq row : importUserList) { + if (isSkipUserImport(req, row, existUsernames, existPhones, existEmails)) { + // 按规则跳过该行 + continue; + } + UserDO userDO = BeanUtil.toBeanIgnoreError(row, UserDO.class); + userDO.setStatus(req.getDefaultStatus()); + userDO.setPwdResetTime(LocalDateTime.now()); + userDO.setGender(EnumUtil.getBy(GenderEnum::getDescription, row.getGender(), GenderEnum.UNKNOWN)); + userDO.setDeptId(deptMap.get(row.getDeptName())); + // 修改 or 新增 + if (UPDATE.validate(req.getDuplicateUser(), row.getUsername(), existUsernames)) { + userDO.setId(userMap.get(row.getUsername())); + updateList.add(userDO); + } else { + userDO.setId(idGenerator.generate()); + userDO.setIsSystem(false); + insertList.add(userDO); + } + userRoleDOList.add(new UserRoleDO(userDO.getId(), roleMap.get(row.getRoleName()))); + } + doImportUser(insertList, updateList, userRoleDOList); + RedisUtils.delete(CacheConstants.DATA_IMPORT_KEY + req.getImportKey()); + insertList.stream().forEach(e -> { + // 注册AI Bot + logger.info("开始注册{}", e.getUsername()); + AiBotUtil.usersRegister(e.getUsername(), e.getPassword()); + }); + return new UserImportResp(insertList.size() + updateList.size(), insertList.size(), updateList.size()); + } + + @Transactional(rollbackFor = Exception.class) + public void doImportUser(List insertList, List updateList, List userRoleDOList) { + if (CollUtil.isNotEmpty(insertList)) { + baseMapper.insertBatch(insertList); + } + if (CollUtil.isNotEmpty(updateList)) { + this.updateBatchById(updateList); + userRoleService.deleteByUserIds(updateList.stream().map(UserDO::getId).toList()); + } + if (CollUtil.isNotEmpty(userRoleDOList)) { + userRoleService.saveBatch(userRoleDOList); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + @CacheUpdate(key = "#id", value = "#req.nickname", name = CacheConstants.USER_KEY_PREFIX) + public void update(UserReq req, Long id) { + final String errorMsgTemplate = "修改失败,[{}] 已存在"; + String username = req.getUsername(); + CheckUtils.throwIf(this.isNameExists(username, id), errorMsgTemplate, username); + String email = req.getEmail(); + CheckUtils.throwIf(StrUtil.isNotBlank(email) && this.isEmailExists(email, id), errorMsgTemplate, email); + String phone = req.getPhone(); + CheckUtils.throwIf(StrUtil.isNotBlank(phone) && this.isPhoneExists(phone, id), errorMsgTemplate, phone); + DisEnableStatusEnum newStatus = req.getStatus(); + CheckUtils.throwIf(DisEnableStatusEnum.DISABLE.equals(newStatus) && ObjectUtil.equal(id, LoginHelper + .getUserId()), "不允许禁用当前用户"); + UserDO oldUser = super.getById(id); + if (Boolean.TRUE.equals(oldUser.getIsSystem())) { + CheckUtils.throwIfEqual(DisEnableStatusEnum.DISABLE, newStatus, "[{}] 是系统内置用户,不允许禁用", oldUser + .getNickname()); + Collection disjunctionRoleIds = CollUtil.disjunction(req.getRoleIds(), userRoleService + .listRoleIdByUserId(id)); + CheckUtils.throwIfNotEmpty(disjunctionRoleIds, "[{}] 是系统内置用户,不允许变更角色", oldUser.getNickname()); + } + // 更新信息 + UserDO newUser = BeanUtil.toBean(req, UserDO.class); + newUser.setId(id); + baseMapper.updateById(newUser); + // 保存用户和角色关联 + boolean isSaveUserRoleSuccess = userRoleService.add(req.getRoleIds(), id); + // 如果禁用用户,则踢出在线用户 + if (DisEnableStatusEnum.DISABLE.equals(newStatus)) { + onlineUserService.kickOut(id); + return; + } + // 如果角色有变更,则更新在线用户权限信息 + if (isSaveUserRoleSuccess) { + OnlineUserQuery query = new OnlineUserQuery(); + query.setUserId(id); + List loginUserList = onlineUserService.list(query); + loginUserList.parallelStream().forEach(loginUser -> { + loginUser.setRoles(roleService.listByUserId(loginUser.getId())); + loginUser.setPermissions(roleService.listPermissionByUserId(loginUser.getId())); + LoginHelper.updateLoginUser(loginUser, loginUser.getToken()); + }); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + @CacheInvalidate(key = "#ids", name = CacheConstants.USER_KEY_PREFIX, multi = true) + public void delete(List ids) { + CheckUtils.throwIf(CollUtil.contains(ids, LoginHelper.getUserId()), "不允许删除当前用户"); + List list = baseMapper.lambdaQuery() + .select(UserDO::getNickname, UserDO::getIsSystem) + .in(UserDO::getId, ids) + .list(); + Optional isSystemData = list.stream().filter(UserDO::getIsSystem).findFirst(); + CheckUtils.throwIf(isSystemData::isPresent, "所选用户 [{}] 是系统内置用户,不允许删除", isSystemData.orElseGet(UserDO::new) + .getNickname()); + // 删除用户和角色关联 + userRoleService.deleteByUserIds(ids); + // 删除历史密码 + userPasswordHistoryService.deleteByUserIds(ids); + // 删除用户 + super.delete(ids); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void resetPassword(UserPasswordResetReq req, Long id) { + UserDO userDO = super.getById(id); + + baseMapper.lambdaUpdate() + .set(UserDO::getPassword, req.getNewPassword()) + .set(UserDO::getPwdResetTime, LocalDateTime.now()) + .eq(UserDO::getId, id) + .update(); + + // 修改AiBot平台密码 + AiBotUtil.modifyPassword(userDO.getPassword(), req.getNewPassword()); + } + + @Override + public void updateRole(UserRoleUpdateReq updateReq, Long id) { + super.getById(id); + // 保存用户和角色关联 + userRoleService.add(updateReq.getRoleIds(), id); + } + + @Override + public String updateAvatar(MultipartFile avatarFile, Long id) throws IOException { + String avatarImageType = FileNameUtil.extName(avatarFile.getOriginalFilename()); + CheckUtils.throwIf(!StrUtil.equalsAnyIgnoreCase(avatarImageType, avatarSupportSuffix), "头像仅支持 {} 格式的图片", String + .join(StringConstants.CHINESE_COMMA, avatarSupportSuffix)); + // 更新用户头像 + String base64 = ImgUtil.toBase64DataUri(ImgUtil.scale(ImgUtil.toImage(avatarFile + .getBytes()), 100, 100, null), avatarImageType); + baseMapper.lambdaUpdate().set(UserDO::getAvatar, base64).eq(UserDO::getId, id).update(); + return base64; + } + + @Override + @CacheUpdate(key = "#id", value = "#req.nickname", name = CacheConstants.USER_KEY_PREFIX) + public void updateBasicInfo(UserBasicInfoUpdateReq req, Long id) { + super.getById(id); + baseMapper.lambdaUpdate() + .set(UserDO::getNickname, req.getNickname()) + .set(UserDO::getGender, req.getGender()) + .eq(UserDO::getId, id) + .update(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updatePassword(String oldPassword, String newPassword, Long id) { + CheckUtils.throwIfEqual(newPassword, oldPassword, "新密码不能与当前密码相同"); + UserDO user = super.getById(id); + String password = user.getPassword(); + if (StrUtil.isNotBlank(password)) { + CheckUtils.throwIf(!ObjectUtil.equal(oldPassword, password), "当前密码错误"); + } + // 校验密码合法性 + int passwordRepetitionTimes = this.checkPassword(newPassword, user); + // 更新密码和密码重置时间 + baseMapper.lambdaUpdate() + .set(UserDO::getPassword, newPassword) + .set(UserDO::getPwdResetTime, LocalDateTime.now()) + .eq(UserDO::getId, id) + .update(); + // 保存历史密码 + userPasswordHistoryService.add(id, password, passwordRepetitionTimes); + + // 修改AI BOT密码 + AiBotUtil.modifyPassword(oldPassword, newPassword); + + // 修改后登出 + StpUtil.logout(); + } + + @Override + public void updatePhone(String newPhone, String oldPassword, Long id) { + UserDO user = super.getById(id); + CheckUtils.throwIf(!ObjectUtil.equal(oldPassword, user.getPassword()), "当前密码错误"); + CheckUtils.throwIf(this.isPhoneExists(newPhone, id), "手机号已绑定其他账号,请更换其他手机号"); + CheckUtils.throwIfEqual(newPhone, user.getPhone(), "新手机号不能与当前手机号相同"); + // 更新手机号 + baseMapper.lambdaUpdate().set(UserDO::getPhone, newPhone).eq(UserDO::getId, id).update(); + } + + @Override + public void updateEmail(String newEmail, String oldPassword, Long id) { + UserDO user = super.getById(id); + CheckUtils.throwIf(!ObjectUtil.equal(oldPassword, user.getPassword()), "当前密码错误"); + CheckUtils.throwIf(this.isEmailExists(newEmail, id), "邮箱已绑定其他账号,请更换其他邮箱"); + CheckUtils.throwIfEqual(newEmail, user.getEmail(), "新邮箱不能与当前邮箱相同"); + // 更新邮箱 + baseMapper.lambdaUpdate().set(UserDO::getEmail, newEmail).eq(UserDO::getId, id).update(); + } + + @Override + //@DS("#school") + public UserDO getByUsername(String username) { + return baseMapper.selectByUsername(username); + } + + /** + * 通过用户id、消耗点数查询余额是否充足 + * + * @param query + * @return + */ + @Override + public boolean getUserBalance(UserBalanceQuery query) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(UserDO::getId, query.getId()); + queryWrapper.eq(UserDO::getInviteBeans, query.getInviteBeans()); + UserDO userDO = baseMapper.selectOne(queryWrapper); + return userDO.getSysBeans() - query.getInviteBeans() > 0; + } + + @Override + public UserDO getByPhone(String phone) { + return baseMapper.selectByPhone(phone); + } + + @Override + public UserDO getByEmail(String email) { + return baseMapper.selectByEmail(email); + } + + @Override + public Long countByDeptIds(List deptIds) { + return baseMapper.lambdaQuery().in(UserDO::getDeptId, deptIds).count(); + } + + @Override + @Cached(key = "#id", name = CacheConstants.USER_KEY_PREFIX, cacheType = CacheType.BOTH, syncLocal = true) + public String getNicknameById(Long id) { + return baseMapper.selectNicknameById(id); + } + + @Override + protected List list(UserQuery query, SortQuery sortQuery, Class targetClass) { + QueryWrapper queryWrapper = this.buildQueryWrapper(query); + // 设置排序 + super.sort(queryWrapper, sortQuery); + List entityList = baseMapper.selectUserList(queryWrapper); + if (this.getEntityClass() == targetClass) { + return (List)entityList; + } + return BeanUtil.copyToList(entityList, targetClass); + } + + @Override + protected QueryWrapper buildQueryWrapper(UserQuery query) { + String description = query.getDescription(); + DisEnableStatusEnum status = query.getStatus(); + List createTimeList = query.getCreateTime(); + Long deptId = query.getDeptId(); + List deptIds = query.getDeptIds(); + return new QueryWrapper().and(StrUtil.isNotBlank(description), q -> q.like("t1.username", description) + .or() + .like("t1.nickname", description) + .or() + .like("t1.description", description)) + .eq(null != status, "t1.status", status) + .between(CollUtil.isNotEmpty(createTimeList), "t1.create_time", CollUtil.getFirst(createTimeList), CollUtil + .getLast(createTimeList)) + .and(null != deptId && !SysConstants.SUPER_DEPT_ID.equals(deptId), q -> { + List deptIdList = deptService.listChildren(deptId) + .stream() + .map(DeptDO::getId) + .collect(Collectors.toList()); + deptIdList.add(deptId); + q.in("t1.dept_id", deptIdList); + }) + .in(CollUtil.isNotEmpty(deptIds), "t1.dept_id", deptIds); + } + + @Override + protected void beforeAdd(UserReq req) { + final String errorMsgTemplate = "新增失败,[{}] 已存在"; + String username = req.getUsername(); + CheckUtils.throwIf(this.isNameExists(username, null), errorMsgTemplate, username); + String email = req.getEmail(); + CheckUtils.throwIf(StrUtil.isNotBlank(email) && this.isEmailExists(email, null), errorMsgTemplate, email); + String phone = req.getPhone(); + CheckUtils.throwIf(StrUtil.isNotBlank(phone) && this.isPhoneExists(phone, null), errorMsgTemplate, phone); + } + + @Override + protected void afterAdd(UserReq req, UserDO user) { + Long userId = user.getId(); + baseMapper.lambdaUpdate().set(UserDO::getPwdResetTime, LocalDateTime.now()).eq(UserDO::getId, userId).update(); + // 保存用户和角色关联 + userRoleService.add(req.getRoleIds(), userId); + + // 注册AI Bot + AiBotUtil.usersRegister(user.getUsername(), user.getPassword()); + } + + /** + * 判断是否跳过导入 + * + * @param req 导入参数 + * @param row 导入数据 + * @param existUsernames 导入数据中已存在的用户名 + * @param existEmails 导入数据中已存在的邮箱 + * @param existPhones 导入数据中已存在的手机号 + * @return 是否跳过 + */ + private boolean isSkipUserImport(UserImportReq req, + UserImportRowReq row, + List existUsernames, + List existPhones, + List existEmails) { + return SKIP.validate(req.getDuplicateUser(), row.getUsername(), existUsernames) || SKIP.validate(req + .getDuplicateEmail(), row.getEmail(), existEmails) || SKIP.validate(req.getDuplicatePhone(), row + .getPhone(), existPhones); + } + + /** + * 判断是否退出导入 + * + * @param req 导入参数 + * @param list 导入数据 + * @param existUsernames 导入数据中已存在的用户名 + * @param existEmails 导入数据中已存在的邮箱 + * @param existPhones 导入数据中已存在的手机号 + * @return 是否退出 + */ + private boolean isExitImportUser(UserImportReq req, + List list, + List existUsernames, + List existEmails, + List existPhones) { + return list.stream() + .anyMatch(row -> EXIT.validate(req.getDuplicateUser(), row.getUsername(), existUsernames) || EXIT + .validate(req.getDuplicateEmail(), row.getEmail(), existEmails) || EXIT.validate(req + .getDuplicatePhone(), row.getPhone(), existPhones)); + } + + /** + * 按指定数据集获取数据库已存在的数量 + * + * @param userRowList 导入的数据源 + * @param rowField 导入数据的字段 + * @param dbField 对比数据库的字段 + * @return 存在的数量 + */ + private int countExistByField(List userRowList, + Function rowField, + SFunction dbField, + boolean fieldEncrypt) { + List fieldValues = userRowList.stream().map(rowField).filter(Objects::nonNull).toList(); + if (fieldValues.isEmpty()) { + return 0; + } + return (int)this.count(Wrappers.lambdaQuery() + .in(dbField, fieldEncrypt ? SecureUtils.encryptFieldByAes(fieldValues) : fieldValues)); + } + + /** + * 按指定数据集获取数据库已存在内容 + * + * @param userRowList 导入的数据源 + * @param rowField 导入数据的字段 + * @param dbField 对比数据库的字段 + * @return 存在的内容 + */ + private List listExistByField(List userRowList, + Function rowField, + SFunction dbField) { + List fieldValues = userRowList.stream().map(rowField).filter(Objects::nonNull).toList(); + if (fieldValues.isEmpty()) { + return Collections.emptyList(); + } + List userDOList = baseMapper.selectList(Wrappers.lambdaQuery() + .in(dbField, SecureUtils.encryptFieldByAes(fieldValues)) + .select(dbField)); + return userDOList.stream().map(dbField).filter(Objects::nonNull).toList(); + } + + /** + * 过滤无效的导入用户数据,批量导入不严格校验数据 + */ + private List filterErrorUserImportData(List userImportList) { + // 校验过滤 + List list = userImportList.stream() + .filter(row -> ValidationUtil.validate(row).size() == 0) + .toList(); + // 用户名去重 + return list.stream() + .collect(Collectors.toMap(UserImportRowReq::getUsername, user -> user, (existing, replacement) -> existing)) + .values() + .stream() + .toList(); + } + + /** + * 检测密码合法性 + * + * @param password 密码 + * @param user 用户信息 + * @return 密码允许重复使用次数 + */ + private int checkPassword(String password, UserDO user) { + Map passwordPolicy = optionService.getByCategory(CATEGORY); + // 密码最小长度 + PASSWORD_MIN_LENGTH.validate(password, MapUtil.getInt(passwordPolicy, PASSWORD_MIN_LENGTH.name()), user); + // 密码是否必须包含特殊字符 + PASSWORD_REQUIRE_SYMBOLS.validate(password, MapUtil.getInt(passwordPolicy, PASSWORD_REQUIRE_SYMBOLS + .name()), user); + // 密码是否允许包含正反序账号名 + PASSWORD_ALLOW_CONTAIN_USERNAME.validate(password, MapUtil + .getInt(passwordPolicy, PASSWORD_ALLOW_CONTAIN_USERNAME.name()), user); + // 密码重复使用次数 + int passwordRepetitionTimes = MapUtil.getInt(passwordPolicy, PASSWORD_REPETITION_TIMES.name()); + PASSWORD_REPETITION_TIMES.validate(password, passwordRepetitionTimes, user); + return passwordRepetitionTimes; + } + + /** + * 名称是否存在 + * + * @param name 名称 + * @param id ID + * @return 是否存在 + */ + private boolean isNameExists(String name, Long id) { + return baseMapper.lambdaQuery().eq(UserDO::getUsername, name).ne(null != id, UserDO::getId, id).exists(); + } + + /** + * 邮箱是否存在 + * + * @param email 邮箱 + * @param id ID + * @return 是否存在 + */ + private boolean isEmailExists(String email, Long id) { + Long count = baseMapper.selectCountByEmail(email, id); + return null != count && count > 0; + } + + /** + * 手机号码是否存在 + * + * @param phone 手机号码 + * @param id ID + * @return 是否存在 + */ + private boolean isPhoneExists(String phone, Long id) { + Long count = baseMapper.selectCountByPhone(phone, id); + return null != count && count > 0; + } + + private List listByUsernames(List usernames) { + return this.list(Wrappers.lambdaQuery() + .in(UserDO::getUsername, usernames) + .select(UserDO::getId, UserDO::getUsername)); + } + + public List selectTeacherById(Long deptId) { + return baseMapper.selectTeacherById(deptId); + } +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/UserSocialServiceImpl.java b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/UserSocialServiceImpl.java new file mode 100644 index 0000000..5810e03 --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/service/impl/UserSocialServiceImpl.java @@ -0,0 +1,81 @@ +package top.continew.admin.system.service.impl; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import lombok.RequiredArgsConstructor; + +import org.springframework.stereotype.Service; + +import cn.hutool.json.JSONUtil; + +import top.continew.admin.system.enums.SocialSourceEnum; +import top.continew.admin.system.mapper.UserSocialMapper; +import top.continew.admin.system.model.entity.UserSocialDO; +import top.continew.admin.system.service.UserSocialService; +import top.continew.starter.core.util.validate.CheckUtils; + +import me.zhyd.oauth.model.AuthUser; + +/** + * 用户社会化关联业务实现 + * + * @author Charles7c + * @since 2023/10/11 22:10 + */ +@Service +@RequiredArgsConstructor +public class UserSocialServiceImpl implements UserSocialService { + + private final UserSocialMapper baseMapper; + + @Override + public UserSocialDO getBySourceAndOpenId(String source, String openId) { + return baseMapper.selectBySourceAndOpenId(source, openId); + } + + @Override + public void saveOrUpdate(UserSocialDO userSocial) { + if (null == userSocial.getCreateTime()) { + baseMapper.insert(userSocial); + } else { + baseMapper.lambdaUpdate() + .set(UserSocialDO::getMetaJson, userSocial.getMetaJson()) + .set(UserSocialDO::getLastLoginTime, userSocial.getLastLoginTime()) + .eq(UserSocialDO::getSource, userSocial.getSource()) + .eq(UserSocialDO::getOpenId, userSocial.getOpenId()) + .update(); + } + } + + @Override + public List listByUserId(Long userId) { + return baseMapper.lambdaQuery().eq(UserSocialDO::getUserId, userId).list(); + } + + @Override + public void bind(AuthUser authUser, Long userId) { + String source = authUser.getSource(); + String openId = authUser.getUuid(); + List userSocialList = this.listByUserId(userId); + Set boundSocialSet = userSocialList.stream().map(UserSocialDO::getSource).collect(Collectors.toSet()); + String description = SocialSourceEnum.valueOf(source).getDescription(); + CheckUtils.throwIf(boundSocialSet.contains(source), "您已经绑定过了 [{}] 平台,请先解绑", description); + UserSocialDO userSocial = this.getBySourceAndOpenId(source, openId); + CheckUtils.throwIfNotNull(userSocial, "[{}] 平台账号 [{}] 已被其他用户绑定", description, authUser.getUsername()); + userSocial = new UserSocialDO(); + userSocial.setUserId(userId); + userSocial.setSource(source); + userSocial.setOpenId(openId); + userSocial.setMetaJson(JSONUtil.toJsonStr(authUser)); + userSocial.setLastLoginTime(LocalDateTime.now()); + baseMapper.insert(userSocial); + } + + @Override + public void deleteBySourceAndUserId(String source, Long userId) { + baseMapper.lambdaUpdate().eq(UserSocialDO::getSource, source).eq(UserSocialDO::getUserId, userId).remove(); + } +} diff --git a/continew-admin-system/src/main/java/top/continew/admin/system/util/ValidateGroup.java b/continew-admin-system/src/main/java/top/continew/admin/system/util/ValidateGroup.java new file mode 100644 index 0000000..b6e0dcf --- /dev/null +++ b/continew-admin-system/src/main/java/top/continew/admin/system/util/ValidateGroup.java @@ -0,0 +1,29 @@ +package top.continew.admin.system.util; + +import jakarta.validation.groups.Default; + +/** + * 分组校验 + * + * @author Charles7c + * @since 2024/7/3 22:01 + */ +public interface ValidateGroup extends Default { + + /** + * 分组校验-增删改查 + */ + interface Storage extends ValidateGroup { + /** + * 本地存储 + */ + interface Local extends Storage { + } + + /** + * 兼容S3协议存储 + */ + interface S3 extends Storage { + } + } +} \ No newline at end of file diff --git a/continew-admin-system/src/main/resources/mapper/DeptMapper.xml b/continew-admin-system/src/main/resources/mapper/DeptMapper.xml new file mode 100644 index 0000000..1d1c306 --- /dev/null +++ b/continew-admin-system/src/main/resources/mapper/DeptMapper.xml @@ -0,0 +1,18 @@ + + + + + + + + + diff --git a/continew-admin-system/src/main/resources/mapper/DictItemMapper.xml b/continew-admin-system/src/main/resources/mapper/DictItemMapper.xml new file mode 100644 index 0000000..d6d51aa --- /dev/null +++ b/continew-admin-system/src/main/resources/mapper/DictItemMapper.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/continew-admin-system/src/main/resources/mapper/FileMapper.xml b/continew-admin-system/src/main/resources/mapper/FileMapper.xml new file mode 100644 index 0000000..b01e4a6 --- /dev/null +++ b/continew-admin-system/src/main/resources/mapper/FileMapper.xml @@ -0,0 +1,39 @@ + + + + + + + + diff --git a/continew-admin-system/src/main/resources/mapper/LogMapper.xml b/continew-admin-system/src/main/resources/mapper/LogMapper.xml new file mode 100644 index 0000000..2f6d379 --- /dev/null +++ b/continew-admin-system/src/main/resources/mapper/LogMapper.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + diff --git a/continew-admin-system/src/main/resources/mapper/MenuMapper.xml b/continew-admin-system/src/main/resources/mapper/MenuMapper.xml new file mode 100644 index 0000000..50d60cf --- /dev/null +++ b/continew-admin-system/src/main/resources/mapper/MenuMapper.xml @@ -0,0 +1,24 @@ + + + + + + + \ No newline at end of file diff --git a/continew-admin-system/src/main/resources/mapper/MessageMapper.xml b/continew-admin-system/src/main/resources/mapper/MessageMapper.xml new file mode 100644 index 0000000..6959964 --- /dev/null +++ b/continew-admin-system/src/main/resources/mapper/MessageMapper.xml @@ -0,0 +1,14 @@ + + + + diff --git a/continew-admin-system/src/main/resources/mapper/MessageUserMapper.xml b/continew-admin-system/src/main/resources/mapper/MessageUserMapper.xml new file mode 100644 index 0000000..8b6ae76 --- /dev/null +++ b/continew-admin-system/src/main/resources/mapper/MessageUserMapper.xml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/continew-admin-system/src/main/resources/mapper/NoticeMapper.xml b/continew-admin-system/src/main/resources/mapper/NoticeMapper.xml new file mode 100644 index 0000000..f5bc622 --- /dev/null +++ b/continew-admin-system/src/main/resources/mapper/NoticeMapper.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file diff --git a/continew-admin-system/src/main/resources/mapper/RoleMapper.xml b/continew-admin-system/src/main/resources/mapper/RoleMapper.xml new file mode 100644 index 0000000..16664e2 --- /dev/null +++ b/continew-admin-system/src/main/resources/mapper/RoleMapper.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/continew-admin-system/src/main/resources/mapper/RoleMenuMapper.xml b/continew-admin-system/src/main/resources/mapper/RoleMenuMapper.xml new file mode 100644 index 0000000..0f8bbde --- /dev/null +++ b/continew-admin-system/src/main/resources/mapper/RoleMenuMapper.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/continew-admin-system/src/main/resources/mapper/UserMapper.xml b/continew-admin-system/src/main/resources/mapper/UserMapper.xml new file mode 100644 index 0000000..8f5509c --- /dev/null +++ b/continew-admin-system/src/main/resources/mapper/UserMapper.xml @@ -0,0 +1,68 @@ + + + + + + SELECT + t1.id, + t1.create_user, + t1.create_time, + t1.update_user, + t1.update_time, + t1.username, + t1.nickname, + t1.password, + t1.gender, + t1.email, + t1.phone, + t1.avatar, + t1.description, + t1.status, + t1.is_system, + t1.pwd_reset_time, + t1.dept_id, + t2.name AS deptName + FROM sys_user AS t1 + LEFT JOIN sys_dept AS t2 ON t2.id = t1.dept_id + + + + + + + + + + + + diff --git a/continew-admin-system/src/main/resources/mapper/UserPasswordHistoryMapper.xml b/continew-admin-system/src/main/resources/mapper/UserPasswordHistoryMapper.xml new file mode 100644 index 0000000..283b6d1 --- /dev/null +++ b/continew-admin-system/src/main/resources/mapper/UserPasswordHistoryMapper.xml @@ -0,0 +1,15 @@ + + + + + DELETE t1 FROM sys_user_password_history t1 + LEFT JOIN ( + SELECT id + FROM sys_user_password_history + WHERE user_id = #{userId} + ORDER BY create_time DESC + LIMIT #{count} + ) t2 ON t2.id = t1.id + WHERE t2.id IS NULL + + \ No newline at end of file diff --git a/continew-admin-system/src/main/resources/mapper/UserSocialMapper.xml b/continew-admin-system/src/main/resources/mapper/UserSocialMapper.xml new file mode 100644 index 0000000..56deed3 --- /dev/null +++ b/continew-admin-system/src/main/resources/mapper/UserSocialMapper.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/continew-admin-webapi/pom.xml b/continew-admin-webapi/pom.xml new file mode 100644 index 0000000..5b74b03 --- /dev/null +++ b/continew-admin-webapi/pom.xml @@ -0,0 +1,182 @@ + + + 4.0.0 + + top.continew + continew-admin + ${revision} + + + continew-admin-webapi + API 及打包部署模块 + + + + + top.continew.admin.ContiNewAdminApplication + + bin/ + + config/ + + lib/ + + + + + + top.continew + continew-starter-log-interceptor + + + + + top.continew + continew-admin-system + + + + + top.continew + continew-admin-business + ${revision} + + + + + top.continew + continew-admin-job + ${revision} + + + + + top.continew + continew-admin-generator + + + + + org.liquibase + liquibase-core + + + + + + + + + org.springframework.boot + spring-boot-starter-test + test + + + cn.dev33 + sa-token-core + 1.39.0 + compile + + + + + + continew-admin + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + top.continew.admin.ContiNewAdminApplication + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + ${config-path}/ + db/ + templates/ + logback-spring.xml + + + + ${main-class} + + ../${lib-path} + true + + false + + + + ../${config-path} + + + ${project.build.directory}/app/${bin-path} + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-dependencies + package + + copy-dependencies + + + ${project.build.directory}/app/${lib-path} + + + + + + + org.apache.maven.plugins + maven-resources-plugin + + + copy-resources + package + + copy-resources + + + + + src/main/resources/${config-path} + + + src/main/resources + + db/ + templates/ + logback-spring.xml + + + + ${project.build.directory}/app/${config-path} + + + + + + + diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/ContiNewAdminApplication.java b/continew-admin-webapi/src/main/java/top/continew/admin/ContiNewAdminApplication.java new file mode 100644 index 0000000..8d929c1 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/ContiNewAdminApplication.java @@ -0,0 +1,70 @@ +package top.continew.admin; + +import cn.dev33.satoken.annotation.SaIgnore; +import cn.hutool.core.net.NetUtil; +import cn.hutool.core.util.URLUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.alicp.jetcache.anno.config.EnableMethodCache; +import com.github.xiaoymin.knife4j.spring.configuration.Knife4jProperties; +import io.swagger.v3.oas.annotations.Hidden; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.x.file.storage.spring.EnableFileStorage; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import top.continew.starter.core.autoconfigure.project.ProjectProperties; +import top.continew.starter.extension.crud.annotation.EnableCrudRestController; +import top.continew.starter.web.annotation.EnableGlobalResponse; +import top.continew.starter.web.model.R; + +/** + * 启动程序 + * + * @author Charles7c + * @since 2022/12/8 23:15 + */ +@Slf4j +@EnableFileStorage +@EnableMethodCache(basePackages = "top.continew.admin") +@EnableGlobalResponse +@EnableCrudRestController +@RestController +@SpringBootApplication +@RequiredArgsConstructor +public class ContiNewAdminApplication implements ApplicationRunner { + + private final ProjectProperties projectProperties; + private final ServerProperties serverProperties; + + public static void main(String[] args) { + SpringApplication.run(ContiNewAdminApplication.class, args); + } + + @Hidden + @SaIgnore + @GetMapping("/") + public R index() { + return R.ok("%s service started successfully.".formatted(projectProperties.getName()), null); + } + + @Override + public void run(ApplicationArguments args) { + String hostAddress = NetUtil.getLocalhostStr(); + Integer port = serverProperties.getPort(); + String contextPath = serverProperties.getServlet().getContextPath(); + String baseUrl = URLUtil.normalize("%s:%s%s".formatted(hostAddress, port, contextPath)); + log.info("----------------------------------------------"); + log.info("{} service started successfully.", projectProperties.getName()); + log.info("API 地址:{}", baseUrl); + Knife4jProperties knife4jProperties = SpringUtil.getBean(Knife4jProperties.class); + if (!knife4jProperties.isProduction()) { + log.info("API 文档:{}/doc.html", baseUrl); + } + log.info("----------------------------------------------"); + } +} diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/config/file/ConstantConfig.java b/continew-admin-webapi/src/main/java/top/continew/admin/config/file/ConstantConfig.java new file mode 100644 index 0000000..b16e480 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/config/file/ConstantConfig.java @@ -0,0 +1,31 @@ +package top.continew.admin.config.file; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +import java.io.File; + +/** + * @Author: + * @Date: 2022/09/29/11:26 + * @Description: + */ + +@Configuration +public class ConstantConfig { + + @Value("${common.file.path}") + private String path; + + public String getPath() { + File file = new File(path); + if (!file.exists() && !file.isDirectory()) { + file.mkdirs(); + } + return path; + } + + public void setPath(String path) { + this.path = path; + } +} diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/config/file/ResourceConfig.java b/continew-admin-webapi/src/main/java/top/continew/admin/config/file/ResourceConfig.java new file mode 100644 index 0000000..31772e6 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/config/file/ResourceConfig.java @@ -0,0 +1,23 @@ +package top.continew.admin.config.file; + +import jakarta.annotation.Resource; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * @Author: + * @Date: 2025/05/28/14:58 + * @Description: + */ + +@Configuration +public class ResourceConfig implements WebMvcConfigurer { + @Resource + private ConstantConfig constantConfig; + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("/downFile/**").addResourceLocations("file:" + constantConfig.getPath()); + } +} diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/config/filter/FilterConfiguration.java b/continew-admin-webapi/src/main/java/top/continew/admin/config/filter/FilterConfiguration.java new file mode 100644 index 0000000..0ecda0f --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/config/filter/FilterConfiguration.java @@ -0,0 +1,33 @@ +package top.continew.admin.config.filter; + +import lombok.RequiredArgsConstructor; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; + +/** + * 过滤器配置类 + * + * @author jiay + * @since 2025/08/28 00:00 + */ +@Configuration +@RequiredArgsConstructor +public class FilterConfiguration { + + private final ThirdPartyTokenFilter thirdPartyTokenFilter; + + /** + * 注册第三方平台Token校验过滤器 + */ + @Bean + public FilterRegistrationBean thirdPartyTokenFilterRegistration() { + FilterRegistrationBean registration = new FilterRegistrationBean<>(); + registration.setFilter(thirdPartyTokenFilter); + registration.addUrlPatterns("/billing/*"); // 只对计费接口生效 + registration.setName("ThirdPartyTokenFilter"); + registration.setOrder(Ordered.HIGHEST_PRECEDENCE + 10); + return registration; + } +} \ No newline at end of file diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/config/filter/ThirdPartyTokenFilter.java b/continew-admin-webapi/src/main/java/top/continew/admin/config/filter/ThirdPartyTokenFilter.java new file mode 100644 index 0000000..7770347 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/config/filter/ThirdPartyTokenFilter.java @@ -0,0 +1,103 @@ +package top.continew.admin.config.filter; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.FilterConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import cn.dev33.satoken.stp.StpUtil; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +/** + * 第三方平台Token校验过滤器 + * + * @author jiay + * @since 2025/08/28 00:00 + */ +@Slf4j +@Component +public class ThirdPartyTokenFilter implements Filter { + + /** + * 需要校验的第三方平台接口路径 + */ + private static final List THIRD_PARTY_PATHS = Arrays.asList("/billing/deduct", "/billing/callback"); + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + log.info("ThirdPartyTokenFilter initialized"); + } + + @Override + public void doFilter(ServletRequest request, + ServletResponse response, + FilterChain chain) throws IOException, ServletException { + + HttpServletRequest httpRequest = (HttpServletRequest)request; + HttpServletResponse httpResponse = (HttpServletResponse)response; + + String requestURI = httpRequest.getRequestURI(); + + // 判断是否是需要校验的第三方平台接口 + boolean isThirdPartyApi = THIRD_PARTY_PATHS.stream().anyMatch(path -> requestURI.contains(path)); + + if (isThirdPartyApi) { + try { + // 从请求头获取token + String token = httpRequest.getHeader("Authorization"); + if (token == null || token.isEmpty()) { + token = httpRequest.getHeader("token"); + } + + if (token == null || token.isEmpty()) { + log.warn("第三方平台请求缺少token, URI: {}", requestURI); + sendErrorResponse(httpResponse, "缺少认证token"); + return; + } + + if (token.startsWith("Bearer ")) { + token = token.substring(7); + } + + Object loginId = StpUtil.getLoginIdByToken(token); + if (loginId == null) { + log.warn("第三方平台token无效, URI: {}, token: {}", requestURI, token); + sendErrorResponse(httpResponse, "token无效或已过期"); + return; + } + log.debug("第三方平台token校验成功, URI: {}, userId: {}", requestURI, loginId); + + } catch (Exception e) { + log.error("第三方平台token校验异常, URI: {}", requestURI, e); + sendErrorResponse(httpResponse, "token校验失败"); + return; + } + } + + chain.doFilter(request, response); + } + + @Override + public void destroy() { + log.info("ThirdPartyTokenFilter destroyed"); + } + + /** + * 发送错误响应 + */ + private void sendErrorResponse(HttpServletResponse response, String message) throws IOException { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + response.setContentType("application/json;charset=UTF-8"); + response.getWriter().write("{\"success\":false,\"message\":\"" + message + "\",\"code\":401}"); + } +} \ No newline at end of file diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/config/log/LogConfiguration.java b/continew-admin-webapi/src/main/java/top/continew/admin/config/log/LogConfiguration.java new file mode 100644 index 0000000..d7b05ee --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/config/log/LogConfiguration.java @@ -0,0 +1,28 @@ +package top.continew.admin.config.log; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import top.continew.admin.system.mapper.LogMapper; +import top.continew.admin.system.service.UserService; +import top.continew.starter.log.core.dao.LogDao; +import top.continew.starter.log.interceptor.autoconfigure.ConditionalOnEnabledLog; +import top.continew.starter.web.autoconfigure.trace.TraceProperties; + +/** + * 日志配置 + * + * @author Charles7c + * @since 2022/12/24 23:15 + */ +@Configuration +@ConditionalOnEnabledLog +public class LogConfiguration { + + /** + * 日志持久层接口本地实现类 + */ + @Bean + public LogDao logDao(UserService userService, LogMapper logMapper, TraceProperties traceProperties) { + return new LogDaoLocalImpl(userService, logMapper, traceProperties); + } +} diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/config/log/LogDaoLocalImpl.java b/continew-admin-webapi/src/main/java/top/continew/admin/config/log/LogDaoLocalImpl.java new file mode 100644 index 0000000..a62e6e0 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/config/log/LogDaoLocalImpl.java @@ -0,0 +1,150 @@ +package top.continew.admin.config.log; + +import cn.dev33.satoken.SaManager; +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.URLUtil; +import cn.hutool.http.HttpStatus; +import cn.hutool.json.JSONUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpHeaders; +import org.springframework.scheduling.annotation.Async; +import top.continew.admin.auth.model.req.AccountLoginReq; +import top.continew.admin.common.constant.SysConstants; +import top.continew.admin.system.enums.LogStatusEnum; +import top.continew.admin.system.mapper.LogMapper; +import top.continew.admin.system.model.entity.LogDO; +import top.continew.admin.system.service.UserService; +import top.continew.starter.core.constant.StringConstants; +import top.continew.starter.core.util.ExceptionUtils; +import top.continew.starter.core.util.StrUtils; +import top.continew.starter.log.core.dao.LogDao; +import top.continew.starter.log.core.model.LogRecord; +import top.continew.starter.log.core.model.LogRequest; +import top.continew.starter.log.core.model.LogResponse; +import top.continew.starter.web.autoconfigure.trace.TraceProperties; +import top.continew.starter.web.model.R; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Map; +import java.util.Set; + +/** + * 日志持久层接口本地实现类 + * + * @author Charles7c + * @since 2023/12/16 23:55 + */ +@RequiredArgsConstructor +public class LogDaoLocalImpl implements LogDao { + + private final UserService userService; + private final LogMapper logMapper; + private final TraceProperties traceProperties; + + @Async + @Override + public void add(LogRecord logRecord) { + LogDO logDO = new LogDO(); + // 设置请求信息 + LogRequest logRequest = logRecord.getRequest(); + this.setRequest(logDO, logRequest); + // 设置响应信息 + LogResponse logResponse = logRecord.getResponse(); + this.setResponse(logDO, logResponse); + // 设置基本信息 + logDO.setDescription(logRecord.getDescription()); + logDO.setModule(StrUtils.blankToDefault(logRecord.getModule(), null, m -> m + .replace("API", StringConstants.EMPTY) + .trim())); + logDO.setTimeTaken(logRecord.getTimeTaken().toMillis()); + logDO.setCreateTime(LocalDateTime.ofInstant(logRecord.getTimestamp(), ZoneId.systemDefault())); + // 设置操作人 + this.setCreateUser(logDO, logRequest, logResponse); + logMapper.insert(logDO); + } + + /** + * 设置请求信息 + * + * @param logDO 日志信息 + * @param logRequest 请求信息 + */ + private void setRequest(LogDO logDO, LogRequest logRequest) { + logDO.setRequestMethod(logRequest.getMethod()); + logDO.setRequestUrl(logRequest.getUrl().toString()); + logDO.setRequestHeaders(JSONUtil.toJsonStr(logRequest.getHeaders())); + logDO.setRequestBody(logRequest.getBody()); + logDO.setIp(logRequest.getIp()); + logDO.setAddress(logRequest.getAddress()); + logDO.setBrowser(logRequest.getBrowser()); + logDO.setOs(StrUtil.subBefore(logRequest.getOs(), " or", false)); + } + + /** + * 设置响应信息 + * + * @param logDO 日志信息 + * @param logResponse 响应信息 + */ + private void setResponse(LogDO logDO, LogResponse logResponse) { + Map responseHeaders = logResponse.getHeaders(); + logDO.setResponseHeaders(JSONUtil.toJsonStr(responseHeaders)); + logDO.setTraceId(responseHeaders.get(traceProperties.getTraceIdName())); + String responseBody = logResponse.getBody(); + logDO.setResponseBody(responseBody); + // 状态 + Integer statusCode = logResponse.getStatus(); + logDO.setStatusCode(statusCode); + logDO.setStatus(statusCode >= HttpStatus.HTTP_BAD_REQUEST ? LogStatusEnum.FAILURE : LogStatusEnum.SUCCESS); + if (StrUtil.isNotBlank(responseBody)) { + R result = JSONUtil.toBean(responseBody, R.class); + if (!result.isSuccess()) { + logDO.setStatus(LogStatusEnum.FAILURE); + logDO.setErrorMsg(result.getMsg()); + } + } + } + + /** + * 设置操作人 + * + * @param logDO 日志信息 + * @param logRequest 请求信息 + * @param logResponse 响应信息 + */ + private void setCreateUser(LogDO logDO, LogRequest logRequest, LogResponse logResponse) { + String requestUri = URLUtil.getPath(logDO.getRequestUrl()); + // 解析退出接口信息 + String responseBody = logResponse.getBody(); + if (requestUri.startsWith(SysConstants.LOGOUT_URI) && StrUtil.isNotBlank(responseBody)) { + R result = JSONUtil.toBean(responseBody, R.class); + logDO.setCreateUser(Convert.toLong(result.getData(), null)); + return; + } + // 解析登录接口信息 + if (requestUri.startsWith(SysConstants.LOGIN_URI) && LogStatusEnum.SUCCESS.equals(logDO.getStatus())) { + String requestBody = logRequest.getBody(); + AccountLoginReq loginReq = JSONUtil.toBean(requestBody, AccountLoginReq.class); + logDO.setCreateUser(ExceptionUtils.exToNull(() -> userService.getByUsername(loginReq.getUsername()) + .getId())); + return; + } + // 解析 Token 信息 + Map requestHeaders = logRequest.getHeaders(); + String headerName = HttpHeaders.AUTHORIZATION; + boolean isContainsAuthHeader = CollUtil.containsAny(requestHeaders.keySet(), Set.of(headerName, headerName + .toLowerCase())); + if (MapUtil.isNotEmpty(requestHeaders) && isContainsAuthHeader) { + String authorization = requestHeaders.getOrDefault(headerName, requestHeaders.get(headerName + .toLowerCase())); + String token = authorization.replace(SaManager.getConfig() + .getTokenPrefix() + StringConstants.SPACE, StringConstants.EMPTY); + logDO.setCreateUser(Convert.toLong(StpUtil.getLoginIdByToken(token))); + } + } +} diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/config/satoken/LoginPasswordProperties.java b/continew-admin-webapi/src/main/java/top/continew/admin/config/satoken/LoginPasswordProperties.java new file mode 100644 index 0000000..994f107 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/config/satoken/LoginPasswordProperties.java @@ -0,0 +1,22 @@ +package top.continew.admin.config.satoken; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 密码配置属性 + * + * @author Charles7c + * @since 2024/6/15 22:15 + */ +@Data +@Component +@ConfigurationProperties(prefix = "auth.password") +public class LoginPasswordProperties { + + /** + * 排除(放行)路径配置 + */ + private String[] excludes = new String[0]; +} \ No newline at end of file diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/config/satoken/PathTemplateExtractor.java b/continew-admin-webapi/src/main/java/top/continew/admin/config/satoken/PathTemplateExtractor.java new file mode 100644 index 0000000..b1d4f4d --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/config/satoken/PathTemplateExtractor.java @@ -0,0 +1,32 @@ +package top.continew.admin.config.satoken; + +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.web.servlet.HandlerMapping; + +/** + * @Author: jia + * @Date: 2025/06/10/10:21 + * @Description: + */ +public class PathTemplateExtractor { + + /** + * 从请求中获取原始路径模板(带 {variable} 格式) + */ + public static String getPathTemplate(HttpServletRequest request) { + // 关键:从请求属性获取路径模板 + String pathTemplate = (String)request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE); + return pathTemplate != null ? pathTemplate : request.getRequestURI(); + } + + /** + * 获取静态路径(移除所有变量部分) + */ + public static String getStaticPath(HttpServletRequest request) { + String template = getPathTemplate(request); + // 移除所有 {variable} 格式的参数 + String res = template.replaceAll("/\\{[^/]+\\}", ""); + String[] parts = res.split("/"); + return parts[parts.length - 1]; + } +} \ No newline at end of file diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/config/satoken/SaTokenConfiguration.java b/continew-admin-webapi/src/main/java/top/continew/admin/config/satoken/SaTokenConfiguration.java new file mode 100644 index 0000000..1056da6 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/config/satoken/SaTokenConfiguration.java @@ -0,0 +1,96 @@ +package top.continew.admin.config.satoken; + +import cn.dev33.satoken.context.SaHolder; +import cn.dev33.satoken.interceptor.SaInterceptor; +import cn.dev33.satoken.router.SaRouter; +import cn.dev33.satoken.stp.StpInterface; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.util.StringUtils; +import top.continew.admin.common.model.dto.LoginUser; +import top.continew.admin.common.util.helper.LoginHelper; +import top.continew.admin.system.mapper.DeptMapper; +import top.continew.admin.system.model.entity.DeptDO; +import top.continew.starter.auth.satoken.autoconfigure.SaTokenExtensionProperties; +import top.continew.starter.core.constant.StringConstants; +import top.continew.starter.core.util.validate.CheckUtils; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Sa-Token 配置 + * + * @author Charles7c + * @since 2022/12/19 22:13 + */ +@Configuration +@RequiredArgsConstructor +public class SaTokenConfiguration { + + @Value("${depts.routers}") + private String routers; + @Resource + private DeptMapper deptMapper; + + private final SaTokenExtensionProperties properties; + private final LoginPasswordProperties loginPasswordProperties; + + /** + * Sa-Token 权限认证配置 + */ + @Bean + public StpInterface stpInterface() { + return new SaTokenPermissionImpl(); + } + + /** + * SaToken 拦截器配置 + */ + @Bean + public SaInterceptor saInterceptor() { + return new SaInterceptor(handle -> SaRouter.match(StringConstants.PATH_PATTERN) + .notMatch(properties.getSecurity().getExcludes()) + .check(r -> { + HttpServletRequest request = (HttpServletRequest)SaHolder.getRequest().getSource(); + String servletPath = PathTemplateExtractor.getStaticPath(request); + LoginUser loginUser = LoginHelper.getLoginUserSafely(); + if (loginUser == null) { + // 如果用户未登录,则跳过此检查 + return; + } + Long deptId = loginUser.getDeptId(); + DeptDO deptDO = deptMapper.selectById(deptId); + String routers = deptDO.getRouters(); + List collect = Arrays.stream(this.routers.split(",")).collect(Collectors.toList()); + if (!collect.contains(servletPath)) { + return; + } + if (!StringUtils.hasText(routers)) { + throw new RuntimeException("用户所属学校无权限!"); + } + if (routers.contains(servletPath)) { + return; + } else { + throw new RuntimeException("用户所属学校无权限!"); + } + }) + .check(r -> { + LoginUser loginUser = LoginHelper.getLoginUserSafely(); + if (loginUser == null) { + // 如果用户未登录,则跳过此检查 + return; + } + if (SaRouter.isMatchCurrURI(loginPasswordProperties.getExcludes())) { + return; + } + CheckUtils.throwIf(loginUser.isPasswordExpired(), "密码已过期,请修改密码"); + })); + } + +} \ No newline at end of file diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/config/satoken/SaTokenPermissionImpl.java b/continew-admin-webapi/src/main/java/top/continew/admin/config/satoken/SaTokenPermissionImpl.java new file mode 100644 index 0000000..0db8b92 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/config/satoken/SaTokenPermissionImpl.java @@ -0,0 +1,29 @@ +package top.continew.admin.config.satoken; + +import cn.dev33.satoken.stp.StpInterface; +import top.continew.admin.common.model.dto.LoginUser; +import top.continew.admin.common.util.helper.LoginHelper; + +import java.util.ArrayList; +import java.util.List; + +/** + * Sa-Token 权限认证实现 + * + * @author Charles7c + * @since 2023/3/1 22:28 + */ +public class SaTokenPermissionImpl implements StpInterface { + + @Override + public List getPermissionList(Object loginId, String loginType) { + LoginUser loginUser = LoginHelper.getLoginUser(); + return new ArrayList<>(loginUser.getPermissions()); + } + + @Override + public List getRoleList(Object loginId, String loginType) { + LoginUser loginUser = LoginHelper.getLoginUser(); + return new ArrayList<>(loginUser.getRoleCodes()); + } +} diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/auth/AuthController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/auth/AuthController.java new file mode 100644 index 0000000..035f1e7 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/auth/AuthController.java @@ -0,0 +1,324 @@ +package top.continew.admin.controller.auth; + +import cn.dev33.satoken.annotation.SaIgnore; +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.bean.BeanUtil; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import top.continew.admin.auth.model.req.AccountLoginReq; +import top.continew.admin.auth.model.req.EmailLoginReq; +import top.continew.admin.auth.model.req.EmailRegisterReq; +import top.continew.admin.auth.model.req.PhoneLoginReq; +import top.continew.admin.auth.model.req.PhoneRegisterReq; +import top.continew.admin.auth.model.req.UserRegisterReq; +import top.continew.admin.auth.model.resp.DeptInfo; +import top.continew.admin.auth.model.resp.LoginResp; +import top.continew.admin.auth.model.resp.RouteResp; +import top.continew.admin.auth.model.resp.UserInfoResp; +import top.continew.admin.auth.service.LoginService; +import top.continew.admin.business.service.PlantformBalanceService; +import top.continew.admin.common.constant.CacheConstants; +import top.continew.admin.common.model.dto.LoginUser; +import top.continew.admin.common.util.JwtUtil; +import top.continew.admin.common.util.SecureUtils; +import top.continew.admin.common.util.helper.LoginHelper; +import top.continew.admin.system.model.entity.DeptDO; +import top.continew.admin.system.model.resp.SimpleUserInfoResp; +import top.continew.admin.system.model.resp.UserDetailResp; +import top.continew.admin.system.service.DeptService; +import top.continew.admin.system.service.UserService; +import top.continew.starter.cache.redisson.util.RedisUtils; +import top.continew.starter.core.util.ExceptionUtils; +import top.continew.starter.core.util.validate.ValidationUtils; +import top.continew.starter.log.core.annotation.Log; +import top.continew.starter.web.model.R; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * 认证 API + * + * @author Charles7c + * @since 2022/12/21 20:37 + */ +@Log(module = "登录") +@Tag(name = "认证 API") +@RestController +@RequiredArgsConstructor +@RequestMapping("/auth") +public class AuthController { + + private static final String CAPTCHA_EXPIRED = "验证码已失效"; + private static final String CAPTCHA_ERROR = "验证码错误"; + private final LoginService loginService; + private final UserService userService; + private final DeptService deptService; + private final PlantformBalanceService plantformBalanceService; + + @SaIgnore + @Operation(summary = "账号登录", description = "根据账号和密码进行登录认证") + @PostMapping("/account") + public LoginResp accountLogin(@Validated @RequestBody AccountLoginReq loginReq, HttpServletRequest request) { + String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + loginReq.getUuid(); + String captcha = RedisUtils.get(captchaKey); + ValidationUtils.throwIfBlank(captcha, CAPTCHA_EXPIRED); + ValidationUtils.throwIfNotEqualIgnoreCase(loginReq.getCaptcha(), captcha, CAPTCHA_ERROR); + RedisUtils.delete(captchaKey); + // 用户登录 + String rawPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(loginReq.getPassword())); + ValidationUtils.throwIfBlank(rawPassword, "密码解密失败"); + LoginUser loginUser = loginService.accountLogin(loginReq.getUsername(), rawPassword, request); + + // 生成JWT Token + String token = JwtUtil.generateToken(loginUser.getId().toString()); + System.out.println("Generated Token: " + token); + + // 从Token中获取用户名和其他信息(例如过期时间) + String username = JwtUtil.getUsernameFromToken(token); + Date expirationDate = JwtUtil.getExpirationDateFromToken(token); + System.out.println("id: " + username); + return LoginResp.builder() + .generateToken(token) + .token(loginUser.getToken()) + .aiBotToken(loginUser.getAiBotToken()) + .sdBotToken(loginUser.getSdBotToken()) + .build(); + } + + @SaIgnore + @Operation(summary = "手机号登录", description = "根据手机号和验证码进行登录认证") + @PostMapping("/phone") + public LoginResp phoneLogin(@Validated @RequestBody PhoneLoginReq loginReq) { + String phone = loginReq.getPhone(); + String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + phone; + String captcha = RedisUtils.get(captchaKey); + ValidationUtils.throwIfBlank(captcha, CAPTCHA_EXPIRED); + ValidationUtils.throwIfNotEqualIgnoreCase(loginReq.getCaptcha(), captcha, CAPTCHA_ERROR); + RedisUtils.delete(captchaKey); + LoginUser loginUser = loginService.phoneLogin(phone); + return LoginResp.builder() + .token(loginUser.getToken()) + .aiBotToken(loginUser.getAiBotToken()) + .sdBotToken(loginUser.getSdBotToken()) + .build(); + } + + @SaIgnore + @Operation(summary = "邮箱登录", description = "根据邮箱和验证码进行登录认证") + @PostMapping("/email") + public LoginResp emailLogin(@Validated @RequestBody EmailLoginReq loginReq) { + String email = loginReq.getEmail(); + String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + email; + String captcha = RedisUtils.get(captchaKey); + ValidationUtils.throwIfBlank(captcha, CAPTCHA_EXPIRED); + ValidationUtils.throwIfNotEqualIgnoreCase(loginReq.getCaptcha(), captcha, CAPTCHA_ERROR); + RedisUtils.delete(captchaKey); + LoginUser loginUser = loginService.emailLogin(email); + return LoginResp.builder() + .token(loginUser.getToken()) + .aiBotToken(loginUser.getAiBotToken()) + .sdBotToken(loginUser.getSdBotToken()) + .build(); + } + + @Operation(summary = "用户退出", description = "注销用户的当前登录") + @Parameter(name = "Authorization", description = "令牌", required = true, example = "Bearer xxxx-xxxx-xxxx-xxxx", in = ParameterIn.HEADER) + @PostMapping("/logout") + public Object logout() { + Object loginId = StpUtil.getLoginId(-1L); + StpUtil.logout(); + return loginId; + } + + @Log(ignore = true) + @Operation(summary = "获取用户信息", description = "获取登录用户信息") + @GetMapping("/user/info") + public UserInfoResp getUserInfo() { + LoginUser loginUser = LoginHelper.getLoginUser(); + UserDetailResp userDetailResp = userService.get(loginUser.getId()); + UserInfoResp userInfoResp = BeanUtil.copyProperties(userDetailResp, UserInfoResp.class); + userInfoResp.setPermissions(loginUser.getPermissions()); + userInfoResp.setRoles(loginUser.getRoleCodes()); + userInfoResp.setPwdExpired(loginUser.isPasswordExpired()); + userInfoResp.setRouters(loginUser.getRouters()); + + // 获取当前用户的部门ID + Long deptId = userDetailResp.getDeptId(); + if (deptId != null) { + // 根据用户ID查询所有相关部门(包括父部门和子部门) + List deptInfoList = getRelatedDeptsByUserId(deptId); + userInfoResp.setDeptInfos(deptInfoList.toArray(new DeptInfo[0])); + } + + return userInfoResp; + } + + /** + * 根据用户部门ID获取所有相关部门(包括父部门和子部门) + */ + private List getRelatedDeptsByUserId(Long deptId) { + List deptInfoList = new ArrayList<>(); + + // 获取用户所在部门及其所有父部门 + DeptDO currentDept = deptService.getById(deptId); + if (currentDept != null) { + // 添加当前部门 + DeptInfo currentDeptInfo = new DeptInfo(); + currentDeptInfo.setDeptId(currentDept.getId()); + currentDeptInfo.setName(currentDept.getName()); + deptInfoList.add(currentDeptInfo); + + // 添加父部门 + addParentDepts(currentDept.getParentId(), deptInfoList); + + /*// 获取并添加所有子部门 + List allChildren = deptService.listChildren(deptId); + for (DeptDO child : allChildren) { + DeptInfo childDeptInfo = new DeptInfo(); + childDeptInfo.setDeptId(child.getId()); + childDeptInfo.setName(child.getName()); + deptInfoList.add(childDeptInfo); + }*/ + } + + return deptInfoList; + } + + /** + * 递归添加所有父部门 + */ + private void addParentDepts(Long parentId, List deptInfoList) { + if (parentId == null || parentId == 0) { + return; + } + + DeptDO parentDept = deptService.getById(parentId); + if (parentDept != null) { + DeptInfo parentDeptInfo = new DeptInfo(); + parentDeptInfo.setDeptId(parentDept.getId()); + parentDeptInfo.setName(parentDept.getName()); + deptInfoList.add(parentDeptInfo); + + // 递归添加上级部门 + addParentDepts(parentDept.getParentId(), deptInfoList); + } + } + + @Log(ignore = true) + @Operation(summary = "验证token字符串", description = "验证token字符串") + @GetMapping("/check/token/{token}") + public boolean checkToken(@PathVariable String token) { + try { + Object loginIdByToken = StpUtil.getLoginIdByToken(token); + return true; + } catch (Exception e) { + return false; + } + } + + @Log(ignore = true) + @Operation(summary = "验证token字符串并返回用户信息", description = "验证token字符串并返回用户信息") + @GetMapping("/check/checkTokenRn/{token}") + public R checkTokenRn(@PathVariable String token) { + try { + Long loginIdByToken = Long.parseLong((String)StpUtil.getLoginIdByToken(token)); + UserDetailResp userDetailResp = userService.get(loginIdByToken); + SimpleUserInfoResp simpleBeanInfo = BeanUtil.copyProperties(userDetailResp, SimpleUserInfoResp.class); + return R.ok(simpleBeanInfo); + } catch (Exception e) { + return R.fail("token无效", e.getMessage()); + } + } + + @Log(ignore = true) + @Operation(summary = "验证token", description = "验证token") + @GetMapping("/check/token") + public String checkToken() { + LoginUser loginUser = LoginHelper.getLoginUser(); + ValidationUtils.throwIfNull(loginUser, "token失效"); + //String token = JwtUtil.generateToken(loginUser.getId().toString()); + return loginUser.getId().toString(); + } + + @Log(ignore = true) + @Operation(summary = "获取路由信息", description = "获取登录用户的路由信息") + @GetMapping("/route") + public List listRoute() { + return loginService.buildRouteTree(LoginHelper.getUserId()); + } + + @SaIgnore + @Operation(summary = "用户名密码注册", description = "根据用户名、密码和验证码进行用户注册") + @PostMapping("/register") + public LoginResp userRegister(@Validated @RequestBody UserRegisterReq registerReq, HttpServletRequest request) { + LoginUser loginUser = loginService.userRegister(registerReq, request); + + // 生成JWT Token + String token = JwtUtil.generateToken(loginUser.getId().toString()); + + //添加用户子账户 + // for (int i = 0; i < ChargeTypeEnum.values().length; i++) { + // PlantformBalanceAddReq plantformBalanceAddReq = new PlantformBalanceAddReq().initPlantformBalance(); + // plantformBalanceAddReq.setChargeType(ChargeTypeEnum.getType(i)); + // plantformBalanceAddReq.setUserId(loginUser.getId()); + // plantformBalanceService.addPlantformBalance(plantformBalanceAddReq); + // } + + return LoginResp.builder() + .generateToken(token) + .token(loginUser.getToken()) + .aiBotToken(loginUser.getAiBotToken()) + .sdBotToken(loginUser.getSdBotToken()) + .build(); + } + + @SaIgnore + @Operation(summary = "手机号注册", description = "根据手机号、密码和验证码进行用户注册") + @PostMapping("/register/phone") + public LoginResp phoneRegister(@Validated @RequestBody PhoneRegisterReq registerReq, HttpServletRequest request) { + LoginUser loginUser = loginService.phoneRegister(registerReq, request); + + // 生成JWT Token + String token = JwtUtil.generateToken(loginUser.getId().toString()); + + //添加用户子账户 + // for (int i = 0; i < ChargeTypeEnum.values().length; i++) { + // PlantformBalanceAddReq plantformBalanceAddReq = new PlantformBalanceAddReq().initPlantformBalance(); + // plantformBalanceAddReq.setChargeType(ChargeTypeEnum.getType(i)); + // plantformBalanceAddReq.setUserId(loginUser.getId()); + // plantformBalanceService.addPlantformBalance(plantformBalanceAddReq); + // } + + return LoginResp.builder() + .generateToken(token) + .token(loginUser.getToken()) + .aiBotToken(loginUser.getAiBotToken()) + .sdBotToken(loginUser.getSdBotToken()) + .build(); + } + + @SaIgnore + @Operation(summary = "邮箱注册", description = "根据邮箱、密码和验证码进行用户注册") + @PostMapping("/register/email") + public LoginResp emailRegister(@Validated @RequestBody EmailRegisterReq registerReq, HttpServletRequest request) { + LoginUser loginUser = loginService.emailRegister(registerReq, request); + + // 生成JWT Token + String token = JwtUtil.generateToken(loginUser.getId().toString()); + + return LoginResp.builder() + .generateToken(token) + .token(loginUser.getToken()) + .aiBotToken(loginUser.getAiBotToken()) + .sdBotToken(loginUser.getSdBotToken()) + .build(); + } +} diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/auth/SocialAuthController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/auth/SocialAuthController.java new file mode 100644 index 0000000..b0e8906 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/auth/SocialAuthController.java @@ -0,0 +1,78 @@ +package top.continew.admin.controller.auth; + +import cn.dev33.satoken.annotation.SaIgnore; +import cn.dev33.satoken.stp.StpUtil; +import com.xkcoding.justauth.AuthRequestFactory; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import me.zhyd.oauth.model.AuthCallback; +import me.zhyd.oauth.model.AuthResponse; +import me.zhyd.oauth.model.AuthUser; +import me.zhyd.oauth.request.AuthRequest; +import me.zhyd.oauth.utils.AuthStateUtils; +import org.springframework.web.bind.annotation.*; +import top.continew.admin.auth.model.resp.LoginResp; +import top.continew.admin.auth.model.resp.SocialAuthAuthorizeResp; +import top.continew.admin.auth.service.LoginService; +import top.continew.admin.common.model.dto.LoginUser; +import top.continew.starter.core.exception.BadRequestException; +import top.continew.starter.core.util.validate.ValidationUtils; +import top.continew.starter.log.core.annotation.Log; + +/** + * 三方账号认证 API + * + * @author Charles7c + * @since 2023/10/8 22:52 + */ +@Log(module = "登录") +@Tag(name = "三方账号认证 API") +@SaIgnore +@RestController +@RequiredArgsConstructor +@RequestMapping("/oauth") +public class SocialAuthController { + + private final LoginService loginService; + private final AuthRequestFactory authRequestFactory; + + @Operation(summary = "三方账号登录授权", description = "三方账号登录授权") + @Parameter(name = "source", description = "来源", example = "gitee", in = ParameterIn.PATH) + @GetMapping("/{source}") + public SocialAuthAuthorizeResp authorize(@PathVariable String source) { + AuthRequest authRequest = this.getAuthRequest(source); + return SocialAuthAuthorizeResp.builder() + .authorizeUrl(authRequest.authorize(AuthStateUtils.createState())) + .build(); + } + + @Operation(summary = "三方账号登录", description = "三方账号登录") + @Parameter(name = "source", description = "来源", example = "gitee", in = ParameterIn.PATH) + @PostMapping("/{source}") + public LoginResp login(@PathVariable String source, @RequestBody AuthCallback callback) { + if (StpUtil.isLogin()) { + StpUtil.logout(); + } + AuthRequest authRequest = this.getAuthRequest(source); + AuthResponse response = authRequest.login(callback); + ValidationUtils.throwIf(!response.ok(), response.getMsg()); + AuthUser authUser = response.getData(); + LoginUser loginUser = loginService.socialLogin(authUser); + return LoginResp.builder() + .token(loginUser.getToken()) + .aiBotToken(loginUser.getAiBotToken()) + .sdBotToken(loginUser.getSdBotToken()) + .build(); + } + + private AuthRequest getAuthRequest(String source) { + try { + return authRequestFactory.get(source); + } catch (Exception e) { + throw new BadRequestException("暂不支持 [%s] 平台账号登录".formatted(source)); + } + } +} \ No newline at end of file diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/AiBotController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/AiBotController.java new file mode 100644 index 0000000..a63e383 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/AiBotController.java @@ -0,0 +1,34 @@ +package top.continew.admin.controller.business; + +import com.alibaba.fastjson2.JSONObject; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import top.continew.admin.common.util.AiBotUtil; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.web.model.R; + +/** + * @author 张波 + * @since 2024/10/26 17:39 + */ +@Tag(name = "AI BOT接口") +@RestController +@RequestMapping("/aiBot") +public class AiBotController { + + @Operation(summary = "分页获取个人图库") + @GetMapping(value = "/getImagePage") + public PageResp getImagePage(Boolean isAsc, PageQuery pageQuery) { + return AiBotUtil.getImagePage(pageQuery.getPage(), pageQuery.getSize(), isAsc); + } + + @Operation(summary = "获取图片拓展信息") + @GetMapping(value = "/getImageExif") + public R getImageExif(String imageUrl) { + return R.ok(AiBotUtil.getImageExif(imageUrl)); + } +} diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/BillingController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/BillingController.java new file mode 100644 index 0000000..7f9cf43 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/BillingController.java @@ -0,0 +1,62 @@ +package top.continew.admin.controller.business; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import top.continew.admin.business.model.entity.TaskRecordHistoryDO; +import top.continew.admin.business.model.req.*; +import top.continew.admin.business.model.resp.BillingDeductResp; +import top.continew.admin.business.model.resp.TaskCallbackResp; +import top.continew.admin.business.service.BillingService; + +/** + * 计费控制器 + * + * @author jiay + * @since 2025/08/28 00:00 + */ +@Tag(name = "计费管理") +@RequiredArgsConstructor +@RestController +@RequestMapping("/billing") +public class BillingController { + + private final BillingService billingService; + + @Operation(summary = "第三方平台计费预扣减(本地计算费用)", description = "第三方平台计费预扣减接口,本地根据计费规则计算预扣减金额") + @PostMapping("/judgeBalance") + public BillingDeductResp judgeBalance(@RequestBody @Valid BillingJudgeReq req) { + return billingService.judgeBalance(req); + } + + @Operation(summary = "第三方平台计费预扣减(第三方传入费用)", description = "第三方平台计费预扣减接口,使用第三方平台传入的预扣减金额") + @PostMapping("/judgeBalanceWithAmount") + public BillingDeductResp judgeBalanceWithAmount(@RequestBody @Valid BillingJudgeWithAmountReq req) { + return billingService.judgeBalanceWithAmount(req); + } + + @Operation(summary = "任务完成回调(本地计算费用)", description = "第三方平台任务完成后回调接口,本地根据计费规则计算实际消费金额") + @PostMapping("/callback") + public TaskCallbackResp taskCallback(@RequestBody @Valid TaskCallbackReq req) { + return billingService.taskCallback(req); + } + + @Operation(summary = "查询任务集合", description = "根据根任务ID查询任务集合") + @PostMapping("/queryTaskListByRootId") + public Page queryTaskListByRootId(@RequestBody @Valid TaskQueryReq req) { + return billingService.queryTaskListByRootId(req); + } + + @Operation(summary = "任务完成回调(第三方传入费用)", description = "第三方平台任务完成后回调接口,使用第三方平台传入的实际消费金额") + @PostMapping("/callbackWithAmount") + public TaskCallbackResp taskCallbackWithAmount(@RequestBody @Valid TaskCallbackWithAmountReq req) { + return billingService.taskCallbackWithAmount(req); + } +} \ No newline at end of file diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/BillingRuleController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/BillingRuleController.java new file mode 100644 index 0000000..6467229 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/BillingRuleController.java @@ -0,0 +1,30 @@ +package top.continew.admin.controller.business; + +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.RestController; +import top.continew.admin.business.model.query.BillingRuleQuery; +import top.continew.admin.business.model.req.BillingRuleReq; +import top.continew.admin.business.model.resp.BillingRuleResp; +import top.continew.admin.business.service.BillingRuleService; +import top.continew.starter.extension.crud.annotation.CrudRequestMapping; +import top.continew.starter.extension.crud.controller.BaseController; +import top.continew.starter.extension.crud.enums.Api; + +/** + * 计费规则管理 API + * + * @author jiay + * @since 2025/11/14 + */ +@Tag(name = "计费规则管理 API") +@RestController +@CrudRequestMapping(value = "/billing/rule", api = {Api.LIST, Api.PAGE, Api.GET, Api.ADD, Api.UPDATE, Api.DELETE, + Api.EXPORT}) +public class BillingRuleController extends BaseController { + + @Override + protected void checkPermission(Api api) { + // 可以在这里添加权限检查 + // 例如: StpUtil.checkPermission("billing:rule:" + api.name().toLowerCase()); + } +} diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/ConsumptionHistoryController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/ConsumptionHistoryController.java new file mode 100644 index 0000000..4acf103 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/ConsumptionHistoryController.java @@ -0,0 +1,83 @@ +package top.continew.admin.controller.business; + +/** + * 用户充值消费历史记录管理 API + * + * @author jiay + * @since 2025/05/23 07:51 + */ + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import top.continew.admin.business.model.query.ConsumptionHistoryQuery; +import top.continew.admin.business.model.req.ConsumptionHistoryAddReq; +import top.continew.admin.business.model.req.ConsumptionHistoryEditReq; +import top.continew.admin.business.model.req.ConsumptionReq; +import top.continew.admin.business.model.resp.ConsumptionHistoryResp; +import top.continew.admin.business.service.ConsumptionHistoryService; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.web.model.R; + +import java.util.List; + +@Tag(name = "用户充值消费历史记录管理 API") +@RestController +@RequiredArgsConstructor +@RequestMapping(value = "/consumptionHistory") +public class ConsumptionHistoryController { + + private final ConsumptionHistoryService consumptionHistoryService; + + @Operation(summary = "用户充值消费历史记录", description = "用户充值消费历史记录") + @PostMapping("/add") + public R add(@RequestBody @Valid ConsumptionHistoryAddReq req) { + return consumptionHistoryService.addConsumptionHistory(req) ? R.ok() : R.fail(); + } + + @Operation(summary = "编辑用户充值消费历史记录", description = "编辑用户充值消费历史记录") + @PostMapping("/edit") + public R edit(@RequestBody @Valid ConsumptionHistoryEditReq req) { + return consumptionHistoryService.editConsumptionHistory(req) ? R.ok() : R.fail(); + } + + @Operation(summary = "删除用户充值消费历史记录", description = "删除用户充值消费历史记录") + @DeleteMapping("/delete") + public R delete(@RequestParam Long id) { + return consumptionHistoryService.deleteConsumptionHistory(id) ? R.ok() : R.fail(); + } + + @Operation(summary = "获取用户充值消费历史记录", description = "获取用户充值消费历史记录") + @GetMapping("/get") + public R getConsumptionHistory(@RequestParam(name = "id", required = true) Long id) { + return R.ok(consumptionHistoryService.getConsumptionHistory(id)); + } + + @Operation(summary = "查询用户充值消费历史记录列表", description = "查询用户充值消费历史记录列表") + @GetMapping("/list") + public R> getConsumptionHistoryList(ConsumptionHistoryQuery query) { + return R.ok(consumptionHistoryService.getConsumptionHistoryList(query)); + } + + @Operation(summary = "分页查询用户充值消费历史记录列表", description = "分页查询用户充值消费历史记录列表") + @GetMapping + public R> page(ConsumptionHistoryQuery query, @Validated PageQuery pageQuery) { + return R.ok(consumptionHistoryService.getConsumptionHistoryPage(query, pageQuery)); + } + + @Operation(summary = "判断余额是否充足", description = "判断余额是否充足") + @GetMapping("/checkBalance") + public R checkBalance(ConsumptionReq req) { + return consumptionHistoryService.checkBalance(req); + } +} \ No newline at end of file diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/CopyrightController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/CopyrightController.java new file mode 100644 index 0000000..e8b2c5c --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/CopyrightController.java @@ -0,0 +1,76 @@ +package top.continew.admin.controller.business; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import top.continew.admin.business.model.query.CopyrightPageQuery; +import top.continew.admin.business.model.query.CopyrightQuery; +import top.continew.admin.business.model.req.BatchReq; +import top.continew.admin.business.model.req.CopyrightReq; +import top.continew.admin.business.model.resp.CopyrightDetailResp; +import top.continew.admin.business.model.resp.CopyrightResp; +import top.continew.admin.business.service.CopyrightService; +import top.continew.starter.extension.crud.annotation.CrudRequestMapping; +import top.continew.starter.extension.crud.controller.BaseController; +import top.continew.starter.extension.crud.enums.Api; +import top.continew.starter.extension.crud.model.query.SortQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.log.core.annotation.Log; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 数字人-文案管理 API + * + * @author 王志维 + * @since 2025/12/29 + */ +@Tag(name = "数字人-文案管理 API") +@RestController +@RequiredArgsConstructor +@CrudRequestMapping(value = "/business/copyright", api = {Api.GET, Api.UPDATE, Api.LIST}) +public class CopyrightController extends BaseController { + + @Log + @Operation(summary = "查询文案", description = "查询文案列表") + @ResponseBody + @GetMapping({"/list"}) + public List list(CopyrightQuery query, SortQuery sortQuery) { + return baseService.list(query, sortQuery); + } + + @Log + @ResponseBody + @Operation(summary = "新增文案", description = "新增文案") + @PostMapping() + public Long addCopyright(@RequestBody CopyrightReq req) { + return baseService.add(req); + } + + @Log + @Operation(summary = "分页查询文案", description = "分页查询文案列表") + @ResponseBody + @GetMapping({"/page"}) + public PageResp page(CopyrightQuery query, CopyrightPageQuery pageQuery) { + return baseService.pageList(query, pageQuery); + } + + @Log + @Operation(summary = "批量删除文案", description = "根据文案ID列表批量删除文案") + @PostMapping("/batchDelete") + public void deleteBatch(@Validated @RequestBody BatchReq req) { + baseService.delete(req.getIds()); + } + + @Log + @Operation(summary = "批量删除文案(通过路径参数)", description = "根据路径中的ID列表批量删除文案,ID以逗号分隔") + @DeleteMapping("/{ids}") + public void delete(@PathVariable("ids") String ids) { + List idList = Arrays.stream(ids.split(",")).map(Long::valueOf).collect(Collectors.toList()); + baseService.delete(idList); + } +} \ No newline at end of file diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/CoursesController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/CoursesController.java new file mode 100644 index 0000000..80024e3 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/CoursesController.java @@ -0,0 +1,127 @@ +package top.continew.admin.controller.business; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; +import top.continew.admin.business.enums.AssignmentsTypeEnum; +import top.continew.admin.business.model.req.AssignmentSubmissionsReq; +import top.continew.admin.business.model.req.AssignmentsCorrectReq; +import top.continew.admin.business.model.req.AssignmentsReq; +import top.continew.admin.business.model.req.CoursesReq; +import top.continew.admin.business.model.resp.*; +import top.continew.admin.business.service.AssignmentSubmissionsService; +import top.continew.admin.business.service.AssignmentsService; +import top.continew.admin.business.service.CoursesService; +import top.continew.admin.system.service.DeptService; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.BaseIdResp; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.web.model.R; + +import java.util.List; + +/** + * @author 张波 + * @since 2024/10/22 12:26 + */ +@Tag(name = "课程 API") +@RestController +@RequiredArgsConstructor +@RequestMapping(value = "/courses") +public class CoursesController { + + private final CoursesService coursesService; + + private final AssignmentsService assignmentsService; + + private final AssignmentSubmissionsService assignmentSubmissionsService; + + private final DeptService deptService; + + @Operation(summary = "创建课程", description = "创建课程") + @PostMapping("/add") + public R add(@RequestBody CoursesReq req) { + coursesService.add(req); + return R.ok(); + } + + @Operation(summary = "创建作业", description = "创建作业") + @PostMapping("/assignments/add") + public BaseIdResp assignmentsAdd(@Valid @RequestBody AssignmentsReq assignmentsReq) { + Long id = assignmentsService.add(assignmentsReq); + return BaseIdResp.builder().id(id).build(); + } + + @Operation(summary = "删除作业", description = "删除作业") + @DeleteMapping("/assignments/delete") + public R assignmentsDelete(Long id) { + assignmentsService.removeById(id); + return R.ok(); + } + + @Operation(summary = "查询课程列表", description = "查询课程列表") + @Parameter(name = "role", description = "学生角色或者老师角色", example = "pupil/teacher", in = ParameterIn.PATH) + //@GetMapping("/list/{role}/{db}") + @GetMapping("/list/{role}") + public PageResp list(@PathVariable("role") String role, + //@PathVariable("db") String db, + PageQuery pageQuery) { + //todo + /* LoginUser loginUser = LoginHelper.getLoginUser(); + DeptResp dept = deptService.get(loginUser.getDeptId()); + String dbName = dept.getDbName(); + if (dept.getParentId() != 0) { + DeptDO deptDO = deptService.selectDoById(loginUser.getDeptId()); + dbName = deptDO.getDbName(); + }*/ + return coursesService.list(null, role, pageQuery); + } + + @Operation(summary = "查询课程作业列表", description = "查询课程作业列表") + @Parameter(name = "coursesId", description = "课程ID", example = "1", in = ParameterIn.PATH) + @Parameter(name = "type", description = "类型(作业/考试)", example = "1", in = ParameterIn.PATH) + @Parameter(name = "role", description = "学生角色或者老师角色", example = "pupil/teacher", in = ParameterIn.PATH) + @GetMapping("/assignments/list/{coursesId}/{type}/{role}") + public R> assignmentsList(@PathVariable Long coursesId, + @PathVariable AssignmentsTypeEnum type, + @PathVariable("role") String role) { + return R.ok(coursesService.assignmentsList(coursesId, type, role)); + } + + @Operation(summary = "查询作业完成情况") + @GetMapping("/assignments/degreeOfCompletion") + public PageResp assignmentsDegreeOfCompletion(Long assignmentsId, PageQuery pageQuery) { + return assignmentSubmissionsService.degreeOfCompletion(assignmentsId, pageQuery); + } + + @Operation(summary = "作业详情查询") + @GetMapping("/assignments/detail") + public R assignmentsDetail(Long assignmentsId) { + return R.ok(assignmentsService.detail(assignmentsId)); + } + + @Operation(summary = "作业提交记录详情查询") + @GetMapping("/assignments/submissions/detail") + public R assignmentsSubmissionSDetail(Long submissionsId) { + return R.ok(assignmentSubmissionsService.detail(submissionsId)); + } + + @Operation(summary = "提交作业") + @PutMapping("/assignments/submit") + public R assignmentsSubmit(@Valid @RequestBody AssignmentSubmissionsReq req) { + assignmentSubmissionsService.submit(req); + return R.ok(); + } + + @Operation(summary = "批改作业") + @PutMapping("/assignments/correct") + public R assignmentsCorrect(@Valid @RequestBody AssignmentsCorrectReq req) { + assignmentSubmissionsService.correct(req); + return R.ok(); + } + +} diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/DigitalHumanController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/DigitalHumanController.java new file mode 100644 index 0000000..73811d6 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/DigitalHumanController.java @@ -0,0 +1,76 @@ +package top.continew.admin.controller.business; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import top.continew.admin.business.model.query.DigitalHumanPageQuery; +import top.continew.admin.business.model.query.DigitalHumanQuery; +import top.continew.admin.business.model.req.BatchReq; +import top.continew.admin.business.model.req.DigitalHumanReq; +import top.continew.admin.business.model.resp.DigitalHumanDetailResp; +import top.continew.admin.business.model.resp.DigitalHumanResp; +import top.continew.admin.business.service.DigitalHumanService; +import top.continew.starter.extension.crud.annotation.CrudRequestMapping; +import top.continew.starter.extension.crud.controller.BaseController; +import top.continew.starter.extension.crud.enums.Api; +import top.continew.starter.extension.crud.model.query.SortQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.log.core.annotation.Log; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 数字人任务管理 API + * + * @author 王志维 + * @since 2025/12/29 + */ +@Tag(name = "数字人任务管理 API") +@RestController +@RequiredArgsConstructor +@CrudRequestMapping(value = "/business/digitalHuman", api = {Api.GET, Api.UPDATE, Api.LIST}) +public class DigitalHumanController extends BaseController { + + @Log + @Operation(summary = "查询数字人任务", description = "查询数字人任务列表") + @ResponseBody + @GetMapping({"/list"}) + public List list(DigitalHumanQuery query, SortQuery sortQuery) { + return baseService.list(query, sortQuery); + } + + @Log + @ResponseBody + @Operation(summary = "新增数字人任务", description = "新增数字人任务") + @PostMapping() + public Long addDigitalHuman(@RequestBody DigitalHumanReq req) { + return baseService.add(req); + } + + @Log + @Operation(summary = "分页查询数字人任务", description = "分页查询数字人任务列表") + @ResponseBody + @GetMapping({"/page"}) + public PageResp page(DigitalHumanQuery query, DigitalHumanPageQuery pageQuery) { + return baseService.pageList(query, pageQuery); + } + + @Log + @Operation(summary = "批量删除数字人任务", description = "根据数字人任务ID列表批量删除数字人任务") + @PostMapping("/batchDelete") + public void deleteBatch(@Validated @RequestBody BatchReq req) { + baseService.delete(req.getIds()); + } + + @Log + @Operation(summary = "批量删除数字人任务(通过路径参数)", description = "根据路径中的ID列表批量删除数字人任务,ID以逗号分隔") + @DeleteMapping("/{ids}") + public void delete(@PathVariable("ids") String ids) { + List idList = Arrays.stream(ids.split(",")).map(Long::valueOf).collect(Collectors.toList()); + baseService.delete(idList); + } +} \ No newline at end of file diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/EffectController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/EffectController.java new file mode 100644 index 0000000..4c7c860 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/EffectController.java @@ -0,0 +1,76 @@ +package top.continew.admin.controller.business; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import top.continew.admin.business.model.query.EffectPageQuery; +import top.continew.admin.business.model.query.EffectQuery; +import top.continew.admin.business.model.req.BatchReq; +import top.continew.admin.business.model.req.EffectReq; +import top.continew.admin.business.model.resp.EffectDetailResp; +import top.continew.admin.business.model.resp.EffectResp; +import top.continew.admin.business.service.EffectService; +import top.continew.starter.extension.crud.annotation.CrudRequestMapping; +import top.continew.starter.extension.crud.controller.BaseController; +import top.continew.starter.extension.crud.enums.Api; +import top.continew.starter.extension.crud.model.query.SortQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.log.core.annotation.Log; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 数字人-特效管理 API + * + * @author 王志维 + * @since 2025/12/29 + */ +@Tag(name = "数字人-特效管理 API") +@RestController +@RequiredArgsConstructor +@CrudRequestMapping(value = "/business/effect", api = {Api.GET, Api.UPDATE, Api.LIST}) +public class EffectController extends BaseController { + + @Log + @Operation(summary = "查询特效", description = "查询特效列表") + @ResponseBody + @GetMapping({"/list"}) + public List list(EffectQuery query, SortQuery sortQuery) { + return baseService.list(query, sortQuery); + } + + @Log + @ResponseBody + @Operation(summary = "新增特效", description = "新增特效") + @PostMapping() + public Long addEffect(@RequestBody EffectReq req) { + return baseService.add(req); + } + + @Log + @Operation(summary = "分页查询特效", description = "分页查询特效列表") + @ResponseBody + @GetMapping({"/page"}) + public PageResp page(EffectQuery query, EffectPageQuery pageQuery) { + return baseService.pageList(query, pageQuery); + } + + @Log + @Operation(summary = "批量删除特效", description = "根据特效ID列表批量删除特效") + @PostMapping("/batchDelete") + public void deleteBatch(@Validated @RequestBody BatchReq req) { + baseService.delete(req.getIds()); + } + + @Log + @Operation(summary = "批量删除特效(通过路径参数)", description = "根据路径中的ID列表批量删除特效,ID以逗号分隔") + @DeleteMapping("/{ids}") + public void delete(@PathVariable("ids") String ids) { + List idList = Arrays.stream(ids.split(",")).map(Long::valueOf).collect(Collectors.toList()); + baseService.delete(idList); + } +} \ No newline at end of file diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/EffectTemplateController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/EffectTemplateController.java new file mode 100644 index 0000000..5e164e0 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/EffectTemplateController.java @@ -0,0 +1,78 @@ +package top.continew.admin.controller.business; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import top.continew.admin.business.model.query.EffectTemplatePageQuery; +import top.continew.admin.business.model.query.EffectTemplateQuery; +import top.continew.admin.business.model.req.BatchReq; +import top.continew.admin.business.model.req.EffectTemplateReq; +import top.continew.admin.business.model.resp.EffectTemplateDetailResp; +import top.continew.admin.business.model.resp.EffectTemplateResp; +import top.continew.admin.business.service.EffectTemplateService; +import top.continew.starter.extension.crud.annotation.CrudRequestMapping; +import top.continew.starter.extension.crud.controller.BaseController; +import top.continew.starter.extension.crud.enums.Api; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.log.core.annotation.Log; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 数字人-特效模板管理 API + * + * @author 王志维 + * @since 2025/12/29 + */ +@Tag(name = "数字人-特效模板管理 API") +@RestController +@RequiredArgsConstructor +@CrudRequestMapping(value = "/business/effectTemplate", api = {Api.GET, Api.UPDATE, Api.LIST}) +public class EffectTemplateController extends BaseController { + + @Log + @ResponseBody + @Operation(summary = "新增特效模板", description = "新增特效模板") + @PostMapping() + public Long addEffectTemplate(@RequestBody EffectTemplateReq req) { + return baseService.add(req); + } + + @Log + @Operation(summary = "分页查询特效模板", description = "分页查询特效模板列表") + @ResponseBody + @GetMapping({"/page"}) + public PageResp page(EffectTemplateQuery query, EffectTemplatePageQuery pageQuery) { + return baseService.pageList(query, pageQuery); + } + + @Log + @Operation(summary = "批量删除特效模板", description = "根据特效模板ID列表批量删除特效模板") + @PostMapping("/batchDelete") + public void deleteBatch(@Validated @RequestBody BatchReq req) { + baseService.delete(req.getIds()); + } + + @Log + @Operation(summary = "批量删除特效模板(通过路径参数)", description = "根据路径中的ID列表批量删除特效模板,ID以逗号分隔") + @DeleteMapping("/{ids}") + public void delete(@PathVariable("ids") String ids) { + List idList = Arrays.stream(ids.split(",")).map(Long::valueOf).collect(Collectors.toList()); + baseService.delete(idList); + } + + @Log + @Operation(summary = "上传特效模板", description = "上传特效模板文件") + @PostMapping("/uploadTemplate") + public Long uploadTemplate(@RequestParam("file") MultipartFile file, + @RequestParam(value = "name", required = false) String name, + @RequestParam(value = "type", required = false) String type, + @RequestParam(value = "userId", required = false) String userId) { + return baseService.uploadTemplate(file, name, type, userId); + } +} \ No newline at end of file diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/FreeQuotaConfigController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/FreeQuotaConfigController.java new file mode 100644 index 0000000..9a94579 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/FreeQuotaConfigController.java @@ -0,0 +1,30 @@ +package top.continew.admin.controller.business; + +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.RestController; +import top.continew.admin.business.model.query.FreeQuotaConfigQuery; +import top.continew.admin.business.model.req.FreeQuotaConfigReq; +import top.continew.admin.business.model.resp.FreeQuotaConfigResp; +import top.continew.admin.business.service.FreeQuotaConfigService; +import top.continew.starter.extension.crud.annotation.CrudRequestMapping; +import top.continew.starter.extension.crud.controller.BaseController; +import top.continew.starter.extension.crud.enums.Api; + +/** + * 免费额度配置管理 API + * + * @author jiay + * @since 2025/11/17 + */ +@Tag(name = "免费额度配置管理 API") +@RestController +@CrudRequestMapping(value = "/free-quota/config", api = {Api.LIST, Api.PAGE, Api.GET, Api.ADD, Api.UPDATE, Api.DELETE, + Api.EXPORT}) +public class FreeQuotaConfigController extends BaseController { + + @Override + protected void checkPermission(Api api) { + // 可以在这里添加权限检查 + // 例如: StpUtil.checkPermission("free-quota:config:" + api.name().toLowerCase()); + } +} diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/FreeQuotaGrantController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/FreeQuotaGrantController.java new file mode 100644 index 0000000..f36f4cd --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/FreeQuotaGrantController.java @@ -0,0 +1,48 @@ +package top.continew.admin.controller.business; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; +import top.continew.admin.business.model.req.FreeQuotaGrantReq; +import top.continew.admin.business.model.resp.FreeQuotaGrantResp; +import top.continew.admin.business.service.FreeQuotaGrantService; + +/** + * 免费额度赠送 API + * + * @author jiay + * @since 2025/11/17 + */ +@Tag(name = "免费额度赠送 API") +@RequiredArgsConstructor +@RestController +@RequestMapping("/free-quota/grant") +public class FreeQuotaGrantController { + + private final FreeQuotaGrantService freeQuotaGrantService; + + @Operation(summary = "批量赠送免费额度", description = "支持批量赠送给用户或部门,可自定义额度或使用默认配置") + @PostMapping("/batch") + public FreeQuotaGrantResp batchGrant(@RequestBody @Valid FreeQuotaGrantReq req) { + //return freeQuotaGrantService.batchGrant(req); + + return FreeQuotaGrantResp.builder().build(); + } + + @Operation(summary = "给单个用户赠送默认免费额度", description = "给指定用户赠送所有启用的默认免费额度") + @PostMapping("/user/{userId}") + public FreeQuotaGrantResp grantToUser(@Parameter(description = "用户ID", required = true) @PathVariable Long userId) { + //return freeQuotaGrantService.grantToUser(userId); + return FreeQuotaGrantResp.builder().build(); + } + + @Operation(summary = "给部门所有用户赠送默认免费额度", description = "给指定部门下所有用户赠送所有启用的默认免费额度") + @PostMapping("/dept/{deptId}") + public FreeQuotaGrantResp grantToDept(@Parameter(description = "部门ID", required = true) @PathVariable Long deptId) { + //return freeQuotaGrantService.grantToDept(deptId); + return FreeQuotaGrantResp.builder().build(); + } +} diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/HomeworkController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/HomeworkController.java new file mode 100644 index 0000000..2c788a8 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/HomeworkController.java @@ -0,0 +1,69 @@ +package top.continew.admin.controller.business; + +import io.swagger.v3.oas.annotations.Operation; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import top.continew.admin.business.model.query.HomeworkQuery; +import top.continew.admin.business.model.req.HomeworkAddReq; +import top.continew.admin.business.model.req.HomeworkEditReq; +import top.continew.admin.business.model.resp.HomeworkResp; +import top.continew.admin.business.service.HomeworkService; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.*; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.web.model.R; + +import java.util.List; + +/** + * 作业管理 API + * + * @author jiay + * @since 2025/06/26 10:24 + */ +@Tag(name = "作业 API") +@RestController +@RequiredArgsConstructor +@RequestMapping(value = "/homework") +public class HomeworkController { + private final HomeworkService homeworkService; + + @Operation(summary = "添加作业", description = "添加作业") + @PostMapping("/add") + public R add(@RequestBody @Valid HomeworkAddReq req) { + return R.ok(homeworkService.addHomework(req)); + } + + @Operation(summary = "编辑作业", description = "编辑作业") + @PostMapping("/edit") + public R edit(@RequestBody @Valid HomeworkEditReq req) { + return homeworkService.editHomework(req) ? R.ok() : R.fail(); + } + + @Operation(summary = "删除作业", description = "删除作业") + @DeleteMapping("/delete") + public R delete(@RequestParam Long id) { + return homeworkService.deleteHomework(id) ? R.ok() : R.fail(); + } + + @Operation(summary = "获取作业", description = "获取作业") + @GetMapping("/get") + public R getHomework(@RequestParam(name = "id", required = true) Long id) { + return R.ok(homeworkService.getHomework(id)); + } + + @Operation(summary = "查询作业列表", description = "查询作业列表") + @GetMapping("/list") + public R> getHomeworkList(HomeworkQuery query) { + return R.ok(homeworkService.getHomeworkList(query)); + } + + @Operation(summary = "分页查询作业列表", description = "分页查询作业列表") + @GetMapping + public R> page(HomeworkQuery query, @Validated PageQuery pageQuery) { + return R.ok(homeworkService.getHomeworkPage(query, pageQuery)); + } + +} \ No newline at end of file diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/HuandaResourceController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/HuandaResourceController.java new file mode 100644 index 0000000..ab96146 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/HuandaResourceController.java @@ -0,0 +1,77 @@ +package top.continew.admin.controller.business; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import top.continew.admin.business.model.query.HuandaResourcePageQuery; +import top.continew.admin.business.model.query.HuandaResourceQuery; +import top.continew.admin.business.model.req.BatchReq; +import top.continew.admin.business.model.req.HuandaResourceReq; +import top.continew.admin.business.model.resp.HuandaResourceDetailResp; +import top.continew.admin.business.model.resp.HuandaResourceResp; +import top.continew.admin.business.service.HuandaResourceService; +import top.continew.starter.extension.crud.annotation.CrudRequestMapping; +import top.continew.starter.extension.crud.controller.BaseController; +import top.continew.starter.extension.crud.enums.Api; +import top.continew.starter.extension.crud.model.query.SortQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.log.core.annotation.Log; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 换哒-文件资源管理 API + * + * @author 王志维 + * @since 2025/12/23 16:53 + */ +@Tag(name = "换哒-文件资源管理 API") +@RestController +@RequiredArgsConstructor +@CrudRequestMapping(value = "/business/huandaResource", api = {Api.GET, Api.UPDATE, Api.LIST}) +public class HuandaResourceController extends BaseController { + + @Log + @Operation(summary = "查询换哒资源", description = "查询换哒资源列表") + @ResponseBody + @GetMapping({"/list"}) + public List list(HuandaResourceQuery query, SortQuery sortQuery) { + return baseService.list(query, sortQuery); + } + + @Log + @ResponseBody + @Operation(summary = "新增换哒资源", description = "新增换哒资源") + @PostMapping() + public Long addResource(@RequestBody HuandaResourceReq req) { + return baseService.add(req); + } + + @Log + @Operation(summary = "分页查询换哒资源", description = "分页查询换哒资源列表") + @ResponseBody + @GetMapping({"/page"}) + public PageResp page(HuandaResourceQuery query, HuandaResourcePageQuery pageQuery) { + return baseService.pageList(query, pageQuery); + } + + @Log + @Operation(summary = "批量删除换哒资源", description = "根据资源ID列表批量删除换哒资源") + @PostMapping("/batchDelete") + public void deleteBatch(@Validated @RequestBody BatchReq req) { + baseService.delete(req.getIds()); + } + + @Log + @Operation(summary = "批量删除换哒资源(通过路径参数)", description = "根据路径中的ID列表批量删除换哒资源,ID以逗号分隔") + @DeleteMapping("/{ids}") + public void delete(@PathVariable("ids") String ids) { + List idList = Arrays.stream(ids.split(",")).map(Long::valueOf).collect(Collectors.toList()); + baseService.delete(idList); + } + +} \ No newline at end of file diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/ImageController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/ImageController.java new file mode 100644 index 0000000..26c39fa --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/ImageController.java @@ -0,0 +1,41 @@ +package top.continew.admin.controller.business; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import top.continew.admin.business.service.AssignmentSubmissionsService; +import top.continew.admin.system.service.DeptService; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; + +/** + * @author 张波 + * @since 2024/10/26 18:31 + */ +@Tag(name = "图片接口") +@RestController +@RequiredArgsConstructor +@RequestMapping("/image") +public class ImageController { + + private final AssignmentSubmissionsService assignmentSubmissionsService; + + private final DeptService deptService; + + @Operation(summary = "获取优秀图片列表") + @GetMapping("/getGoodImage") + public PageResp getGoodImageList(PageQuery pageQuery) { + //todo + /* LoginUser loginUser = LoginHelper.getLoginUser(); + DeptResp dept = deptService.get(loginUser.getDeptId()); + String dbName = dept.getDbName(); + if (dept.getParentId() != 0) { + DeptDO deptDO = deptService.selectDoById(loginUser.getDeptId()); + dbName = deptDO.getDbName(); + }*/ + return assignmentSubmissionsService.getGoodImageList(null, pageQuery); + } +} diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/ModelController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/ModelController.java new file mode 100644 index 0000000..3a7dff4 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/ModelController.java @@ -0,0 +1,27 @@ +package top.continew.admin.controller.business; + +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.RestController; +import top.continew.admin.business.model.query.ModelQuery; +import top.continew.admin.business.model.req.ModelReq; +import top.continew.admin.business.model.resp.ModelResp; +import top.continew.admin.business.service.ModelService; +import top.continew.starter.extension.crud.annotation.CrudRequestMapping; +import top.continew.starter.extension.crud.controller.BaseController; +import top.continew.starter.extension.crud.enums.Api; + +/** + * 模型 API + * + * @author 张波 + * @since 2024/10/19 14:30 + */ +@Tag(name = "模型 API") +@RestController +@CrudRequestMapping(value = "/model", api = {Api.LIST, Api.PAGE, Api.GET, Api.ADD, Api.UPDATE, Api.DELETE, Api.EXPORT}) +public class ModelController extends BaseController { + + @Override + protected void checkPermission(Api api) { + } +} diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/OrderAudioController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/OrderAudioController.java new file mode 100644 index 0000000..6771381 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/OrderAudioController.java @@ -0,0 +1,32 @@ +package top.continew.admin.controller.business; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import top.continew.admin.business.model.req.OrderAudioReq; +import top.continew.admin.business.service.OrderAudioService; +import top.continew.starter.web.model.R; + +/** + * @author hzy + * @since 2025/10/22 12:26 + */ +@Tag(name = "音频任务订单 API") +@RestController +@RequiredArgsConstructor +@RequestMapping(value = "/audio") +public class OrderAudioController { + + private final OrderAudioService orderAudioService; + + @Operation(summary = "创建音频任务订单", description = "创建音频任务订单") + @PostMapping("/add") + public R add(@RequestBody OrderAudioReq req) { + orderAudioService.add(req); + return R.ok(); + } +} diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/PlantformBalanceController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/PlantformBalanceController.java new file mode 100644 index 0000000..25b3e6c --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/PlantformBalanceController.java @@ -0,0 +1,101 @@ +package top.continew.admin.controller.business; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import top.continew.admin.business.model.query.PlantformBalanceQuery; +import top.continew.admin.business.model.req.CheckPlantBalanceReq; +import top.continew.admin.business.model.req.PlantformBalanceAddReq; +import top.continew.admin.business.model.req.PlantformBalanceEditReq; +import top.continew.admin.business.model.resp.PlantformBalanceResp; +import top.continew.admin.business.service.PlantformBalanceService; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.web.model.R; + +import java.util.List; +import cn.dev33.satoken.stp.StpUtil; + +/** + * 用户子账号余额 API + * + * @author jiay + * @since 2025/06/24 16:47 + */ +@Tag(name = "用户子账号余额 API") +@RestController +@RequiredArgsConstructor +@RequestMapping(value = "/plantformBalance") +public class PlantformBalanceController { + private final PlantformBalanceService plantformBalanceService; + + @Operation(summary = "添加用户子账号余额", description = "添加用户子账号余额") + @PostMapping("/add") + public R add(@RequestBody @Valid PlantformBalanceAddReq req) { + return R.ok(plantformBalanceService.addPlantformBalance(req)); + } + + @Operation(summary = "编辑用户子账号余额", description = "编辑用户子账号余额") + @PostMapping("/edit") + public R edit(@RequestBody @Valid PlantformBalanceEditReq req) { + return plantformBalanceService.editPlantformBalance(req) ? R.ok() : R.fail(); + } + + @Operation(summary = "删除用户子账号余额", description = "删除用户子账号余额") + @DeleteMapping("/delete") + public R delete(@RequestParam Long id) { + return plantformBalanceService.deletePlantformBalance(id) ? R.ok() : R.fail(); + } + + @Operation(summary = "获取用户子账号余额", description = "获取用户子账号余额") + @GetMapping("/get") + public R getPlantformBalance(@RequestParam(name = "id", required = true) Long id) { + return R.ok(plantformBalanceService.getPlantformBalance(id)); + } + + @Operation(summary = "查询用户子账号余额列表", description = "查询用户子账号余额列表") + @GetMapping("/list") + public R> getPlantformBalanceList(PlantformBalanceQuery query) { + return R.ok(plantformBalanceService.getPlantformBalanceList(query)); + } + + @Operation(summary = "分页查询用户子账号余额列表", description = "分页查询用户子账号余额列表") + @GetMapping + public R> page(PlantformBalanceQuery query, @Validated PageQuery pageQuery) { + return R.ok(plantformBalanceService.getPlantformBalancePage(query, pageQuery)); + } + + @Operation(summary = "添加子账户余额", description = "添加子账户余额") + @GetMapping("/addPlantBalance") + public R addPlantBalance(CheckPlantBalanceReq req) { + return plantformBalanceService.checkPlantBalance(null); + } + + @Operation(summary = "减少子账户余额", description = "减少子账户余额") + @GetMapping("/reducePlantBalance") + public R reducePlantBalance(CheckPlantBalanceReq req) { + return plantformBalanceService.checkPlantBalance(null); + } + + @Operation(summary = "获取当前用户子账户余额列表", description = "根据token解析userId,获取用户所有子账户余额,按taskType区分") + @GetMapping("/userBalances") + public R> getUserBalances() { + Long userId = StpUtil.getLoginIdAsLong(); + return R.ok(plantformBalanceService.getUserBalancesByUserId(userId)); + } + + @Operation(summary = "判断平台子账户余额是否充足", description = "判断平台子账户余额是否充足") + @GetMapping("/checkPlantBalance") + public R checkPlantBalance(CheckPlantBalanceReq req) { + return plantformBalanceService.checkPlantBalance(req); + } +} \ No newline at end of file diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/ScoreController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/ScoreController.java new file mode 100644 index 0000000..45675c5 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/ScoreController.java @@ -0,0 +1,67 @@ +package top.continew.admin.controller.business; + +import io.swagger.v3.oas.annotations.Operation; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import top.continew.admin.business.model.query.ScoreQuery; +import top.continew.admin.business.model.req.ScoreAddReq; +import top.continew.admin.business.model.req.ScoreEditReq; +import top.continew.admin.business.model.resp.ScoreResp; +import top.continew.admin.business.service.ScoreService; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.*; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.web.model.R; +import java.util.List; + +/** + * 分数管理 API + * + * @author jiay + * @since 2025/06/26 10:24 + */ +@Tag(name = "分数 API") +@RestController +@RequiredArgsConstructor +@RequestMapping(value = "/score") +public class ScoreController { + private final ScoreService scoreService; + + @Operation(summary = "添加分数", description = "添加分数") + @PostMapping("/add") + public R add(@RequestBody @Valid ScoreAddReq req) { + return R.ok(scoreService.addScore(req)); + } + + @Operation(summary = "编辑分数", description = "编辑分数") + @PostMapping("/edit") + public R edit(@RequestBody @Valid ScoreEditReq req) { + return scoreService.editScore(req) ? R.ok() : R.fail(); + } + + @Operation(summary = "删除分数", description = "删除分数") + @DeleteMapping("/delete") + public R delete(@RequestParam Long id) { + return scoreService.deleteScore(id) ? R.ok() : R.fail(); + } + + @Operation(summary = "获取分数", description = "获取分数") + @GetMapping("/get") + public R getScore(@RequestParam(name = "id", required = true) Long id) { + return R.ok(scoreService.getScore(id)); + } + + @Operation(summary = "查询分数列表", description = "查询分数列表") + @GetMapping("/list") + public R> getScoreList(ScoreQuery query) { + return R.ok(scoreService.getScoreList(query)); + } + + @Operation(summary = "分页查询分数列表", description = "分页查询分数列表") + @GetMapping + public R> page(ScoreQuery query, @Validated PageQuery pageQuery) { + return R.ok(scoreService.getScorePage(query, pageQuery)); + } +} \ No newline at end of file diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/SignInController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/SignInController.java new file mode 100644 index 0000000..d1484bd --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/SignInController.java @@ -0,0 +1,109 @@ +package top.continew.admin.controller.business; + +import com.alibaba.fastjson.JSONObject; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import top.continew.admin.business.model.query.SignInQuery; +import top.continew.admin.business.model.req.SignInAddReq; +import top.continew.admin.business.model.req.SignInEditReq; +import top.continew.admin.business.model.resp.SignInResp; +import top.continew.admin.business.service.SignInService; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.query.SortQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.web.model.R; + +import java.util.List; +import java.util.Map; + +/** + * 签到管理 API + * + * @author jiay + * @since 2025/06/26 10:24 + */ +@Tag(name = "签到 API") +@RestController +@RequiredArgsConstructor +@RequestMapping(value = "/signIn") +public class SignInController { + private final SignInService signInService; + + @Operation(summary = "添加签到", description = "添加签到") + @PostMapping("/add") + public R add(@RequestBody Map data) { + try { + Map obj = (Map)((Map)((Map)data.get("data")).get("ref_data")).get("fields"); + String course = (String)obj.get("课程名称"); + String no = (String)obj.get("学号"); + String name = (String)obj.get("姓名"); + String phone = (String)obj.get("手机"); + String location = JSONObject.toJSONString(obj.get("定位")); + + SignInAddReq req = new SignInAddReq(); + req.setSignIn(1); + req.setCourseName(course); + req.setLocation(location); + req.setUserName(name); + req.setPhone(phone); + req.setStuNo(no); + return R.ok(signInService.addSignIn(req)); + } catch (RuntimeException exception) { + exception.printStackTrace(); + } + return R.fail(); + } + + @Operation(summary = "编辑签到", description = "编辑签到") + @PostMapping("/edit") + public R edit(@RequestBody @Valid SignInEditReq req) { + return signInService.editSignIn(req) ? R.ok() : R.fail(); + } + + @Operation(summary = "删除签到", description = "删除签到") + @DeleteMapping("/delete") + public R delete(@RequestParam Long id) { + return signInService.deleteSignIn(id) ? R.ok() : R.fail(); + } + + @Operation(summary = "获取签到", description = "获取签到") + @GetMapping("/get") + public R getSignIn(@RequestParam(name = "id", required = true) Long id) { + return R.ok(signInService.getSignIn(id)); + } + + @Operation(summary = "查询今日未签到列表", description = "查询签到列表") + @PutMapping("/getTodayNosignInList") + public R> getTodayNosignInList(SignInQuery query) { + return R.ok(signInService.getTodayNosignInList(query)); + } + + @Operation(summary = "分页查询历史签到列表", description = "分页查询历史签到列表") + @PutMapping("/getHisSignInList") + public R> getHisSignInList(SignInQuery query, PageQuery pageQuery) { + return R.ok(signInService.getHisSignInList(query, pageQuery)); + } + + @Operation(summary = "导出历史签到列表", description = "导出历史签到列表") + @GetMapping("/exportHisSignInList") + public void exportHisSignInList(SignInQuery query, SortQuery sortQuery, HttpServletResponse response) { + signInService.exportHisSignInList(query, sortQuery, response); + } + + @Operation(summary = "导出今日未签到列表", description = "导出今日未签到列表") + @GetMapping("/exportTodayNosignInList") + public void exportTodayNosignInList(SignInQuery query, SortQuery sortQuery, HttpServletResponse response) { + signInService.exportTodayNosignInList(query, sortQuery, response); + } +} \ No newline at end of file diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/SubtitleItemController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/SubtitleItemController.java new file mode 100644 index 0000000..5e3c500 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/SubtitleItemController.java @@ -0,0 +1,76 @@ +package top.continew.admin.controller.business; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import top.continew.admin.business.model.query.SubtitleItemPageQuery; +import top.continew.admin.business.model.query.SubtitleItemQuery; +import top.continew.admin.business.model.req.BatchReq; +import top.continew.admin.business.model.req.SubtitleItemReq; +import top.continew.admin.business.model.resp.SubtitleItemDetailResp; +import top.continew.admin.business.model.resp.SubtitleItemResp; +import top.continew.admin.business.service.SubtitleItemService; +import top.continew.starter.extension.crud.annotation.CrudRequestMapping; +import top.continew.starter.extension.crud.controller.BaseController; +import top.continew.starter.extension.crud.enums.Api; +import top.continew.starter.extension.crud.model.query.SortQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.log.core.annotation.Log; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 数字人-字幕项管理 API + * + * @author 王志维 + * @since 2025/12/29 + */ +@Tag(name = "数字人-字幕项管理 API") +@RestController +@RequiredArgsConstructor +@CrudRequestMapping(value = "/business/subtitleItem", api = {Api.GET, Api.UPDATE, Api.LIST}) +public class SubtitleItemController extends BaseController { + + @Log + @Operation(summary = "查询字幕项", description = "查询字幕项列表") + @ResponseBody + @GetMapping({"/list"}) + public List list(SubtitleItemQuery query, SortQuery sortQuery) { + return baseService.list(query, sortQuery); + } + + @Log + @ResponseBody + @Operation(summary = "新增字幕项", description = "新增字幕项") + @PostMapping() + public Long addSubtitleItem(@RequestBody SubtitleItemReq req) { + return baseService.add(req); + } + + @Log + @Operation(summary = "分页查询字幕项", description = "分页查询字幕项列表") + @ResponseBody + @GetMapping({"/page"}) + public PageResp page(SubtitleItemQuery query, SubtitleItemPageQuery pageQuery) { + return baseService.pageList(query, pageQuery); + } + + @Log + @Operation(summary = "批量删除字幕项", description = "根据字幕项ID列表批量删除字幕项") + @PostMapping("/batchDelete") + public void deleteBatch(@Validated @RequestBody BatchReq req) { + baseService.delete(req.getIds()); + } + + @Log + @Operation(summary = "批量删除字幕项(通过路径参数)", description = "根据路径中的ID列表批量删除字幕项,ID以逗号分隔") + @DeleteMapping("/{ids}") + public void delete(@PathVariable("ids") String ids) { + List idList = Arrays.stream(ids.split(",")).map(Long::valueOf).collect(Collectors.toList()); + baseService.delete(idList); + } +} \ No newline at end of file diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/TaskRecordHistoryController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/TaskRecordHistoryController.java new file mode 100644 index 0000000..809a080 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/business/TaskRecordHistoryController.java @@ -0,0 +1,70 @@ +package top.continew.admin.controller.business; + +import io.swagger.v3.oas.annotations.Operation; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import top.continew.admin.business.model.query.TaskRecordHistoryQuery; +import top.continew.admin.business.model.req.TaskRecordHistoryAddReq; +import top.continew.admin.business.model.req.TaskRecordHistoryEditReq; +import top.continew.admin.business.model.resp.TaskRecordHistoryResp; +import top.continew.admin.business.service.TaskRecordHistoryService; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.web.model.R; + +import java.util.List; + +/** + * 音视图文生成记录管理 API + * + * @author jiay + * @since 2025/05/23 07:51 + */ +@Tag(name = "音视图文生成记录管理 API") +@RestController +@RequiredArgsConstructor +@RequestMapping(value = "/taskRecordHistory") +public class TaskRecordHistoryController { + + private final TaskRecordHistoryService taskRecordHistoryService; + + @Operation(summary = "添加音视图文生成记录", description = "添加音视图文生成记录") + @PostMapping("/add") + public R add(@RequestBody @Valid TaskRecordHistoryAddReq req) { + return R.ok(taskRecordHistoryService.addTaskRecordHistory(req)); + } + + @Operation(summary = "编辑音视图文生成记录", description = "编辑音视图文生成记录") + @PostMapping("/edit") + public R edit(@RequestBody @Valid TaskRecordHistoryEditReq req) { + return taskRecordHistoryService.editTaskRecordHistory(req) ? R.ok() : R.fail(); + } + + @Operation(summary = "删除音视图文生成记录", description = "删除音视图文生成记录") + @DeleteMapping("/delete") + public R delete(@RequestParam Long id) { + return taskRecordHistoryService.deleteTaskRecordHistory(id) ? R.ok() : R.fail(); + } + + @Operation(summary = "获取音视图文生成记录", description = "获取音视图文生成记录") + @GetMapping("/get") + public R getTaskRecordHistory(@RequestParam(name = "id", required = true) Long id) { + return R.ok(taskRecordHistoryService.getTaskRecordHistory(id)); + } + + @Operation(summary = "查询音视图文生成记录列表", description = "查询音视图文生成记录列表") + @GetMapping("/list") + public R> getTaskRecordHistoryList(TaskRecordHistoryQuery query) { + return R.ok(taskRecordHistoryService.getTaskRecordHistoryList(query)); + } + + @Operation(summary = "分页查询音视图文生成记录列表", description = "分页查询音视图文生成记录列表") + @GetMapping + public R> page(TaskRecordHistoryQuery query, @Validated PageQuery pageQuery) { + return R.ok(taskRecordHistoryService.getTaskRecordHistoryPage(query, pageQuery)); + } + +} \ No newline at end of file diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/common/CaptchaController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/common/CaptchaController.java new file mode 100644 index 0000000..e1ce8f3 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/common/CaptchaController.java @@ -0,0 +1,252 @@ +package top.continew.admin.controller.common; + +import cn.dev33.satoken.annotation.SaIgnore; +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.lang.Dict; +import cn.hutool.core.lang.RegexPool; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.servlet.JakartaServletUtil; +import com.anji.captcha.model.common.RepCodeEnum; +import com.anji.captcha.model.common.ResponseModel; +import com.anji.captcha.model.vo.CaptchaVO; +import com.anji.captcha.service.CaptchaService; +import com.wf.captcha.base.Captcha; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.mail.MessagingException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import lombok.RequiredArgsConstructor; +import org.dromara.sms4j.api.SmsBlend; +import org.dromara.sms4j.api.entity.SmsResponse; +import org.dromara.sms4j.comm.constant.SupplierConstant; +import org.dromara.sms4j.core.factory.SmsFactory; +import org.redisson.api.RateIntervalUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import top.continew.admin.common.config.properties.CaptchaProperties; +import top.continew.admin.common.constant.CacheConstants; +import top.continew.admin.common.model.resp.CaptchaResp; +import top.continew.admin.system.service.OptionService; +import top.continew.starter.cache.redisson.util.RedisUtils; +import top.continew.starter.captcha.graphic.core.GraphicCaptchaService; +import top.continew.starter.core.autoconfigure.project.ProjectProperties; +import top.continew.starter.core.util.TemplateUtils; +import top.continew.starter.core.util.validate.CheckUtils; +import top.continew.starter.core.util.validate.ValidationUtils; +import top.continew.starter.log.core.annotation.Log; +import top.continew.starter.messaging.mail.util.MailUtils; +import top.continew.starter.security.limiter.annotation.RateLimiter; +import top.continew.starter.security.limiter.annotation.RateLimiters; +import top.continew.starter.security.limiter.enums.LimitType; +import top.continew.starter.web.model.R; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * 验证码 API + * + * @author Charles7c + * @since 2022/12/11 14:00 + */ +@Tag(name = "验证码 API") +@SaIgnore +@Validated +@RestController +@RequiredArgsConstructor +@RequestMapping("/captcha") +public class CaptchaController { + + private static final Logger logger = LoggerFactory.getLogger(CaptchaController.class); + + private final ProjectProperties projectProperties; + private final CaptchaProperties captchaProperties; + private final CaptchaService behaviorCaptchaService; + private final GraphicCaptchaService graphicCaptchaService; + private final OptionService optionService; + + @Log(ignore = true) + @Operation(summary = "获取行为验证码", description = "获取行为验证码(Base64编码)") + @GetMapping("/behavior") + public Object getBehaviorCaptcha(CaptchaVO captchaReq, HttpServletRequest request) { + captchaReq.setBrowserInfo(JakartaServletUtil.getClientIP(request) + request.getHeader(HttpHeaders.USER_AGENT)); + ResponseModel responseModel = behaviorCaptchaService.get(captchaReq); + CheckUtils.throwIf(() -> !StrUtil.equals(RepCodeEnum.SUCCESS.getCode(), responseModel + .getRepCode()), responseModel.getRepMsg()); + return responseModel.getRepData(); + } + + @Log(ignore = true) + @Operation(summary = "校验行为验证码", description = "校验行为验证码") + @PostMapping("/behavior") + public Object checkBehaviorCaptcha(@RequestBody CaptchaVO captchaReq, HttpServletRequest request) { + captchaReq.setBrowserInfo(JakartaServletUtil.getClientIP(request) + request.getHeader(HttpHeaders.USER_AGENT)); + return behaviorCaptchaService.check(captchaReq); + } + + @Log(ignore = true) + @Operation(summary = "获取图片验证码", description = "获取图片验证码(Base64编码,带图片格式:data:image/gif;base64)") + @GetMapping("/image") + public CaptchaResp getImageCaptcha() { + String uuid = IdUtil.fastUUID(); + String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + uuid; + Captcha captcha = graphicCaptchaService.getCaptcha(); + long expireTime = LocalDateTimeUtil.toEpochMilli(LocalDateTime.now() + .plusMinutes(captchaProperties.getExpirationInMinutes())); + RedisUtils.set(captchaKey, captcha.text(), Duration.ofMinutes(captchaProperties.getExpirationInMinutes())); + return CaptchaResp.builder().uuid(uuid).img(captcha.toBase64()).expireTime(expireTime).build(); + } + + /** + * 获取邮箱验证码 + * + *

+ * 限流规则:
+ * 1.同一邮箱同一模板,1分钟2条,1小时8条,24小时20条
+ * 2、同一邮箱所有模板 24 小时 100 条
+ * 3、同一 IP 每分钟限制发送 30 条 + *

+ * + * @param email 邮箱 + * @return / + */ + @Operation(summary = "获取邮箱验证码", description = "发送验证码到指定邮箱") + @GetMapping("/mail") + @RateLimiters({ + @RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX + "MIN", key = "#email + ':' + T(cn.hutool.extra.spring.SpringUtil).getProperty('captcha.mail.templatePath')", rate = 2, interval = 1, unit = RateIntervalUnit.MINUTES, message = "获取验证码操作太频繁,请稍后再试"), + @RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX + "HOUR", key = "#email + ':' + T(cn.hutool.extra.spring.SpringUtil).getProperty('captcha.mail.templatePath')", rate = 8, interval = 1, unit = RateIntervalUnit.HOURS, message = "获取验证码操作太频繁,请稍后再试"), + @RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX + "DAY'", key = "#email + ':' + T(cn.hutool.extra.spring.SpringUtil).getProperty('captcha.mail.templatePath')", rate = 20, interval = 24, unit = RateIntervalUnit.HOURS, message = "获取验证码操作太频繁,请稍后再试"), + @RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX, key = "#email", rate = 100, interval = 24, unit = RateIntervalUnit.HOURS, message = "获取验证码操作太频繁,请稍后再试"), + @RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX, key = "#email", rate = 30, interval = 1, unit = RateIntervalUnit.MINUTES, type = LimitType.IP, message = "获取验证码操作太频繁,请稍后再试")}) + public R getMailCaptcha(@NotBlank(message = "邮箱不能为空") @Pattern(regexp = RegexPool.EMAIL, message = "邮箱格式错误") String email, + CaptchaVO captchaReq) throws MessagingException { + // 行为验证码校验(开发环境可以跳过) + if (captchaReq.getCaptchaId() != null && behaviorCaptchaService != null) { + ResponseModel verificationRes = behaviorCaptchaService.verification(captchaReq); + ValidationUtils.throwIfNotEqual(verificationRes.getRepCode(), RepCodeEnum.SUCCESS.getCode(), verificationRes + .getRepMsg()); + } + // 生成验证码 + CaptchaProperties.CaptchaMail captchaMail = captchaProperties.getMail(); + String captcha = RandomUtil.randomNumbers(captchaMail.getLength()); + // 发送验证码 + Long expirationInMinutes = captchaMail.getExpirationInMinutes(); + Map siteConfig = optionService.getByCategory("SITE"); + String content = TemplateUtils.render(captchaMail.getTemplatePath(), Dict.create() + .set("siteUrl", projectProperties.getUrl()) + .set("siteTitle", siteConfig.get("SITE_TITLE")) + .set("siteCopyright", siteConfig.get("SITE_COPYRIGHT")) + .set("captcha", captcha) + .set("expiration", expirationInMinutes)); + MailUtils.sendHtml(email, "【%s】邮箱验证码".formatted(projectProperties.getName()), content); + // 保存验证码 + String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + email; + RedisUtils.set(captchaKey, captcha, Duration.ofMinutes(expirationInMinutes)); + return R.ok("发送成功,验证码有效期 %s 分钟".formatted(expirationInMinutes)); + } + + /** + * 获取短信验证码 + * + *

+ * 限流规则:
+ * 1.同一号码同一模板,1分钟2条,1小时8条,24小时20条
+ * 2、同一号码所有模板 24 小时 100 条
+ * 3、同一 IP 每分钟限制发送 30 条 + *

+ * + * @param phone 手机号 + * @param captchaReq 行为验证码信息 + * @return / + */ + @Operation(summary = "获取短信验证码", description = "发送验证码到指定手机号") + @GetMapping("/sms") + @RateLimiters({ + @RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX + "MIN", key = "#phone + ':' + T(cn.hutool.extra.spring.SpringUtil).getProperty('captcha.sms.templateId')", rate = 2, interval = 1, unit = RateIntervalUnit.MINUTES, message = "获取验证码操作太频繁,请稍后再试"), + @RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX + "HOUR", key = "#phone + ':' + T(cn.hutool.extra.spring.SpringUtil).getProperty('captcha.sms.templateId')", rate = 8, interval = 1, unit = RateIntervalUnit.HOURS, message = "获取验证码操作太频繁,请稍后再试"), + @RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX + "DAY'", key = "#phone + ':' + T(cn.hutool.extra.spring.SpringUtil).getProperty('captcha.sms.templateId')", rate = 20, interval = 24, unit = RateIntervalUnit.HOURS, message = "获取验证码操作太频繁,请稍后再试"), + @RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX, key = "#phone", rate = 100, interval = 24, unit = RateIntervalUnit.HOURS, message = "获取验证码操作太频繁,请稍后再试"), + @RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX, key = "#phone", rate = 30, interval = 1, unit = RateIntervalUnit.MINUTES, type = LimitType.IP, message = "获取验证码操作太频繁,请稍后再试")}) + public R getSmsCaptcha(@NotBlank(message = "手机号不能为空") @Pattern(regexp = RegexPool.MOBILE, message = "手机号格式错误") String phone, + CaptchaVO captchaReq) { + // 行为验证码校验(开发环境可以跳过) + if (captchaReq.getCaptchaId() != null && behaviorCaptchaService != null) { + ResponseModel verificationRes = behaviorCaptchaService.verification(captchaReq); + ValidationUtils.throwIfNotEqual(verificationRes.getRepCode(), RepCodeEnum.SUCCESS.getCode(), verificationRes + .getRepMsg()); + } + CaptchaProperties.CaptchaSms captchaSms = captchaProperties.getSms(); + // 生成验证码 + String captcha = RandomUtil.randomNumbers(captchaSms.getLength()); + // 发送验证码 + long expirationInMinutes = captchaSms.getExpirationInMinutes(); + SmsBlend smsBlend = SmsFactory.getBySupplier(SupplierConstant.ALIBABA); + Map messageMap = MapUtil.newHashMap(2, true); + messageMap.put("code", captcha); + // messageMap.put("expirationInMinutes", String.valueOf(expirationInMinutes)); + SmsResponse smsResponse = smsBlend.sendMessage(phone, captchaSms + .getTemplateId(), (LinkedHashMap)messageMap); + + // 记录错误日志 + if (!smsResponse.isSuccess()) { + String errorMessage = extractErrorMessage(smsResponse); + logger.error("短信发送失败 - 手机号: {}, 错误信息: {}, 完整响应: {}", phone, errorMessage, smsResponse); + } + + CheckUtils.throwIf(!smsResponse.isSuccess(), "验证码发送失败"); + // 保存验证码 + String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + phone; + RedisUtils.set(captchaKey, captcha, Duration.ofMinutes(expirationInMinutes)); + return R.ok("发送成功,验证码有效期 %s 分钟".formatted(expirationInMinutes)); + } + + private String extractErrorMessage(SmsResponse smsResponse) { + try { + // 首先检查 smsResponse 本身是否有错误信息 + if (smsResponse.getData() == null) { + return "响应数据不完整,无法提取错误信息"; + } + + // 记录完整的响应结构用于调试 + logger.debug("SMS响应对象: {}", smsResponse); + logger.debug("SMS响应数据: {}", smsResponse.getData()); + + // 尝试直接从 smsResponse 的 data 对象中提取错误信息 + Object data = smsResponse.getData(); + + // 如果 data 是 Map 类型,直接查找 message 字段 + if (data instanceof Map dataMap) { + Object message = dataMap.get("Message"); + if (message != null) { + return String.valueOf(message); + } + message = dataMap.get("message"); + if (message != null) { + return String.valueOf(message); + } + // 如果没有直接的 message 字段,返回整个 data 对象的字符串表示 + return "错误详情: " + dataMap; + } + // 如果不是 Map,尝试通过反射或 toString 获取信息 + String dataStr = data.toString(); + if (dataStr.contains("Message") || dataStr.contains("message")) { + return dataStr; + } + return "未知错误,响应数据: " + dataStr; + + } catch (Exception e) { + logger.warn("提取错误信息时发生异常", e); + return "无法解析错误信息: " + e.getMessage(); + } + } +} diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/common/CommonController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/common/CommonController.java new file mode 100644 index 0000000..712960d --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/common/CommonController.java @@ -0,0 +1,113 @@ +package top.continew.admin.controller.common; + +import cn.dev33.satoken.annotation.SaIgnore; +import cn.hutool.core.lang.tree.Tree; +import cn.hutool.core.util.StrUtil; +import com.alicp.jetcache.anno.Cached; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.x.file.storage.core.FileInfo; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import top.continew.admin.common.constant.CacheConstants; +import top.continew.admin.system.model.query.DeptQuery; +import top.continew.admin.system.model.query.MenuQuery; +import top.continew.admin.system.model.query.OptionQuery; +import top.continew.admin.system.model.query.RoleQuery; +import top.continew.admin.system.model.resp.DeptChildResp; +import top.continew.admin.system.model.resp.FileUploadResp; +import top.continew.admin.system.service.*; +import top.continew.starter.core.util.validate.ValidationUtils; +import top.continew.starter.extension.crud.model.query.SortQuery; +import top.continew.starter.extension.crud.model.resp.LabelValueResp; +import top.continew.starter.log.core.annotation.Log; + +import java.util.List; + +/** + * 公共 API + * + * @author Charles7c + * @since 2023/1/22 21:48 + */ +@Tag(name = "公共 API") +@Log(ignore = true) +@Validated +@RestController +@RequiredArgsConstructor +@RequestMapping("/common") +public class CommonController { + + private final FileService fileService; + private final DeptService deptService; + private final MenuService menuService; + private final RoleService roleService; + private final DictItemService dictItemService; + private final OptionService optionService; + + @Operation(summary = "上传文件", description = "上传文件") + @PostMapping("/file") + public FileUploadResp upload(@NotNull(message = "文件不能为空") MultipartFile file) { + ValidationUtils.throwIf(file::isEmpty, "文件不能为空"); + FileInfo fileInfo = fileService.upload(file); + return FileUploadResp.builder().url(fileInfo.getUrl()).build(); + } + + @Operation(summary = "查询部门树", description = "查询树结构的部门列表") + @GetMapping("/tree/dept") + public List> listDeptTree(DeptQuery query, SortQuery sortQuery) { + return deptService.tree(query, sortQuery, true); + } + + @Operation(summary = "查询菜单树", description = "查询树结构的菜单列表") + @GetMapping("/tree/menu") + public List> listMenuTree(MenuQuery query, SortQuery sortQuery) { + return menuService.tree(query, sortQuery, true); + } + + /** + * 获取文件管理左边的部门树形结构 + * + * @param query + * @return + * @author hzy + */ + @Operation(summary = "获取部门树形结构", description = "获取部门树形结构") + @GetMapping("/getDeptResp") + public List getDeptResp(DeptQuery query) { + return deptService.getDeptResp(query); + } + + @Operation(summary = "查询角色字典", description = "查询角色字典列表") + @GetMapping("/dict/role") + public List listRoleDict(RoleQuery query, SortQuery sortQuery) { + return roleService.listDict(query, sortQuery); + } + + @Operation(summary = "查询字典", description = "查询字典列表") + @Parameter(name = "code", description = "字典编码", example = "notice_type", in = ParameterIn.PATH) + @GetMapping("/dict/{code}") + public List listDict(@PathVariable String code) { + return dictItemService.listByDictCode(code); + } + + @SaIgnore + @Operation(summary = "查询参数字典", description = "查询参数字典") + @GetMapping("/dict/option") + @Cached(key = "#category", name = CacheConstants.OPTION_KEY_PREFIX) + public List> listOptionDict(@NotBlank(message = "类别不能为空") String category) { + OptionQuery optionQuery = new OptionQuery(); + optionQuery.setCategory(category); + return optionService.list(optionQuery) + .stream() + .map(option -> new LabelValueResp<>(option.getCode(), StrUtil.nullToDefault(option.getValue(), option + .getDefaultValue()))) + .toList(); + } +} diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/common/DashboardController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/common/DashboardController.java new file mode 100644 index 0000000..5b6b66d --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/common/DashboardController.java @@ -0,0 +1,79 @@ +package top.continew.admin.controller.common; + +import com.alicp.jetcache.anno.CachePenetrationProtect; +import com.alicp.jetcache.anno.CacheRefresh; +import com.alicp.jetcache.anno.CacheType; +import com.alicp.jetcache.anno.Cached; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import top.continew.admin.common.constant.CacheConstants; +import top.continew.admin.system.model.resp.DashboardAccessTrendResp; +import top.continew.admin.system.model.resp.DashboardGeoDistributionResp; +import top.continew.admin.system.model.resp.DashboardPopularModuleResp; +import top.continew.admin.system.model.resp.DashboardTotalResp; +import top.continew.admin.system.service.DashboardService; +import top.continew.admin.system.model.resp.DashboardNoticeResp; +import top.continew.starter.core.util.validate.ValidationUtils; +import top.continew.starter.log.core.annotation.Log; + +import java.util.List; + +/** + * 仪表盘 API + * + * @author Charles7c + * @since 2023/1/22 21:48 + */ +@Tag(name = "仪表盘 API") +@Log(ignore = true) +@Validated +@RestController +@RequiredArgsConstructor +@RequestMapping("/dashboard") +public class DashboardController { + + private final DashboardService dashboardService; + + @Operation(summary = "查询总计信息", description = "查询总计信息") + @GetMapping("/total") + public DashboardTotalResp getTotal() { + return dashboardService.getTotal(); + } + + @Operation(summary = "查询访问趋势信息", description = "查询访问趋势信息") + @Parameter(name = "days", description = "日期数", example = "30", in = ParameterIn.PATH) + @GetMapping("/access/trend/{days}") + @CachePenetrationProtect + @CacheRefresh(refresh = 7200) + @Cached(key = "#days", name = CacheConstants.DASHBOARD_KEY_PREFIX, cacheType = CacheType.BOTH, syncLocal = true) + public List listAccessTrend(@PathVariable Integer days) { + ValidationUtils.throwIf(7 != days && 30 != days, "仅支持查询近 7/30 天访问趋势信息"); + return dashboardService.listAccessTrend(days); + } + + @Operation(summary = "查询热门模块列表", description = "查询热门模块列表") + @GetMapping("/popular/module") + public List listPopularModule() { + return dashboardService.listPopularModule(); + } + + @Operation(summary = "查询访客地域分布信息", description = "查询访客地域分布信息") + @GetMapping("/geo/distribution") + public DashboardGeoDistributionResp getGeoDistribution() { + return dashboardService.getGeoDistribution(); + } + + @Operation(summary = "查询公告列表", description = "查询公告列表") + @GetMapping("/notice") + public List listNotice() { + return dashboardService.listNotice(); + } +} diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/monitor/OnlineUserController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/monitor/OnlineUserController.java new file mode 100644 index 0000000..32ff70a --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/monitor/OnlineUserController.java @@ -0,0 +1,53 @@ +package top.continew.admin.controller.monitor; + +import lombok.RequiredArgsConstructor; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.tags.Tag; + +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.dev33.satoken.stp.StpUtil; + +import top.continew.admin.auth.model.query.OnlineUserQuery; +import top.continew.admin.auth.model.resp.OnlineUserResp; +import top.continew.admin.auth.service.OnlineUserService; +import top.continew.starter.core.util.validate.CheckUtils; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; + +/** + * 在线用户 API + * + * @author Charles7c + * @since 2023/1/20 21:51 + */ +@Tag(name = "在线用户 API") +@RestController +@RequiredArgsConstructor +@RequestMapping("/monitor/online") +public class OnlineUserController { + + private final OnlineUserService baseService; + + @Operation(summary = "分页查询列表", description = "分页查询列表") + @SaCheckPermission("monitor:online:list") + @GetMapping + public PageResp page(OnlineUserQuery query, @Validated PageQuery pageQuery) { + return baseService.page(query, pageQuery); + } + + @Operation(summary = "强退在线用户", description = "强退在线用户") + @Parameter(name = "token", description = "令牌", example = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpblR5cGUiOiJsb2dpbiIsImxvZ2luSWQiOjEsInJuU3RyIjoiTUd6djdyOVFoeHEwdVFqdFAzV3M5YjVJRzh4YjZPSEUifQ.7q7U3ouoN7WPhH2kUEM7vPe5KF3G_qavSG-vRgIxKvE", in = ParameterIn.PATH) + @SaCheckPermission("monitor:online:kickout") + @DeleteMapping("/{token}") + public void kickout(@PathVariable String token) { + String currentToken = StpUtil.getTokenValue(); + CheckUtils.throwIfEqual(token, currentToken, "不能强退自己"); + StpUtil.kickoutByTokenValue(token); + } +} diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/schedule/DemoEnvironmentJob.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/schedule/DemoEnvironmentJob.java new file mode 100644 index 0000000..a601374 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/schedule/DemoEnvironmentJob.java @@ -0,0 +1,148 @@ +package top.continew.admin.controller.schedule; + +import cn.hutool.core.util.StrUtil; +import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; +import com.aizuda.snailjob.common.log.SnailJobLog; +import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy; +import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; +import top.continew.admin.common.constant.CacheConstants; +import top.continew.admin.system.mapper.*; +import top.continew.admin.system.model.entity.*; +import top.continew.starter.cache.redisson.util.RedisUtils; + +import java.util.function.BooleanSupplier; + +/** + * 演示环境任务(任务示例) + * + * @author Charles7c + * @since 2024/8/4 15:30 + */ +@Component +@RequiredArgsConstructor +public class DemoEnvironmentJob { + + private final DictItemMapper dictItemMapper; + private final DictMapper dictMapper; + private final StorageMapper storageMapper; + private final NoticeMapper noticeMapper; + private final MessageMapper messageMapper; + private final MessageUserMapper messageUserMapper; + private final UserMapper userMapper; + private final UserRoleMapper userRoleMapper; + private final UserSocialMapper userSocialMapper; + private final RoleMapper roleMapper; + private final RoleDeptMapper roleDeptMapper; + private final RoleMenuMapper roleMenuMapper; + private final MenuMapper menuMapper; + private final DeptMapper deptMapper; + + private static final Long DICT_ITEM_FLAG = 4L; + private static final Long DICT_FLAG = 2L; + private static final Long STORAGE_FLAG = 2L; + private static final Long NOTICE_FLAG = 7L; + private static final Long MESSAGE_FLAG = 0L; + private static final Long[] USER_FLAG = {1L, 547889293968801831L}; + private static final Long ROLE_FLAG = 547888897925840928L; + private static final Long MENU_FLAG = 10003L; + private static final Long DEPT_FLAG = 547888580614160409L; + + /** + * 重置演示环境数据 + */ + @JobExecutor(name = "ResetEnvironmentData") + @Transactional(rollbackFor = Exception.class) + public void resetEnvironmentData() { + try { + SnailJobLog.REMOTE.info("定时任务 [重置演示环境数据] 开始执行。"); + // 检测待清理数据 + SnailJobLog.REMOTE.info("开始检测演示环境待清理数据项,请稍候..."); + Long dictItemCount = dictItemMapper.lambdaQuery().gt(DictItemDO::getId, DICT_ITEM_FLAG).count(); + this.log(dictItemCount, "字典项"); + Long dictCount = dictMapper.lambdaQuery().gt(DictDO::getId, DICT_FLAG).count(); + this.log(dictCount, "字典"); + Long storageCount = storageMapper.lambdaQuery().gt(StorageDO::getId, STORAGE_FLAG).count(); + this.log(storageCount, "存储"); + Long noticeCount = noticeMapper.lambdaQuery().gt(NoticeDO::getId, NOTICE_FLAG).count(); + this.log(noticeCount, "公告"); + Long messageCount = messageMapper.lambdaQuery().count(); + this.log(messageCount, "通知"); + Long userCount = userMapper.lambdaQuery().notIn(UserDO::getId, USER_FLAG).count(); + this.log(userCount, "用户"); + Long roleCount = roleMapper.lambdaQuery().gt(RoleDO::getId, ROLE_FLAG).count(); + this.log(roleCount, "角色"); + Long menuCount = menuMapper.lambdaQuery().gt(MenuDO::getId, MENU_FLAG).count(); + this.log(menuCount, "菜单"); + Long deptCount = deptMapper.lambdaQuery().gt(DeptDO::getId, DEPT_FLAG).count(); + this.log(deptCount, "部门"); + // 清理数据 + InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().blockAttack(true).build()); + SnailJobLog.REMOTE.info("演示环境待清理数据项检测完成,开始执行清理。"); + this.clean(dictItemCount, "字典项", null, () -> dictItemMapper.lambdaUpdate() + .gt(DictItemDO::getId, DICT_ITEM_FLAG) + .remove()); + this.clean(dictCount, "字典", null, () -> dictMapper.lambdaUpdate().gt(DictDO::getId, DICT_FLAG).remove()); + this.clean(storageCount, "存储", null, () -> storageMapper.lambdaUpdate() + .gt(StorageDO::getId, STORAGE_FLAG) + .remove()); + this.clean(noticeCount, "公告", null, () -> noticeMapper.lambdaUpdate() + .gt(NoticeDO::getId, NOTICE_FLAG) + .remove()); + this.clean(messageCount, "通知", null, () -> { + messageUserMapper.lambdaUpdate().gt(MessageUserDO::getMessageId, MESSAGE_FLAG).remove(); + return messageMapper.lambdaUpdate().gt(MessageDO::getId, MESSAGE_FLAG).remove(); + }); + this.clean(userCount, "用户", null, () -> { + userRoleMapper.lambdaUpdate().notIn(UserRoleDO::getUserId, USER_FLAG).remove(); + userSocialMapper.lambdaUpdate().notIn(UserSocialDO::getUserId, USER_FLAG).remove(); + return userMapper.lambdaUpdate().notIn(UserDO::getId, USER_FLAG).remove(); + }); + this.clean(roleCount, "角色", null, () -> { + roleDeptMapper.lambdaUpdate().ne(RoleDeptDO::getRoleId, ROLE_FLAG).remove(); + roleMenuMapper.lambdaUpdate().ne(RoleMenuDO::getRoleId, ROLE_FLAG).remove(); + return roleMapper.lambdaUpdate().gt(RoleDO::getId, ROLE_FLAG).remove(); + }); + this.clean(menuCount, "菜单", CacheConstants.MENU_KEY_PREFIX, () -> menuMapper.lambdaUpdate() + .gt(MenuDO::getId, MENU_FLAG) + .remove()); + this.clean(deptCount, "部门", null, () -> deptMapper.lambdaUpdate().gt(DeptDO::getId, DEPT_FLAG).remove()); + SnailJobLog.REMOTE.info("演示环境数据已清理完成。"); + SnailJobLog.REMOTE.info("定时任务 [重置演示环境数据] 执行结束。"); + } finally { + InterceptorIgnoreHelper.clearIgnoreStrategy(); + } + } + + /** + * 输出日志 + * + * @param count 待清理数据项数量 + * @param resource 资源名称 + */ + private void log(Long count, String resource) { + if (count > 0) { + SnailJobLog.REMOTE.info("检测到 [{}] 待清理数据项:{}条", resource, count); + } + } + + /** + * 清理数据 + * + * @param count 待清理数据项数量 + * @param resource 资源名称 + * @param cacheKey 缓存键 + * @param supplier 清理数据项函数 + */ + private void clean(Long count, String resource, String cacheKey, BooleanSupplier supplier) { + if (count > 0 && supplier.getAsBoolean()) { + SnailJobLog.REMOTE.info("[{}] 数据项清理完成。", resource); + if (StrUtil.isNotBlank(cacheKey)) { + RedisUtils.deleteByPattern(cacheKey); + SnailJobLog.REMOTE.info("[{}] 数据项缓存清理完成。", resource); + } + } + } +} diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/schedule/JobController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/schedule/JobController.java new file mode 100644 index 0000000..7cd5fea --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/schedule/JobController.java @@ -0,0 +1,90 @@ +package top.continew.admin.controller.schedule; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import top.continew.admin.job.model.query.JobQuery; +import top.continew.admin.job.model.req.JobReq; +import top.continew.admin.job.model.req.JobStatusReq; +import top.continew.admin.job.model.resp.JobResp; +import top.continew.admin.job.service.JobService; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.extension.crud.util.ValidateGroup; +import top.continew.starter.log.core.annotation.Log; + +import java.util.List; + +/** + * 任务 API + * + * @author KAI + * @author Charles7c + * @since 2024/6/25 22:24 + */ +@Tag(name = " 任务 API") +@Validated +@RestController +@RequiredArgsConstructor +@RequestMapping("/schedule/job") +public class JobController { + + private final JobService baseService; + + @Operation(summary = "分页查询任务列表", description = "分页查询任务列表") + @SaCheckPermission("schedule:job:list") + @GetMapping + public PageResp page(JobQuery query) { + return baseService.page(query); + } + + @Operation(summary = "新增任务", description = "新增任务") + @SaCheckPermission("schedule:job:add") + @PostMapping + public void add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody JobReq req) { + baseService.add(req); + } + + @Operation(summary = "修改任务", description = "修改任务") + @Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH) + @SaCheckPermission("schedule:job:update") + @PutMapping("/{id}") + public void update(@Validated(ValidateGroup.Crud.Update.class) @RequestBody JobReq req, @PathVariable Long id) { + baseService.update(req, id); + } + + @Operation(summary = "修改任务状态", description = "修改任务状态") + @SaCheckPermission("schedule:job:update") + @PatchMapping("/{id}/status") + public void updateStatus(@Validated @RequestBody JobStatusReq req, @PathVariable Long id) { + baseService.updateStatus(req, id); + } + + @Operation(summary = "删除任务", description = "删除任务") + @Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH) + @SaCheckPermission("schedule:job:delete") + @DeleteMapping("/{id}") + public void delete(@PathVariable Long id) { + baseService.delete(id); + } + + @Operation(summary = "执行任务", description = "执行任务") + @Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH) + @SaCheckPermission("schedule:job:trigger") + @PostMapping("/trigger/{id}") + public void trigger(@PathVariable Long id) { + baseService.trigger(id); + } + + @Log(ignore = true) + @Operation(summary = "查询任务分组列表", description = "查询任务分组列表") + @SaCheckPermission("schedule:job:list") + @GetMapping("/group") + public List listGroup() { + return baseService.listGroup(); + } +} diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/schedule/JobLogController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/schedule/JobLogController.java new file mode 100644 index 0000000..538779f --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/schedule/JobLogController.java @@ -0,0 +1,74 @@ +package top.continew.admin.controller.schedule; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import top.continew.admin.job.model.JobInstanceLogPageResult; +import top.continew.admin.job.model.query.JobInstanceLogQuery; +import top.continew.admin.job.model.query.JobLogQuery; +import top.continew.admin.job.model.query.JobInstanceQuery; +import top.continew.admin.job.model.resp.JobLogResp; +import top.continew.admin.job.model.resp.JobInstanceResp; +import top.continew.admin.job.service.JobLogService; +import top.continew.starter.extension.crud.model.resp.PageResp; + +import java.util.List; + +/** + * 任务日志 API + * + * @author KAI + * @author Charles7c + * @since 2024/6/27 22:24 + */ +@Tag(name = " 任务日志 API") +@Validated +@RestController +@RequiredArgsConstructor +@RequestMapping("/schedule/log") +public class JobLogController { + + private final JobLogService baseService; + + @Operation(summary = "分页查询任务日志列表", description = "分页查询任务日志列表") + @SaCheckPermission("schedule:log:list") + @GetMapping + public PageResp page(JobLogQuery query) { + return baseService.page(query); + } + + @Operation(summary = "停止任务", description = "停止任务") + @Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH) + @SaCheckPermission("schedule:log:stop") + @PostMapping("/stop/{id}") + public void stop(@PathVariable Long id) { + baseService.stop(id); + } + + @Operation(summary = "重试任务", description = "重试任务") + @Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH) + @SaCheckPermission("schedule:log:retry") + @PostMapping("/retry/{id}") + public void retry(@PathVariable Long id) { + baseService.retry(id); + } + + @Operation(summary = "查询任务实例列表", description = "查询任务实例列表") + @SaCheckPermission("schedule:log:list") + @GetMapping("/instance") + public List listInstance(JobInstanceQuery query) { + return baseService.listInstance(query); + } + + @Operation(summary = "分页查询任务实例日志列表", description = "分页查询任务实例日志列表") + @SaCheckPermission("schedule:log:list") + @GetMapping("/instance/log") + public JobInstanceLogPageResult pageInstanceLog(JobInstanceLogQuery query) { + return baseService.pageInstanceLog(query); + } +} diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/schedule/TestJob.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/schedule/TestJob.java new file mode 100644 index 0000000..1b74102 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/schedule/TestJob.java @@ -0,0 +1,74 @@ +package top.continew.admin.controller.schedule; + +import cn.hutool.core.util.StrUtil; +import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; +import com.aizuda.snailjob.common.log.SnailJobLog; +import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; +import top.continew.admin.system.mapper.*; +import top.continew.admin.system.model.entity.*; +import top.continew.starter.cache.redisson.util.RedisUtils; + +import java.util.function.BooleanSupplier; + +/** + * 演示环境任务(任务示例) + * + * @author Charles7c + * @since 2024/8/4 15:30 + */ +@Component +@RequiredArgsConstructor +public class TestJob { + + /** + * 重置演示环境数据 + */ + @JobExecutor(name = "TestJob") + @Transactional(rollbackFor = Exception.class) + public void resetEnvironmentData() { + try { + SnailJobLog.REMOTE.info("定时任务 [重置演示环境数据] 开始执行。"); + // 检测待清理数据 + SnailJobLog.REMOTE.info("开始检测演示环境待清理数据项,请稍候..."); + this.log(12L, "字典项"); + + SnailJobLog.REMOTE.info("演示环境数据已清理完成。"); + SnailJobLog.REMOTE.info("定时任务 [重置演示环境数据] 执行结束。"); + } finally { + InterceptorIgnoreHelper.clearIgnoreStrategy(); + } + } + + /** + * 输出日志 + * + * @param count 待清理数据项数量 + * @param resource 资源名称 + */ + private void log(Long count, String resource) { + if (count > 0) { + SnailJobLog.REMOTE.info("检测到 [{}] 待清理数据项:{}条", resource, count); + } + } + + /** + * 清理数据 + * + * @param count 待清理数据项数量 + * @param resource 资源名称 + * @param cacheKey 缓存键 + * @param supplier 清理数据项函数 + */ + private void clean(Long count, String resource, String cacheKey, BooleanSupplier supplier) { + if (count > 0 && supplier.getAsBoolean()) { + SnailJobLog.REMOTE.info("[{}] 数据项清理完成。", resource); + if (StrUtil.isNotBlank(cacheKey)) { + RedisUtils.deleteByPattern(cacheKey); + SnailJobLog.REMOTE.info("[{}] 数据项缓存清理完成。", resource); + } + } + } +} diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/DeptController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/DeptController.java new file mode 100644 index 0000000..48c6b4e --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/DeptController.java @@ -0,0 +1,88 @@ +package top.continew.admin.controller.system; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import top.continew.admin.business.model.query.ModelQuery; +import top.continew.admin.business.model.req.DeptModelReq; +import top.continew.admin.business.model.req.DeptPlatformReq; +import top.continew.admin.business.model.resp.ModelResp; +import top.continew.admin.business.service.DeptModelService; +import top.continew.admin.business.service.DeptPlatformService; +import top.continew.admin.system.model.query.DeptQuery; +import top.continew.admin.system.model.req.DeptReq; +import top.continew.admin.system.model.resp.DeptResp; +import top.continew.admin.system.service.DeptService; +import top.continew.starter.extension.crud.annotation.CrudRequestMapping; +import top.continew.starter.extension.crud.controller.BaseController; +import top.continew.starter.extension.crud.enums.Api; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.web.model.R; + +import java.util.List; + +/** + * 部门管理 API + * + * @author Charles7c + * @since 2023/1/22 17:50 + */ +@Tag(name = "部门管理 API") +@Valid +@RestController +@CrudRequestMapping(value = "/system/dept", api = {Api.TREE, Api.GET, Api.ADD, Api.UPDATE, Api.DELETE, Api.EXPORT}) +@RequiredArgsConstructor +public class DeptController extends BaseController { + + private final DeptModelService deptModelService; + + private final DeptPlatformService deptPlatformService; + + @Operation(summary = "部门与操作平台绑定", description = "部门与操作平台绑定") + @PutMapping("/bind/platform") + public R bindPlatform(@Validated @RequestBody DeptPlatformReq req) { + deptPlatformService.bind(req); + return R.ok(); + } + + @Operation(summary = "部门与模型绑定", description = "部门与模型绑定") + @PutMapping("/bind/model") + public R bindPlatform(@Validated @RequestBody DeptModelReq req) { + deptModelService.bind(req); + return R.ok(); + } + + @Operation(summary = "获取部门绑定的操作平台", description = "获取部门绑定的操作平台") + @GetMapping("/getPlatformByDept") + public R getPlatformByDept(@NotNull(message = "操作平台列表不能为空") Long deptId) { + List res = deptPlatformService.getPlatformByDept(deptId); + return R.ok(res); + } + + @Operation(summary = "获取部门绑定的模型ID", description = "获取部门绑定的模型ID") + @GetMapping("/getModelByDept") + public R getModelByDept(@NotNull(message = "操作平台列表不能为空") Long deptId) { + List res = deptModelService.getModelByDept(deptId); + return R.ok(res); + } + + @Operation(summary = "获取用户可见的操作平台", description = "获取用户可见的操作平台") + @GetMapping("/getPlatform") + public R getPlatforms() { + return R.ok(deptPlatformService.getPlatforms()); + } + + @Operation(summary = "获取用户可见模型", description = "获取用户可见模型") + @GetMapping("/getModels") + public PageResp getModels(ModelQuery query, @Validated PageQuery pageQuery) { + return deptModelService.getModels(query, pageQuery); + } +} diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/DictController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/DictController.java new file mode 100644 index 0000000..6792a61 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/DictController.java @@ -0,0 +1,58 @@ +package top.continew.admin.controller.system; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.lang.tree.Tree; +import cn.hutool.core.lang.tree.TreeUtil; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import top.continew.admin.business.model.resp.ModelCategoryResp; +import top.continew.admin.business.service.ModelCategoryService; +import top.continew.admin.system.model.query.DictQuery; +import top.continew.admin.system.model.req.DictReq; +import top.continew.admin.system.model.resp.DictResp; +import top.continew.admin.system.service.DictService; +import top.continew.starter.extension.crud.annotation.CrudRequestMapping; +import top.continew.starter.extension.crud.controller.BaseController; +import top.continew.starter.extension.crud.enums.Api; +import top.continew.starter.web.model.R; + +import java.util.List; + +/** + * 字典管理 API + * + * @author Charles7c + * @since 2023/9/11 21:29 + */ +@Tag(name = "字典管理 API") +@RestController +@RequiredArgsConstructor +@CrudRequestMapping(value = "/system/dict", api = {Api.LIST, Api.GET, Api.ADD, Api.UPDATE, Api.DELETE}) +public class DictController extends BaseController { + + private final ModelCategoryService modelCategoryService; + + @Operation(summary = "获取模型分类列表", description = "获取模型分类列表") + @GetMapping(value = "/modelCategory/list") + public R getModelCategoryList() { + return R.ok(modelCategoryService.list()); + } + + @Operation(summary = "获取模型分类树", description = "获取模型分类树") + @GetMapping(value = "/modelCategory/tree") + public R getModelCategoryTree() { + List categoryRespList = BeanUtil.copyToList(modelCategoryService + .list(), ModelCategoryResp.class); + + List> treeList = TreeUtil.build(categoryRespList, 0L, (data, tree) -> { + tree.setId(data.getId()); + tree.setName(data.getName()); + tree.setParentId(data.getParentId()); + tree.putExtra("type", data.getType()); + }); + return R.ok(treeList); + } +} \ No newline at end of file diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/DictItemController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/DictItemController.java new file mode 100644 index 0000000..4b7f18e --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/DictItemController.java @@ -0,0 +1,23 @@ +package top.continew.admin.controller.system; + +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.RestController; +import top.continew.admin.system.model.query.DictItemQuery; +import top.continew.admin.system.model.req.DictItemReq; +import top.continew.admin.system.model.resp.DictItemResp; +import top.continew.admin.system.service.DictItemService; +import top.continew.starter.extension.crud.annotation.CrudRequestMapping; +import top.continew.starter.extension.crud.controller.BaseController; +import top.continew.starter.extension.crud.enums.Api; + +/** + * 字典项管理 API + * + * @author Charles7c + * @since 2023/9/11 21:29 + */ +@Tag(name = "字典项管理 API") +@RestController +@CrudRequestMapping(value = "/system/dict/item", api = {Api.PAGE, Api.GET, Api.ADD, Api.UPDATE, Api.DELETE}) +public class DictItemController extends BaseController { +} \ No newline at end of file diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/FileController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/FileController.java new file mode 100644 index 0000000..4cedbe5 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/FileController.java @@ -0,0 +1,45 @@ +package top.continew.admin.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import top.continew.admin.system.model.query.FileQuery; +import top.continew.admin.system.model.req.FileReq; +import top.continew.admin.system.model.resp.FileResp; +import top.continew.admin.system.model.resp.FileStatisticsResp; +import top.continew.admin.system.service.FileService; +import top.continew.starter.extension.crud.annotation.CrudRequestMapping; +import top.continew.starter.extension.crud.controller.BaseController; +import top.continew.starter.extension.crud.enums.Api; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.log.core.annotation.Log; + +/** + * 文件管理 API + * + * @author Charles7c + * @since 2023/12/23 10:38 + */ +@Tag(name = "文件管理 API") +@RestController +@RequiredArgsConstructor +@CrudRequestMapping(value = "/system/file", api = {Api.PAGE, Api.UPDATE, Api.DELETE}) +public class FileController extends BaseController { + @Operation(summary = "查询文件分页列表", description = "查询文件分页列表") + @GetMapping("/getFilesPage") + public PageResp getFilesPage(FileQuery query, @Validated PageQuery pageQuery) { + return baseService.getFilesPage(query, pageQuery); + } + + @Log(ignore = true) + @Operation(summary = "查询文件资源统计", description = "查询文件资源统计") + @SaCheckPermission("system:file:list") + @GetMapping("/statistics") + public FileStatisticsResp statistics() { + return baseService.statistics(); + } +} diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/LogController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/LogController.java new file mode 100644 index 0000000..0a0e924 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/LogController.java @@ -0,0 +1,68 @@ +package top.continew.admin.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.feiniaojin.gracefulresponse.api.ExcludeFromGracefulResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import top.continew.admin.system.model.query.LogQuery; +import top.continew.admin.system.model.resp.log.LogDetailResp; +import top.continew.admin.system.model.resp.log.LogResp; +import top.continew.admin.system.service.LogService; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.query.SortQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; + +/** + * 系统日志 API + * + * @author Charles7c + * @since 2023/1/18 23:55 + */ +@Tag(name = "系统日志 API") +@RestController +@RequiredArgsConstructor +@RequestMapping("/system/log") +public class LogController { + + private final LogService baseService; + + @Operation(summary = "分页查询列表", description = "分页查询列表") + @SaCheckPermission("monitor:log:list") + @GetMapping + public PageResp page(LogQuery query, @Validated PageQuery pageQuery) { + return baseService.page(query, pageQuery); + } + + @Operation(summary = "查询详情", description = "查询详情") + @Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH) + @SaCheckPermission("monitor:log:list") + @GetMapping("/{id}") + public LogDetailResp get(@PathVariable Long id) { + return baseService.get(id); + } + + @ExcludeFromGracefulResponse + @Operation(summary = "导出登录日志", description = "导出登录日志") + @SaCheckPermission("monitor:log:export") + @GetMapping("/export/login") + public void exportLoginLog(LogQuery query, SortQuery sortQuery, HttpServletResponse response) { + baseService.exportLoginLog(query, sortQuery, response); + } + + @ExcludeFromGracefulResponse + @Operation(summary = "导出操作日志", description = "导出操作日志") + @SaCheckPermission("monitor:log:export") + @GetMapping("/export/operation") + public void exportOperationLog(LogQuery query, SortQuery sortQuery, HttpServletResponse response) { + baseService.exportOperationLog(query, sortQuery, response); + } +} diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/MenuController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/MenuController.java new file mode 100644 index 0000000..30ebd2a --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/MenuController.java @@ -0,0 +1,64 @@ +package top.continew.admin.controller.system; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import top.continew.admin.system.model.query.MenuQuery; +import top.continew.admin.system.model.req.MenuReq; +import top.continew.admin.system.model.resp.MenuResp; +import top.continew.admin.system.service.MenuService; +import top.continew.starter.core.constant.StringConstants; +import top.continew.starter.core.util.URLUtils; +import top.continew.starter.core.util.validate.ValidationUtils; +import top.continew.starter.extension.crud.annotation.CrudRequestMapping; +import top.continew.starter.extension.crud.controller.BaseController; +import top.continew.starter.extension.crud.enums.Api; +import top.continew.starter.extension.crud.model.resp.BaseIdResp; +import top.continew.starter.extension.crud.util.ValidateGroup; + +/** + * 菜单管理 API + * + * @author Charles7c + * @since 2023/2/15 20:35 + */ +@Tag(name = "菜单管理 API") +@RestController +@CrudRequestMapping(value = "/system/menu", api = {Api.TREE, Api.GET, Api.ADD, Api.UPDATE, Api.DELETE}) +public class MenuController extends BaseController { + + @Override + public BaseIdResp add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody MenuReq req) { + this.checkPath(req); + return super.add(req); + } + + @Override + public void update(@Validated(ValidateGroup.Crud.Update.class) @RequestBody MenuReq req, @PathVariable Long id) { + this.checkPath(req); + super.update(req, id); + } + + /** + * 检查路由地址格式 + * + * @param req 创建或修改信息 + */ + private void checkPath(MenuReq req) { + Boolean isExternal = ObjectUtil.defaultIfNull(req.getIsExternal(), false); + String path = req.getPath(); + ValidationUtils.throwIf(Boolean.TRUE.equals(isExternal) && !URLUtils + .isHttpUrl(path), "路由地址格式错误,请以 http:// 或 https:// 开头"); + // 非外链菜单参数修正 + if (Boolean.FALSE.equals(isExternal)) { + ValidationUtils.throwIf(URLUtils.isHttpUrl(path), "路由地址格式错误"); + req.setPath(StrUtil.isBlank(path) ? path : StrUtil.prependIfMissing(path, StringConstants.SLASH)); + req.setName(StrUtil.removePrefix(req.getName(), StringConstants.SLASH)); + req.setComponent(StrUtil.removePrefix(req.getComponent(), StringConstants.SLASH)); + } + } +} diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/MessageController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/MessageController.java new file mode 100644 index 0000000..f919039 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/MessageController.java @@ -0,0 +1,65 @@ +package top.continew.admin.controller.system; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import top.continew.admin.common.util.helper.LoginHelper; +import top.continew.admin.system.model.query.MessageQuery; +import top.continew.admin.system.model.resp.MessageResp; +import top.continew.admin.system.model.resp.MessageUnreadResp; +import top.continew.admin.system.service.MessageService; +import top.continew.admin.system.service.MessageUserService; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.log.core.annotation.Log; + +import java.util.List; + +/** + * 消息管理 API + * + * @author Bull-BCLS + * @since 2023/10/15 19:05 + */ +@Tag(name = "消息管理 API") +@RestController +@RequiredArgsConstructor +@RequestMapping("/system/message") +public class MessageController { + + private final MessageService baseService; + private final MessageUserService messageUserService; + + @Operation(summary = "分页查询列表", description = "分页查询列表") + @GetMapping + public PageResp page(MessageQuery query, @Validated PageQuery pageQuery) { + query.setUserId(LoginHelper.getUserId()); + return baseService.page(query, pageQuery); + } + + @Operation(summary = "删除数据", description = "删除数据") + @Parameter(name = "ids", description = "ID 列表", example = "1,2", in = ParameterIn.PATH) + @DeleteMapping("/{ids}") + public void delete(@PathVariable List ids) { + baseService.delete(ids); + } + + @Operation(summary = "标记已读", description = "将消息标记为已读状态") + @Parameter(name = "ids", description = "消息ID列表", example = "1,2", in = ParameterIn.QUERY) + @PatchMapping("/read") + public void readMessage(@RequestParam(required = false) List ids) { + messageUserService.readMessage(ids); + } + + @Log(ignore = true) + @Operation(summary = "查询未读消息数量", description = "查询当前用户的未读消息数量") + @Parameter(name = "isDetail", description = "是否查询详情", example = "true", in = ParameterIn.QUERY) + @GetMapping("/unread") + public MessageUnreadResp countUnreadMessage(@RequestParam(required = false) Boolean detail) { + return messageUserService.countUnreadMessageByUserId(LoginHelper.getUserId(), detail); + } +} \ No newline at end of file diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/NoticeController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/NoticeController.java new file mode 100644 index 0000000..c1cb213 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/NoticeController.java @@ -0,0 +1,57 @@ +package top.continew.admin.controller.system; + +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import top.continew.admin.system.model.query.NoticeQuery; +import top.continew.admin.system.model.req.NoticeReq; +import top.continew.admin.system.model.resp.NoticeDetailResp; +import top.continew.admin.system.model.resp.NoticeResp; +import top.continew.admin.system.service.NoticeService; +import top.continew.starter.core.util.validate.ValidationUtils; +import top.continew.starter.extension.crud.annotation.CrudRequestMapping; +import top.continew.starter.extension.crud.controller.BaseController; +import top.continew.starter.extension.crud.enums.Api; +import top.continew.starter.extension.crud.model.resp.BaseIdResp; +import top.continew.starter.extension.crud.util.ValidateGroup; + +import java.time.LocalDateTime; + +/** + * 公告管理 API + * + * @author Charles7c + * @since 2023/8/20 10:55 + */ +@Tag(name = "公告管理 API") +@RestController +@CrudRequestMapping(value = "/system/notice", api = {Api.PAGE, Api.GET, Api.ADD, Api.UPDATE, Api.DELETE}) +public class NoticeController extends BaseController { + + @Override + public BaseIdResp add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody NoticeReq req) { + this.checkTime(req); + return super.add(req); + } + + @Override + public void update(@Validated(ValidateGroup.Crud.Update.class) @RequestBody NoticeReq req, @PathVariable Long id) { + this.checkTime(req); + super.update(req, id); + } + + /** + * 检查时间 + * + * @param req 创建或修改信息 + */ + private void checkTime(NoticeReq req) { + LocalDateTime effectiveTime = req.getEffectiveTime(); + LocalDateTime terminateTime = req.getTerminateTime(); + if (null != effectiveTime && null != terminateTime) { + ValidationUtils.throwIf(terminateTime.isBefore(effectiveTime), "终止时间必须晚于生效时间"); + } + } +} \ No newline at end of file diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/OptionController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/OptionController.java new file mode 100644 index 0000000..1380d9d --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/OptionController.java @@ -0,0 +1,53 @@ +package top.continew.admin.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import top.continew.admin.system.model.query.OptionQuery; +import top.continew.admin.system.model.req.OptionReq; +import top.continew.admin.system.model.req.OptionResetValueReq; +import top.continew.admin.system.model.resp.OptionResp; +import top.continew.admin.system.service.OptionService; + +import java.util.List; + +/** + * 参数管理 API + * + * @author Bull-BCLS + * @since 2023/8/26 19:38 + */ +@Tag(name = "参数管理 API") +@Validated +@RestController +@RequiredArgsConstructor +@RequestMapping("/system/option") +public class OptionController { + + private final OptionService baseService; + + @Operation(summary = "查询参数列表", description = "查询参数列表") + @SaCheckPermission("system:config:list") + @GetMapping + public List list(@Validated OptionQuery query) { + return baseService.list(query); + } + + @Operation(summary = "修改参数", description = "修改参数") + @SaCheckPermission("system:config:update") + @PutMapping + public void update(@Valid @RequestBody List options) { + baseService.update(options); + } + + @Operation(summary = "重置参数", description = "重置参数") + @SaCheckPermission("system:config:reset") + @PatchMapping("/value") + public void resetValue(@Validated @RequestBody OptionResetValueReq req) { + baseService.resetValue(req); + } +} \ No newline at end of file diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/RoleController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/RoleController.java new file mode 100644 index 0000000..98c2a13 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/RoleController.java @@ -0,0 +1,26 @@ +package top.continew.admin.controller.system; + +import io.swagger.v3.oas.annotations.tags.Tag; + +import org.springframework.web.bind.annotation.RestController; + +import top.continew.admin.system.model.query.RoleQuery; +import top.continew.admin.system.model.req.RoleReq; +import top.continew.admin.system.model.resp.RoleDetailResp; +import top.continew.admin.system.model.resp.RoleResp; +import top.continew.admin.system.service.RoleService; +import top.continew.starter.extension.crud.annotation.CrudRequestMapping; +import top.continew.starter.extension.crud.controller.BaseController; +import top.continew.starter.extension.crud.enums.Api; + +/** + * 角色管理 API + * + * @author Charles7c + * @since 2023/2/8 23:11 + */ +@Tag(name = "角色管理 API") +@RestController +@CrudRequestMapping(value = "/system/role", api = {Api.PAGE, Api.GET, Api.ADD, Api.UPDATE, Api.DELETE}) +public class RoleController extends BaseController { +} diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/StorageController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/StorageController.java new file mode 100644 index 0000000..48b2c5d --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/StorageController.java @@ -0,0 +1,23 @@ +package top.continew.admin.controller.system; + +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.RestController; +import top.continew.admin.system.model.query.StorageQuery; +import top.continew.admin.system.model.req.StorageReq; +import top.continew.admin.system.model.resp.StorageResp; +import top.continew.admin.system.service.StorageService; +import top.continew.starter.extension.crud.annotation.CrudRequestMapping; +import top.continew.starter.extension.crud.controller.BaseController; +import top.continew.starter.extension.crud.enums.Api; + +/** + * 存储管理 API + * + * @author Charles7c + * @since 2023/12/26 22:09 + */ +@Tag(name = "存储管理 API") +@RestController +@CrudRequestMapping(value = "/system/storage", api = {Api.PAGE, Api.GET, Api.ADD, Api.UPDATE, Api.DELETE}) +public class StorageController extends BaseController { +} \ No newline at end of file diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/UserCenterController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/UserCenterController.java new file mode 100644 index 0000000..f11ec3e --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/UserCenterController.java @@ -0,0 +1,140 @@ +package top.continew.admin.controller.system; + +import com.xkcoding.justauth.AuthRequestFactory; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import me.zhyd.oauth.model.AuthCallback; +import me.zhyd.oauth.model.AuthResponse; +import me.zhyd.oauth.model.AuthUser; +import me.zhyd.oauth.request.AuthRequest; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import top.continew.admin.common.constant.CacheConstants; +import top.continew.admin.common.util.SecureUtils; +import top.continew.admin.common.util.helper.LoginHelper; +import top.continew.admin.system.enums.SocialSourceEnum; +import top.continew.admin.system.model.entity.UserSocialDO; +import top.continew.admin.system.model.req.UserBasicInfoUpdateReq; +import top.continew.admin.system.model.req.UserEmailUpdateRequest; +import top.continew.admin.system.model.req.UserPasswordUpdateReq; +import top.continew.admin.system.model.req.UserPhoneUpdateReq; +import top.continew.admin.system.model.resp.AvatarResp; +import top.continew.admin.system.model.resp.UserSocialBindResp; +import top.continew.admin.system.service.UserService; +import top.continew.admin.system.service.UserSocialService; +import top.continew.starter.cache.redisson.util.RedisUtils; +import top.continew.starter.core.util.ExceptionUtils; +import top.continew.starter.core.util.validate.ValidationUtils; + +import java.io.IOException; +import java.util.List; + +/** + * 个人中心 API + * + * @author Charles7c + * @since 2023/1/2 11:41 + */ +@Tag(name = "个人中心 API") +@Validated +@RestController +@RequiredArgsConstructor +@RequestMapping("/system/user") +public class UserCenterController { + + private static final String DECRYPT_FAILED = "当前密码解密失败"; + private static final String CAPTCHA_EXPIRED = "验证码已失效"; + private final UserService userService; + private final UserSocialService userSocialService; + private final AuthRequestFactory authRequestFactory; + + @Operation(summary = "修改头像", description = "用户修改个人头像") + @PostMapping("/avatar") + public AvatarResp updateAvatar(@NotNull(message = "头像不能为空") MultipartFile avatarFile) throws IOException { + ValidationUtils.throwIf(avatarFile::isEmpty, "头像不能为空"); + String newAvatar = userService.updateAvatar(avatarFile, LoginHelper.getUserId()); + return AvatarResp.builder().avatar(newAvatar).build(); + } + + @Operation(summary = "修改基础信息", description = "修改用户基础信息") + @PatchMapping("/basic/info") + public void updateBasicInfo(@Validated @RequestBody UserBasicInfoUpdateReq req) { + userService.updateBasicInfo(req, LoginHelper.getUserId()); + } + + @Operation(summary = "修改密码", description = "修改用户登录密码") + @PatchMapping("/password") + public void updatePassword(@Validated @RequestBody UserPasswordUpdateReq updateReq) { + String rawOldPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(updateReq + .getOldPassword())); + ValidationUtils.throwIfNull(rawOldPassword, DECRYPT_FAILED); + String rawNewPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(updateReq + .getNewPassword())); + ValidationUtils.throwIfNull(rawNewPassword, "新密码解密失败"); + userService.updatePassword(rawOldPassword, rawNewPassword, LoginHelper.getUserId()); + } + + @Operation(summary = "修改手机号", description = "修改手机号") + @PatchMapping("/phone") + public void updatePhone(@Validated @RequestBody UserPhoneUpdateReq updateReq) { + String rawOldPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(updateReq + .getOldPassword())); + ValidationUtils.throwIfBlank(rawOldPassword, DECRYPT_FAILED); + String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + updateReq.getPhone(); + String captcha = RedisUtils.get(captchaKey); + ValidationUtils.throwIfBlank(captcha, CAPTCHA_EXPIRED); + ValidationUtils.throwIfNotEqualIgnoreCase(updateReq.getCaptcha(), captcha, "验证码错误"); + RedisUtils.delete(captchaKey); + userService.updatePhone(updateReq.getPhone(), rawOldPassword, LoginHelper.getUserId()); + } + + @Operation(summary = "修改邮箱", description = "修改用户邮箱") + @PatchMapping("/email") + public void updateEmail(@Validated @RequestBody UserEmailUpdateRequest updateReq) { + String rawOldPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(updateReq + .getOldPassword())); + ValidationUtils.throwIfBlank(rawOldPassword, DECRYPT_FAILED); + String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + updateReq.getEmail(); + String captcha = RedisUtils.get(captchaKey); + ValidationUtils.throwIfBlank(captcha, CAPTCHA_EXPIRED); + ValidationUtils.throwIfNotEqualIgnoreCase(updateReq.getCaptcha(), captcha, "验证码错误"); + RedisUtils.delete(captchaKey); + userService.updateEmail(updateReq.getEmail(), rawOldPassword, LoginHelper.getUserId()); + } + + @Operation(summary = "查询绑定的三方账号", description = "查询绑定的三方账号") + @GetMapping("/social") + public List listSocialBind() { + List userSocialList = userSocialService.listByUserId(LoginHelper.getUserId()); + return userSocialList.stream().map(userSocial -> { + String source = userSocial.getSource(); + UserSocialBindResp userSocialBind = new UserSocialBindResp(); + userSocialBind.setSource(source); + userSocialBind.setDescription(SocialSourceEnum.valueOf(source).getDescription()); + return userSocialBind; + }).toList(); + } + + @Operation(summary = "绑定三方账号", description = "绑定三方账号") + @Parameter(name = "source", description = "来源", example = "gitee", in = ParameterIn.PATH) + @PostMapping("/social/{source}") + public void bindSocial(@PathVariable String source, @RequestBody AuthCallback callback) { + AuthRequest authRequest = authRequestFactory.get(source); + AuthResponse response = authRequest.login(callback); + ValidationUtils.throwIf(!response.ok(), response.getMsg()); + AuthUser authUser = response.getData(); + userSocialService.bind(authUser, LoginHelper.getUserId()); + } + + @Operation(summary = "解绑三方账号", description = "解绑三方账号") + @Parameter(name = "source", description = "来源", example = "gitee", in = ParameterIn.PATH) + @DeleteMapping("/social/{source}") + public void unbindSocial(@PathVariable String source) { + userSocialService.deleteBySourceAndUserId(source, LoginHelper.getUserId()); + } +} diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/UserController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/UserController.java new file mode 100644 index 0000000..43c41d4 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/system/UserController.java @@ -0,0 +1,136 @@ +package top.continew.admin.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.hutool.core.util.ReUtil; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import top.continew.admin.business.service.PlantformBalanceService; +import top.continew.admin.common.constant.RegexConstants; +import top.continew.admin.common.util.SecureUtils; +import top.continew.admin.system.model.query.UserBalanceQuery; +import top.continew.admin.system.model.query.UserQuery; +import top.continew.admin.system.model.req.UserImportReq; +import top.continew.admin.system.model.req.UserPasswordResetReq; +import top.continew.admin.system.model.req.UserReq; +import top.continew.admin.system.model.req.UserRoleUpdateReq; +import top.continew.admin.system.model.resp.*; +import top.continew.admin.system.service.UserService; +import top.continew.starter.core.util.ExceptionUtils; +import top.continew.starter.core.util.validate.ValidationUtils; +import top.continew.starter.extension.crud.annotation.CrudRequestMapping; +import top.continew.starter.extension.crud.controller.BaseController; +import top.continew.starter.extension.crud.enums.Api; +import top.continew.starter.extension.crud.model.query.SortQuery; +import top.continew.starter.extension.crud.model.resp.BaseIdResp; +import top.continew.starter.extension.crud.util.ValidateGroup; +import top.continew.starter.web.model.R; + +import java.io.IOException; +import java.util.List; + +/** + * 用户管理 API + * + * @author Charles7c + * @since 2023/2/20 21:00 + */ +@Tag(name = "用户管理 API") +@Validated +@RestController +@RequiredArgsConstructor +@CrudRequestMapping(value = "/system/user", api = {Api.PAGE, Api.LIST, Api.GET, Api.ADD, Api.UPDATE, Api.DELETE, + Api.EXPORT}) +public class UserController extends BaseController { + + private final UserService userService; + private final PlantformBalanceService plantformBalanceService; + + @Override + public List list(UserQuery query, SortQuery sortQuery) { + return baseService.list(query, sortQuery); + } + + @Override + public BaseIdResp add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody UserReq req) { + String rawPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(req.getPassword())); + ValidationUtils.throwIfNull(rawPassword, "密码解密失败"); + ValidationUtils.throwIf(!ReUtil + .isMatch(RegexConstants.PASSWORD, rawPassword), "密码长度为 8-32 个字符,支持大小写字母、数字、特殊字符,至少包含字母和数字"); + req.setPassword(rawPassword); + BaseIdResp user = super.add(req); + + //添加用户子账户 + // for (int i = 0; i < ChargeTypeEnum.values().length; i++) { + // PlantformBalanceAddReq plantformBalanceAddReq = new PlantformBalanceAddReq().initPlantformBalance(); + // plantformBalanceAddReq.setChargeType(ChargeTypeEnum.getType(i)); + // plantformBalanceAddReq.setUserId(user.getId()); + // plantformBalanceService.addPlantformBalance(plantformBalanceAddReq); + // } + + return user; + } + + @Operation(summary = "下载用户导入模板", description = "下载用户导入模板") + @SaCheckPermission("system:user:import") + @GetMapping(value = "downloadImportUserTemplate", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) + public void downloadImportUserTemplate(HttpServletResponse response) throws IOException { + userService.downloadImportUserTemplate(response); + } + + @Operation(summary = "解析用户导入数据", description = "解析用户导入数据") + @SaCheckPermission("system:user:import") + @PostMapping(value = "parseImportUser") + public UserImportParseResp parseImportUser(@NotNull(message = "文件不能为空") MultipartFile file) { + ValidationUtils.throwIf(file::isEmpty, "文件不能为空"); + return userService.parseImportUser(file); + } + + @Operation(summary = "导入用户", description = "导入用户") + @SaCheckPermission("system:user:import") + @PostMapping(value = "import") + public UserImportResp importUser(@Validated @RequestBody UserImportReq req) { + return userService.importUser(req); + } + + @Operation(summary = "重置密码", description = "重置用户登录密码") + @Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH) + @SaCheckPermission("system:user:resetPwd") + @PatchMapping("/{id}/password") + public void resetPassword(@Validated @RequestBody UserPasswordResetReq req, @PathVariable Long id) { + String rawNewPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(req.getNewPassword())); + ValidationUtils.throwIfNull(rawNewPassword, "新密码解密失败"); + ValidationUtils.throwIf(!ReUtil + .isMatch(RegexConstants.PASSWORD, rawNewPassword), "密码长度为 8-32 个字符,支持大小写字母、数字、特殊字符,至少包含字母和数字"); + req.setNewPassword(rawNewPassword); + baseService.resetPassword(req, id); + } + + @Operation(summary = "分配角色", description = "为用户新增或移除角色") + @Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH) + @SaCheckPermission("system:user:updateRole") + @PatchMapping("/{id}/role") + public void updateRole(@Validated @RequestBody UserRoleUpdateReq updateReq, @PathVariable Long id) { + baseService.updateRole(updateReq, id); + } + + @Operation(summary = "余额是否充足", description = "通过用户id、消耗点数查询余额是否充足") + @GetMapping("/getUserBalance") + public R getUserBalance(UserBalanceQuery query) { + return R.ok(baseService.getUserBalance(query)); + } + + @Operation(summary = "判断余额是否充足", description = "通过用户id、消耗点数判断余额是否充足") + @GetMapping("/judgeUserBalance") + public R judgeUserBalance(UserBalanceQuery query) { + return R.ok(baseService.getUserBalance(query)); + } +} diff --git a/continew-admin-webapi/src/main/java/top/continew/admin/controller/tool/GeneratorController.java b/continew-admin-webapi/src/main/java/top/continew/admin/controller/tool/GeneratorController.java new file mode 100644 index 0000000..882d917 --- /dev/null +++ b/continew-admin-webapi/src/main/java/top/continew/admin/controller/tool/GeneratorController.java @@ -0,0 +1,100 @@ +package top.continew.admin.controller.tool; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import top.continew.admin.generator.model.entity.FieldConfigDO; +import top.continew.admin.generator.model.entity.GenConfigDO; +import top.continew.admin.generator.model.query.TableQuery; +import top.continew.admin.generator.model.req.GenConfigReq; +import top.continew.admin.generator.model.resp.GeneratePreviewResp; +import top.continew.admin.generator.model.resp.TableResp; +import top.continew.admin.generator.service.GeneratorService; +import top.continew.admin.system.service.DictService; +import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.resp.LabelValueResp; +import top.continew.starter.extension.crud.model.resp.PageResp; + +import java.sql.SQLException; +import java.util.List; + +/** + * 代码生成 API + * + * @author Charles7c + * @since 2023/8/3 22:58 + */ +@Tag(name = "代码生成 API") +@Validated +@RestController +@RequiredArgsConstructor +@RequestMapping("/generator") +public class GeneratorController { + + private final GeneratorService baseService; + private final DictService dictService; + + @Operation(summary = "分页查询数据表", description = "分页查询数据表") + @SaCheckPermission("tool:generator:list") + @GetMapping("/table") + public PageResp pageTable(TableQuery query, @Validated PageQuery pageQuery) throws SQLException { + return baseService.pageTable(query, pageQuery); + } + + @Operation(summary = "查询生成配置信息", description = "查询生成配置信息") + @Parameter(name = "tableName", description = "表名称", required = true, example = "sys_user", in = ParameterIn.PATH) + @SaCheckPermission("tool:generator:list") + @GetMapping("/config/{tableName}") + public GenConfigDO getGenConfig(@PathVariable String tableName) throws SQLException { + return baseService.getGenConfig(tableName); + } + + @Operation(summary = "查询字段配置列表", description = "查询字段配置列表") + @Parameter(name = "tableName", description = "表名称", required = true, example = "sys_user", in = ParameterIn.PATH) + @Parameter(name = "requireSync", description = "是否需要同步", example = "false", in = ParameterIn.QUERY) + @SaCheckPermission("tool:generator:list") + @GetMapping("/field/{tableName}") + public List listFieldConfig(@PathVariable String tableName, + @RequestParam(required = false, defaultValue = "false") Boolean requireSync) { + return baseService.listFieldConfig(tableName, requireSync); + } + + @Operation(summary = "保存配置信息", description = "保存配置信息") + @Parameter(name = "tableName", description = "表名称", required = true, example = "sys_user", in = ParameterIn.PATH) + @SaCheckPermission("tool:generator:list") + @PostMapping("/config/{tableName}") + public void saveConfig(@Validated @RequestBody GenConfigReq req, @PathVariable String tableName) { + baseService.saveConfig(req, tableName); + } + + @Operation(summary = "生成预览", description = "预览生成代码") + @Parameter(name = "tableName", description = "表名称", required = true, example = "sys_user", in = ParameterIn.PATH) + @SaCheckPermission("tool:generator:list") + @GetMapping("/preview/{tableName}") + public List preview(@PathVariable String tableName) { + return baseService.preview(tableName); + } + + @Operation(summary = "生成代码", description = "生成代码") + @Parameter(name = "tableName", description = "表名称", required = true, example = "sys_user", in = ParameterIn.PATH) + @SaCheckPermission("tool:generator:list") + @PostMapping("/{tableNames}") + public void generate(@PathVariable List tableNames, HttpServletResponse response) { + baseService.generate(tableNames, response); + } + + @Operation(summary = "查询字典", description = "查询字典列表") + @SaCheckPermission("tool:generator:list") + @GetMapping("/dict") + public List listDict() { + List dictList = dictService.listDict(null, null); + dictList.addAll(dictService.listEnumDict()); + return dictList; + } +} diff --git a/continew-admin-webapi/src/main/resources/banner.txt b/continew-admin-webapi/src/main/resources/banner.txt new file mode 100644 index 0000000..35d8e39 --- /dev/null +++ b/continew-admin-webapi/src/main/resources/banner.txt @@ -0,0 +1,9 @@ + ____ _ _ _ _ _ _ _ + / ___| ___ _ __ | |_ (_)| \ | | ___ __ __ / \ __| | _ __ ___ (_) _ __ + | | / _ \ | '_ \ | __|| || \| | / _ \\ \ /\ / /_____ / _ \ / _` || '_ ` _ \ | || '_ \ + | |___| (_) || | | || |_ | || |\ || __/ \ V V /|_____|/ ___ \| (_| || | | | | || || | | | + \____|\___/ |_| |_| \__||_||_| \_| \___| \_/\_/ /_/ \_\\__,_||_| |_| |_||_||_| |_| + + :: ${project.name} :: v${project.version} + :: ContiNew Starter :: v2.6.0 + :: Spring Boot :: v${spring-boot.version} diff --git a/continew-admin-webapi/src/main/resources/config/application-dev.yml b/continew-admin-webapi/src/main/resources/config/application-dev.yml new file mode 100644 index 0000000..336ad4f --- /dev/null +++ b/continew-admin-webapi/src/main/resources/config/application-dev.yml @@ -0,0 +1,360 @@ +--- ### 项目配置 +project: + # URL(跨域配置默认放行此 URL,第三方登录回调默认使用此 URL 为前缀,请注意更改为你实际的前端 URL) + url: http://localhost:5173 + +--- ### 服务器配置 +server: + # HTTP 端口(默认 8080) + port: 8003 + address: 0.0.0.0 + +--- ### 数据源配置 +spring.datasource: + type: com.zaxxer.hikari.HikariDataSource + ## 动态数据源配置(可配多主多从:m1、s1...;纯粹多库:mysql、oracle...;混合配置:m1、s1、oracle...) + dynamic: + # 是否启用 P6Spy(SQL 性能分析组件,该插件有性能损耗,不建议生产环境使用) + p6spy: true + # 设置默认的数据源或者数据源组(默认:master) + primary: digital_human + # 严格匹配数据源(true:未匹配到指定数据源时抛异常;false:使用默认数据源;默认 false) + strict: false + datasource: + # 主库配置(可配多个,构成多主) + digital_human: +# url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:digital_human}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&autoReconnect=true&maxReconnects=10&failOverReadOnly=false&allowPublicKeyRetrieval=true + url: jdbc:mysql://106.54.11.219:3306/digital_human?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&autoReconnect=true&maxReconnects=10&failOverReadOnly=false + username: ${DB_USER:root} + # password: ${DB_PWD:147369Wan} + password: ${DB_PWD:123456} + # password: ${DB_PWD:C9MUjc5ChtqHeCtQ} + driver-class-name: com.mysql.cj.jdbc.Driver + type: ${spring.datasource.type} + + hikari: + # 最大连接数量(默认 10,根据实际环境调整) + # 注意:当连接达到上限,并且没有空闲连接可用时,获取连接将在超时前阻塞最多 connectionTimeout 毫秒 + max-pool-size: 20 + # 获取连接超时时间(默认 30000 毫秒,30 秒) + connection-timeout: 30000 + # 空闲连接最大存活时间(默认 600000 毫秒,10 分钟) + idle-timeout: 600000 + # 保持连接活动的频率,以防止它被数据库或网络基础设施超时。该值必须小于 maxLifetime(默认 0,禁用) + keepaliveTime: 30000 + # 连接最大生存时间(默认 1800000 毫秒,30 分钟) + max-lifetime: 1800000 +## Liquibase 配置 +spring.liquibase: + # 是否启用 + enabled: false + # 配置文件路径 + change-log: classpath:/db/changelog/db.changelog-master.yaml + +--- ### 缓存配置 +spring.data: + ## Redis 配置(单机模式) + redis: + # 地址 + host: 106.54.11.219 +# host: 106.54.11.219 + # 端口(默认 6379) + port: 6379 + # port: 16380 + # 密码(未设置密码时请注释掉) + password: 123456 + # 数据库索引 + database: ${REDIS_DB:11} + # 连接超时时间 + timeout: 10s + # 是否开启 SSL + ssl: + enabled: false + ## Redisson 配置 + redisson: + enabled: true + mode: SINGLE +## JetCache 配置 +jetcache: + # 统计间隔(默认 0,表示不统计) + statIntervalMinutes: 15 + ## 本地/进程级/一级缓存配置 + local: + default: + # 缓存类型 + type: caffeine + # key 转换器的全局配置 + keyConvertor: jackson + # 以毫秒为单位指定超时时间的全局配置 + expireAfterWriteInMillis: 7200000 + # 每个缓存实例的最大元素的全局配置,仅 local 类型的缓存需要指定 + limit: 1000 + ## 远程/分布式/二级缓存配置 + remote: + default: + # 缓存类型 + type: redisson + # key 转换器的全局配置(用于将复杂的 KEY 类型转换为缓存实现可以接受的类型) + keyConvertor: jackson + # 以毫秒为单位指定超时时间的全局配置 + expireAfterWriteInMillis: 7200000 + # 2.7+ 支持两级缓存更新以后失效其他 JVM 中的 local cache,但多个服务共用 Redis 同一个 channel 可能会造成广播风暴,需要在这里指定channel。 + # 你可以决定多个不同的服务是否共用同一个 channel,如果没有指定则不开启。 + broadcastChannel: ${spring.application.name} + # 序列化器的全局配置,仅 remote 类型的缓存需要指定 + valueEncoder: java + valueDecoder: java + +--- ### 验证码配置 +continew-starter.captcha: + ## 行为验证码 + behavior: + enabled: true + cache-type: REDIS + water-mark: + # 一分钟内接口请求次数限制开关(默认:0,关闭,开启后下方失败锁定配置才会生效) + req-frequency-limit-enable: 0 + # 一分钟内验证码最多失败次数限制(默认:5次) + req-get-lock-limit: 5 + # 一分钟内验证码最多失败次数限制达标后锁定时间(默认:300秒) + req-get-lock-seconds: 300 + ## 图形验证码 + graphic: + # 类型 + type: SPEC + # 内容长度 + length: 4 + # 过期时间 + expirationInMinutes: 2 +## 其他验证码配置 +captcha: + ## 邮箱验证码配置 + mail: + # 内容长度 + length: 6 + # 过期时间 + expirationInMinutes: 5 + # 模板路径 + templatePath: mail/captcha.ftl + ## 短信验证码配置 + sms: + # 内容长度 + length: 4 + # 过期时间 + expirationInMinutes: 5 + # 模板 ID + templateId: SMS_493720484 + +--- ### 日志配置 +continew-starter.log: + # 是否打印日志,开启后可打印访问日志(类似于 Nginx access log) + is-print: true +## 项目日志配置(配置重叠部分,优先级高于 logback-spring.xml 中的配置) +logging: + level: + top.continew.admin: DEBUG + top.continew.starter: DEBUG + file: + path: ./logs + +--- ### 跨域配置 +continew-starter.web.cors: + enabled: true + # 配置允许跨域的域名 + allowed-origins: '*' + # 配置允许跨域的请求方式 + allowed-methods: '*' + # 配置允许跨域的请求头 + allowed-headers: '*' + # 配置允许跨域的响应头 + exposed-headers: '*' + +--- ### 接口文档配置 +springdoc: + swagger-ui: + enabled: true + +--- ### WebSocket 配置 +continew-starter.messaging.websocket: + enabled: true + path: /websocket + # 配置允许跨域的域名 + allowed-origins: '*' + +--- ### 短信配置 +sms: + # 从 YAML 读取配置 + config-type: YAML + http-log: true + is-print: false + blends: + alibaba: + # 短信厂商 + supplier: alibaba + requestUrl: dysmsapi.aliyuncs.com + access-key-id: LTAI5t8rsaBDrno4xd4F6EwE + access-key-secret: QNmjxatrgzBYukAKr8BZ4r7gd37SHw + signature: 武汉可学智能科技 +--- ### 邮件配置 +spring.mail: + # 根据需要更换 + host: smtp.126.com + port: 465 + username: 你的邮箱 + password: 你的邮箱授权码 + properties: + mail: + smtp: + auth: true + socketFactory: + class: javax.net.ssl.SSLSocketFactory + port: 465 + +--- ### Just Auth 配置 +justauth: + enabled: true + type: + GITEE: + client-id: 5d271b7f638941812aaf8bfc2e2f08f06d6235ef934e0e39537e2364eb8452c4 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${project.url}/social/callback?source=gitee + GITHUB: + client-id: 38080dad08cfbdfacca9 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${project.url}/social/callback?source=github + cache: + type: REDIS + +--- ### Sa-Token 扩展配置 +sa-token.extension: + # 安全配置:排除(放行)路径配置 + security.excludes: + # 白名单 + - /consumptionHistory/add + - /taskRecordHistory/add + - /consumptionHistory/checkBalance + - /downFile/** + - /error + - /signIn/add + # 静态资源 + - /*.html + - /*/*.html + - /*/*.css + - /*/*.js + - /websocket/** + # 接口文档相关资源 + - /favicon.ico + - /doc.html + - /webjars/** + - /swagger-ui/** + - /swagger-resources/** + - /*/api-docs/** + # 本地存储资源 + - /file/** + +--- ### 安全配置 +continew-starter.security: + ## 字段加/解密配置 + crypto: + enabled: true + # 对称加密算法密钥 + password: abcdefghijklmnop + # 非对称加密算法密钥(在线生成 RSA 密钥对:http://web.chacuo.net/netrsakeypair) + public-key: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAM51dgYtMyF+tTQt80sfFOpSV27a7t9uaUVeFrdGiVxscuizE7H8SMntYqfn9lp8a5GH5P1/GGehVjUD2gF/4kcCAwEAAQ== + private-key: MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAznV2Bi0zIX61NC3zSx8U6lJXbtru325pRV4Wt0aJXGxy6LMTsfxIye1ip+f2WnxrkYfk/X8YZ6FWNQPaAX/iRwIDAQABAkEAk/VcAusrpIqA5Ac2P5Tj0VX3cOuXmyouaVcXonr7f+6y2YTjLQuAnkcfKKocQI/juIRQBFQIqqW/m1nmz1wGeQIhAO8XaA/KxzOIgU0l/4lm0A2Wne6RokJ9HLs1YpOzIUmVAiEA3Q9DQrpAlIuiT1yWAGSxA9RxcjUM/1kdVLTkv0avXWsCIE0X8woEjK7lOSwzMG6RpEx9YHdopjViOj1zPVH61KTxAiBmv/dlhqkJ4rV46fIXELZur0pj6WC3N7a4brR8a+CLLQIhAMQyerWl2cPNVtE/8tkziHKbwW3ZUiBXU24wFxedT9iV + ## 密码编码器配置 + password: + enabled: true + # BCryptPasswordEncoder + encoding-id: bcrypt + ## 限流器配置 + limiter: + enabled: true + key-prefix: RateLimiter + +--- ### 文件上传配置 +spring.servlet: + multipart: + enabled: true + # 单文件上传大小限制 + max-file-size: 10MB + # 单次总上传文件大小限制 + max-request-size: 20MB +## 头像支持格式配置 +avatar: + support-suffix: jpg,jpeg,png,gif + +--- ### Snail Job 配置 +snail-job: + enabled: false + # 客户端地址(默认自动获取本机 IP) + #host: 127.0.0.1 + # 客户端端口(默认:1789) + port: 1789 + # 命名空间 ID + namespace: ${SCHEDULE_NAMESPACE:764d604ec6fc45f68cd92514c40e9e1a} + # 分组名 + group: ${SCHEDULE_GROUP:continew-admin} + # 令牌 + token: ${SCHEDULE_TOKEN:SJ_Wyz3dmsdbDOkDujOTSSoBjGQP1BMsVnj} + ## 服务端配置(任务调度中心) + server: + # 服务端地址,若服务端集群部署则此处配置域名 + host: ${SCHEDULE_HOST:127.0.0.1} + # Netty 端口号 + port: ${SCHEDULE_PORT:1788} + # API 配置 + api: + # URL + url: http://127.0.0.1:8001/snail-job + # 用户名 + username: ${SCHEDULE_USERNAME:admin} + # 密码 + password: ${SCHEDULE_PASSWORD:admin} + ## 重试数据批量上报滑动窗口配置 + retry: + reportSlidingWindow: + # 窗口期单位 + chrono-unit: SECONDS + # 窗口期时间长度 + duration: 10 + # 总量窗口期阈值 + total-threshold: 50 + # 窗口数量预警 + window-total-threshold: 150 + ## 调度线程池配置 + dispatcherThreadPool: + # 核心线程数 + corePoolSize: 16 + # 最大线程数 + maximumPoolSize: 16 + # 线程存活时间 + keepAliveTime: 1 + # 时间单位 + timeUnit: SECONDS + # 队列容量 + queueCapacity: 10000 + +sd-bot: + url: https://api.aibot.wang + Wxcz-Access-Key: ak_hjCG5qjcx_j7EtVCFT3 + +ai-bot: + url: https://api.xueai.art + Wxcz-Access-Key: ak_hjCG5qjcx_j7EtVCFT3 + +jwt: + secret: + public: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhF1cy7j57pBdPlO3Sm7N8expgroxq0br6fAinuXxiIAn3NklpgwLpw/V+leeKMzyYIJ2pFR6KmVybF9Z38Qs1H3C88r7ImYopBmO+IEqoEdm3UzNsNcdOSW4S9Lk1N5qtiniHDC/ZhlANzxeW7QbtS76RfZ4SSaeVUbfLJk/7RfFZ5HfLi1pBrT59SpfaJpKsbaMUBmdrEgGuGrT/K6jLsxqUcnOsbqAirtuP/NRpcO9OTsgpAh4tlFZKGg1hImIuAKwFZO0euZbjrQ9z7JwZoVSk0W99n0ULlofSIF939sSfhEX3UmgrEGsAwGVtZKROtJMHc/ywgBvaKpXKZsqjwIDAQAB + private: MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCEXVzLuPnukF0+U7dKbs3x7GmCujGrRuvp8CKe5fGIgCfc2SWmDAunD9X6V54ozPJggnakVHoqZXJsX1nfxCzUfcLzyvsiZiikGY74gSqgR2bdTM2w1x05JbhL0uTU3mq2KeIcML9mGUA3PF5btBu1LvpF9nhJJp5VRt8smT/tF8Vnkd8uLWkGtPn1Kl9omkqxtoxQGZ2sSAa4atP8rqMuzGpRyc6xuoCKu24/81Glw705OyCkCHi2UVkoaDWEiYi4ArAVk7R65luOtD3PsnBmhVKTRb32fRQuWh9IgX3f2xJ+ERfdSaCsQawDAZW1kpE60kwdz/LCAG9oqlcpmyqPAgMBAAECggEAIHf2Z1K8nDR1avCGKY/8nLdtOlAKFwWSoRPyqh+lm+Iz5QyvG4gg454S0xHjDgcX/WyDRdQjE3tHEiZhmW45wPYIeFz2NX3xwi8yzuqAVkithuNsIVDjLj1+c7sLvtvAOWcUu+2JRbYz9lisnLoVgeBALCFFaEw80pZL8Fi3QaUT77Tbl1TpuJHhRARuqKUdHgTe95tpcOF8s7OPJFu0aqWrDbrtA11CuQKXyJtuchrJtC4NSwddpz1h8sZ45BrjWcerqXu4InYa0NEEO9O8ik34wESiyvu2jiZsUk3Z/vDKKwiGGjR1ARXWy5t4N88Yfg00JJJpd1VCuZwQeqPAYQKBgQDTTxLWk1UpGAEAA1bBBoI4XddoB/0+cckE3M0tXdDD9HR260LP/9YOY7j9vqJiWCYaqv7PYXLOZyd3ypdbnh7Er44Z8heKwI+uVn6FwF4NsajSkdUpZg901y1+fqyO3z8AHCMEBSmjUiOGL4UQgwYw7R/55dtVJ6Tb5BCTIUniwwKBgQCgXAC7l3XypNIDJoqD3i4lg61APLwbxKxFW7UL1k1ENu80gkSUF7VXG8cf4H4cgP/8o+WJvhVeJjiz8ps5JoAMozEr78XFf0FhoL1xBhgO6W93DWSOdI1yVyijtPfVw/0xV6G/+ZbMR8qUIIY0gRkkiXtAazD35exT5vgBM+IERQKBgBv7wcvm0QVTJ3a/+nIvkXkzfD5LqI7ndK6PKyDR5r68ldvVxhBTmh5nuGzqHQkXEVzAvmPqTNDmukRT8P1yEDMN6Kyo8dMvMxfsXm8KDaqUl33jUC5eVLetc1noHqQBDo/OKhjzMhNt4kR38JBCOcSq8mwes5AwYtW3+zXw158PAoGAYhHiINewOoSyK1sWuy7V6fttqSt7nz/YW1KKloKpHl66S9KfPjKBpJRIYyZX+faql0dkyaiGSN33L5XfeEQja5ciK0yoFvCMe7hiIw0nCXeZw6VdKgH9vz+uiu/R4dnKrrJ5KX7qymPdcFmzuch6J+4sgmkLuY/TyDwo0BN61kECgYAnJ/2C0LXzYqEhtt1/80SSWnOdJBi5cnDVZ4OhE/Jbz2fBpu3OwIs7ILNw0v40RdRjKQnYyaRYU1KS0L6DbALkuqrrSLlDbaW9Q/gQfBgcFbpvNM9ZjGD9uNEK9wqplegON6JDAJkucpU3USapVbU4z/Go4bZbu4XMXJkIWYbdzA== + +common: + file: + path: D:/workspace/sxwz_backend/continew-admin-webapi/target/data/file/ + + #案例中心: ['casebase'], 提示词库: ['prompt'],在线视频: ['onlinetutorial'],教程列表: ['book'],作品中心: ['worksLibrary'],班级管理: ['classmanagement'],课程管理: ['curriculumMgt'],课程中心: ['curriculumCenter'],设计效率工具: ['DesignEfficiency'],专业赋能工具: ['ProfessionalEmpowerment'],教研学智能体: ['TeachingResearchStudies'],生成平台: ['operate'],模型索引: ['modelDisplay'],优秀作品: ['excellentWorks'] + #routers: casebase,prompt,onlinetutorial,book,worksLibrary,classmanagement,curriculumMgt,curriculumCenter,DesignEfficiency,ProfessionalEmpowerment,TeachingResearchStudies,operate,modelDisplay,excellentWorks +depts: + # routers: chat,agent,ai_programming,Multimodal,painting,music,video,ai3D,Cultural,comfyUI,agent_plus,ai_programming_plus,Fine_tuning,wenxian + routers: LLM,agent,AIprogram,Multimodal,AIpaint,music,video,AI3d,AIwriting,comfyui,agentPlus,programPlus,fineTuning,deepResearch,wenxian + diff --git a/continew-admin-webapi/src/main/resources/config/application-generator.yml b/continew-admin-webapi/src/main/resources/config/application-generator.yml new file mode 100644 index 0000000..c1eb739 --- /dev/null +++ b/continew-admin-webapi/src/main/resources/config/application-generator.yml @@ -0,0 +1,108 @@ +--- ### 代码生成器配置 +generator: + # 排除数据表 + excludeTables: + - DATABASECHANGELOG + - DATABASECHANGELOGLOCK + - gen_config + - gen_field_config + ## 类型映射 + typeMappings: + MYSQL: + String: + - varchar + - char + - text + - mediumtext + - longtext + - tinytext + - json + Integer: + - int + - tinyint + - smallint + - mediumint + - integer + Long: + - bigint + Float: + - float + Double: + - double + Boolean: + - bit + BigDecimal: + - decimal + LocalDate: + - date + LocalDateTime: + - datetime + - timestamp + ## 模板配置 + templateConfigs: + DO: + # 模板路径 + templatePath: backend/Entity.ftl + # 包名称 + packageName: model.entity + # 排除字段 + excludeFields: + - id + - createUser + - createTime + - updateUser + - updateTime + Query: + templatePath: backend/Query.ftl + packageName: model.query + Req: + templatePath: backend/Req.ftl + packageName: model.req + Resp: + templatePath: backend/Resp.ftl + packageName: model.resp + excludeFields: + - id + - createUser + - createTime + DetailResp: + templatePath: backend/DetailResp.ftl + packageName: model.resp + excludeFields: + - id + - createUser + - createTime + - updateUser + - updateTime + Mapper: + templatePath: backend/Mapper.ftl + packageName: mapper + Service: + templatePath: backend/Service.ftl + packageName: service + ServiceImpl: + templatePath: backend/ServiceImpl.ftl + packageName: service.impl + Controller: + templatePath: backend/Controller.ftl + packageName: controller + api: + templatePath: frontend/api.ftl + packageName: src/apis + extension: .ts + backend: false + index: + templatePath: frontend/index.ftl + packageName: src/views + extension: .vue + backend: false + AddModal: + templatePath: frontend/AddModal.ftl + packageName: src/views + extension: .vue + backend: false + DetailDrawer: + templatePath: frontend/DetailDrawer.ftl + packageName: src/views + extension: .vue + backend: false \ No newline at end of file diff --git a/continew-admin-webapi/src/main/resources/config/application-prod.yml b/continew-admin-webapi/src/main/resources/config/application-prod.yml new file mode 100644 index 0000000..7e5eb3f --- /dev/null +++ b/continew-admin-webapi/src/main/resources/config/application-prod.yml @@ -0,0 +1,364 @@ +--- ### 项目配置 +project: + # URL(跨域配置默认放行此 URL,第三方登录回调默认使用此 URL 为前缀,请注意更改为你实际的前端 URL) + url: https://sxwz.xueai.art + # 是否为生产环境 + production: true + +--- ### 服务器配置 +server: + # HTTP 端口(默认 8080) + port: 18000 + +--- ### 数据源配置 +spring.datasource: + type: com.zaxxer.hikari.HikariDataSource + ## 动态数据源配置(可配多主多从:m1、s1...;纯粹多库:mysql、oracle...;混合配置:m1、s1、oracle...) + dynamic: + # 是否启用 P6Spy(SQL 性能分析组件,该插件有性能损耗,不建议生产环境使用) + p6spy: false + # 设置默认的数据源或者数据源组(默认:master) + primary: huanda + # 严格匹配数据源(true:未匹配到指定数据源时抛异常;false:使用默认数据源;默认 false) + strict: false + datasource: + # 主库配置(可配多个,构成多主) + huanda: + url: jdbc:mysql://${DB_HOST:127.0.0.1}:${DB_PORT:3306}/${DB_NAME:huanda}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&autoReconnect=true&maxReconnects=10&failOverReadOnly=false&allowPublicKeyRetrieval=true + username: ${DB_USER:root} + password: ${DB_PWD:123456} + driver-class-name: com.mysql.cj.jdbc.Driver + type: ${spring.datasource.type} + # # 从库配置(可配多个,构成多从) + # slave_1: + # url: jdbc:mysql://${DB_HOST:127.0.0.1}:${DB_PORT:3306}/${DB_NAME:continew_admin}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&rewriteBatchedStatements=true&autoReconnect=true&maxReconnects=10&failOverReadOnly=false + # username: + # password: + # lazy: true + # driver-class-name: com.mysql.cj.jdbc.Driver + # type: ${spring.datasource.type} + # # PostgreSQL 库配置 + # postgresql: + # url: jdbc:postgresql://${DB_HOST:127.0.0.1}:${DB_PORT:5432}/${DB_NAME:continew_admin}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&rewriteBatchedStatements=true&autoReconnect=true&maxReconnects=10&failOverReadOnly=false + # username: ${DB_USER:root} + # password: ${DB_PWD:123456} + # driver-class-name: org.postgresql.Driver + # type: ${spring.datasource.type} + # Hikari 连接池配置(完整配置请参阅:https://github.com/brettwooldridge/HikariCP) + hikari: + # 最大连接数量(默认 10,根据实际环境调整) + # 注意:当连接达到上限,并且没有空闲连接可用时,获取连接将在超时前阻塞最多 connectionTimeout 毫秒 + max-pool-size: 20 + # 获取连接超时时间(默认 30000 毫秒,30 秒) + connection-timeout: 30000 + # 空闲连接最大存活时间(默认 600000 毫秒,10 分钟) + idle-timeout: 600000 + # 保持连接活动的频率,以防止它被数据库或网络基础设施超时。该值必须小于 maxLifetime(默认 0,禁用) + keepaliveTime: 30000 + # 连接最大生存时间(默认 1800000 毫秒,30 分钟) + max-lifetime: 1800000 +## Liquibase 配置 +spring.liquibase: + # 是否启用 + enabled: true + # 配置文件路径 + change-log: classpath:/db/changelog/db.changelog-master.yaml + +--- ### 缓存配置 +spring.data: + ## Redis 配置(单机模式) + redis: + # 地址 + host: ${REDIS_HOST:127.0.0.1} + # 端口(默认 6379) + port: ${REDIS_PORT:6379} + # 密码(未设置密码时请注释掉) + password: ${REDIS_PWD:123456} + # 数据库索引 + database: ${REDIS_DB:1} + # 连接超时时间 + timeout: 10s + # 是否开启 SSL + ssl: + enabled: false + ## Redisson 配置 + redisson: + enabled: true + mode: SINGLE +## JetCache 配置 +jetcache: + # 统计间隔(默认 0,表示不统计) + statIntervalMinutes: 0 + ## 本地/进程级/一级缓存配置 + local: + default: + # 缓存类型 + type: caffeine + # key 转换器的全局配置 + keyConvertor: jackson + # 以毫秒为单位指定超时时间的全局配置 + expireAfterWriteInMillis: 7200000 + # 每个缓存实例的最大元素的全局配置,仅 local 类型的缓存需要指定 + limit: 1000 + ## 远程/分布式/二级缓存配置 + remote: + default: + # 缓存类型 + type: redisson + # key 转换器的全局配置(用于将复杂的 KEY 类型转换为缓存实现可以接受的类型) + keyConvertor: jackson + # 以毫秒为单位指定超时时间的全局配置 + expireAfterWriteInMillis: 7200000 + # 2.7+ 支持两级缓存更新以后失效其他 JVM 中的 local cache,但多个服务共用 Redis 同一个 channel 可能会造成广播风暴,需要在这里指定channel。 + # 你可以决定多个不同的服务是否共用同一个 channel,如果没有指定则不开启。 + broadcastChannel: ${spring.application.name} + # 序列化器的全局配置,仅 remote 类型的缓存需要指定 + valueEncoder: java + valueDecoder: java + +--- ### 验证码配置 +continew-starter.captcha: + ## 行为验证码 + behavior: + enabled: true + cache-type: REDIS + water-mark: + # 一分钟内接口请求次数限制开关(默认:0,关闭,开启后下方失败锁定配置才会生效) + req-frequency-limit-enable: 0 + # 一分钟内验证码最多失败次数限制(默认:5次) + req-get-lock-limit: 5 + # 一分钟内验证码最多失败次数限制达标后锁定时间(默认:300秒) + req-get-lock-seconds: 300 + ## 图形验证码 + graphic: + # 类型 + type: SPEC + # 内容长度 + length: 4 + # 过期时间 + expirationInMinutes: 2 +## 其他验证码配置 +captcha: + ## 邮箱验证码配置 + mail: + # 内容长度 + length: 6 + # 过期时间 + expirationInMinutes: 5 + # 模板路径 + templatePath: mail/captcha.ftl + ## 短信验证码配置 + sms: + # 内容长度 + length: 4 + # 过期时间 + expirationInMinutes: 5 + # 模板 ID + templateId: SMS_493720484 + +--- ### 日志配置 +continew-starter.log: + # 是否打印日志,开启后可打印访问日志(类似于 Nginx access log) + is-print: false +## 项目日志配置(配置重叠部分,优先级高于 logback-spring.xml 中的配置) +logging: + level: + top.continew.admin: INFO + top.continew.starter: INFO + file: + path: ../logs + +--- ### 跨域配置 +continew-starter.web.cors: + enabled: true + # 配置允许跨域的域名 + allowed-origins: + - ${project.url} + # 配置允许跨域的请求方式 + allowed-methods: '*' + # 配置允许跨域的请求头 + allowed-headers: '*' + # 配置允许跨域的响应头 + exposed-headers: '*' + +--- ### 接口文档配置 +## 接口文档增强配置 +knife4j: + # 开启生产环境屏蔽 + production: ${project.production} + +--- ### WebSocket 配置 +continew-starter.messaging.websocket: + enabled: false + path: /websocket + # 配置允许跨域的域名 + allowed-origins: + - ${project.url} + +--- ### 短信配置 +sms: + # 从 YAML 读取配置 + config-type: YAML + http-log: true + is-print: false + blends: + alibaba: + # 短信厂商 + supplier: alibaba + requestUrl: dysmsapi.aliyuncs.com + access-key-id: LTAI5t8rsaBDrno4xd4F6EwE + access-key-secret: QNmjxatrgzBYukAKr8BZ4r7gd37SHw + signature: 武汉可学智能科技 + +--- ### 邮件配置 +spring.mail: + # 根据需要更换 + host: smtp.126.com + port: 465 + username: 你的邮箱 + password: 你的邮箱授权码 + properties: + mail: + smtp: + auth: true + socketFactory: + class: javax.net.ssl.SSLSocketFactory + port: 465 + +--- ### Just Auth 配置 +justauth: + enabled: true + type: + GITEE: + client-id: 5d271b7f638941812aaf8bfc2e2f08f06d6235ef934e0e39537e2364eb8452c4 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${project.url}/social/callback?source=gitee + GITHUB: + client-id: 38080dad08cfbdfacca9 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${project.url}/social/callback?source=github + cache: + type: REDIS + +--- ### Sa-Token 扩展配置 +sa-token.extension: + # 安全配置:排除(放行)路径配置 + security.excludes: + # 白名单 + - /consumptionHistory/add + - /taskRecordHistory/add + - /consumptionHistory/checkBalance + - /downFile/** + - /error + # 静态资源 + - /*.html + - /*/*.html + - /*/*.css + - /*/*.js + - /websocket/** + # 本地存储资源 + - /file/** + +--- ### 安全配置 +continew-starter.security: + ## 字段加/解密配置 + crypto: + enabled: true + # 对称加密算法密钥 + password: abcdefghijklmnop + # 非对称加密算法密钥(在线生成 RSA 密钥对:http://web.chacuo.net/netrsakeypair) + public-key: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAM51dgYtMyF+tTQt80sfFOpSV27a7t9uaUVeFrdGiVxscuizE7H8SMntYqfn9lp8a5GH5P1/GGehVjUD2gF/4kcCAwEAAQ== + private-key: MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAznV2Bi0zIX61NC3zSx8U6lJXbtru325pRV4Wt0aJXGxy6LMTsfxIye1ip+f2WnxrkYfk/X8YZ6FWNQPaAX/iRwIDAQABAkEAk/VcAusrpIqA5Ac2P5Tj0VX3cOuXmyouaVcXonr7f+6y2YTjLQuAnkcfKKocQI/juIRQBFQIqqW/m1nmz1wGeQIhAO8XaA/KxzOIgU0l/4lm0A2Wne6RokJ9HLs1YpOzIUmVAiEA3Q9DQrpAlIuiT1yWAGSxA9RxcjUM/1kdVLTkv0avXWsCIE0X8woEjK7lOSwzMG6RpEx9YHdopjViOj1zPVH61KTxAiBmv/dlhqkJ4rV46fIXELZur0pj6WC3N7a4brR8a+CLLQIhAMQyerWl2cPNVtE/8tkziHKbwW3ZUiBXU24wFxedT9iV + ## 密码编码器配置 + password: + enabled: true + # BCryptPasswordEncoder + encoding-id: bcrypt + ## 限流器配置 + limiter: + enabled: true + key-prefix: RateLimiter + +--- ### 文件上传配置 +spring.servlet: + multipart: + enabled: true + # 单文件上传大小限制 + max-file-size: 10MB + # 单次总上传文件大小限制 + max-request-size: 20MB +## 头像支持格式配置 +avatar: + support-suffix: jpg,jpeg,png,gif + +--- ### Snail Job 配置 +snail-job: + enabled: false + # 客户端地址(默认自动获取本机 IP) + #host: 127.0.0.1 + # 客户端端口(默认:1789) + port: 1789 + # 命名空间 ID + namespace: 764d604ec6fc45f68cd92514c40e9e1a + # 分组名 + group: continew-admin + # 令牌 + token: SJ_Wyz3dmsdbDOkDujOTSSoBjGQP1BMsVnj + ## 服务端配置(任务调度中心) + server: + # 服务端地址,若服务端集群部署则此处配置域名 + host: 127.0.0.1 + # Netty 端口号 + port: 1788 + # API 配置 + api: + # URL + url: http://127.0.0.1:8001/snail-job + # 用户名 + username: admin + # 密码 + password: admin + ## 重试数据批量上报滑动窗口配置 + retry: + reportSlidingWindow: + # 窗口期单位 + chrono-unit: SECONDS + # 窗口期时间长度 + duration: 10 + # 总量窗口期阈值 + total-threshold: 50 + # 窗口数量预警 + window-total-threshold: 150 + ## 调度线程池配置 + dispatcherThreadPool: + # 核心线程数 + corePoolSize: 16 + # 最大线程数 + maximumPoolSize: 16 + # 线程存活时间 + keepAliveTime: 1 + # 时间单位 + timeUnit: SECONDS + # 队列容量 + queueCapacity: 10000 + +sd-bot: + url: https://api.aibot.wang + Wxcz-Access-Key: ak_hjCG5qjcx_j7EtVCFT3 + +ai-bot: + url: https://api.xueai.art + Wxcz-Access-Key: ak_hjCG5qjcx_j7EtVCFT3 + +jwt: + secret: + public: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhF1cy7j57pBdPlO3Sm7N8expgroxq0br6fAinuXxiIAn3NklpgwLpw/V+leeKMzyYIJ2pFR6KmVybF9Z38Qs1H3C88r7ImYopBmO+IEqoEdm3UzNsNcdOSW4S9Lk1N5qtiniHDC/ZhlANzxeW7QbtS76RfZ4SSaeVUbfLJk/7RfFZ5HfLi1pBrT59SpfaJpKsbaMUBmdrEgGuGrT/K6jLsxqUcnOsbqAirtuP/NRpcO9OTsgpAh4tlFZKGg1hImIuAKwFZO0euZbjrQ9z7JwZoVSk0W99n0ULlofSIF939sSfhEX3UmgrEGsAwGVtZKROtJMHc/ywgBvaKpXKZsqjwIDAQAB + private: MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCEXVzLuPnukF0+U7dKbs3x7GmCujGrRuvp8CKe5fGIgCfc2SWmDAunD9X6V54ozPJggnakVHoqZXJsX1nfxCzUfcLzyvsiZiikGY74gSqgR2bdTM2w1x05JbhL0uTU3mq2KeIcML9mGUA3PF5btBu1LvpF9nhJJp5VRt8smT/tF8Vnkd8uLWkGtPn1Kl9omkqxtoxQGZ2sSAa4atP8rqMuzGpRyc6xuoCKu24/81Glw705OyCkCHi2UVkoaDWEiYi4ArAVk7R65luOtD3PsnBmhVKTRb32fRQuWh9IgX3f2xJ+ERfdSaCsQawDAZW1kpE60kwdz/LCAG9oqlcpmyqPAgMBAAECggEAIHf2Z1K8nDR1avCGKY/8nLdtOlAKFwWSoRPyqh+lm+Iz5QyvG4gg454S0xHjDgcX/WyDRdQjE3tHEiZhmW45wPYIeFz2NX3xwi8yzuqAVkithuNsIVDjLj1+c7sLvtvAOWcUu+2JRbYz9lisnLoVgeBALCFFaEw80pZL8Fi3QaUT77Tbl1TpuJHhRARuqKUdHgTe95tpcOF8s7OPJFu0aqWrDbrtA11CuQKXyJtuchrJtC4NSwddpz1h8sZ45BrjWcerqXu4InYa0NEEO9O8ik34wESiyvu2jiZsUk3Z/vDKKwiGGjR1ARXWy5t4N88Yfg00JJJpd1VCuZwQeqPAYQKBgQDTTxLWk1UpGAEAA1bBBoI4XddoB/0+cckE3M0tXdDD9HR260LP/9YOY7j9vqJiWCYaqv7PYXLOZyd3ypdbnh7Er44Z8heKwI+uVn6FwF4NsajSkdUpZg901y1+fqyO3z8AHCMEBSmjUiOGL4UQgwYw7R/55dtVJ6Tb5BCTIUniwwKBgQCgXAC7l3XypNIDJoqD3i4lg61APLwbxKxFW7UL1k1ENu80gkSUF7VXG8cf4H4cgP/8o+WJvhVeJjiz8ps5JoAMozEr78XFf0FhoL1xBhgO6W93DWSOdI1yVyijtPfVw/0xV6G/+ZbMR8qUIIY0gRkkiXtAazD35exT5vgBM+IERQKBgBv7wcvm0QVTJ3a/+nIvkXkzfD5LqI7ndK6PKyDR5r68ldvVxhBTmh5nuGzqHQkXEVzAvmPqTNDmukRT8P1yEDMN6Kyo8dMvMxfsXm8KDaqUl33jUC5eVLetc1noHqQBDo/OKhjzMhNt4kR38JBCOcSq8mwes5AwYtW3+zXw158PAoGAYhHiINewOoSyK1sWuy7V6fttqSt7nz/YW1KKloKpHl66S9KfPjKBpJRIYyZX+faql0dkyaiGSN33L5XfeEQja5ciK0yoFvCMe7hiIw0nCXeZw6VdKgH9vz+uiu/R4dnKrrJ5KX7qymPdcFmzuch6J+4sgmkLuY/TyDwo0BN61kECgYAnJ/2C0LXzYqEhtt1/80SSWnOdJBi5cnDVZ4OhE/Jbz2fBpu3OwIs7ILNw0v40RdRjKQnYyaRYU1KS0L6DbALkuqrrSLlDbaW9Q/gQfBgcFbpvNM9ZjGD9uNEK9wqplegON6JDAJkucpU3USapVbU4z/Go4bZbu4XMXJkIWYbdzA== + +common: + file: + path: /docker/kexue/data/file/ + +depts: +# routers: chat,agent,ai_programming,Multimodal,painting,music,video,ai3D,Cultural,comfyUI,agent_plus,ai_programming_plus,Fine_tuning,wenxian + routers: LLM,agent,AIprogram,Multimodal,AIpaint,music,video,AI3d,AIwriting,comfyui,agentPlus,programPlus,fineTuning,deepResearch,wenxian \ No newline at end of file diff --git a/continew-admin-webapi/src/main/resources/config/application-test.yml b/continew-admin-webapi/src/main/resources/config/application-test.yml new file mode 100644 index 0000000..e4cc04d --- /dev/null +++ b/continew-admin-webapi/src/main/resources/config/application-test.yml @@ -0,0 +1,360 @@ +--- ### 项目配置 +project: + # URL(跨域配置默认放行此 URL,第三方登录回调默认使用此 URL 为前缀,请注意更改为你实际的前端 URL) + url: http://localhost:5173 + +--- ### 服务器配置 +server: + # HTTP 端口(默认 8080) + port: 8003 + address: 0.0.0.0 + +--- ### 数据源配置 +spring.datasource: + type: com.zaxxer.hikari.HikariDataSource + ## 动态数据源配置(可配多主多从:m1、s1...;纯粹多库:mysql、oracle...;混合配置:m1、s1、oracle...) + dynamic: + # 是否启用 P6Spy(SQL 性能分析组件,该插件有性能损耗,不建议生产环境使用) + p6spy: true + # 设置默认的数据源或者数据源组(默认:master) + primary: digital_human + # 严格匹配数据源(true:未匹配到指定数据源时抛异常;false:使用默认数据源;默认 false) + strict: false + datasource: + # 主库配置(可配多个,构成多主) + digital_human: + url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:digital_human}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&autoReconnect=true&maxReconnects=10&failOverReadOnly=false&allowPublicKeyRetrieval=true + #url: jdbc:mysql://106.54.11.219:3306/digital_human?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&autoReconnect=true&maxReconnects=10&failOverReadOnly=false + username: ${DB_USER:root} + # password: ${DB_PWD:147369Wan} + password: ${DB_PWD:123456} + # password: ${DB_PWD:C9MUjc5ChtqHeCtQ} + driver-class-name: com.mysql.cj.jdbc.Driver + type: ${spring.datasource.type} + + hikari: + # 最大连接数量(默认 10,根据实际环境调整) + # 注意:当连接达到上限,并且没有空闲连接可用时,获取连接将在超时前阻塞最多 connectionTimeout 毫秒 + max-pool-size: 20 + # 获取连接超时时间(默认 30000 毫秒,30 秒) + connection-timeout: 30000 + # 空闲连接最大存活时间(默认 600000 毫秒,10 分钟) + idle-timeout: 600000 + # 保持连接活动的频率,以防止它被数据库或网络基础设施超时。该值必须小于 maxLifetime(默认 0,禁用) + keepaliveTime: 30000 + # 连接最大生存时间(默认 1800000 毫秒,30 分钟) + max-lifetime: 1800000 +## Liquibase 配置 +spring.liquibase: + # 是否启用 + enabled: false + # 配置文件路径 + change-log: classpath:/db/changelog/db.changelog-master.yaml + +--- ### 缓存配置 +spring.data: + ## Redis 配置(单机模式) + redis: + # 地址 + host: localhost + # host: 106.54.11.219 + # 端口(默认 6379) + port: 6379 + # port: 16380 + # 密码(未设置密码时请注释掉) + password: 123456 + # 数据库索引 + database: ${REDIS_DB:11} + # 连接超时时间 + timeout: 10s + # 是否开启 SSL + ssl: + enabled: false + ## Redisson 配置 + redisson: + enabled: true + mode: SINGLE +## JetCache 配置 +jetcache: + # 统计间隔(默认 0,表示不统计) + statIntervalMinutes: 15 + ## 本地/进程级/一级缓存配置 + local: + default: + # 缓存类型 + type: caffeine + # key 转换器的全局配置 + keyConvertor: jackson + # 以毫秒为单位指定超时时间的全局配置 + expireAfterWriteInMillis: 7200000 + # 每个缓存实例的最大元素的全局配置,仅 local 类型的缓存需要指定 + limit: 1000 + ## 远程/分布式/二级缓存配置 + remote: + default: + # 缓存类型 + type: redisson + # key 转换器的全局配置(用于将复杂的 KEY 类型转换为缓存实现可以接受的类型) + keyConvertor: jackson + # 以毫秒为单位指定超时时间的全局配置 + expireAfterWriteInMillis: 7200000 + # 2.7+ 支持两级缓存更新以后失效其他 JVM 中的 local cache,但多个服务共用 Redis 同一个 channel 可能会造成广播风暴,需要在这里指定channel。 + # 你可以决定多个不同的服务是否共用同一个 channel,如果没有指定则不开启。 + broadcastChannel: ${spring.application.name} + # 序列化器的全局配置,仅 remote 类型的缓存需要指定 + valueEncoder: java + valueDecoder: java + +--- ### 验证码配置 +continew-starter.captcha: + ## 行为验证码 + behavior: + enabled: true + cache-type: REDIS + water-mark: + # 一分钟内接口请求次数限制开关(默认:0,关闭,开启后下方失败锁定配置才会生效) + req-frequency-limit-enable: 0 + # 一分钟内验证码最多失败次数限制(默认:5次) + req-get-lock-limit: 5 + # 一分钟内验证码最多失败次数限制达标后锁定时间(默认:300秒) + req-get-lock-seconds: 300 + ## 图形验证码 + graphic: + # 类型 + type: SPEC + # 内容长度 + length: 4 + # 过期时间 + expirationInMinutes: 2 +## 其他验证码配置 +captcha: + ## 邮箱验证码配置 + mail: + # 内容长度 + length: 6 + # 过期时间 + expirationInMinutes: 5 + # 模板路径 + templatePath: mail/captcha.ftl + ## 短信验证码配置 + sms: + # 内容长度 + length: 4 + # 过期时间 + expirationInMinutes: 5 + # 模板 ID + templateId: SMS_493720484 + +--- ### 日志配置 +continew-starter.log: + # 是否打印日志,开启后可打印访问日志(类似于 Nginx access log) + is-print: true +## 项目日志配置(配置重叠部分,优先级高于 logback-spring.xml 中的配置) +logging: + level: + top.continew.admin: DEBUG + top.continew.starter: DEBUG + file: + path: ./logs + +--- ### 跨域配置 +continew-starter.web.cors: + enabled: true + # 配置允许跨域的域名 + allowed-origins: '*' + # 配置允许跨域的请求方式 + allowed-methods: '*' + # 配置允许跨域的请求头 + allowed-headers: '*' + # 配置允许跨域的响应头 + exposed-headers: '*' + +--- ### 接口文档配置 +springdoc: + swagger-ui: + enabled: true + +--- ### WebSocket 配置 +continew-starter.messaging.websocket: + enabled: true + path: /websocket + # 配置允许跨域的域名 + allowed-origins: '*' + +--- ### 短信配置 +sms: + # 从 YAML 读取配置 + config-type: YAML + http-log: true + is-print: false + blends: + alibaba: + # 短信厂商 + supplier: alibaba + requestUrl: dysmsapi.aliyuncs.com + access-key-id: LTAI5t8rsaBDrno4xd4F6EwE + access-key-secret: QNmjxatrgzBYukAKr8BZ4r7gd37SHw + signature: 武汉可学智能科技 +--- ### 邮件配置 +spring.mail: + # 根据需要更换 + host: smtp.126.com + port: 465 + username: 你的邮箱 + password: 你的邮箱授权码 + properties: + mail: + smtp: + auth: true + socketFactory: + class: javax.net.ssl.SSLSocketFactory + port: 465 + +--- ### Just Auth 配置 +justauth: + enabled: true + type: + GITEE: + client-id: 5d271b7f638941812aaf8bfc2e2f08f06d6235ef934e0e39537e2364eb8452c4 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${project.url}/social/callback?source=gitee + GITHUB: + client-id: 38080dad08cfbdfacca9 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${project.url}/social/callback?source=github + cache: + type: REDIS + +--- ### Sa-Token 扩展配置 +sa-token.extension: + # 安全配置:排除(放行)路径配置 + security.excludes: + # 白名单 + - /consumptionHistory/add + - /taskRecordHistory/add + - /consumptionHistory/checkBalance + - /downFile/** + - /error + - /signIn/add + # 静态资源 + - /*.html + - /*/*.html + - /*/*.css + - /*/*.js + - /websocket/** + # 接口文档相关资源 + - /favicon.ico + - /doc.html + - /webjars/** + - /swagger-ui/** + - /swagger-resources/** + - /*/api-docs/** + # 本地存储资源 + - /file/** + +--- ### 安全配置 +continew-starter.security: + ## 字段加/解密配置 + crypto: + enabled: true + # 对称加密算法密钥 + password: abcdefghijklmnop + # 非对称加密算法密钥(在线生成 RSA 密钥对:http://web.chacuo.net/netrsakeypair) + public-key: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAM51dgYtMyF+tTQt80sfFOpSV27a7t9uaUVeFrdGiVxscuizE7H8SMntYqfn9lp8a5GH5P1/GGehVjUD2gF/4kcCAwEAAQ== + private-key: MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAznV2Bi0zIX61NC3zSx8U6lJXbtru325pRV4Wt0aJXGxy6LMTsfxIye1ip+f2WnxrkYfk/X8YZ6FWNQPaAX/iRwIDAQABAkEAk/VcAusrpIqA5Ac2P5Tj0VX3cOuXmyouaVcXonr7f+6y2YTjLQuAnkcfKKocQI/juIRQBFQIqqW/m1nmz1wGeQIhAO8XaA/KxzOIgU0l/4lm0A2Wne6RokJ9HLs1YpOzIUmVAiEA3Q9DQrpAlIuiT1yWAGSxA9RxcjUM/1kdVLTkv0avXWsCIE0X8woEjK7lOSwzMG6RpEx9YHdopjViOj1zPVH61KTxAiBmv/dlhqkJ4rV46fIXELZur0pj6WC3N7a4brR8a+CLLQIhAMQyerWl2cPNVtE/8tkziHKbwW3ZUiBXU24wFxedT9iV + ## 密码编码器配置 + password: + enabled: true + # BCryptPasswordEncoder + encoding-id: bcrypt + ## 限流器配置 + limiter: + enabled: true + key-prefix: RateLimiter + +--- ### 文件上传配置 +spring.servlet: + multipart: + enabled: true + # 单文件上传大小限制 + max-file-size: 10MB + # 单次总上传文件大小限制 + max-request-size: 20MB +## 头像支持格式配置 +avatar: + support-suffix: jpg,jpeg,png,gif + +--- ### Snail Job 配置 +snail-job: + enabled: false + # 客户端地址(默认自动获取本机 IP) + #host: 127.0.0.1 + # 客户端端口(默认:1789) + port: 1789 + # 命名空间 ID + namespace: ${SCHEDULE_NAMESPACE:764d604ec6fc45f68cd92514c40e9e1a} + # 分组名 + group: ${SCHEDULE_GROUP:continew-admin} + # 令牌 + token: ${SCHEDULE_TOKEN:SJ_Wyz3dmsdbDOkDujOTSSoBjGQP1BMsVnj} + ## 服务端配置(任务调度中心) + server: + # 服务端地址,若服务端集群部署则此处配置域名 + host: ${SCHEDULE_HOST:127.0.0.1} + # Netty 端口号 + port: ${SCHEDULE_PORT:1788} + # API 配置 + api: + # URL + url: http://127.0.0.1:8001/snail-job + # 用户名 + username: ${SCHEDULE_USERNAME:admin} + # 密码 + password: ${SCHEDULE_PASSWORD:admin} + ## 重试数据批量上报滑动窗口配置 + retry: + reportSlidingWindow: + # 窗口期单位 + chrono-unit: SECONDS + # 窗口期时间长度 + duration: 10 + # 总量窗口期阈值 + total-threshold: 50 + # 窗口数量预警 + window-total-threshold: 150 + ## 调度线程池配置 + dispatcherThreadPool: + # 核心线程数 + corePoolSize: 16 + # 最大线程数 + maximumPoolSize: 16 + # 线程存活时间 + keepAliveTime: 1 + # 时间单位 + timeUnit: SECONDS + # 队列容量 + queueCapacity: 10000 + +sd-bot: + url: https://api.aibot.wang + Wxcz-Access-Key: ak_hjCG5qjcx_j7EtVCFT3 + +ai-bot: + url: https://api.xueai.art + Wxcz-Access-Key: ak_hjCG5qjcx_j7EtVCFT3 + +jwt: + secret: + public: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhF1cy7j57pBdPlO3Sm7N8expgroxq0br6fAinuXxiIAn3NklpgwLpw/V+leeKMzyYIJ2pFR6KmVybF9Z38Qs1H3C88r7ImYopBmO+IEqoEdm3UzNsNcdOSW4S9Lk1N5qtiniHDC/ZhlANzxeW7QbtS76RfZ4SSaeVUbfLJk/7RfFZ5HfLi1pBrT59SpfaJpKsbaMUBmdrEgGuGrT/K6jLsxqUcnOsbqAirtuP/NRpcO9OTsgpAh4tlFZKGg1hImIuAKwFZO0euZbjrQ9z7JwZoVSk0W99n0ULlofSIF939sSfhEX3UmgrEGsAwGVtZKROtJMHc/ywgBvaKpXKZsqjwIDAQAB + private: MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCEXVzLuPnukF0+U7dKbs3x7GmCujGrRuvp8CKe5fGIgCfc2SWmDAunD9X6V54ozPJggnakVHoqZXJsX1nfxCzUfcLzyvsiZiikGY74gSqgR2bdTM2w1x05JbhL0uTU3mq2KeIcML9mGUA3PF5btBu1LvpF9nhJJp5VRt8smT/tF8Vnkd8uLWkGtPn1Kl9omkqxtoxQGZ2sSAa4atP8rqMuzGpRyc6xuoCKu24/81Glw705OyCkCHi2UVkoaDWEiYi4ArAVk7R65luOtD3PsnBmhVKTRb32fRQuWh9IgX3f2xJ+ERfdSaCsQawDAZW1kpE60kwdz/LCAG9oqlcpmyqPAgMBAAECggEAIHf2Z1K8nDR1avCGKY/8nLdtOlAKFwWSoRPyqh+lm+Iz5QyvG4gg454S0xHjDgcX/WyDRdQjE3tHEiZhmW45wPYIeFz2NX3xwi8yzuqAVkithuNsIVDjLj1+c7sLvtvAOWcUu+2JRbYz9lisnLoVgeBALCFFaEw80pZL8Fi3QaUT77Tbl1TpuJHhRARuqKUdHgTe95tpcOF8s7OPJFu0aqWrDbrtA11CuQKXyJtuchrJtC4NSwddpz1h8sZ45BrjWcerqXu4InYa0NEEO9O8ik34wESiyvu2jiZsUk3Z/vDKKwiGGjR1ARXWy5t4N88Yfg00JJJpd1VCuZwQeqPAYQKBgQDTTxLWk1UpGAEAA1bBBoI4XddoB/0+cckE3M0tXdDD9HR260LP/9YOY7j9vqJiWCYaqv7PYXLOZyd3ypdbnh7Er44Z8heKwI+uVn6FwF4NsajSkdUpZg901y1+fqyO3z8AHCMEBSmjUiOGL4UQgwYw7R/55dtVJ6Tb5BCTIUniwwKBgQCgXAC7l3XypNIDJoqD3i4lg61APLwbxKxFW7UL1k1ENu80gkSUF7VXG8cf4H4cgP/8o+WJvhVeJjiz8ps5JoAMozEr78XFf0FhoL1xBhgO6W93DWSOdI1yVyijtPfVw/0xV6G/+ZbMR8qUIIY0gRkkiXtAazD35exT5vgBM+IERQKBgBv7wcvm0QVTJ3a/+nIvkXkzfD5LqI7ndK6PKyDR5r68ldvVxhBTmh5nuGzqHQkXEVzAvmPqTNDmukRT8P1yEDMN6Kyo8dMvMxfsXm8KDaqUl33jUC5eVLetc1noHqQBDo/OKhjzMhNt4kR38JBCOcSq8mwes5AwYtW3+zXw158PAoGAYhHiINewOoSyK1sWuy7V6fttqSt7nz/YW1KKloKpHl66S9KfPjKBpJRIYyZX+faql0dkyaiGSN33L5XfeEQja5ciK0yoFvCMe7hiIw0nCXeZw6VdKgH9vz+uiu/R4dnKrrJ5KX7qymPdcFmzuch6J+4sgmkLuY/TyDwo0BN61kECgYAnJ/2C0LXzYqEhtt1/80SSWnOdJBi5cnDVZ4OhE/Jbz2fBpu3OwIs7ILNw0v40RdRjKQnYyaRYU1KS0L6DbALkuqrrSLlDbaW9Q/gQfBgcFbpvNM9ZjGD9uNEK9wqplegON6JDAJkucpU3USapVbU4z/Go4bZbu4XMXJkIWYbdzA== + +common: + file: + path: D:/workspace/sxwz_backend/continew-admin-webapi/target/data/file/ + + #案例中心: ['casebase'], 提示词库: ['prompt'],在线视频: ['onlinetutorial'],教程列表: ['book'],作品中心: ['worksLibrary'],班级管理: ['classmanagement'],课程管理: ['curriculumMgt'],课程中心: ['curriculumCenter'],设计效率工具: ['DesignEfficiency'],专业赋能工具: ['ProfessionalEmpowerment'],教研学智能体: ['TeachingResearchStudies'],生成平台: ['operate'],模型索引: ['modelDisplay'],优秀作品: ['excellentWorks'] + #routers: casebase,prompt,onlinetutorial,book,worksLibrary,classmanagement,curriculumMgt,curriculumCenter,DesignEfficiency,ProfessionalEmpowerment,TeachingResearchStudies,operate,modelDisplay,excellentWorks +depts: + # routers: chat,agent,ai_programming,Multimodal,painting,music,video,ai3D,Cultural,comfyUI,agent_plus,ai_programming_plus,Fine_tuning,wenxian + routers: LLM,agent,AIprogram,Multimodal,AIpaint,music,video,AI3d,AIwriting,comfyui,agentPlus,programPlus,fineTuning,deepResearch,wenxian + diff --git a/continew-admin-webapi/src/main/resources/config/application.yml b/continew-admin-webapi/src/main/resources/config/application.yml new file mode 100644 index 0000000..47b4677 --- /dev/null +++ b/continew-admin-webapi/src/main/resources/config/application.yml @@ -0,0 +1,286 @@ +--- ### 项目配置 +project: + # 名称 + name: ContiNew Admin + # 应用名称 + app-name: continew-admin + # 版本 + version: 3.3.0 + # 描述 + description: 持续迭代优化的前后端分离中后台管理系统框架,开箱即用,持续提供舒适的开发体验。 + # 基本包 + base-package: top.continew.admin + ## 作者信息配置 + contact: + name: Charles7c + email: charles7c@126.com + url: https://blog.charles7c.top/about/me + ## 许可协议信息配置 + license: + name: Apache-2.0 + url: https://github.com/continew-org/continew-admin/blob/dev/LICENSE + +--- ### 日志配置 +continew-starter.log: + # 包含信息 + includes: + - DESCRIPTION + - MODULE + - REQUEST_HEADERS + - REQUEST_BODY + - IP_ADDRESS + - BROWSER + - OS + - RESPONSE_HEADERS + - RESPONSE_BODY +## 项目日志配置 +logging: + config: classpath:logback-spring.xml + +--- ### 链路跟踪配置 +continew-starter.web: + trace: + enabled: true + header-name: traceId + ## TLog 配置 + tlog: + enable-invoke-time-print: false + pattern: '[$spanId][$traceId]' + mdc-enable: false + +--- ### 全局响应配置 +continew-starter.web: + response: + # 是否开启国际化(默认:false) + i18n: false + # 自定义失败 HTTP 状态码(默认:200,建议业务和通信状态码区分) + default-http-status-code-on-error: 200 + # 自定义成功响应码(默认:0) + default-success-code: 0 + # 自定义成功提示(默认:ok) + default-success-msg: ok + # 自定义失败响应码(默认:1) + default-error-code: 1 + # 自定义失败提示(默认:error) + default-error-msg: error + # 是否将原生异常错误信息填充到状态信息中 + origin-exception-using-detail-message: false + +--- ### 接口文档配置 +springdoc: + # 设置对象型参数的展示形式(设为 true 表示将对象型参数平展开,即对象内的属性直接作为参数展示而不是嵌套在对象内,默认 false) + # 如果不添加该全局配置,可以在需要如此处理的对象参数类上使用 @ParameterObject + default-flat-param-object: true + # 分组配置 + group-configs: + - group: all + paths-to-match: /** + paths-to-exclude: + - /error + - group: auth + display-name: 系统认证 + packages-to-scan: ${project.base-package}.controller.auth + - group: common + display-name: 通用接口 + packages-to-scan: ${project.base-package}.controller.common + - group: system + display-name: 系统管理 + packages-to-scan: ${project.base-package}.controller.system + - group: monitor + display-name: 系统监控 + packages-to-scan: ${project.base-package}.controller.monitor + - group: schedule + display-name: 任务调度 + packages-to-scan: ${project.base-package}.controller.schedule + - group: open + display-name: 能力开放 + packages-to-scan: ${project.base-package}.controller.open + - group: code + display-name: 代码生成 + packages-to-scan: ${project.base-package}.controller.code + ## 组件配置 + components: + # 鉴权配置 + security-schemes: + Authorization: + type: HTTP + in: HEADER + name: ${sa-token.token-name} + scheme: ${sa-token.token-prefix} +## 接口文档增强配置 +knife4j: + enable: true + setting: + # 是否显示默认的 footer(默认 true,显示) + enable-footer: false + # 是否自定义 footer(默认 false,非自定义) + enable-footer-custom: true + # 自定义 footer 内容,支持 Markdown 语法 + footer-custom-content: 'Copyright © 2022-present [${project.contact.name}](${project.contact.url}) ⋅ [${project.name}](${project.url}) v${project.version}' + +--- ### Sa-Token 配置 +sa-token: + # token 名称(同时也是 cookie 名称) + token-name: Authorization + # token 有效期(单位:秒,默认 30 天,-1 代表永不过期) + timeout: -1 + # token 最低活跃频率(单位:秒,默认 -1,代表不限制,永不冻结。如果 token 超过此时间没有访问系统就会被冻结) + active-timeout: -1 + # 是否打开自动续签(如果此值为 true,框架会在每次直接或间接调用 getLoginId() 时进行一次过期检查与续签操作) + auto-renew: true + # 是否允许同一账号多地同时登录(为 true 时允许一起登录,为 false 时新登录挤掉旧登录) + is-concurrent: false + # 在多人登录同一账号时,是否共用一个 token(为 true 时所有登录共用一个 token,为 false 时每次登录新建一个 token) + is-share: false + # 是否输出操作日志 + is-log: false + # JWT 秘钥 + jwt-secret-key: asdasdasifhueuiwyurfewbfjsdafjk + ## 扩展配置 + extension: + enabled: true + enableJwt: true + # 持久层配置 + dao.type: REDIS + +--- ### MyBatis Plus 配置 +mybatis-plus: + # Mapper XML 文件目录配置 + mapper-locations: classpath*:/mapper/**/*Mapper.xml + # 类型别名扫描包配置· + type-aliases-package: ${project.base-package}.**.model + ## MyBatis 配置 + configuration: + # MyBatis 自动映射策略 + # NONE:不启用 PARTIAL:只对非嵌套 resultMap 自动映射 FULL:对所有 resultMap 自动映射 + auto-mapping-behavior: PARTIAL + ## 全局配置 + global-config: + banner: true + db-config: + # 主键类型(默认 assign_id,表示自行赋值) + # auto 代表使用数据库自增策略(需要在表中设置好自增约束) + id-type: ASSIGN_ID + # 逻辑删除字段 + logic-delete-field: isDeleted + # 逻辑删除全局值(默认 1,表示已删除) + logic-delete-value: 1 + # 逻辑未删除全局值(默认 0,表示未删除) + logic-not-delete-value: 0 + ## 扩展配置 + extension: + enabled: true + # Mapper 接口扫描包配置 + mapper-package: ${project.base-package}.**.mapper + # ID 生成器配置 + id-generator: + type: COSID + # 数据权限配置 + data-permission: + enabled: true + # 分页插件配置 + pagination: + enabled: true + db-type: MYSQL + +--- ### CosId 配置 +cosid: + namespace: ${spring.application.name} + machine: + enabled: true + # 机器号分配器 + distributor: + type: REDIS + guarder: + # 开启机器号守护 + enabled: true + snowflake: + enabled: true + zone-id: Asia/Shanghai + epoch: 1577203200000 + share: + # 开启时钟回拨同步 + clock-sync: true + friendly: true + provider: + safe-js: + machine-bit: 7 + sequence-bit: 9 + +--- ### 认证配置 +auth: + ## 密码配置 + password: + excludes: + - /auth/route + - /auth/user/info + - /auth/logout + - /system/user/password + +--- ### 服务器配置 +server: + servlet: + # 应用访问路径 + context-path: / + ## Undertow 服务器配置 + undertow: + # HTTP POST 请求内容的大小上限(默认 -1,不限制) + max-http-post-size: -1 + # 以下的配置会影响 buffer,这些 buffer 会用于服务器连接的 IO 操作,有点类似 Netty 的池化内存管理 + # 每块 buffer的空间大小(越小的空间被利用越充分,不要设置太大,以免影响其他应用,合适即可) + buffer-size: 512 + # 是否分配的直接内存(NIO 直接分配的堆外内存) + direct-buffers: true + threads: + # 设置 IO 线程数,它主要执行非阻塞的任务,它们会负责多个连接(默认每个 CPU 核心一个线程) + io: 8 + # 阻塞任务线程池,当执行类似 Servlet 请求阻塞操作,Undertow 会从这个线程池中取得线程(它的值设置取决于系统的负载) + worker: 256 + +--- ### Spring 配置 +spring: + application: + name: ${project.app-name} + main: + # 允许定义重名的 bean 对象覆盖原有的 bean + allow-bean-definition-overriding: true + # 允许循环依赖 + allow-circular-references: true + ## 环境配置 + profiles: + # 启用的环境 + active: dev + include: + - generator + ## 线程池配置(默认启用扩展配置,如未指定 corePoolSize、maxPoolSize 则根据机器配置自动设置) + task: + # 异步任务 + execution: + thread-name-prefix: task-pool + # 任务拒绝策略(默认 ABORT,不执行新任务,直接抛出 RejectedExecutionException 异常) + # CALLER_RUNS:提交的任务在执行被拒绝时,会由提交任务的线程去执行 + rejected-policy: CALLER_RUNS + pool: + keep-alive: 300s + shutdown: + # 是否等待任务执行完成再关闭线程池(默认 false) + await-termination: true + # 等待时间 + await-termination-period: 30s + # 定时任务 + scheduling: + thread-name-prefix: schedule-pool + # 任务拒绝策略(默认 ABORT,不执行新任务,直接抛出 RejectedExecutionException 异常) + # CALLER_RUNS:提交的任务在执行被拒绝时,会由提交任务的线程去执行 + rejected-policy: CALLER_RUNS + shutdown: + # 是否等待任务执行完成再关闭线程池(默认 false) + await-termination: true + # 等待时间 + await-termination-period: 30s + +--- ### 健康检查配置 +management.health: + mail: + # 关闭邮箱健康检查(邮箱配置错误或邮箱服务器不可用时,健康检查会报错) + enabled: false diff --git a/continew-admin-webapi/src/main/resources/db/changelog/db.changelog-master.yaml b/continew-admin-webapi/src/main/resources/db/changelog/db.changelog-master.yaml new file mode 100644 index 0000000..5a9f7be --- /dev/null +++ b/continew-admin-webapi/src/main/resources/db/changelog/db.changelog-master.yaml @@ -0,0 +1,14 @@ +databaseChangeLog: + - include: + file: db/changelog/mysql/continew-admin_table.sql + - include: + file: db/changelog/mysql/continew-admin_column.sql + - include: + file: db/changelog/mysql/continew-admin_data.sql +# PostgreSQL +# - include: +# file: db/changelog/postgresql/continew-admin_table.sql +# - include: +# file: db/changelog/postgresql/continew-admin_column.sql +# - include: +# file: db/changelog/postgresql/continew-admin_data.sql \ No newline at end of file diff --git a/continew-admin-webapi/src/main/resources/db/changelog/mysql/change/202505/20250512.sql b/continew-admin-webapi/src/main/resources/db/changelog/mysql/change/202505/20250512.sql new file mode 100644 index 0000000..beb8416 --- /dev/null +++ b/continew-admin-webapi/src/main/resources/db/changelog/mysql/change/202505/20250512.sql @@ -0,0 +1,3 @@ +-- 部门表增加数据库名 +alter table sys_dept add `db_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '数据库名称' after `update_time`; + diff --git a/continew-admin-webapi/src/main/resources/db/changelog/mysql/change/202505/20250514.sql b/continew-admin-webapi/src/main/resources/db/changelog/mysql/change/202505/20250514.sql new file mode 100644 index 0000000..7aa514b --- /dev/null +++ b/continew-admin-webapi/src/main/resources/db/changelog/mysql/change/202505/20250514.sql @@ -0,0 +1,70 @@ +CREATE TABLE `order_audio` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID', + `create_user` bigint NOT NULL COMMENT '创建人', + `create_time` datetime NOT NULL COMMENT '创建时间', + `update_user` bigint DEFAULT NULL COMMENT '修改人', + `update_time` datetime DEFAULT NULL COMMENT '修改时间', + `state` varchar(20) DEFAULT NULL COMMENT '状态', + `delete_flag` int DEFAULT NULL COMMENT '删除标示', + `type` varchar(30) DEFAULT NULL COMMENT '任务类型', + `model` varchar(200) DEFAULT NULL COMMENT '使用的模型', + `invite_beans` int DEFAULT '0' COMMENT '消耗点数', + `file_url` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '存储结果文件路径', + `order_type` tinyint DEFAULT NULL COMMENT '订单类型:1=音频平台;2=视频平台;3=3D平台', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=713044548485439496 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='音频平台任务订单表'; + + +CREATE TABLE `order_three` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID', + `create_user` bigint NOT NULL COMMENT '创建人', + `create_time` datetime NOT NULL COMMENT '创建时间', + `update_user` bigint DEFAULT NULL COMMENT '修改人', + `update_time` datetime DEFAULT NULL COMMENT '修改时间', + `state` varchar(20) DEFAULT NULL COMMENT '状态', + `delete_flag` int DEFAULT NULL COMMENT '删除表示', + `type` varchar(30) DEFAULT NULL COMMENT '任务类型', + `model` varchar(200) DEFAULT NULL COMMENT '使用的模型', + `invite_beans` int DEFAULT '0' COMMENT '消耗点数', + `file_url` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '存储结果文件路径', + `order_type` tinyint DEFAULT NULL COMMENT '订单类型:1=音频平台;2=视频平台;3=3D平台', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=641219522769977352 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='3D平台任务订单表'; + +CREATE TABLE `order_video` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID', + `create_user` bigint NOT NULL COMMENT '创建人', + `create_time` datetime NOT NULL COMMENT '创建时间', + `update_user` bigint DEFAULT NULL COMMENT '修改人', + `update_time` datetime DEFAULT NULL COMMENT '修改时间', + `state` varchar(20) DEFAULT NULL COMMENT '状态', + `delete_flag` int DEFAULT NULL COMMENT '删除表示', + `type` varchar(30) DEFAULT NULL COMMENT '任务类型', + `model` varchar(200) DEFAULT NULL COMMENT '使用的模型', + `invite_beans` int DEFAULT '0' COMMENT '消耗点数', + `file_url` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '存储结果文件路径', + `order_type` tinyint DEFAULT NULL COMMENT '订单类型:1=音频平台;2=视频平台;3=3D平台', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=641219522769977352 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='视频平台任务订单表'; + + +CREATE TABLE `order_history` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID', + `create_user` bigint NOT NULL COMMENT '创建人', + `create_time` datetime NOT NULL COMMENT '创建时间', + `update_user` bigint DEFAULT NULL COMMENT '修改人', + `update_time` datetime DEFAULT NULL COMMENT '修改时间', + `state` varchar(20) DEFAULT NULL COMMENT '状态', + `delete_flag` int DEFAULT NULL COMMENT '删除表示', + `type` varchar(30) DEFAULT NULL COMMENT '任务类型', + `model` varchar(200) DEFAULT NULL COMMENT '使用的模型', + `invite_beans` int DEFAULT '0' COMMENT '消耗点数', + `remainder` int DEFAULT NULL COMMENT '剩余的点数', + `form_type` varchar(20) DEFAULT NULL COMMENT '记录类型:支出;增加', + `platform` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '生成平台', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=641219522769977352 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='平台任务订单历史表'; + + + + diff --git a/continew-admin-webapi/src/main/resources/db/changelog/mysql/continew-admin_column.sql b/continew-admin-webapi/src/main/resources/db/changelog/mysql/continew-admin_column.sql new file mode 100644 index 0000000..09feb98 --- /dev/null +++ b/continew-admin-webapi/src/main/resources/db/changelog/mysql/continew-admin_column.sql @@ -0,0 +1,9 @@ +-- liquibase formatted sql + +-- changeset Charles7c:3.3-1 +ALTER TABLE `gen_field_config` ADD COLUMN `dict_code` varchar(30) DEFAULT NULL COMMENT '字典编码' AFTER `query_type`; + +-- changeset Charles7c:3.3-2 +ALTER TABLE `sys_role` + ADD COLUMN `menu_check_strictly` bit(1) DEFAULT b'0' COMMENT '菜单选择是否父子节点关联' AFTER `is_system`, + ADD COLUMN `dept_check_strictly` bit(1) DEFAULT b'0' COMMENT '部门选择是否父子节点关联' AFTER `menu_check_strictly`; diff --git a/continew-admin-webapi/src/main/resources/db/changelog/mysql/continew-admin_data.sql b/continew-admin-webapi/src/main/resources/db/changelog/mysql/continew-admin_data.sql new file mode 100644 index 0000000..12c7ad9 --- /dev/null +++ b/continew-admin-webapi/src/main/resources/db/changelog/mysql/continew-admin_data.sql @@ -0,0 +1,201 @@ +-- liquibase formatted sql + +-- changeset Charles7c:1 +-- comment 初始化表数据 +-- 初始化默认菜单 +INSERT INTO `sys_menu` +(`id`, `title`, `parent_id`, `type`, `path`, `name`, `component`, `redirect`, `icon`, `is_external`, `is_cache`, `is_hidden`, `permission`, `sort`, `status`, `create_user`, `create_time`, `update_user`, `update_time`) +VALUES +(1000, '系统管理', 0, 1, '/system', 'System', 'Layout', '/system/user', 'settings', b'0', b'0', b'0', NULL, 1, 1, 1, NOW(), NULL, NULL), +(1010, '用户管理', 1000, 2, '/system/user', 'SystemUser', 'system/user/index', NULL, 'user', b'0', b'0', b'0', NULL, 1, 1, 1, NOW(), NULL, NULL), +(1011, '查看', 1010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:user:list', 1, 1, 1, NOW(), NULL, NULL), +(1012, '新增', 1010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:user:add', 2, 1, 1, NOW(), NULL, NULL), +(1013, '修改', 1010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:user:update', 3, 1, 1, NOW(), NULL, NULL), +(1014, '删除', 1010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:user:delete', 4, 1, 1, NOW(), NULL, NULL), +(1015, '导出', 1010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:user:export', 5, 1, 1, NOW(), NULL, NULL), +(1016, '重置密码', 1010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:user:resetPwd', 6, 1, 1, NOW(), NULL, NULL), +(1030, '角色管理', 1000, 2, '/system/role', 'SystemRole', 'system/role/index', NULL, 'user-group', b'0', b'0', b'0', NULL, 2, 1, 1, NOW(), NULL, NULL), +(1031, '查看', 1030, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:role:list', 1, 1, 1, NOW(), NULL, NULL), +(1032, '新增', 1030, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:role:add', 2, 1, 1, NOW(), NULL, NULL), +(1033, '修改', 1030, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:role:update', 3, 1, 1, NOW(), NULL, NULL), +(1034, '删除', 1030, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:role:delete', 4, 1, 1, NOW(), NULL, NULL), +(1050, '菜单管理', 1000, 2, '/system/menu', 'SystemMenu', 'system/menu/index', NULL, 'menu', b'0', b'0', b'0', NULL, 3, 1, 1, NOW(), NULL, NULL), +(1051, '查看', 1050, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:menu:list', 1, 1, 1, NOW(), NULL, NULL), +(1052, '新增', 1050, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:menu:add', 2, 1, 1, NOW(), NULL, NULL), +(1053, '修改', 1050, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:menu:update', 3, 1, 1, NOW(), NULL, NULL), +(1054, '删除', 1050, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:menu:delete', 4, 1, 1, NOW(), NULL, NULL), +(1060, '部门管理', 1000, 2, '/system/dept', 'SystemDept', 'system/dept/index', NULL, 'mind-mapping', b'0', b'0', b'0', NULL, 4, 1, 1, NOW(), NULL, NULL), +(1061, '查看', 1060, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:dept:list', 1, 1, 1, NOW(), NULL, NULL), +(1062, '新增', 1060, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:dept:add', 2, 1, 1, NOW(), NULL, NULL), +(1063, '修改', 1060, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:dept:update', 3, 1, 1, NOW(), NULL, NULL), +(1064, '删除', 1060, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:dept:delete', 4, 1, 1, NOW(), NULL, NULL), +(1065, '导出', 1060, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:dept:export', 5, 1, 1, NOW(), NULL, NULL), +(1070, '字典管理', 1000, 2, '/system/dict', 'SystemDict', 'system/dict/index', NULL, 'bookmark', b'0', b'0', b'0', NULL, 5, 1, 1, NOW(), NULL, NULL), +(1071, '查看', 1070, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:dict:list', 1, 1, 1, NOW(), NULL, NULL), +(1072, '新增', 1070, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:dict:add', 2, 1, 1, NOW(), NULL, NULL), +(1073, '修改', 1070, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:dict:update', 3, 1, 1, NOW(), NULL, NULL), +(1074, '删除', 1070, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:dict:delete', 4, 1, 1, NOW(), NULL, NULL), +(1080, '字典项管理', 1000, 2, '/system/dict/item', 'SystemDictItem', 'system/dict/item/index', NULL, 'bookmark', b'0', b'0', b'1', NULL, 5, 1, 1, NOW(), NULL, NULL), +(1081, '查看', 1080, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:dict:item:list', 1, 1, 1, NOW(), NULL, NULL), +(1082, '新增', 1080, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:dict:item:add', 2, 1, 1, NOW(), NULL, NULL), +(1083, '修改', 1080, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:dict:item:update', 3, 1, 1, NOW(), NULL, NULL), +(1084, '删除', 1080, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:dict:item:delete', 4, 1, 1, NOW(), NULL, NULL), +(1090, '通知公告', 1000, 2, '/system/notice', 'SystemNotice', 'system/notice/index', NULL, 'notification', b'0', b'0', b'0', NULL, 6, 1, 1, NOW(), NULL, NULL), +(1091, '查看', 1090, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:notice:list', 1, 1, 1, NOW(), NULL, NULL), +(1092, '新增', 1090, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:notice:add', 2, 1, 1, NOW(), NULL, NULL), +(1093, '修改', 1090, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:notice:update', 3, 1, 1, NOW(), NULL, NULL), +(1094, '删除', 1090, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:notice:delete', 4, 1, 1, NOW(), NULL, NULL), +(1100, '文件管理', 1000, 2, '/system/file', 'SystemFile', 'system/file/index', NULL, 'file', b'0', b'0', b'0', NULL, 7, 1, 1, NOW(), NULL, NULL), +(1101, '查看', 1100, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:file:list', 1, 1, 1, NOW(), NULL, NULL), +(1102, '上传', 1100, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:file:upload', 2, 1, 1, NOW(), NULL, NULL), +(1103, '修改', 1100, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:file:update', 3, 1, 1, NOW(), NULL, NULL), +(1104, '删除', 1100, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:file:delete', 4, 1, 1, NOW(), NULL, NULL), +(1105, '下载', 1100, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:file:download', 5, 1, 1, NOW(), NULL, NULL), +(1110, '存储管理', 1000, 2, '/system/storage', 'SystemStorage', 'system/storage/index', NULL, 'storage', b'0', b'0', b'0', NULL, 8, 1, 1, NOW(), NULL, NULL), +(1111, '查看', 1110, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:storage:list', 1, 1, 1, NOW(), NULL, NULL), +(1112, '新增', 1110, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:storage:add', 2, 1, 1, NOW(), NULL, NULL), +(1113, '修改', 1110, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:storage:update', 3, 1, 1, NOW(), NULL, NULL), +(1114, '删除', 1110, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:storage:delete', 4, 1, 1, NOW(), NULL, NULL), +(1190, '系统配置', 1000, 2, '/system/config', 'SystemConfig', 'system/config/index', NULL, 'desktop', b'0', b'0', b'0', NULL, 999, 1, 1, NOW(), NULL, NULL), +(1191, '查看', 1190, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:config:list', 1, 1, 1, NOW(), NULL, NULL), +(1192, '修改', 1190, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:config:update', 2, 1, 1, NOW(), NULL, NULL), +(1193, '重置', 1190, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:config:reset', 3, 1, 1, NOW(), NULL, NULL), +(2000, '系统监控', 0, 1, '/monitor', 'Monitor', 'Layout', '/monitor/online', 'computer', b'0', b'0', b'0', NULL, 2, 1, 1, NOW(), NULL, NULL), +(2010, '在线用户', 2000, 2, '/monitor/online', 'MonitorOnline', 'monitor/online/index', NULL, 'user', b'0', b'0', b'0', NULL, 1, 1, 1, NOW(), NULL, NULL), +(2011, '查看', 2010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'monitor:online:list', 1, 1, 1, NOW(), NULL, NULL), +(2012, '强退', 2010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'monitor:online:kickout', 2, 1, 1, NOW(), NULL, NULL), +(2020, '系统日志', 2000, 2, '/monitor/log', 'MonitorLog', 'monitor/log/index', NULL, 'history', b'0', b'0', b'0', NULL, 2, 1, 1, NOW(), NULL, NULL), +(2021, '查看', 2020, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'monitor:log:list', 1, 1, 1, NOW(), NULL, NULL), +(2022, '导出', 2020, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'monitor:log:export', 2, 1, 1, NOW(), NULL, NULL), +(3000, '系统工具', 0, 1, '/tool', 'Tool', 'Layout', '/tool/generator', 'tool', b'0', b'0', b'0', NULL, 3, 1, 1, NOW(), NULL, NULL), +(3010, '代码生成', 3000, 2, '/tool/generator', 'ToolGenerator', 'tool/generator/index', NULL, 'code', b'0', b'0', b'0', NULL, 1, 1, 1, NOW(), NULL, NULL), +(3011, '查看', 3010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'tool:generator:list', 1, 1, 1, NOW(), NULL, NULL), +(3012, '配置', 3010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'tool:generator:config', 2, 1, 1, NOW(), NULL, NULL), +(3013, '预览', 3010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'tool:generator:preview', 3, 1, 1, NOW(), NULL, NULL), +(3014, '生成', 3010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'tool:generator:generate', 4, 1, 1, NOW(), NULL, NULL), +(10000, '关于项目', 0, 1, '/project', 'Project', 'Layout', NULL, 'apps', b'0', b'0', b'0', NULL, 999, 1, 1, NOW(), NULL, NULL), +(10001, '接口文档', 10000, 2, 'https://api.continew.top/doc.html', NULL, NULL, NULL, 'code-square', b'1', b'0', b'0', NULL, 1, 1, 1, NOW(), NULL, NULL), +(10002, 'Gitee', 10000, 2, 'https://gitee.com/continew/continew-admin', NULL, NULL, NULL, 'gitee', b'1', b'0', b'0', NULL, 2, 1, 1, NOW(), NULL, NULL), +(10003, 'GitHub', 10000, 2, 'https://github.com/charles7c/continew-admin', NULL, NULL, NULL, 'github', b'1', b'0', b'0', NULL, 3, 1, 1, NOW(), NULL, NULL); + +-- 初始化默认部门 +INSERT INTO `sys_dept` +(`id`, `name`, `parent_id`, `ancestors`, `description`, `sort`, `status`, `is_system`, `create_user`, `create_time`, `update_user`, `update_time`) +VALUES +(1, 'Xxx科技有限公司', 0, '0', '系统初始部门', 1, 1, b'1', 1, NOW(), NULL, NULL), +(547887852587843590, '天津总部', 1, '0,1', NULL, 1, 1, b'0', 1, NOW(), NULL, NULL), +(547888008188133385, '研发部', 547887852587843590, '0,1,547887852587843590', NULL, 1, 1, b'0', 1, NOW(), NULL, NULL), +(547888460711591948, 'UI部', 547887852587843590, '0,1,547887852587843590', NULL, 2, 1, b'0', 1, NOW(), NULL, NULL), +(547888483713155087, '测试部', 547887852587843590, '0,1,547887852587843590', NULL, 3, 1, b'0', 1, NOW(), NULL, NULL), +(547888505959743506, '运维部', 547887852587843590, '0,1,547887852587843590', NULL, 4, 1, b'0', 1, NOW(), NULL, NULL), +(547888556819873814, '研发一组', 547888008188133385, '0,1,547887852587843590,547888008188133385', NULL, 1, 1, b'0', 1, NOW(), NULL, NULL), +(547888580614160409, '研发二组', 547888008188133385, '0,1,547887852587843590,547888008188133385', NULL, 2, 2, b'0', 1, NOW(), NULL, NULL); + +-- 初始化默认角色 +INSERT INTO `sys_role` +(`id`, `name`, `code`, `data_scope`, `description`, `sort`, `is_system`, `create_user`, `create_time`, `update_user`, `update_time`) +VALUES +(1, '系统管理员', 'admin', 1, '系统初始角色', 1, b'1', 1, NOW(), NULL, NULL), +(547888897925840928, '测试人员', 'test', 5, NULL, 2, b'0', 1, NOW(), NULL, NULL); + +-- 初始化默认用户:admin/admin123;test/123456 +INSERT INTO `sys_user` +(`id`, `username`, `nickname`, `password`, `gender`, `email`, `phone`, `avatar`, `description`, `status`, `is_system`, `pwd_reset_time`, `dept_id`, `create_user`, `create_time`, `update_user`, `update_time`) +VALUES +(1, 'admin', '系统管理员', '{bcrypt}$2a$10$4jGwK2BMJ7FgVR.mgwGodey8.xR8FLoU1XSXpxJ9nZQt.pufhasSa', 1, '42190c6c5639d2ca4edb4150a35e058559ccf8270361a23745a2fd285a273c28', '5bda89a4609a65546422ea56bfe5eab4', NULL, '系统初始用户', 1, b'1', NOW(), 1, 1, NOW(), NULL, NULL), +(547889293968801831, 'test', '测试员', '{bcrypt}$2a$10$meMbyso06lupZjxT88fG8undZo6.DSNUmifRfnnre8r/s13ciq6M6', 2, NULL, NULL, NULL, NULL, 2, b'0', NOW(), 547888483713155087, 1, NOW(), NULL, NULL); + +-- 初始化默认参数 +INSERT INTO `sys_option` +(`id`, `category`, `name`, `code`, `value`, `default_value`, `description`, `update_user`, `update_time`) +VALUES +(1, 'SITE', '系统标题', 'SITE_TITLE', NULL, 'ContiNew Admin', '用于显示登录页面的系统标题。', NULL, NULL), +(2, 'SITE', '系统描述', 'SITE_DESCRIPTION', NULL, '持续迭代优化的前后端分离中后台管理系统框架', NULL, NULL, NULL), +(3, 'SITE', '版权信息', 'SITE_COPYRIGHT', NULL, 'Copyright © 2022 - present ContiNew Admin 版权所有', '用于显示登录页面的底部版权信息。', NULL, NULL), +(4, 'SITE', '备案号', 'SITE_BEIAN', NULL, '津ICP备2022005864号-3', 'ICP备案号', NULL, NULL), +(5, 'SITE', 'favicon', 'SITE_FAVICON', NULL, '/favicon.ico', '用于显示浏览器地址栏的系统LOGO。', NULL, NULL), +(6, 'SITE', '系统LOGO', 'SITE_LOGO', NULL, '/logo.svg', '用于显示登录页面的系统LOGO。', NULL, NULL), +(7, 'PASSWORD', '登录密码错误锁定账号的次数', 'PASSWORD_ERROR_LOCK_COUNT', NULL, '5', '取值范围为 0-10(0 表示不锁定)。', NULL, NULL), +(8, 'PASSWORD', '登录密码错误锁定账号的时间(min)', 'PASSWORD_ERROR_LOCK_MINUTES', NULL, '5', '取值范围为 1-1440(一天)。', NULL, NULL), +(9, 'PASSWORD', '密码有效期(天)', 'PASSWORD_EXPIRATION_DAYS', NULL, '0', '取值范围为 0-999(0 表示永久有效)。', NULL, NULL), +(10, 'PASSWORD', '密码到期提前提示(天)', 'PASSWORD_EXPIRATION_WARNING_DAYS', NULL, '0', '密码到期 N 天前进行提示(0 表示不提示)。', NULL, NULL), +(11, 'PASSWORD', '密码重复使用次数', 'PASSWORD_REPETITION_TIMES', NULL, '3', '不允许使用最近 N 次密码,取值范围为 3-32。', NULL, NULL), +(12, 'PASSWORD', '密码最小长度', 'PASSWORD_MIN_LENGTH', NULL, '8', '取值范围为 8-32。', NULL, NULL), +(13, 'PASSWORD', '密码是否允许包含正反序账号名', 'PASSWORD_ALLOW_CONTAIN_USERNAME', NULL, '1', NULL, NULL, NULL), +(14, 'PASSWORD', '密码是否必须包含特殊字符', 'PASSWORD_REQUIRE_SYMBOLS', NULL, '0', NULL, NULL, NULL), +(15, 'MAIL', '发送协议', 'MAIL_PROTOCOL', NULL, 'smtp', NULL, NULL, NULL), +(16, 'MAIL', '服务器地址', 'MAIL_HOST', NULL, 'smtp.126.com', NULL, NULL, NULL), +(17, 'MAIL', '服务器端口', 'MAIL_PORT', NULL, '465', NULL, NULL, NULL), +(18, 'MAIL', '用户名', 'MAIL_USERNAME', NULL, 'charles7c@126.com', NULL, NULL, NULL), +(19, 'MAIL', '密码', 'MAIL_PASSWORD', NULL, NULL, NULL, NULL, NULL), +(20, 'MAIL', '是否启用SSL', 'MAIL_SSL_ENABLED', NULL, '1', NULL, NULL, NULL), +(21, 'MAIL', 'SSL端口', 'MAIL_SSL_PORT', NULL, '465', NULL, NULL, NULL); + +-- 初始化默认字典 +INSERT INTO `sys_dict` +(`id`, `name`, `code`, `description`, `is_system`, `create_user`, `create_time`, `update_user`, `update_time`) +VALUES +(1, '公告类型', 'notice_type', NULL, b'1', 1, NOW(), NULL, NULL), +(2, '消息类型', 'message_type', NULL, b'1', 1, NOW(), NULL, NULL); + +INSERT INTO `sys_dict_item` +(`id`, `label`, `value`, `color`, `sort`, `description`, `status`, `dict_id`, `create_user`, `create_time`, `update_user`, `update_time`) +VALUES +(1, '通知', '1', 'blue', 1, NULL, 1, 1, 1, NOW(), NULL, NULL), +(2, '活动', '2', 'orangered', 2, NULL, 1, 1, 1, NOW(), NULL, NULL), +(3, '安全消息', '1', 'blue', 1, NULL, 1, 2, 1, NOW(), NULL, NULL), +(4, '活动消息', '2', 'orangered', 2, NULL, 1, 2, 1, NOW(), NULL, NULL); + +-- 初始化默认用户和角色关联数据 +INSERT INTO `sys_user_role` +(`user_id`, `role_id`) +VALUES +(1, 1), +(547889293968801831, 547888897925840928); + +-- 初始化默认角色和菜单关联数据 +INSERT INTO `sys_role_menu` +(`role_id`, `menu_id`) +VALUES +(547888897925840928, 1000), +(547888897925840928, 1010), +(547888897925840928, 1011), +(547888897925840928, 1012), +(547888897925840928, 1013), +(547888897925840928, 1014); + +-- 初始化默认角色和部门关联数据 +INSERT INTO `sys_role_dept` (`role_id`, `dept_id`) VALUES (547888897925840928, 547888483713155087); + +-- 初始化默认存储 +INSERT INTO `sys_storage` +(`id`, `name`, `code`, `type`, `access_key`, `secret_key`, `endpoint`, `bucket_name`, `domain`, `description`, `is_default`, `sort`, `status`, `create_user`, `create_time`, `update_user`, `update_time`) +VALUES +(1, '开发环境', 'local_dev', 2, NULL, NULL, NULL, 'C:/continew-admin/data/file/', 'http://localhost:8000/file', '本地存储', b'1', 1, 1, 1, NOW(), NULL, NULL), +(2, '生产环境', 'local_prod', 2, NULL, NULL, NULL, '../data/file/', 'http://api.continew.top/file', '本地存储', b'0', 2, 2, 1, NOW(), NULL, NULL); + +-- changeset Kai:3.2-1 +INSERT INTO `sys_menu` (`id`, `title`, `parent_id`, `type`, `path`, `name`, `component`, `redirect`, `icon`, `is_external`, `is_cache`, `is_hidden`, `permission`, `sort`, `status`, `create_user`, `create_time`, `update_user`, `update_time`) +VALUES +(4000, '任务调度', 0, 1, '/schedule', 'Schedule', 'Layout', '/schedule/job', 'schedule', b'0', b'0', b'0', NULL, 997, 1, 1, NOW(), NULL, NULL), +(4010, '任务管理', 4000, 2, '/schedule/job', 'ScheduleJob', 'schedule/job/index', NULL, 'select-all', b'0', b'0', b'0', NULL, 1, 1, 1, NOW(), NULL, NULL), +(4011, '查看', 4010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'schedule:job:list', 1, 1, 1, NOW(), NULL, NULL), +(4012, '新增', 4010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'schedule:job:add', 2, 1, 1, NOW(), NULL, NULL), +(4013, '修改', 4010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'schedule:job:update', 3, 1, 1, NOW(), NULL, NULL), +(4014, '删除', 4010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'schedule:job:delete', 4, 1, 1, NOW(), NULL, NULL), +(4015, '执行', 1010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'schedule:job:trigger', 5, 1, 1, NOW(), NULL, NULL), +(4020, '任务日志', 4000, 2, '/schedule/log', 'ScheduleLog', 'schedule/log/index', NULL, 'find-replace', b'0', b'0', b'0', NULL, 2, 1, 1, NOW(), NULL, NULL), +(4021, '查看', 4020, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'schedule:log:list', 1, 1, 1, NOW(), NULL, NULL), +(4022, '停止', 4020, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'schedule:log:stop', 2, 1, 1, NOW(), NULL, NULL), +(4023, '重试', 4020, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'schedule:log:retry', 3, 1, 1, NOW(), NULL, NULL); + +-- changeset Charles7c:3.2-2 +UPDATE `sys_menu` SET `type` = 2, `path` = '/system/notice/detail', name = 'SystemNoticeDetail', component = 'system/notice/page/detail', `is_external` = b'0', `is_cache` = b'0', `is_hidden` = b'1' WHERE `id` = 1091; +UPDATE `sys_menu` SET `type` = 2, `path` = '/system/notice/add', name = 'SystemNoticeAdd', component = 'system/notice/page/add', `is_external` = b'0', `is_cache` = b'0', `is_hidden` = b'1' WHERE `id` = 1092; + +-- changeset Charles7c:3.3-1 +UPDATE `sys_menu` SET `parent_id` = 4010 WHERE `id` = 4015; +INSERT INTO `sys_menu` +(`id`, `title`, `parent_id`, `type`, `path`, `name`, `component`, `redirect`, `icon`, `is_external`, `is_cache`, `is_hidden`, `permission`, `sort`, `status`, `create_user`, `create_time`, `update_user`, `update_time`) +VALUES +(1017, '导入', 1010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:user:import', 7, 1, 1, NOW(), NULL, NULL); \ No newline at end of file diff --git a/continew-admin-webapi/src/main/resources/db/changelog/mysql/continew-admin_table.sql b/continew-admin-webapi/src/main/resources/db/changelog/mysql/continew-admin_table.sql new file mode 100644 index 0000000..cf75451 --- /dev/null +++ b/continew-admin-webapi/src/main/resources/db/changelog/mysql/continew-admin_table.sql @@ -0,0 +1,326 @@ +-- liquibase formatted sql + +-- changeset Charles7c:1 +-- comment 初始化表结构 +CREATE TABLE IF NOT EXISTS `sys_menu` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', + `title` varchar(30) NOT NULL COMMENT '标题', + `parent_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '上级菜单ID', + `type` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '类型(1:目录;2:菜单;3:按钮)', + `path` varchar(255) DEFAULT NULL COMMENT '路由地址', + `name` varchar(50) DEFAULT NULL COMMENT '组件名称', + `component` varchar(255) DEFAULT NULL COMMENT '组件路径', + `redirect` varchar(255) DEFAULT NULL COMMENT '重定向地址', + `icon` varchar(50) DEFAULT NULL COMMENT '图标', + `is_external` bit(1) DEFAULT b'0' COMMENT '是否外链', + `is_cache` bit(1) DEFAULT b'0' COMMENT '是否缓存', + `is_hidden` bit(1) DEFAULT b'0' COMMENT '是否隐藏', + `permission` varchar(100) DEFAULT NULL COMMENT '权限标识', + `sort` int NOT NULL DEFAULT 999 COMMENT '排序', + `status` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '状态(1:启用;2:禁用)', + `create_user` bigint(20) NOT NULL COMMENT '创建人', + `create_time` datetime NOT NULL COMMENT '创建时间', + `update_user` bigint(20) DEFAULT NULL COMMENT '修改人', + `update_time` datetime DEFAULT NULL COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `uk_title_parent_id`(`title`, `parent_id`) USING BTREE, + INDEX `idx_parent_id`(`parent_id`) USING BTREE, + INDEX `idx_create_user`(`create_user`) USING BTREE, + INDEX `idx_update_user`(`update_user`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='菜单表'; + +CREATE TABLE IF NOT EXISTS `sys_dept` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', + `name` varchar(30) NOT NULL COMMENT '名称', + `parent_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '上级部门ID', + `ancestors` varchar(512) NOT NULL DEFAULT '' COMMENT '祖级列表', + `description` varchar(200) DEFAULT NULL COMMENT '描述', + `sort` int NOT NULL DEFAULT 999 COMMENT '排序', + `status` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '状态(1:启用;2:禁用)', + `is_system` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否为系统内置数据', + `create_user` bigint(20) NOT NULL COMMENT '创建人', + `create_time` datetime NOT NULL COMMENT '创建时间', + `update_user` bigint(20) DEFAULT NULL COMMENT '修改人', + `update_time` datetime DEFAULT NULL COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `uk_name_parent_id`(`name`, `parent_id`) USING BTREE, + INDEX `idx_parent_id`(`parent_id`) USING BTREE, + INDEX `idx_create_user`(`create_user`) USING BTREE, + INDEX `idx_update_user`(`update_user`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='部门表'; + +CREATE TABLE IF NOT EXISTS `sys_role` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', + `name` varchar(30) NOT NULL COMMENT '名称', + `code` varchar(30) NOT NULL COMMENT '编码', + `data_scope` tinyint(1) NOT NULL DEFAULT 4 COMMENT '数据权限(1:全部数据权限;2:本部门及以下数据权限;3:本部门数据权限;4:仅本人数据权限;5:自定义数据权限)', + `description` varchar(200) DEFAULT NULL COMMENT '描述', + `sort` int NOT NULL DEFAULT 999 COMMENT '排序', + `is_system` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否为系统内置数据', + `create_user` bigint(20) NOT NULL COMMENT '创建人', + `create_time` datetime NOT NULL COMMENT '创建时间', + `update_user` bigint(20) DEFAULT NULL COMMENT '修改人', + `update_time` datetime DEFAULT NULL COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `uk_name`(`name`) USING BTREE, + UNIQUE INDEX `uk_code`(`code`) USING BTREE, + INDEX `idx_create_user`(`create_user`) USING BTREE, + INDEX `idx_update_user`(`update_user`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色表'; + +CREATE TABLE IF NOT EXISTS `sys_user` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', + `username` varchar(64) NOT NULL COMMENT '用户名', + `nickname` varchar(30) NOT NULL COMMENT '昵称', + `password` varchar(255) DEFAULT NULL COMMENT '密码', + `gender` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT '性别(0:未知;1:男;2:女)', + `email` varchar(255) DEFAULT NULL COMMENT '邮箱', + `phone` varchar(255) DEFAULT NULL COMMENT '手机号码', + `avatar` longtext DEFAULT NULL COMMENT '头像', + `description` varchar(200) DEFAULT NULL COMMENT '描述', + `status` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '状态(1:启用;2:禁用)', + `is_system` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否为系统内置数据', + `pwd_reset_time` datetime DEFAULT NULL COMMENT '最后一次修改密码时间', + `dept_id` bigint(20) NOT NULL COMMENT '部门ID', + `create_user` bigint(20) DEFAULT NULL COMMENT '创建人', + `create_time` datetime NOT NULL COMMENT '创建时间', + `update_user` bigint(20) DEFAULT NULL COMMENT '修改人', + `update_time` datetime DEFAULT NULL COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `uk_username`(`username`) USING BTREE, + UNIQUE INDEX `uk_email`(`email`) USING BTREE, + UNIQUE INDEX `uk_phone`(`phone`) USING BTREE, + INDEX `idx_dept_id`(`dept_id`) USING BTREE, + INDEX `idx_create_user`(`create_user`) USING BTREE, + INDEX `idx_update_user`(`update_user`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'; + +CREATE TABLE IF NOT EXISTS `sys_user_password_history` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', + `user_id` bigint(20) NOT NULL COMMENT '用户ID', + `password` varchar(255) NOT NULL COMMENT '密码', + `create_time` datetime NOT NULL COMMENT '创建时间', + PRIMARY KEY (`id`) USING BTREE, + INDEX `idx_user_id`(`user_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户历史密码表'; + +CREATE TABLE IF NOT EXISTS `sys_user_social` ( + `source` varchar(255) NOT NULL COMMENT '来源', + `open_id` varchar(255) NOT NULL COMMENT '开放ID', + `user_id` bigint(20) NOT NULL COMMENT '用户ID', + `meta_json` text DEFAULT NULL COMMENT '附加信息', + `last_login_time` datetime DEFAULT NULL COMMENT '最后登录时间', + `create_time` datetime NOT NULL COMMENT '创建时间', + UNIQUE INDEX `uk_source_open_id`(`source`, `open_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户社会化关联表'; + +CREATE TABLE IF NOT EXISTS `sys_user_role` ( + `user_id` bigint(20) NOT NULL COMMENT '用户ID', + `role_id` bigint(20) NOT NULL COMMENT '角色ID', + PRIMARY KEY (`user_id`, `role_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户和角色关联表'; + +CREATE TABLE IF NOT EXISTS `sys_role_menu` ( + `role_id` bigint(20) NOT NULL COMMENT '角色ID', + `menu_id` bigint(20) NOT NULL COMMENT '菜单ID', + PRIMARY KEY (`role_id`, `menu_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色和菜单关联表'; + +CREATE TABLE IF NOT EXISTS `sys_role_dept` ( + `role_id` bigint(20) NOT NULL COMMENT '角色ID', + `dept_id` bigint(20) NOT NULL COMMENT '部门ID', + PRIMARY KEY (`role_id`, `dept_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色和部门关联表'; + +CREATE TABLE IF NOT EXISTS `sys_option` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', + `category` varchar(50) NOT NULL COMMENT '类别', + `name` varchar(50) NOT NULL COMMENT '名称', + `code` varchar(100) NOT NULL COMMENT '键', + `value` text DEFAULT NULL COMMENT '值', + `default_value` text DEFAULT NULL COMMENT '默认值', + `description` varchar(200) DEFAULT NULL COMMENT '描述', + `update_user` bigint(20) DEFAULT NULL COMMENT '修改人', + `update_time` datetime DEFAULT NULL COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `uk_category_code`(`category`, `code`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='参数表'; + +CREATE TABLE IF NOT EXISTS `sys_dict` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', + `name` varchar(30) NOT NULL COMMENT '名称', + `code` varchar(30) NOT NULL COMMENT '编码', + `description` varchar(200) DEFAULT NULL COMMENT '描述', + `is_system` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否为系统内置数据', + `create_user` bigint(20) NOT NULL COMMENT '创建人', + `create_time` datetime NOT NULL COMMENT '创建时间', + `update_user` bigint(20) DEFAULT NULL COMMENT '修改人', + `update_time` datetime DEFAULT NULL COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `uk_name`(`name`) USING BTREE, + UNIQUE INDEX `uk_code`(`code`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='字典表'; + +CREATE TABLE IF NOT EXISTS `sys_dict_item` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', + `label` varchar(30) NOT NULL COMMENT '标签', + `value` varchar(30) NOT NULL COMMENT '值', + `color` varchar(30) DEFAULT NULL COMMENT '标签颜色', + `sort` int NOT NULL DEFAULT 999 COMMENT '排序', + `description` varchar(200) DEFAULT NULL COMMENT '描述', + `status` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '状态(1:启用;2:禁用)', + `dict_id` bigint(20) NOT NULL COMMENT '字典ID', + `create_user` bigint(20) NOT NULL COMMENT '创建人', + `create_time` datetime NOT NULL COMMENT '创建时间', + `update_user` bigint(20) DEFAULT NULL COMMENT '修改人', + `update_time` datetime DEFAULT NULL COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `uk_value_dict_id`(`value`, `dict_id`) USING BTREE, + INDEX `idx_dict_id`(`dict_id`) USING BTREE, + INDEX `idx_create_user`(`create_user`) USING BTREE, + INDEX `idx_update_user`(`update_user`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='字典项表'; + +CREATE TABLE IF NOT EXISTS `sys_log` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', + `trace_id` varchar(255) DEFAULT NULL COMMENT '链路ID', + `description` varchar(255) NOT NULL COMMENT '日志描述', + `module` varchar(50) NOT NULL COMMENT '所属模块', + `request_url` varchar(512) NOT NULL COMMENT '请求URL', + `request_method` varchar(10) NOT NULL COMMENT '请求方式', + `request_headers` text DEFAULT NULL COMMENT '请求头', + `request_body` text DEFAULT NULL COMMENT '请求体', + `status_code` int NOT NULL COMMENT '状态码', + `response_headers` text DEFAULT NULL COMMENT '响应头', + `response_body` mediumtext DEFAULT NULL COMMENT '响应体', + `time_taken` bigint(20) NOT NULL COMMENT '耗时(ms)', + `ip` varchar(100) DEFAULT NULL COMMENT 'IP', + `address` varchar(255) DEFAULT NULL COMMENT 'IP归属地', + `browser` varchar(100) DEFAULT NULL COMMENT '浏览器', + `os` varchar(100) DEFAULT NULL COMMENT '操作系统', + `status` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '状态(1:成功;2:失败)', + `error_msg` text DEFAULT NULL COMMENT '错误信息', + `create_user` bigint(20) DEFAULT NULL COMMENT '创建人', + `create_time` datetime NOT NULL COMMENT '创建时间', + PRIMARY KEY (`id`) USING BTREE, + INDEX `idx_module`(`module`) USING BTREE, + INDEX `idx_ip`(`ip`) USING BTREE, + INDEX `idx_create_time`(`create_time`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统日志表'; + +CREATE TABLE IF NOT EXISTS `sys_message` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', + `title` varchar(50) NOT NULL COMMENT '标题', + `content` varchar(255) DEFAULT NULL COMMENT '内容', + `type` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '类型(1:系统消息)', + `create_user` bigint(20) DEFAULT NULL COMMENT '创建人', + `create_time` datetime NOT NULL COMMENT '创建时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='消息表'; + +CREATE TABLE IF NOT EXISTS `sys_message_user` ( + `message_id` bigint(20) NOT NULL COMMENT '消息ID', + `user_id` bigint(11) NOT NULL COMMENT '用户ID', + `is_read` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否已读', + `read_time` datetime DEFAULT NULL COMMENT '读取时间', + PRIMARY KEY (`message_id`, `user_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='消息和用户关联表'; + +CREATE TABLE IF NOT EXISTS `sys_notice` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', + `title` varchar(150) NOT NULL COMMENT '标题', + `content` mediumtext NOT NULL COMMENT '内容', + `type` varchar(30) NOT NULL COMMENT '类型', + `effective_time` datetime DEFAULT NULL COMMENT '生效时间', + `terminate_time` datetime DEFAULT NULL COMMENT '终止时间', + `sort` int NOT NULL DEFAULT 999 COMMENT '排序', + `create_user` bigint(20) NOT NULL COMMENT '创建人', + `create_time` datetime NOT NULL COMMENT '创建时间', + `update_user` bigint(20) DEFAULT NULL COMMENT '修改人', + `update_time` datetime DEFAULT NULL COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + INDEX `idx_create_user`(`create_user`) USING BTREE, + INDEX `idx_update_user`(`update_user`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='公告表'; + +CREATE TABLE IF NOT EXISTS `sys_storage` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', + `name` varchar(100) NOT NULL COMMENT '名称', + `code` varchar(30) NOT NULL COMMENT '编码', + `type` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '类型(1:兼容S3协议存储;2:本地存储)', + `access_key` varchar(255) DEFAULT NULL COMMENT 'Access Key(访问密钥)', + `secret_key` varchar(255) DEFAULT NULL COMMENT 'Secret Key(私有密钥)', + `endpoint` varchar(255) DEFAULT NULL COMMENT 'Endpoint(终端节点)', + `bucket_name` varchar(255) DEFAULT NULL COMMENT '桶名称', + `domain` varchar(255) NOT NULL DEFAULT '' COMMENT '域名', + `description` varchar(200) DEFAULT NULL COMMENT '描述', + `is_default` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否为默认存储', + `sort` int NOT NULL DEFAULT 999 COMMENT '排序', + `status` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '状态(1:启用;2:禁用)', + `create_user` bigint(20) NOT NULL COMMENT '创建人', + `create_time` datetime NOT NULL COMMENT '创建时间', + `update_user` bigint(20) DEFAULT NULL COMMENT '修改人', + `update_time` datetime DEFAULT NULL COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `uk_code`(`code`) USING BTREE, + INDEX `idx_create_user`(`create_user`) USING BTREE, + INDEX `idx_update_user`(`update_user`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='存储表'; + +CREATE TABLE IF NOT EXISTS `sys_file` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', + `name` varchar(255) NOT NULL COMMENT '名称', + `size` bigint(20) NOT NULL COMMENT '大小(字节)', + `url` varchar(512) NOT NULL COMMENT 'URL', + `extension` varchar(100) DEFAULT NULL COMMENT '扩展名', + `thumbnail_size` bigint(20) DEFAULT NULL COMMENT '缩略图大小(字节)', + `thumbnail_url` varchar(512) DEFAULT NULL COMMENT '缩略图URL', + `type` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '类型(1:其他;2:图片;3:文档;4:视频;5:音频)', + `storage_id` bigint(20) NOT NULL COMMENT '存储ID', + `create_user` bigint(20) NOT NULL COMMENT '创建人', + `create_time` datetime NOT NULL COMMENT '创建时间', + `update_user` bigint(20) NOT NULL COMMENT '修改人', + `update_time` datetime NOT NULL COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + INDEX `idx_url`(`url`) USING BTREE, + INDEX `idx_type`(`type`) USING BTREE, + INDEX `idx_create_user`(`create_user`) USING BTREE, + INDEX `idx_update_user`(`update_user`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文件表'; + +CREATE TABLE IF NOT EXISTS `gen_config` ( + `table_name` varchar(64) NOT NULL COMMENT '表名称', + `module_name` varchar(60) NOT NULL COMMENT '模块名称', + `package_name` varchar(60) NOT NULL COMMENT '包名称', + `business_name` varchar(50) NOT NULL COMMENT '业务名称', + `author` varchar(100) NOT NULL COMMENT '作者', + `table_prefix` varchar(20) DEFAULT NULL COMMENT '表前缀', + `is_override` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否覆盖', + `create_time` datetime NOT NULL COMMENT '创建时间', + `update_time` datetime DEFAULT NULL COMMENT '修改时间', + PRIMARY KEY (`table_name`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='生成配置表'; + +CREATE TABLE IF NOT EXISTS `gen_field_config` ( + `table_name` varchar(64) NOT NULL COMMENT '表名称', + `column_name` varchar(64) NOT NULL COMMENT '列名称', + `column_type` varchar(25) NOT NULL COMMENT '列类型', + `column_size` bigint(20) DEFAULT NULL COMMENT '列大小', + `field_name` varchar(64) NOT NULL COMMENT '字段名称', + `field_type` varchar(25) NOT NULL COMMENT '字段类型', + `field_sort` int NOT NULL DEFAULT 999 COMMENT '字段排序', + `comment` varchar(512) DEFAULT NULL COMMENT '注释', + `is_required` bit(1) NOT NULL DEFAULT b'1' COMMENT '是否必填', + `show_in_list` bit(1) NOT NULL DEFAULT b'1' COMMENT '是否在列表中显示', + `show_in_form` bit(1) NOT NULL DEFAULT b'1' COMMENT '是否在表单中显示', + `show_in_query` bit(1) NOT NULL DEFAULT b'1' COMMENT '是否在查询中显示', + `form_type` tinyint(1) UNSIGNED DEFAULT NULL COMMENT '表单类型', + `query_type` tinyint(1) UNSIGNED DEFAULT NULL COMMENT '查询方式', + `create_time` datetime NOT NULL COMMENT '创建时间', + INDEX `idx_table_name`(`table_name`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='字段配置表'; + +-- changeset kils:3.2.0-1 +ALTER TABLE `sys_option` + MODIFY COLUMN `value` longtext DEFAULT NULL COMMENT '值', + MODIFY COLUMN `default_value` longtext DEFAULT NULL COMMENT '默认值'; \ No newline at end of file diff --git a/continew-admin-webapi/src/main/resources/db/changelog/postgresql/continew-admin_column.sql b/continew-admin-webapi/src/main/resources/db/changelog/postgresql/continew-admin_column.sql new file mode 100644 index 0000000..3d8508e --- /dev/null +++ b/continew-admin-webapi/src/main/resources/db/changelog/postgresql/continew-admin_column.sql @@ -0,0 +1 @@ +-- liquibase formatted sql diff --git a/continew-admin-webapi/src/main/resources/db/changelog/postgresql/continew-admin_data.sql b/continew-admin-webapi/src/main/resources/db/changelog/postgresql/continew-admin_data.sql new file mode 100644 index 0000000..e59351f --- /dev/null +++ b/continew-admin-webapi/src/main/resources/db/changelog/postgresql/continew-admin_data.sql @@ -0,0 +1,187 @@ +-- liquibase formatted sql + +-- changeset Charles7c:1 +-- comment 初始化表数据 +-- 初始化默认菜单 +INSERT INTO "sys_menu" +("id", "title", "parent_id", "type", "path", "name", "component", "redirect", "icon", "is_external", "is_cache", "is_hidden", "permission", "sort", "status", "create_user", "create_time", "update_user", "update_time") +VALUES +(1000, '系统管理', 0, 1, '/system', 'System', 'Layout', '/system/user', 'settings', false, false, false, NULL, 1, 1, 1, NOW(), NULL, NULL), +(1010, '用户管理', 1000, 2, '/system/user', 'SystemUser', 'system/user/index', NULL, 'user', false, false, false, NULL, 1, 1, 1, NOW(), NULL, NULL), +(1011, '查看', 1010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:user:list', 1, 1, 1, NOW(), NULL, NULL), +(1012, '新增', 1010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:user:add', 2, 1, 1, NOW(), NULL, NULL), +(1013, '修改', 1010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:user:update', 3, 1, 1, NOW(), NULL, NULL), +(1014, '删除', 1010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:user:delete', 4, 1, 1, NOW(), NULL, NULL), +(1015, '导出', 1010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:user:export', 5, 1, 1, NOW(), NULL, NULL), +(1016, '导入', 1010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:user:import', 6, 1, 1, NOW(), NULL, NULL), +(1017, '重置密码', 1010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:user:resetPwd', 7, 1, 1, NOW(), NULL, NULL), +(1030, '角色管理', 1000, 2, '/system/role', 'SystemRole', 'system/role/index', NULL, 'user-group', false, false, false, NULL, 2, 1, 1, NOW(), NULL, NULL), +(1031, '查看', 1030, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:role:list', 1, 1, 1, NOW(), NULL, NULL), +(1032, '新增', 1030, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:role:add', 2, 1, 1, NOW(), NULL, NULL), +(1033, '修改', 1030, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:role:update', 3, 1, 1, NOW(), NULL, NULL), +(1034, '删除', 1030, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:role:delete', 4, 1, 1, NOW(), NULL, NULL), +(1050, '菜单管理', 1000, 2, '/system/menu', 'SystemMenu', 'system/menu/index', NULL, 'menu', false, false, false, NULL, 3, 1, 1, NOW(), NULL, NULL), +(1051, '查看', 1050, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:menu:list', 1, 1, 1, NOW(), NULL, NULL), +(1052, '新增', 1050, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:menu:add', 2, 1, 1, NOW(), NULL, NULL), +(1053, '修改', 1050, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:menu:update', 3, 1, 1, NOW(), NULL, NULL), +(1054, '删除', 1050, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:menu:delete', 4, 1, 1, NOW(), NULL, NULL), +(1060, '部门管理', 1000, 2, '/system/dept', 'SystemDept', 'system/dept/index', NULL, 'mind-mapping', false, false, false, NULL, 4, 1, 1, NOW(), NULL, NULL), +(1061, '查看', 1060, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:dept:list', 1, 1, 1, NOW(), NULL, NULL), +(1062, '新增', 1060, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:dept:add', 2, 1, 1, NOW(), NULL, NULL), +(1063, '修改', 1060, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:dept:update', 3, 1, 1, NOW(), NULL, NULL), +(1064, '删除', 1060, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:dept:delete', 4, 1, 1, NOW(), NULL, NULL), +(1065, '导出', 1060, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:dept:export', 5, 1, 1, NOW(), NULL, NULL), +(1070, '字典管理', 1000, 2, '/system/dict', 'SystemDict', 'system/dict/index', NULL, 'bookmark', false, false, false, NULL, 5, 1, 1, NOW(), NULL, NULL), +(1071, '查看', 1070, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:dict:list', 1, 1, 1, NOW(), NULL, NULL), +(1072, '新增', 1070, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:dict:add', 2, 1, 1, NOW(), NULL, NULL), +(1073, '修改', 1070, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:dict:update', 3, 1, 1, NOW(), NULL, NULL), +(1074, '删除', 1070, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:dict:delete', 4, 1, 1, NOW(), NULL, NULL), +(1080, '字典项管理', 1000, 2, '/system/dict/item', 'SystemDictItem', 'system/dict/item/index', NULL, 'bookmark', false, false, true, NULL, 5, 1, 1, NOW(), NULL, NULL), +(1081, '查看', 1080, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:dict:item:list', 1, 1, 1, NOW(), NULL, NULL), +(1082, '新增', 1080, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:dict:item:add', 2, 1, 1, NOW(), NULL, NULL), +(1083, '修改', 1080, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:dict:item:update', 3, 1, 1, NOW(), NULL, NULL), +(1084, '删除', 1080, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:dict:item:delete', 4, 1, 1, NOW(), NULL, NULL), +(1090, '通知公告', 1000, 2, '/system/notice', 'SystemNotice', 'system/notice/index', NULL, 'notification', false, false, false, NULL, 6, 1, 1, NOW(), NULL, NULL), +(1091, '查看', 1090, 2, '/system/notice/detail', 'SystemNoticeDetail', 'system/notice/page/detail', NULL, NULL, false, false, true, 'system:notice:list', 1, 1, 1, NOW(), NULL, NULL), +(1092, '新增', 1090, 2, '/system/notice/add', 'SystemNoticeAdd', 'system/notice/page/add', NULL, NULL, false, false, true, 'system:notice:add', 2, 1, 1, NOW(), NULL, NULL), +(1093, '修改', 1090, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:notice:update', 3, 1, 1, NOW(), NULL, NULL), +(1094, '删除', 1090, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:notice:delete', 4, 1, 1, NOW(), NULL, NULL), +(1100, '文件管理', 1000, 2, '/system/file', 'SystemFile', 'system/file/index', NULL, 'file', false, false, false, NULL, 7, 1, 1, NOW(), NULL, NULL), +(1101, '查看', 1100, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:file:list', 1, 1, 1, NOW(), NULL, NULL), +(1102, '上传', 1100, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:file:upload', 2, 1, 1, NOW(), NULL, NULL), +(1103, '修改', 1100, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:file:update', 3, 1, 1, NOW(), NULL, NULL), +(1104, '删除', 1100, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:file:delete', 4, 1, 1, NOW(), NULL, NULL), +(1105, '下载', 1100, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:file:download', 5, 1, 1, NOW(), NULL, NULL), +(1110, '存储管理', 1000, 2, '/system/storage', 'SystemStorage', 'system/storage/index', NULL, 'storage', false, false, false, NULL, 8, 1, 1, NOW(), NULL, NULL), +(1111, '查看', 1110, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:storage:list', 1, 1, 1, NOW(), NULL, NULL), +(1112, '新增', 1110, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:storage:add', 2, 1, 1, NOW(), NULL, NULL), +(1113, '修改', 1110, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:storage:update', 3, 1, 1, NOW(), NULL, NULL), +(1114, '删除', 1110, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:storage:delete', 4, 1, 1, NOW(), NULL, NULL), +(1190, '系统配置', 1000, 2, '/system/config', 'SystemConfig', 'system/config/index', NULL, 'desktop', false, false, false, NULL, 999, 1, 1, NOW(), NULL, NULL), +(1191, '查看', 1190, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:config:list', 1, 1, 1, NOW(), NULL, NULL), +(1192, '修改', 1190, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:config:update', 2, 1, 1, NOW(), NULL, NULL), +(1193, '重置', 1190, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:config:reset', 3, 1, 1, NOW(), NULL, NULL), +(2000, '系统监控', 0, 1, '/monitor', 'Monitor', 'Layout', '/monitor/online', 'computer', false, false, false, NULL, 2, 1, 1, NOW(), NULL, NULL), +(2010, '在线用户', 2000, 2, '/monitor/online', 'MonitorOnline', 'monitor/online/index', NULL, 'user', false, false, false, NULL, 1, 1, 1, NOW(), NULL, NULL), +(2011, '查看', 2010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'monitor:online:list', 1, 1, 1, NOW(), NULL, NULL), +(2012, '强退', 2010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'monitor:online:kickout', 2, 1, 1, NOW(), NULL, NULL), +(2020, '系统日志', 2000, 2, '/monitor/log', 'MonitorLog', 'monitor/log/index', NULL, 'history', false, false, false, NULL, 2, 1, 1, NOW(), NULL, NULL), +(2021, '查看', 2020, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'monitor:log:list', 1, 1, 1, NOW(), NULL, NULL), +(2022, '导出', 2020, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'monitor:log:export', 2, 1, 1, NOW(), NULL, NULL), +(3000, '系统工具', 0, 1, '/tool', 'Tool', 'Layout', '/tool/generator', 'tool', false, false, false, NULL, 3, 1, 1, NOW(), NULL, NULL), +(3010, '代码生成', 3000, 2, '/tool/generator', 'ToolGenerator', 'tool/generator/index', NULL, 'code', false, false, false, NULL, 1, 1, 1, NOW(), NULL, NULL), +(3011, '查看', 3010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'tool:generator:list', 1, 1, 1, NOW(), NULL, NULL), +(3012, '配置', 3010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'tool:generator:config', 2, 1, 1, NOW(), NULL, NULL), +(3013, '预览', 3010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'tool:generator:preview', 3, 1, 1, NOW(), NULL, NULL), +(3014, '生成', 3010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'tool:generator:generate', 4, 1, 1, NOW(), NULL, NULL), +(4000, '任务调度', 0, 1, '/schedule', 'Schedule', 'Layout', '/schedule/job', 'schedule', b'0', b'0', b'0', NULL, 997, 1, 1, NOW(), NULL, NULL), +(4010, '任务管理', 4000, 2, '/schedule/job', 'ScheduleJob', 'schedule/job/index', NULL, 'select-all', b'0', b'0', b'0', NULL, 1, 1, 1, NOW(), NULL, NULL), +(4011, '查看', 4010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'schedule:job:list', 1, 1, 1, NOW(), NULL, NULL), +(4012, '新增', 4010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'schedule:job:add', 2, 1, 1, NOW(), NULL, NULL), +(4013, '修改', 4010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'schedule:job:update', 3, 1, 1, NOW(), NULL, NULL), +(4014, '删除', 4010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'schedule:job:delete', 4, 1, 1, NOW(), NULL, NULL), +(4015, '执行', 4010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'schedule:job:trigger', 5, 1, 1, NOW(), NULL, NULL), +(4020, '任务日志', 4000, 2, '/schedule/log', 'ScheduleLog', 'schedule/log/index', NULL, 'find-replace', b'0', b'0', b'0', NULL, 2, 1, 1, NOW(), NULL, NULL), +(4021, '查看', 4020, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'schedule:log:list', 1, 1, 1, NOW(), NULL, NULL), +(4022, '停止', 4020, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'schedule:log:stop', 2, 1, 1, NOW(), NULL, NULL), +(4023, '重试', 4020, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'schedule:log:retry', 3, 1, 1, NOW(), NULL, NULL), +(10000, '关于项目', 0, 1, '/project', 'Project', 'Layout', NULL, 'apps', false, false, false, NULL, 999, 1, 1, NOW(), NULL, NULL), +(10001, '接口文档', 10000, 2, 'https://api.continew.top/doc.html', NULL, NULL, NULL, 'code-square', true, false, false, NULL, 1, 1, 1, NOW(), NULL, NULL), +(10002, 'Gitee', 10000, 2, 'https://gitee.com/continew/continew-admin', NULL, NULL, NULL, 'gitee', true, false, false, NULL, 2, 1, 1, NOW(), NULL, NULL), +(10003, 'GitHub', 10000, 2, 'https://github.com/charles7c/continew-admin', NULL, NULL, NULL, 'github', true, false, false, NULL, 3, 1, 1, NOW(), NULL, NULL); + +-- 初始化默认部门 +INSERT INTO "sys_dept" +("id", "name", "parent_id", "ancestors", "description", "sort", "status", "is_system", "create_user", "create_time", "update_user", "update_time") +VALUES +(1, 'Xxx科技有限公司', 0, '0', '系统初始部门', 1, 1, true, 1, NOW(), NULL, NULL), +(547887852587843590, '天津总部', 1, '0,1', NULL, 1, 1, false, 1, NOW(), NULL, NULL), +(547888008188133385, '研发部', 547887852587843590, '0,1,547887852587843590', NULL, 1, 1, false, 1, NOW(), NULL, NULL), +(547888460711591948, 'UI部', 547887852587843590, '0,1,547887852587843590', NULL, 2, 1, false, 1, NOW(), NULL, NULL), +(547888483713155087, '测试部', 547887852587843590, '0,1,547887852587843590', NULL, 3, 1, false, 1, NOW(), NULL, NULL), +(547888505959743506, '运维部', 547887852587843590, '0,1,547887852587843590', NULL, 4, 1, false, 1, NOW(), NULL, NULL), +(547888556819873814, '研发一组', 547888008188133385, '0,1,547887852587843590,547888008188133385', NULL, 1, 1, false, 1, NOW(), NULL, NULL), +(547888580614160409, '研发二组', 547888008188133385, '0,1,547887852587843590,547888008188133385', NULL, 2, 2, false, 1, NOW(), NULL, NULL); + +-- 初始化默认角色 +INSERT INTO "sys_role" +("id", "name", "code", "data_scope", "description", "sort", "is_system", "create_user", "create_time", "update_user", "update_time") +VALUES +(1, '系统管理员', 'admin', 1, '系统初始角色', 1, true, 1, NOW(), NULL, NULL), +(547888897925840928, '测试人员', 'test', 5, NULL, 2, false, 1, NOW(), NULL, NULL); + +-- 初始化默认用户:admin/admin123;test/123456 +INSERT INTO "sys_user" +("id", "username", "nickname", "password", "gender", "email", "phone", "avatar", "description", "status", "is_system", "pwd_reset_time", "dept_id", "create_user", "create_time", "update_user", "update_time") +VALUES +(1, 'admin', '系统管理员', '{bcrypt}$2a$10$4jGwK2BMJ7FgVR.mgwGodey8.xR8FLoU1XSXpxJ9nZQt.pufhasSa', 1, '42190c6c5639d2ca4edb4150a35e058559ccf8270361a23745a2fd285a273c28', '5bda89a4609a65546422ea56bfe5eab4', NULL, '系统初始用户', 1, true, NOW(), 1, 1, NOW(), NULL, NULL), +(547889293968801831, 'test', '测试员', '{bcrypt}$2a$10$meMbyso06lupZjxT88fG8undZo6.DSNUmifRfnnre8r/s13ciq6M6', 2, NULL, NULL, NULL, NULL, 2, false, NOW(), 547888483713155087, 1, NOW(), NULL, NULL); + +-- 初始化默认参数 +INSERT INTO "sys_option" +("id", "category", "name", "code", "value", "default_value", "description", "update_user", "update_time") +VALUES +(1, 'SITE', '系统标题', 'SITE_TITLE', NULL, 'ContiNew Admin', '用于显示登录页面的系统标题。', NULL, NULL), +(2, 'SITE', '系统描述', 'SITE_DESCRIPTION', NULL, '持续迭代优化的前后端分离中后台管理系统框架', NULL, NULL, NULL), +(3, 'SITE', '版权信息', 'SITE_COPYRIGHT', NULL, 'Copyright © 2022 - present ContiNew Admin 版权所有', '用于显示登录页面的底部版权信息。', NULL, NULL), +(4, 'SITE', '备案号', 'SITE_BEIAN', NULL, '津ICP备2022005864号-3', 'ICP备案号', NULL, NULL), +(5, 'SITE', 'favicon', 'SITE_FAVICON', NULL, '/favicon.ico', '用于显示浏览器地址栏的系统LOGO。', NULL, NULL), +(6, 'SITE', '系统LOGO', 'SITE_LOGO', NULL, '/logo.svg', '用于显示登录页面的系统LOGO。', NULL, NULL), +(7, 'PASSWORD', '登录密码错误锁定账号的次数', 'PASSWORD_ERROR_LOCK_COUNT', NULL, '5', '取值范围为 0-10(0 表示不锁定)。', NULL, NULL), +(8, 'PASSWORD', '登录密码错误锁定账号的时间(min)', 'PASSWORD_ERROR_LOCK_MINUTES', NULL, '5', '取值范围为 1-1440(一天)。', NULL, NULL), +(9, 'PASSWORD', '密码有效期(天)', 'PASSWORD_EXPIRATION_DAYS', NULL, '0', '取值范围为 0-999(0 表示永久有效)。', NULL, NULL), +(10, 'PASSWORD', '密码到期提前提示(天)', 'PASSWORD_EXPIRATION_WARNING_DAYS', NULL, '0', '密码到期 N 天前进行提示(0 表示不提示)。', NULL, NULL), +(11, 'PASSWORD', '密码重复使用次数', 'PASSWORD_REPETITION_TIMES', NULL, '3', '不允许使用最近 N 次密码,取值范围为 3-32。', NULL, NULL), +(12, 'PASSWORD', '密码最小长度', 'PASSWORD_MIN_LENGTH', NULL, '8', '取值范围为 8-32。', NULL, NULL), +(13, 'PASSWORD', '密码是否允许包含正反序账号名', 'PASSWORD_ALLOW_CONTAIN_USERNAME', NULL, '1', NULL, NULL, NULL), +(14, 'PASSWORD', '密码是否必须包含特殊字符', 'PASSWORD_REQUIRE_SYMBOLS', NULL, '0', NULL, NULL, NULL), +(15, 'MAIL', '发送协议', 'MAIL_PROTOCOL', NULL, 'smtp', NULL, NULL, NULL), +(16, 'MAIL', '服务器地址', 'MAIL_HOST', NULL, 'smtp.126.com', NULL, NULL, NULL), +(17, 'MAIL', '服务器端口', 'MAIL_PORT', NULL, '465', NULL, NULL, NULL), +(18, 'MAIL', '用户名', 'MAIL_USERNAME', NULL, 'charles7c@126.com', NULL, NULL, NULL), +(19, 'MAIL', '密码', 'MAIL_PASSWORD', NULL, NULL, NULL, NULL, NULL), +(20, 'MAIL', '是否启用SSL', 'MAIL_SSL_ENABLED', NULL, '1', NULL, NULL, NULL), +(21, 'MAIL', 'SSL端口', 'MAIL_SSL_PORT', NULL, '465', NULL, NULL, NULL); + +-- 初始化默认字典 +INSERT INTO "sys_dict" +("id", "name", "code", "description", "is_system", "create_user", "create_time", "update_user", "update_time") +VALUES +(1, '公告类型', 'notice_type', NULL, true, 1, NOW(), NULL, NULL), +(2, '消息类型', 'message_type', NULL, true, 1, NOW(), NULL, NULL); + +INSERT INTO "sys_dict_item" +("id", "label", "value", "color", "sort", "description", "status", "dict_id", "create_user", "create_time", "update_user", "update_time") +VALUES +(1, '通知', '1', 'blue', 1, NULL, 1, 1, 1, NOW(), NULL, NULL), +(2, '活动', '2', 'orangered', 2, NULL, 1, 1, 1, NOW(), NULL, NULL), +(3, '安全消息', '1', 'blue', 1, NULL, 1, 2, 1, NOW(), NULL, NULL), +(4, '活动消息', '2', 'orangered', 2, NULL, 1, 2, 1, NOW(), NULL, NULL); + +-- 初始化默认用户和角色关联数据 +INSERT INTO "sys_user_role" +("user_id", "role_id") +VALUES +(1, 1), +(547889293968801831, 547888897925840928); + +-- 初始化默认角色和菜单关联数据 +INSERT INTO "sys_role_menu" +("role_id", "menu_id") +VALUES +(547888897925840928, 1000), +(547888897925840928, 1010), +(547888897925840928, 1011), +(547888897925840928, 1012), +(547888897925840928, 1013), +(547888897925840928, 1014); + +-- 初始化默认角色和部门关联数据 +INSERT INTO "sys_role_dept" ("role_id", "dept_id") VALUES (547888897925840928, 547888483713155087); + +-- 初始化默认存储 +INSERT INTO "sys_storage" +("id", "name", "code", "type", "access_key", "secret_key", "endpoint", "bucket_name", "domain", "description", "is_default", "sort", "status", "create_user", "create_time", "update_user", "update_time") +VALUES +(1, '开发环境', 'local_dev', 2, NULL, NULL, NULL, 'C:/continew-admin/data/file/', 'http://localhost:8000/file', '本地存储', true, 1, 1, 1, NOW(), NULL, NULL), +(2, '生产环境', 'local_prod', 2, NULL, NULL, NULL, '../data/file/', 'http://api.continew.top/file', '本地存储', false, 2, 2, 1, NOW(), NULL, NULL); diff --git a/continew-admin-webapi/src/main/resources/db/changelog/postgresql/continew-admin_table.sql b/continew-admin-webapi/src/main/resources/db/changelog/postgresql/continew-admin_table.sql new file mode 100644 index 0000000..171f13c --- /dev/null +++ b/continew-admin-webapi/src/main/resources/db/changelog/postgresql/continew-admin_table.sql @@ -0,0 +1,547 @@ +-- liquibase formatted sql + +-- changeset Charles7c:1 +-- comment 初始化表结构 +CREATE TABLE IF NOT EXISTS "sys_menu" ( + "id" int8 NOT NULL, + "title" varchar(30) NOT NULL, + "parent_id" int8 NOT NULL DEFAULT 0, + "type" int2 NOT NULL DEFAULT 1, + "path" varchar(255) DEFAULT NULL, + "name" varchar(50) DEFAULT NULL, + "component" varchar(255) DEFAULT NULL, + "redirect" varchar(255) DEFAULT NULL, + "icon" varchar(50) DEFAULT NULL, + "is_external" bool DEFAULT false, + "is_cache" bool DEFAULT false, + "is_hidden" bool DEFAULT false, + "permission" varchar(100) DEFAULT NULL, + "sort" int4 NOT NULL DEFAULT 999, + "status" int2 NOT NULL DEFAULT 1, + "create_user" int8 NOT NULL, + "create_time" timestamp NOT NULL, + "update_user" int8 DEFAULT NULL, + "update_time" timestamp DEFAULT NULL, + PRIMARY KEY ("id") +); +CREATE INDEX "idx_menu_parent_id" ON "sys_menu" ("parent_id"); +CREATE INDEX "idx_menu_create_user" ON "sys_menu" ("create_user"); +CREATE INDEX "idx_menu_update_user" ON "sys_menu" ("update_user"); +CREATE UNIQUE INDEX "uk_menu_title_parent_id" ON "sys_menu" ("title", "parent_id"); +COMMENT ON COLUMN "sys_menu"."id" IS 'ID'; +COMMENT ON COLUMN "sys_menu"."title" IS '标题'; +COMMENT ON COLUMN "sys_menu"."parent_id" IS '上级菜单ID'; +COMMENT ON COLUMN "sys_menu"."type" IS '类型(1:目录;2:菜单;3:按钮)'; +COMMENT ON COLUMN "sys_menu"."path" IS '路由地址'; +COMMENT ON COLUMN "sys_menu"."name" IS '组件名称'; +COMMENT ON COLUMN "sys_menu"."component" IS '组件路径'; +COMMENT ON COLUMN "sys_menu"."redirect" IS '重定向地址'; +COMMENT ON COLUMN "sys_menu"."icon" IS '图标'; +COMMENT ON COLUMN "sys_menu"."is_external" IS '是否外链'; +COMMENT ON COLUMN "sys_menu"."is_cache" IS '是否缓存'; +COMMENT ON COLUMN "sys_menu"."is_hidden" IS '是否隐藏'; +COMMENT ON COLUMN "sys_menu"."permission" IS '权限标识'; +COMMENT ON COLUMN "sys_menu"."sort" IS '排序'; +COMMENT ON COLUMN "sys_menu"."status" IS '状态(1:启用;2:禁用)'; +COMMENT ON COLUMN "sys_menu"."create_user" IS '创建人'; +COMMENT ON COLUMN "sys_menu"."create_time" IS '创建时间'; +COMMENT ON COLUMN "sys_menu"."update_user" IS '修改人'; +COMMENT ON COLUMN "sys_menu"."update_time" IS '修改时间'; +COMMENT ON TABLE "sys_menu" IS '菜单表'; + +CREATE TABLE IF NOT EXISTS "sys_dept" ( + "id" int8 NOT NULL, + "name" varchar(30) NOT NULL, + "parent_id" int8 NOT NULL DEFAULT 0, + "ancestors" varchar(512) NOT NULL DEFAULT '', + "description" varchar(200) DEFAULT NULL, + "sort" int4 NOT NULL DEFAULT 999, + "status" int2 NOT NULL DEFAULT 1, + "is_system" bool NOT NULL DEFAULT false, + "create_user" int8 NOT NULL, + "create_time" timestamp NOT NULL, + "update_user" int8 DEFAULT NULL, + "update_time" timestamp DEFAULT NULL, + PRIMARY KEY ("id") +); +CREATE INDEX "idx_dept_parent_id" ON "sys_dept" ("parent_id"); +CREATE INDEX "idx_dept_create_user" ON "sys_dept" ("create_user"); +CREATE INDEX "idx_dept_update_user" ON "sys_dept" ("update_user"); +CREATE UNIQUE INDEX "uk_dept_name_parent_id" ON "sys_dept" ("name", "parent_id"); +COMMENT ON COLUMN "sys_dept"."id" IS 'ID'; +COMMENT ON COLUMN "sys_dept"."name" IS '名称'; +COMMENT ON COLUMN "sys_dept"."parent_id" IS '上级部门ID'; +COMMENT ON COLUMN "sys_dept"."ancestors" IS '祖级列表'; +COMMENT ON COLUMN "sys_dept"."description" IS '描述'; +COMMENT ON COLUMN "sys_dept"."sort" IS '排序'; +COMMENT ON COLUMN "sys_dept"."status" IS '状态(1:启用;2:禁用)'; +COMMENT ON COLUMN "sys_dept"."is_system" IS '是否为系统内置数据'; +COMMENT ON COLUMN "sys_dept"."create_user" IS '创建人'; +COMMENT ON COLUMN "sys_dept"."create_time" IS '创建时间'; +COMMENT ON COLUMN "sys_dept"."update_user" IS '修改人'; +COMMENT ON COLUMN "sys_dept"."update_time" IS '修改时间'; +COMMENT ON TABLE "sys_dept" IS '部门表'; + +CREATE TABLE IF NOT EXISTS "sys_role" ( + "id" int8 NOT NULL, + "name" varchar(30) NOT NULL, + "code" varchar(30) NOT NULL, + "data_scope" int2 NOT NULL DEFAULT 4, + "description" varchar(200) DEFAULT NULL, + "sort" int4 NOT NULL DEFAULT 999, + "is_system" bool NOT NULL DEFAULT false, + "menu_check_strictly" bool DEFAULT false, + "dept_check_strictly" bool DEFAULT false, + "create_user" int8 NOT NULL, + "create_time" timestamp NOT NULL, + "update_user" int8 DEFAULT NULL, + "update_time" timestamp DEFAULT NULL, + PRIMARY KEY ("id") +); +CREATE UNIQUE INDEX "uk_role_name" ON "sys_role" ("name"); +CREATE UNIQUE INDEX "uk_role_code" ON "sys_role" ("code"); +CREATE INDEX "idx_role_create_user" ON "sys_role" ("create_user"); +CREATE INDEX "idx_role_update_user" ON "sys_role" ("update_user"); +COMMENT ON COLUMN "sys_role"."id" IS 'ID'; +COMMENT ON COLUMN "sys_role"."name" IS '名称'; +COMMENT ON COLUMN "sys_role"."code" IS '编码'; +COMMENT ON COLUMN "sys_role"."data_scope" IS '数据权限(1:全部数据权限;2:本部门及以下数据权限;3:本部门数据权限;4:仅本人数据权限;5:自定义数据权限)'; +COMMENT ON COLUMN "sys_role"."description" IS '描述'; +COMMENT ON COLUMN "sys_role"."sort" IS '排序'; +COMMENT ON COLUMN "sys_role"."is_system" IS '是否为系统内置数据'; +COMMENT ON COLUMN "sys_role"."menu_check_strictly" IS '菜单选择是否父子节点关联'; +COMMENT ON COLUMN "sys_role"."dept_check_strictly" IS '部门选择是否父子节点关联'; +COMMENT ON COLUMN "sys_role"."create_user" IS '创建人'; +COMMENT ON COLUMN "sys_role"."create_time" IS '创建时间'; +COMMENT ON COLUMN "sys_role"."update_user" IS '修改人'; +COMMENT ON COLUMN "sys_role"."update_time" IS '修改时间'; +COMMENT ON TABLE "sys_role" IS '角色表'; + +CREATE TABLE IF NOT EXISTS "sys_user" ( + "id" int8 NOT NULL, + "username" varchar(64) NOT NULL, + "nickname" varchar(30) NOT NULL, + "password" varchar(255) DEFAULT NULL, + "gender" int2 NOT NULL DEFAULT 0, + "email" varchar(255) DEFAULT NULL, + "phone" varchar(255) DEFAULT NULL, + "avatar" text DEFAULT NULL, + "description" varchar(200) DEFAULT NULL, + "status" int2 NOT NULL DEFAULT 1, + "is_system" bool NOT NULL DEFAULT false, + "pwd_reset_time" timestamp DEFAULT NULL, + "dept_id" int8 NOT NULL, + "create_user" int8 DEFAULT NULL, + "create_time" timestamp NOT NULL, + "update_user" int8 DEFAULT NULL, + "update_time" timestamp DEFAULT NULL, + PRIMARY KEY ("id") +); +CREATE UNIQUE INDEX "uk_user_username" ON "sys_user" ("username"); +CREATE UNIQUE INDEX "uk_user_email" ON "sys_user" ("email"); +CREATE UNIQUE INDEX "uk_user_phone" ON "sys_user" ("phone"); +CREATE INDEX "idx_user_dept_id" ON "sys_user" ("dept_id"); +CREATE INDEX "idx_user_create_user" ON "sys_user" ("create_user"); +CREATE INDEX "idx_user_update_user" ON "sys_user" ("update_user"); +COMMENT ON COLUMN "sys_user"."id" IS 'ID'; +COMMENT ON COLUMN "sys_user"."username" IS '用户名'; +COMMENT ON COLUMN "sys_user"."nickname" IS '昵称'; +COMMENT ON COLUMN "sys_user"."password" IS '密码'; +COMMENT ON COLUMN "sys_user"."gender" IS '性别(0:未知;1:男;2:女)'; +COMMENT ON COLUMN "sys_user"."email" IS '邮箱'; +COMMENT ON COLUMN "sys_user"."phone" IS '手机号码'; +COMMENT ON COLUMN "sys_user"."avatar" IS '头像'; +COMMENT ON COLUMN "sys_user"."description" IS '描述'; +COMMENT ON COLUMN "sys_user"."status" IS '状态(1:启用;2:禁用)'; +COMMENT ON COLUMN "sys_user"."is_system" IS '是否为系统内置数据'; +COMMENT ON COLUMN "sys_user"."pwd_reset_time" IS '最后一次修改密码时间'; +COMMENT ON COLUMN "sys_user"."dept_id" IS '部门ID'; +COMMENT ON COLUMN "sys_user"."create_user" IS '创建人'; +COMMENT ON COLUMN "sys_user"."create_time" IS '创建时间'; +COMMENT ON COLUMN "sys_user"."update_user" IS '修改人'; +COMMENT ON COLUMN "sys_user"."update_time" IS '修改时间'; +COMMENT ON TABLE "sys_user" IS '用户表'; + +CREATE TABLE IF NOT EXISTS "sys_user_password_history" ( + "id" int8 NOT NULL, + "user_id" int8 NOT NULL, + "password" varchar(255) NOT NULL, + "create_time" timestamp NOT NULL, + PRIMARY KEY ("id") +); +CREATE INDEX "idx_uph_user_id" ON "sys_user_password_history" ("user_id"); +COMMENT ON COLUMN "sys_user_password_history"."id" IS 'ID'; +COMMENT ON COLUMN "sys_user_password_history"."user_id" IS '用户ID'; +COMMENT ON COLUMN "sys_user_password_history"."password" IS '密码'; +COMMENT ON COLUMN "sys_user_password_history"."create_time" IS '创建时间'; +COMMENT ON TABLE "sys_user_password_history" IS '用户历史密码表'; + +CREATE TABLE IF NOT EXISTS "sys_user_social" ( + "source" varchar(255) NOT NULL, + "open_id" varchar(255) NOT NULL, + "user_id" int8 NOT NULL, + "meta_json" text DEFAULT NULL, + "last_login_time" timestamp DEFAULT NULL, + "create_time" timestamp NOT NULL +); +CREATE UNIQUE INDEX "uk_user_source_open_id" ON "sys_user_social" ("source", "open_id"); +COMMENT ON COLUMN "sys_user_social"."source" IS '来源'; +COMMENT ON COLUMN "sys_user_social"."open_id" IS '开放ID'; +COMMENT ON COLUMN "sys_user_social"."user_id" IS '用户ID'; +COMMENT ON COLUMN "sys_user_social"."meta_json" IS '附加信息'; +COMMENT ON COLUMN "sys_user_social"."last_login_time" IS '最后登录时间'; +COMMENT ON COLUMN "sys_user_social"."create_time" IS '创建时间'; +COMMENT ON TABLE "sys_user_social" IS '用户社会化关联表'; + +CREATE TABLE IF NOT EXISTS "sys_user_role" ( + "user_id" int8 NOT NULL, + "role_id" int8 NOT NULL, + PRIMARY KEY ("user_id", "role_id") +); +COMMENT ON COLUMN "sys_user_role"."user_id" IS '用户ID'; +COMMENT ON COLUMN "sys_user_role"."role_id" IS '角色ID'; +COMMENT ON TABLE "sys_user_role" IS '用户和角色关联表'; + +CREATE TABLE IF NOT EXISTS "sys_role_menu" ( + "role_id" int8 NOT NULL, + "menu_id" int8 NOT NULL, + PRIMARY KEY ("role_id", "menu_id") +); +COMMENT ON COLUMN "sys_role_menu"."role_id" IS '角色ID'; +COMMENT ON COLUMN "sys_role_menu"."menu_id" IS '菜单ID'; +COMMENT ON TABLE "sys_role_menu" IS '角色和菜单关联表'; + +CREATE TABLE IF NOT EXISTS "sys_role_dept" ( + "role_id" int8 NOT NULL, + "dept_id" int8 NOT NULL, + PRIMARY KEY ("role_id", "dept_id") +); +COMMENT ON COLUMN "sys_role_dept"."role_id" IS '角色ID'; +COMMENT ON COLUMN "sys_role_dept"."dept_id" IS '部门ID'; +COMMENT ON TABLE "sys_role_dept" IS '角色和部门关联表'; + +CREATE TABLE IF NOT EXISTS "sys_option" ( + "id" int8 NOT NULL, + "category" varchar(50) NOT NULL, + "name" varchar(50) NOT NULL, + "code" varchar(100) NOT NULL, + "value" text DEFAULT NULL, + "default_value" text DEFAULT NULL, + "description" varchar(200) DEFAULT NULL, + "update_user" int8 DEFAULT NULL, + "update_time" timestamp DEFAULT NULL, + PRIMARY KEY ("id") +); +CREATE UNIQUE INDEX "uk_option_category_code" ON "sys_option" ("category", "code"); +COMMENT ON COLUMN "sys_option"."id" IS 'ID'; +COMMENT ON COLUMN "sys_option"."category" IS '类别'; +COMMENT ON COLUMN "sys_option"."name" IS '名称'; +COMMENT ON COLUMN "sys_option"."code" IS '键'; +COMMENT ON COLUMN "sys_option"."value" IS '值'; +COMMENT ON COLUMN "sys_option"."default_value" IS '默认值'; +COMMENT ON COLUMN "sys_option"."description" IS '描述'; +COMMENT ON COLUMN "sys_option"."update_user" IS '修改人'; +COMMENT ON COLUMN "sys_option"."update_time" IS '修改时间'; +COMMENT ON TABLE "sys_option" IS '参数表'; + +CREATE TABLE IF NOT EXISTS "sys_dict" ( + "id" int8 NOT NULL, + "name" varchar(30) NOT NULL, + "code" varchar(30) NOT NULL, + "description" varchar(200) DEFAULT NULL, + "is_system" bool NOT NULL DEFAULT false, + "create_user" int8 NOT NULL, + "create_time" timestamp NOT NULL, + "update_user" int8 DEFAULT NULL, + "update_time" timestamp DEFAULT NULL, + PRIMARY KEY ("id") +); +CREATE UNIQUE INDEX "uk_dict_name" ON "sys_dict" ("name"); +CREATE UNIQUE INDEX "uk_dict_code" ON "sys_dict" ("code"); +COMMENT ON COLUMN "sys_dict"."id" IS 'ID'; +COMMENT ON COLUMN "sys_dict"."name" IS '名称'; +COMMENT ON COLUMN "sys_dict"."code" IS '编码'; +COMMENT ON COLUMN "sys_dict"."description" IS '描述'; +COMMENT ON COLUMN "sys_dict"."is_system" IS '是否为系统内置数据'; +COMMENT ON COLUMN "sys_dict"."create_user" IS '创建人'; +COMMENT ON COLUMN "sys_dict"."create_time" IS '创建时间'; +COMMENT ON COLUMN "sys_dict"."update_user" IS '修改人'; +COMMENT ON COLUMN "sys_dict"."update_time" IS '修改时间'; +COMMENT ON TABLE "sys_dict" IS '字典表'; + +CREATE TABLE IF NOT EXISTS "sys_dict_item" ( + "id" int8 NOT NULL, + "label" varchar(30) NOT NULL, + "value" varchar(30) NOT NULL, + "color" varchar(30) DEFAULT NULL, + "sort" int4 NOT NULL DEFAULT 999, + "description" varchar(200) DEFAULT NULL, + "status" int2 NOT NULL DEFAULT 1, + "dict_id" int8 NOT NULL, + "create_user" int8 NOT NULL, + "create_time" timestamp NOT NULL, + "update_user" int8 DEFAULT NULL, + "update_time" timestamp DEFAULT NULL, + PRIMARY KEY ("id") +); +CREATE UNIQUE INDEX "uk_dict_item_value_dict_id" ON "sys_dict_item" ("value", "dict_id"); +CREATE INDEX "idx_dict_item_dict_id" ON "sys_dict_item" ("dict_id"); +CREATE INDEX "idx_dict_item_create_user" ON "sys_dict_item" ("create_user"); +CREATE INDEX "idx_dict_item_update_user" ON "sys_dict_item" ("update_user"); +COMMENT ON COLUMN "sys_dict_item"."id" IS 'ID'; +COMMENT ON COLUMN "sys_dict_item"."label" IS '标签'; +COMMENT ON COLUMN "sys_dict_item"."value" IS '值'; +COMMENT ON COLUMN "sys_dict_item"."color" IS '标签颜色'; +COMMENT ON COLUMN "sys_dict_item"."sort" IS '排序'; +COMMENT ON COLUMN "sys_dict_item"."description" IS '描述'; +COMMENT ON COLUMN "sys_dict_item"."status" IS '状态(1:启用;2:禁用)'; +COMMENT ON COLUMN "sys_dict_item"."dict_id" IS '字典ID'; +COMMENT ON COLUMN "sys_dict_item"."create_user" IS '创建人'; +COMMENT ON COLUMN "sys_dict_item"."create_time" IS '创建时间'; +COMMENT ON COLUMN "sys_dict_item"."update_user" IS '修改人'; +COMMENT ON COLUMN "sys_dict_item"."update_time" IS '修改时间'; +COMMENT ON TABLE "sys_dict_item" IS '字典项表'; + +CREATE TABLE IF NOT EXISTS "sys_log" ( + "id" int8 NOT NULL, + "trace_id" varchar(255) DEFAULT NULL, + "description" varchar(255) NOT NULL, + "module" varchar(50) NOT NULL, + "request_url" varchar(512) NOT NULL, + "request_method" varchar(10) NOT NULL, + "request_headers" text DEFAULT NULL, + "request_body" text DEFAULT NULL, + "status_code" int4 NOT NULL, + "response_headers" text DEFAULT NULL, + "response_body" text DEFAULT NULL, + "time_taken" int8 NOT NULL, + "ip" varchar(100) DEFAULT NULL, + "address" varchar(255) DEFAULT NULL, + "browser" varchar(100) DEFAULT NULL, + "os" varchar(100) DEFAULT NULL, + "status" int2 NOT NULL DEFAULT 1, + "error_msg" text DEFAULT NULL, + "create_user" int8 DEFAULT NULL, + "create_time" timestamp NOT NULL, + PRIMARY KEY ("id") +); +CREATE INDEX "idx_log_module" ON "sys_log" ("module"); +CREATE INDEX "idx_log_ip" ON "sys_log" ("ip"); +CREATE INDEX "idx_log_create_time" ON "sys_log" ("create_time"); +COMMENT ON COLUMN "sys_log"."id" IS 'ID'; +COMMENT ON COLUMN "sys_log"."trace_id" IS '链路ID'; +COMMENT ON COLUMN "sys_log"."description" IS '日志描述'; +COMMENT ON COLUMN "sys_log"."module" IS '所属模块'; +COMMENT ON COLUMN "sys_log"."request_url" IS '请求URL'; +COMMENT ON COLUMN "sys_log"."request_method" IS '请求方式'; +COMMENT ON COLUMN "sys_log"."request_headers" IS '请求头'; +COMMENT ON COLUMN "sys_log"."request_body" IS '请求体'; +COMMENT ON COLUMN "sys_log"."status_code" IS '状态码'; +COMMENT ON COLUMN "sys_log"."response_headers" IS '响应头'; +COMMENT ON COLUMN "sys_log"."response_body" IS '响应体'; +COMMENT ON COLUMN "sys_log"."time_taken" IS '耗时(ms)'; +COMMENT ON COLUMN "sys_log"."ip" IS 'IP'; +COMMENT ON COLUMN "sys_log"."address" IS 'IP归属地'; +COMMENT ON COLUMN "sys_log"."browser" IS '浏览器'; +COMMENT ON COLUMN "sys_log"."os" IS '操作系统'; +COMMENT ON COLUMN "sys_log"."status" IS '状态(1:成功;2:失败)'; +COMMENT ON COLUMN "sys_log"."error_msg" IS '错误信息'; +COMMENT ON COLUMN "sys_log"."create_user" IS '创建人'; +COMMENT ON COLUMN "sys_log"."create_time" IS '创建时间'; +COMMENT ON TABLE "sys_log" IS '系统日志表'; + +CREATE TABLE IF NOT EXISTS "sys_message" ( + "id" int8 NOT NULL, + "title" varchar(50) NOT NULL, + "content" varchar(255) DEFAULT NULL, + "type" int2 NOT NULL DEFAULT 1, + "create_user" int8 DEFAULT NULL, + "create_time" timestamp NOT NULL, + PRIMARY KEY ("id") +); +COMMENT ON COLUMN "sys_message"."id" IS 'ID'; +COMMENT ON COLUMN "sys_message"."title" IS '标题'; +COMMENT ON COLUMN "sys_message"."content" IS '内容'; +COMMENT ON COLUMN "sys_message"."type" IS '类型(1:系统消息)'; +COMMENT ON COLUMN "sys_message"."create_user" IS '创建人'; +COMMENT ON COLUMN "sys_message"."create_time" IS '创建时间'; +COMMENT ON TABLE "sys_message" IS '消息表'; + +CREATE TABLE IF NOT EXISTS "sys_message_user" ( + "message_id" int8 NOT NULL, + "user_id" int8 NOT NULL, + "is_read" bool NOT NULL DEFAULT false, + "read_time" timestamp DEFAULT NULL, + PRIMARY KEY ("message_id", "user_id") +); +COMMENT ON COLUMN "sys_message_user"."message_id" IS '消息ID'; +COMMENT ON COLUMN "sys_message_user"."user_id" IS '用户ID'; +COMMENT ON COLUMN "sys_message_user"."is_read" IS '是否已读'; +COMMENT ON COLUMN "sys_message_user"."read_time" IS '读取时间'; +COMMENT ON TABLE "sys_message_user" IS '消息和用户关联表'; + +CREATE TABLE IF NOT EXISTS "sys_notice" ( + "id" int8 NOT NULL, + "title" varchar(150) NOT NULL, + "content" text NOT NULL, + "type" varchar(30) NOT NULL, + "effective_time" timestamp DEFAULT NULL, + "terminate_time" timestamp DEFAULT NULL, + "sort" int4 NOT NULL DEFAULT 999, + "create_user" int8 NOT NULL, + "create_time" timestamp NOT NULL, + "update_user" int8 DEFAULT NULL, + "update_time" timestamp DEFAULT NULL, + PRIMARY KEY ("id") +); +CREATE INDEX "idx_notice_create_user" ON "sys_notice" ("create_user"); +CREATE INDEX "idx_notice_update_user" ON "sys_notice" ("update_user"); +COMMENT ON COLUMN "sys_notice"."id" IS 'ID'; +COMMENT ON COLUMN "sys_notice"."title" IS '标题'; +COMMENT ON COLUMN "sys_notice"."content" IS '内容'; +COMMENT ON COLUMN "sys_notice"."type" IS '类型'; +COMMENT ON COLUMN "sys_notice"."effective_time" IS '生效时间'; +COMMENT ON COLUMN "sys_notice"."terminate_time" IS '终止时间'; +COMMENT ON COLUMN "sys_notice"."sort" IS '排序'; +COMMENT ON COLUMN "sys_notice"."create_user" IS '创建人'; +COMMENT ON COLUMN "sys_notice"."create_time" IS '创建时间'; +COMMENT ON COLUMN "sys_notice"."update_user" IS '修改人'; +COMMENT ON COLUMN "sys_notice"."update_time" IS '修改时间'; +COMMENT ON TABLE "sys_notice" IS '公告表'; + +CREATE TABLE IF NOT EXISTS "sys_storage" ( + "id" int8 NOT NULL, + "name" varchar(100) NOT NULL, + "code" varchar(30) NOT NULL, + "type" int2 NOT NULL DEFAULT 1, + "access_key" varchar(255) DEFAULT NULL, + "secret_key" varchar(255) DEFAULT NULL, + "endpoint" varchar(255) DEFAULT NULL, + "bucket_name" varchar(255) DEFAULT NULL, + "domain" varchar(255) NOT NULL DEFAULT '', + "description" varchar(200) DEFAULT NULL, + "is_default" bool NOT NULL DEFAULT false, + "sort" int4 NOT NULL DEFAULT 999, + "status" int2 NOT NULL DEFAULT 1, + "create_user" int8 NOT NULL, + "create_time" timestamp NOT NULL, + "update_user" int8 DEFAULT NULL, + "update_time" timestamp DEFAULT NULL, + PRIMARY KEY ("id") +); +CREATE UNIQUE INDEX "uk_storage_code" ON "sys_storage" ("code"); +CREATE INDEX "idx_storage_create_user" ON "sys_storage" ("create_user"); +CREATE INDEX "idx_storage_update_user" ON "sys_storage" ("update_user"); +COMMENT ON COLUMN "sys_storage"."id" IS 'ID'; +COMMENT ON COLUMN "sys_storage"."name" IS '名称'; +COMMENT ON COLUMN "sys_storage"."code" IS '编码'; +COMMENT ON COLUMN "sys_storage"."type" IS '类型(1:兼容S3协议存储;2:本地存储)'; +COMMENT ON COLUMN "sys_storage"."access_key" IS 'Access Key(访问密钥)'; +COMMENT ON COLUMN "sys_storage"."secret_key" IS 'Secret Key(私有密钥)'; +COMMENT ON COLUMN "sys_storage"."endpoint" IS 'Endpoint(终端节点)'; +COMMENT ON COLUMN "sys_storage"."bucket_name" IS '桶名称'; +COMMENT ON COLUMN "sys_storage"."domain" IS '域名'; +COMMENT ON COLUMN "sys_storage"."description" IS '描述'; +COMMENT ON COLUMN "sys_storage"."is_default" IS '是否为默认存储'; +COMMENT ON COLUMN "sys_storage"."sort" IS '排序'; +COMMENT ON COLUMN "sys_storage"."status" IS '状态(1:启用;2:禁用)'; +COMMENT ON COLUMN "sys_storage"."create_user" IS '创建人'; +COMMENT ON COLUMN "sys_storage"."create_time" IS '创建时间'; +COMMENT ON COLUMN "sys_storage"."update_user" IS '修改人'; +COMMENT ON COLUMN "sys_storage"."update_time" IS '修改时间'; +COMMENT ON TABLE "sys_storage" IS '存储表'; + +CREATE TABLE IF NOT EXISTS "sys_file" ( + "id" int8 NOT NULL, + "name" varchar(255) NOT NULL, + "size" int8 NOT NULL, + "url" varchar(512) NOT NULL, + "extension" varchar(100) DEFAULT NULL, + "thumbnail_size" int8 DEFAULT NULL, + "thumbnail_url" varchar(512) DEFAULT NULL, + "type" int2 NOT NULL DEFAULT 1, + "storage_id" int8 NOT NULL, + "create_user" int8 NOT NULL, + "create_time" timestamp NOT NULL, + "update_user" int8 NOT NULL, + "update_time" timestamp NOT NULL, + PRIMARY KEY ("id") +); +CREATE INDEX "idx_file_url" ON "sys_file" ("url"); +CREATE INDEX "idx_file_type" ON "sys_file" ("type"); +CREATE INDEX "idx_file_create_user" ON "sys_file" ("create_user"); +CREATE INDEX "idx_file_update_user" ON "sys_file" ("update_user"); +COMMENT ON COLUMN "sys_file"."id" IS 'ID'; +COMMENT ON COLUMN "sys_file"."name" IS '名称'; +COMMENT ON COLUMN "sys_file"."size" IS '大小(字节)'; +COMMENT ON COLUMN "sys_file"."url" IS 'URL'; +COMMENT ON COLUMN "sys_file"."extension" IS '扩展名'; +COMMENT ON COLUMN "sys_file"."thumbnail_size" IS '缩略图大小(字节)'; +COMMENT ON COLUMN "sys_file"."thumbnail_url" IS '缩略图URL'; +COMMENT ON COLUMN "sys_file"."type" IS '类型(1:其他;2:图片;3:文档;4:视频;5:音频)'; +COMMENT ON COLUMN "sys_file"."storage_id" IS '存储ID'; +COMMENT ON COLUMN "sys_file"."create_user" IS '创建人'; +COMMENT ON COLUMN "sys_file"."create_time" IS '创建时间'; +COMMENT ON COLUMN "sys_file"."update_user" IS '修改人'; +COMMENT ON COLUMN "sys_file"."update_time" IS '修改时间'; +COMMENT ON TABLE "sys_file" IS '文件表'; + +CREATE TABLE IF NOT EXISTS "gen_config" ( + "table_name" varchar(64) NOT NULL, + "module_name" varchar(60) NOT NULL, + "package_name" varchar(60) NOT NULL, + "business_name" varchar(50) NOT NULL, + "author" varchar(100) NOT NULL, + "table_prefix" varchar(20) DEFAULT NULL, + "is_override" bool NOT NULL DEFAULT false, + "create_time" timestamp NOT NULL, + "update_time" timestamp DEFAULT NULL, + PRIMARY KEY ("table_name") +); +COMMENT ON COLUMN "gen_config"."table_name" IS '表名称'; +COMMENT ON COLUMN "gen_config"."module_name" IS '模块名称'; +COMMENT ON COLUMN "gen_config"."package_name" IS '包名称'; +COMMENT ON COLUMN "gen_config"."business_name" IS '业务名称'; +COMMENT ON COLUMN "gen_config"."author" IS '作者'; +COMMENT ON COLUMN "gen_config"."table_prefix" IS '表前缀'; +COMMENT ON COLUMN "gen_config"."is_override" IS '是否覆盖'; +COMMENT ON COLUMN "gen_config"."create_time" IS '创建时间'; +COMMENT ON COLUMN "gen_config"."update_time" IS '修改时间'; +COMMENT ON TABLE "gen_config" IS '生成配置表'; + +CREATE TABLE IF NOT EXISTS "gen_field_config" ( + "table_name" varchar(64) NOT NULL, + "column_name" varchar(64) NOT NULL, + "column_type" varchar(25) NOT NULL, + "column_size" int8 DEFAULT NULL, + "field_name" varchar(64) NOT NULL, + "field_type" varchar(25) NOT NULL, + "field_sort" int4 NOT NULL DEFAULT 999, + "comment" varchar(512) DEFAULT NULL, + "is_required" bool NOT NULL DEFAULT true, + "show_in_list" bool NOT NULL DEFAULT true, + "show_in_form" bool NOT NULL DEFAULT true, + "show_in_query" bool NOT NULL DEFAULT true, + "form_type" int2 DEFAULT NULL, + "query_type" int2 DEFAULT NULL, + "dict_code" varchar(30) DEFAULT NULL, + "create_time" timestamp NOT NULL +); +CREATE INDEX "idx_field_config_table_name" ON "gen_field_config" ("table_name"); +COMMENT ON COLUMN "gen_field_config"."table_name" IS '表名称'; +COMMENT ON COLUMN "gen_field_config"."column_name" IS '列名称'; +COMMENT ON COLUMN "gen_field_config"."column_type" IS '列类型'; +COMMENT ON COLUMN "gen_field_config"."column_size" IS '列大小'; +COMMENT ON COLUMN "gen_field_config"."field_name" IS '字段名称'; +COMMENT ON COLUMN "gen_field_config"."field_type" IS '字段类型'; +COMMENT ON COLUMN "gen_field_config"."field_sort" IS '字段排序'; +COMMENT ON COLUMN "gen_field_config"."comment" IS '注释'; +COMMENT ON COLUMN "gen_field_config"."is_required" IS '是否必填'; +COMMENT ON COLUMN "gen_field_config"."show_in_list" IS '是否在列表中显示'; +COMMENT ON COLUMN "gen_field_config"."show_in_form" IS '是否在表单中显示'; +COMMENT ON COLUMN "gen_field_config"."show_in_query" IS '是否在查询中显示'; +COMMENT ON COLUMN "gen_field_config"."form_type" IS '表单类型'; +COMMENT ON COLUMN "gen_field_config"."query_type" IS '查询方式'; +COMMENT ON COLUMN "gen_field_config"."dict_code" IS '字典编码'; +COMMENT ON COLUMN "gen_field_config"."create_time" IS '创建时间'; +COMMENT ON TABLE "gen_field_config" IS '字段配置表'; \ No newline at end of file diff --git a/continew-admin-webapi/src/main/resources/favicon.ico b/continew-admin-webapi/src/main/resources/favicon.ico new file mode 100644 index 0000000..f3c48de Binary files /dev/null and b/continew-admin-webapi/src/main/resources/favicon.ico differ diff --git a/continew-admin-webapi/src/main/resources/logback-spring.xml b/continew-admin-webapi/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..a69cea8 --- /dev/null +++ b/continew-admin-webapi/src/main/resources/logback-spring.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${CONSOLE_LOG_PATTERN} + ${LOG_CHARSET} + + + + + + + ${FILE_LOG_PATTERN} + ${LOG_CHARSET} + + + + + + + ${LOG_PATH}/${APP_NAME}.log + + + + ${LOG_PATH}/%d{yyyy-MM-dd}/${APP_NAME}.%d{yyyy-MM-dd}.%i.log.gz + + ${FILE_MAX_SIZE} + + ${FILE_MAX_HISTORY} + + + ${FILE_LOG_PATTERN} + ${LOG_CHARSET} + + + + + + + 0 + + 512 + + + + + + + + + + + + + + + + + + + + + + diff --git a/continew-admin-webapi/src/main/resources/table.sql b/continew-admin-webapi/src/main/resources/table.sql new file mode 100644 index 0000000..a66825b --- /dev/null +++ b/continew-admin-webapi/src/main/resources/table.sql @@ -0,0 +1,26 @@ +CREATE TABLE IF NOT EXISTS `t_task_record_history` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID', + `user_id` bigint NOT NULL COMMENT '用户id', + `title` varchar(800) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '文件名', + `task_root_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '0' COMMENT '根任务ID', + `task_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '0' COMMENT '任务订单号', + `parent_task_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '0' COMMENT '记录父任务 ID', + `platform_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '生成平台', + `platform_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '内容来源的平台', + `task_type` tinyint unsigned NOT NULL DEFAULT '1' COMMENT '任务类型:1 - 初始模特穿衣生成(仅根任务可用),2 - 对话修改,3 - 生成视频', + `model_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '使用的模型', + `consumption_points` bigint DEFAULT NULL COMMENT '消耗点数', + `file_url` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '存储结果文件路径', + `tokens` int DEFAULT NULL COMMENT '消费的token数', + `result` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '生成的文字', + `create_user` bigint NOT NULL COMMENT '创建人', + `create_time` datetime NOT NULL COMMENT '创建时间', + `parent_create_time` datetime NOT NULL COMMENT '父任务创建时间', + `update_user` bigint DEFAULT NULL COMMENT '修改人', + `update_time` datetime DEFAULT NULL COMMENT '修改时间', + `charge_type` tinyint unsigned NOT NULL DEFAULT '1' COMMENT '任务类型(文字内容:0, 图:2,音效:3,视频:4, 白膜贴图:5)', + `file_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '文件类型', + `type` tinyint DEFAULT NULL COMMENT '消费类型(0充点,1按次数,2按点数)', + `account_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '从哪个账户进行的扣费', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=791699284218093843 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='换哒-任务生成记录'; \ No newline at end of file diff --git a/continew-admin-webapi/src/main/resources/templates/import/userImportTemplate.xlsx b/continew-admin-webapi/src/main/resources/templates/import/userImportTemplate.xlsx new file mode 100644 index 0000000..03a9360 Binary files /dev/null and b/continew-admin-webapi/src/main/resources/templates/import/userImportTemplate.xlsx differ diff --git a/continew-admin-webapi/src/main/resources/templates/mail/captcha.ftl b/continew-admin-webapi/src/main/resources/templates/mail/captcha.ftl new file mode 100644 index 0000000..0b7f69e --- /dev/null +++ b/continew-admin-webapi/src/main/resources/templates/mail/captcha.ftl @@ -0,0 +1,47 @@ + + + + + + + + + + + +
+
+ +
+
+

亲爱的用户:

+

+ 您好!感谢您使用 ${siteTitle},本次请求的验证码为:${captcha},请在 ${expiration} 分钟内使用此验证码完成验证。 +

+
+

Dear user:

+

+ Hello! Thanks for using ${siteTitle}, The verification code for this request is: ${captcha}, please use this verification code to complete the verification within ${expiration} minutes. +

+
+
+

+ 若非本人操作,请忽略此邮件。此邮件由系统自动发送,请勿直接回复该邮件。
+ Please ignore this email if not by yourself. This email is sent automatically by the system, please do not reply to this email directly. +

+

${siteCopyright}

+
+
+
+
+
+
+ + \ No newline at end of file diff --git a/continew-admin-webapi/src/test/java/top/continew/admin/ContiNewAdminApplicationTests.java b/continew-admin-webapi/src/test/java/top/continew/admin/ContiNewAdminApplicationTests.java new file mode 100644 index 0000000..55b4414 --- /dev/null +++ b/continew-admin-webapi/src/test/java/top/continew/admin/ContiNewAdminApplicationTests.java @@ -0,0 +1,29 @@ +package top.continew.admin; + +import cn.dev33.satoken.jwt.SaJwtTemplate; +import cn.hutool.jwt.JWT; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.ArrayList; +import java.util.List; + +@SpringBootTest +class ContiNewAdminApplicationTests { + + @Test + void contextLoads() { + List list = new ArrayList(); + list.add("w2003210611"); //3ba204d7428a6711c0ecbb4f7aff56a1 + //I/4/Z2qtuAf/8UXEVnMk9dH3DtZbxq8haFbZmqngQ/gCHSMT7cBcE36daVF6cG2zwRRcnTL/FNwcuIxYis3ZbA== + //List list1 = SecureUtils.encryptFieldByAes(list); + //String s = SecureUtils.decryptByRsaPrivateKey("I/4/Z2qtuAf/8UXEVnMk9dH3DtZbxq8haFbZmqngQ/gCHSMT7cBcE36daVF6cG2zwRRcnTL/FNwcuIxYis3ZbA=="); + + String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpblR5cGUiOiJsb2dpbiIsImxvZ2luSWQiOjY0NDEwODAyMjk1OTgzNzIzMCwicm5TdHIiOiIza0kxakdkcGx6bVE2dk5WQU9QdUtYNFhxU1VpbmtLdyJ9.9HnsngOmI2GgGNa2kGW2MXEl3RL5wZbS6sOY7sB1aFc"; + String keyt = "asdasdasifhueuiwyurfewbfjsdafjk"; + + SaJwtTemplate saJwtTemplate = new SaJwtTemplate(); + JWT login = saJwtTemplate.parseToken(token, "login", keyt, false); + System.out.println(login.getPayloads()); + } +} diff --git a/continew-admin-webapi/src/test/java/top/continew/admin/auth/UserRegisterTest.java b/continew-admin-webapi/src/test/java/top/continew/admin/auth/UserRegisterTest.java new file mode 100644 index 0000000..5131487 --- /dev/null +++ b/continew-admin-webapi/src/test/java/top/continew/admin/auth/UserRegisterTest.java @@ -0,0 +1,157 @@ +package top.continew.admin.auth; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.json.JSONUtil; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureWebMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import top.continew.admin.auth.model.req.UserRegisterReq; +import top.continew.admin.common.enums.GenderEnum; +import top.continew.admin.common.util.SecureUtils; + +/** + * 用户注册功能测试 + * + * @author Charles7c + * @since 2024/12/04 10:00 + */ +@SpringBootTest +@AutoConfigureWebMvc +@ActiveProfiles("test") +public class UserRegisterTest { + + @Autowired + private MockMvc mockMvc; + + @Test + public void testUserRegister() throws Exception { + // 创建注册请求 + UserRegisterReq registerReq = new UserRegisterReq(); + registerReq.setUsername("testuser" + System.currentTimeMillis()); + registerReq.setNickname("测试用户"); + + // 加密密码 + String password = "Test123456"; + String encryptedPassword = SecureUtils.encryptByRsaPublicKey(password); + registerReq.setPassword(encryptedPassword); + registerReq.setConfirmPassword(encryptedPassword); + + registerReq.setEmail("test" + System.currentTimeMillis() + "@example.com"); + registerReq.setPhone("138" + String.format("%08d", System.currentTimeMillis() % 100000000)); + registerReq.setGender(GenderEnum.MALE); + registerReq.setCaptcha("1234"); // 测试验证码 + registerReq.setUuid(IdUtil.fastUUID()); + + // 执行注册请求 + mockMvc.perform(MockMvcRequestBuilders.post("/auth/register") + .contentType(MediaType.APPLICATION_JSON) + .content(JSONUtil.toJsonStr(registerReq))) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$.token").exists()) + .andExpect(MockMvcResultMatchers.jsonPath("$.aiBotToken").exists()); + } + + @Test + public void testPhoneRegister() throws Exception { + // 创建手机号注册请求 + top.continew.admin.auth.model.req.PhoneRegisterReq registerReq = new top.continew.admin.auth.model.req.PhoneRegisterReq(); + + registerReq.setPhone("139" + String.format("%08d", System.currentTimeMillis() % 100000000)); + registerReq.setNickname("手机用户"); + + // 加密密码 + String password = "Test123456"; + String encryptedPassword = SecureUtils.encryptByRsaPublicKey(password); + registerReq.setPassword(encryptedPassword); + registerReq.setConfirmPassword(encryptedPassword); + + registerReq.setGender(GenderEnum.FEMALE); + registerReq.setCaptcha("8888"); // 测试验证码 + + // 执行注册请求 + mockMvc.perform(MockMvcRequestBuilders.post("/auth/register/phone") + .contentType(MediaType.APPLICATION_JSON) + .content(JSONUtil.toJsonStr(registerReq))) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$.token").exists()) + .andExpect(MockMvcResultMatchers.jsonPath("$.aiBotToken").exists()); + } + + @Test + public void testEmailRegister() throws Exception { + // 创建邮箱注册请求 + top.continew.admin.auth.model.req.EmailRegisterReq registerReq = new top.continew.admin.auth.model.req.EmailRegisterReq(); + + registerReq.setEmail("email" + System.currentTimeMillis() + "@test.com"); + registerReq.setNickname("邮箱用户"); + + // 加密密码 + String password = "Test123456"; + String encryptedPassword = SecureUtils.encryptByRsaPublicKey(password); + registerReq.setPassword(encryptedPassword); + registerReq.setConfirmPassword(encryptedPassword); + + registerReq.setGender(GenderEnum.UNKNOWN); + registerReq.setCaptcha("888888"); // 测试验证码 + + // 执行注册请求 + mockMvc.perform(MockMvcRequestBuilders.post("/auth/register/email") + .contentType(MediaType.APPLICATION_JSON) + .content(JSONUtil.toJsonStr(registerReq))) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.jsonPath("$.token").exists()) + .andExpect(MockMvcResultMatchers.jsonPath("$.aiBotToken").exists()); + } + + @Test + public void testRegisterWithDuplicateUsername() throws Exception { + // 测试重复用户名注册 + UserRegisterReq registerReq = new UserRegisterReq(); + registerReq.setUsername("admin"); // 使用已存在的用户名 + registerReq.setNickname("重复用户"); + + String password = "Test123456"; + String encryptedPassword = SecureUtils.encryptByRsaPublicKey(password); + registerReq.setPassword(encryptedPassword); + registerReq.setConfirmPassword(encryptedPassword); + + registerReq.setEmail("duplicate@example.com"); + registerReq.setGender(GenderEnum.MALE); + registerReq.setCaptcha("1234"); + registerReq.setUuid(IdUtil.fastUUID()); + + // 执行注册请求,期望失败 + mockMvc.perform(MockMvcRequestBuilders.post("/auth/register") + .contentType(MediaType.APPLICATION_JSON) + .content(JSONUtil.toJsonStr(registerReq))).andExpect(MockMvcResultMatchers.status().is4xxClientError()); + } + + @Test + public void testRegisterWithInvalidPassword() throws Exception { + // 测试密码不一致的情况 + UserRegisterReq registerReq = new UserRegisterReq(); + registerReq.setUsername("testuser" + System.currentTimeMillis()); + registerReq.setNickname("测试用户"); + + String password1 = "Test123456"; + String password2 = "Test654321"; + registerReq.setPassword(SecureUtils.encryptByRsaPublicKey(password1)); + registerReq.setConfirmPassword(SecureUtils.encryptByRsaPublicKey(password2)); + + registerReq.setEmail("test" + System.currentTimeMillis() + "@example.com"); + registerReq.setGender(GenderEnum.MALE); + registerReq.setCaptcha("1234"); + registerReq.setUuid(IdUtil.fastUUID()); + + // 执行注册请求,期望失败 + mockMvc.perform(MockMvcRequestBuilders.post("/auth/register") + .contentType(MediaType.APPLICATION_JSON) + .content(JSONUtil.toJsonStr(registerReq))).andExpect(MockMvcResultMatchers.status().is4xxClientError()); + } +} diff --git a/continew-admin-webapi/src/test/java/top/continew/admin/util/SecureUtilsTest.java b/continew-admin-webapi/src/test/java/top/continew/admin/util/SecureUtilsTest.java new file mode 100644 index 0000000..a32508f --- /dev/null +++ b/continew-admin-webapi/src/test/java/top/continew/admin/util/SecureUtilsTest.java @@ -0,0 +1,117 @@ +package top.continew.admin.util; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import top.continew.admin.common.util.SecureUtils; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * SecureUtils 测试类 + * + * @author Charles7c + * @since 2024/12/04 16:00 + */ +@SpringBootTest +@ActiveProfiles("test") +public class SecureUtilsTest { + + @Test + public void testRsaEncryptDecrypt() { + // 测试数据 + String originalPassword = "Test123456"; + + try { + // 公钥加密 + String encryptedPassword = SecureUtils.encryptByRsaPublicKey(originalPassword); + assertNotNull(encryptedPassword); + assertNotEquals(originalPassword, encryptedPassword); + + System.out.println("原始密码: " + originalPassword); + System.out.println("加密后密码: " + encryptedPassword); + + // 私钥解密 + String decryptedPassword = SecureUtils.decryptByRsaPrivateKey(encryptedPassword); + assertNotNull(decryptedPassword); + assertEquals(originalPassword, decryptedPassword); + + System.out.println("解密后密码: " + decryptedPassword); + + } catch (Exception e) { + e.printStackTrace(); + fail("RSA加密解密测试失败: " + e.getMessage()); + } + } + + @Test + public void testMultiplePasswords() { + String[] passwords = {"Test123456", "Admin@2024", "MyPassword123!", "SimplePass"}; + + for (String password : passwords) { + try { + String encrypted = SecureUtils.encryptByRsaPublicKey(password); + String decrypted = SecureUtils.decryptByRsaPrivateKey(encrypted); + + assertEquals(password, decrypted, "密码加密解密不一致: " + password); + System.out.println("✓ 密码测试通过: " + password); + + } catch (Exception e) { + fail("密码测试失败: " + password + ", 错误: " + e.getMessage()); + } + } + } + + @Test + public void testEmptyAndNullPassword() { + // 测试空字符串 + try { + String encrypted = SecureUtils.encryptByRsaPublicKey(""); + String decrypted = SecureUtils.decryptByRsaPrivateKey(encrypted); + assertEquals("", decrypted); + System.out.println("✓ 空字符串测试通过"); + } catch (Exception e) { + System.out.println("空字符串测试失败: " + e.getMessage()); + } + + // 测试null值应该抛出异常 + assertThrows(Exception.class, () -> { + SecureUtils.encryptByRsaPublicKey(null); + }); + } + + @Test + public void testLongPassword() { + // 测试长密码 + String longPassword = "ThisIsAVeryLongPasswordThatShouldStillWorkWithRSAEncryption123456789!@#$%^&*()"; + + try { + String encrypted = SecureUtils.encryptByRsaPublicKey(longPassword); + String decrypted = SecureUtils.decryptByRsaPrivateKey(encrypted); + + assertEquals(longPassword, decrypted); + System.out.println("✓ 长密码测试通过"); + + } catch (Exception e) { + System.out.println("长密码测试失败: " + e.getMessage()); + // RSA加密有长度限制,这是正常的 + } + } + + @Test + public void testSpecialCharacters() { + // 测试特殊字符 + String specialPassword = "密码@2024#测试$%^&*()"; + + try { + String encrypted = SecureUtils.encryptByRsaPublicKey(specialPassword); + String decrypted = SecureUtils.decryptByRsaPrivateKey(encrypted); + + assertEquals(specialPassword, decrypted); + System.out.println("✓ 特殊字符测试通过: " + specialPassword); + + } catch (Exception e) { + System.out.println("特殊字符测试失败: " + e.getMessage()); + } + } +} diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000..5e7403a --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,23 @@ +services: + kexue-server: + build: ./kexue + restart: always + container_name: kexue-server + ports: + - '18000:18000' + environment: + TZ: Asia/Shanghai + DB_HOST: 124.71.175.96 + DB_PORT: 3306 + DB_USER: root + DB_PWD: C9MUjc5ChtqHeCtQ + DB_NAME: kexue + REDIS_HOST: 124.71.175.96 + REDIS_PORT: 6379 + REDIS_PWD: 5Qsd1rTx3S7rKs0A + REDIS_DB: 0 + volumes: + - /docker/kexue/config/:/app/config/ + - /docker/kexue/data/file/:/app/data/file/ + - /docker/kexue/logs/:/app/logs/ + - /docker/kexue/lib/:/app/lib/ diff --git a/docker/job-server/Dockerfile b/docker/job-server/Dockerfile new file mode 100644 index 0000000..2db96c1 --- /dev/null +++ b/docker/job-server/Dockerfile @@ -0,0 +1,14 @@ +FROM openjdk:17 + +MAINTAINER Charles7c charles7c@126.com + +ARG JAR_FILE=*.jar +COPY ${JAR_FILE} /app/bin/app.jar +WORKDIR /app/bin + +ENTRYPOINT ["java", \ + "-jar", \ + "-XX:+UseZGC", \ + "-Djava.security.egd=file:/dev/./urandom", \ + "-Dspring.profiles.active=prod", \ + "app.jar"] \ No newline at end of file diff --git a/docker/nginx/conf/nginx.conf b/docker/nginx/conf/nginx.conf new file mode 100644 index 0000000..e39e6b1 --- /dev/null +++ b/docker/nginx/conf/nginx.conf @@ -0,0 +1,97 @@ +worker_processes 1; + +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include mime.types; + default_type application/octet-stream; + sendfile on; + keepalive_timeout 65; + # 限制 body 大小 + client_max_body_size 100m; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + # 后端项目 + upstream admin-server { + ip_hash; + server 172.17.0.1:18000; + } + + server { + listen 443 ssl; + server_name api.continew.top; + + # 证书直接存放 /docker/nginx/cert 目录下即可(更改证书名称即可,无需更改证书路径) + ssl_certificate /etc/nginx/cert/xxx.local.pem; # /etc/nginx/cert/ 为 docker 映射路径 不允许更改 + ssl_certificate_key /etc/nginx/cert/xxx.local.key; # /etc/nginx/cert/ 为 docker 映射路径 不允许更改 + ssl_session_timeout 5m; + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_prefer_server_ciphers on; + + location / { + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header REMOTE-HOST $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass http://admin-server/; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root html; + } + } + + # HTTP 请求 将转发到 HTTPS + server { + listen 80; + server_name api.continew.top; + rewrite ^ https://$http_host$request_uri? permanent; + } + + # 前端项目 + server { + listen 443 ssl; + server_name admin.continew.top; + + # 证书直接存放 /docker/nginx/cert 目录下即可(更改证书名称即可,无需更改证书路径) + ssl_certificate /etc/nginx/cert/xxx.local.pem; # /etc/nginx/cert/ 为 docker 映射路径 不允许更改 + ssl_certificate_key /etc/nginx/cert/xxx.local.key; # /etc/nginx/cert/ 为 docker 映射路径 不允许更改 + ssl_session_timeout 5m; + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_prefer_server_ciphers on; + + location / { + root /usr/share/nginx/html; + try_files $uri $uri/ /index.html; + index index.html index.htm; + # Nginx 部署时,POST 请求本地静态文件会返回 405 错误(Not Allowed) + # 用于解决一半 mock 数据,一半后端接口的情况 + error_page 405 =200 https://$host$request_uri; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root html; + } + } + + # 将 HTTP 请求转发到 HTTPS + server { + listen 80; + server_name admin.continew.top; + rewrite ^ https://$http_host$request_uri? permanent; + } +} diff --git a/docker/redis/conf/redis.conf b/docker/redis/conf/redis.conf new file mode 100644 index 0000000..9f89cb8 --- /dev/null +++ b/docker/redis/conf/redis.conf @@ -0,0 +1,29 @@ +bind 0.0.0.0 +# redis 密码 +requirepass 5Qsd1rTx3S7rKs0A + +# key 监听器配置 +# notify-keyspace-events Ex + +# 配置持久化文件存储路径 +dir ../data +# 配置rdb +# 15分钟内有至少1个key被更改则进行快照 +save 900 1 +# 5分钟内有至少10个key被更改则进行快照 +save 300 10 +# 1分钟内有至少10000个key被更改则进行快照 +save 60 10000 +# 开启压缩 +rdbcompression yes +# rdb文件名 用默认的即可 +dbfilename dump.rdb + +# 开启aof +appendonly yes +# 文件名 +appendfilename "appendonly.aof" +# 持久化策略,no:不同步,everysec:每秒一次,always:总是同步,速度比较慢 +# appendfsync always +appendfsync everysec +# appendfsync no diff --git a/docker/redis/data/README.md b/docker/redis/data/README.md new file mode 100644 index 0000000..a7ce096 --- /dev/null +++ b/docker/redis/data/README.md @@ -0,0 +1 @@ +Redis 数据存储目录,请确保赋予了读写权限,否则将无法写入数据 \ No newline at end of file diff --git a/lombok.config b/lombok.config new file mode 100644 index 0000000..1135ca1 --- /dev/null +++ b/lombok.config @@ -0,0 +1,7 @@ +config.stopBubbling=true +lombok.toString.callSuper=CALL +lombok.equalsAndHashCode.callSuper=CALL +clear lombok.val.flagUsage +lombok.val.flagUsage=ERROR +clear lombok.accessors.flagUsage +lombok.accessors.flagUsage=ERROR \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..8dca58b --- /dev/null +++ b/pom.xml @@ -0,0 +1,212 @@ + + + 4.0.0 + + + top.continew + continew-starter + 2.6.0 + + + continew-admin + ${revision} + pom + ContiNew Admin(Continue New Admin)持续迭代优化的前后端分离中后台管理系统框架,开箱即用,持续提供舒适的开发体验。 + https://github.com/continew-org/continew-admin + + + continew-admin-webapi + continew-admin-plugins + continew-admin-system + continew-admin-common + continew-admin-extension + continew-admin-business + + + + + 3.3.0 + + + + + + + + top.continew + continew-admin-webapi + ${revision} + + + + + top.continew + continew-admin-system + ${revision} + + + + + top.continew + continew-admin-business + ${revision} + + + + + top.continew + continew-admin-common + ${revision} + + + + + top.continew + continew-admin-job + ${revision} + + + + + top.continew + continew-admin-generator + 3.3.0 + + + + + + + + cn.hutool + hutool-all + + + + + org.projectlombok + lombok + true + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + -parameters + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + true + + + + + com.diffplug.spotless + spotless-maven-plugin + + + compile + + apply + + + + + + + + .style/p3c-codestyle.xml + + + + + + + org.codehaus.mojo + flatten-maven-plugin + + true + resolveCiFriendliesOnly + + + + flatten + process-resources + + flatten + + + + flatten-clean + clean + + clean + + + + + + + + + + + sonar + + https://sonarcloud.io + charles7c + Charles7c_continew-admin + ${project.groupId}:${project.artifactId} + + + false + + + + + + + + + + + + + huawei-mirror + HuaweiCloud Mirror + https://mirrors.huaweicloud.com/repository/maven/ + + + ali-mirror + AliYun Mirror + https://maven.aliyun.com/repository/public/ + + + diff --git a/src/main/resources/db/changelog/mysql/change/202512/20251225-age-type-change.sql b/src/main/resources/db/changelog/mysql/change/202512/20251225-age-type-change.sql new file mode 100644 index 0000000..00bd828 --- /dev/null +++ b/src/main/resources/db/changelog/mysql/change/202512/20251225-age-type-change.sql @@ -0,0 +1,2 @@ +-- 更新 t_huanda_resource 表的 age 字段,从整数类型改为字符串类型 +ALTER TABLE t_huanda_resource MODIFY COLUMN age VARCHAR(255) COMMENT '人物图片年龄'; \ No newline at end of file