diff --git a/.gitignore b/.gitignore index 4d1c7dcc..4b294208 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,4 @@ # Created by .ignore support plugin (hsz.mobi) -### Example sysUserDetails template template -### Example sysUserDetails template # IntelliJ project files .idea diff --git a/README.en.md b/README.en.md index bb45f787..8542c13e 100644 --- a/README.en.md +++ b/README.en.md @@ -178,10 +178,10 @@ youlai-boot/ | [youlai-gin](https://gitee.com/youlaiorg/youlai-gin) | Go + Gorm | Go | ✅️ | | [youlai-django](https://gitee.com/youlaiorg/youlai-django) | Django + DRF | Python | ✅️ | | [youlai-fastapi](https://gitee.com/youlaiorg/youlai-fastapi) | FastAPI + SQLAlchemy | Python | ✅️ | -| [youlai-laravel](https://gitee.com/youlaiorg/youlai-laravel) | Laravel + Eloquent | PHP | ✅️ | | [youlai-think](https://gitee.com/youlaiorg/youlai-think) | ThinkPHP + ThinkORM | PHP | ✅️ | | [youlai-aspnet](https://gitee.com/youlaiorg/youlai-aspnet) | ASP.NET Core + EF Core | C# | ✅️ | | [youlai-axum](https://gitee.com/youlaiorg/youlai-axum) | Axum + SeaORM | Rust | ✅️ | +| [youlai-laravel](https://gitee.com/youlaiorg/youlai-laravel) | Laravel + Eloquent | PHP | ❌️ | > The nine backends share the same **RESTful API specification** and **database schema**, so the frontend can switch seamlessly. @@ -193,8 +193,8 @@ youlai-boot/ | [youlai-boot-flex](https://gitee.com/youlaiorg/youlai-boot-flex) | youlai-boot | Standalone repo | Built on MyBatis-Flex | ✅️ | | [youlai-boot (db-pg)](https://gitee.com/youlaiorg/youlai-boot/tree/db-pg) | youlai-boot | Branch | PostgreSQL database branch | ✅️ | | [youlai-boot (multi-module)](https://gitee.com/youlaiorg/youlai-boot/tree/multi-module) | youlai-boot | Branch | Multi-module project split | ✅️ | -| [youlai-boot (spring-boot-3)](https://gitee.com/youlaiorg/youlai-boot/tree/spring-boot-3) | youlai-boot | Branch | Spring Boot 3 compatible branch | ✅️ | | [youlai-nest (multi-tenant)](https://gitee.com/youlaiorg/youlai-nest/tree/multi-tenant) | youlai-nest | Branch | Multi-tenant SaaS with tenant isolation & config | ✅️ | +| [youlai-boot (spring-boot-3)](https://gitee.com/youlaiorg/youlai-boot/tree/spring-boot-3) | youlai-boot | Branch | Spring Boot 3 compatible branch | ❌️ | ## Optional Extensions diff --git a/docker-compose.yml b/docker-compose.yml index 34df24fd..0f53b1b8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,7 +5,6 @@ # # 说明: # - name 固定项目名,容器统一管理和卸载,避免与其他项目冲突 -# - MySQL 首次启动时自动执行 ./sql 下初始化脚本(仅首次,数据卷已存在时跳过) # - 中间件配置全部内联于 command 参数,无需外部配置文件 # - 数据持久化使用命名卷(docker volume 统一管理),不污染项目目录 # - RustFS 兼容 MinIO 磁盘格式与 S3 协议(MinIO 社区版已于 2026 年停止维护) @@ -25,7 +24,6 @@ services: - MYSQL_ROOT_PASSWORD=123456 volumes: - youlai-mysql-data:/var/lib/mysql - - ./sql:/docker-entrypoint-initdb.d ports: - 3306:3306 @@ -58,7 +56,7 @@ services: - 9001:9001 xxl-job-admin: - image: xuxueli/xxl-job-admin:2.4.0 + image: xuxueli/xxl-job-admin:3.4.2 container_name: xxl-job-admin restart: unless-stopped environment: diff --git a/pom.xml b/pom.xml index 2ff23159..e4952803 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.youlai youlai-boot - 4.10.0 + 4.11.0 基于 Java 17 + SpringBoot 4 + Spring Security 构建的权限管理系统。 @@ -22,12 +22,10 @@ 5.8.41 - 8.0.0 9.1.0 1.2.24 - 3.5.15 4.5.0 @@ -70,7 +68,6 @@ 4.8.1.B - 2.0.0 @@ -196,7 +193,6 @@ ${druid.version} - com.baomidou mybatis-plus-spring-boot4-starter diff --git a/sql/extensions/dynamic-form.sql b/sql/extensions/dynamic-form.sql index 75a9d31b..18d0fcde 100644 --- a/sql/extensions/dynamic-form.sql +++ b/sql/extensions/dynamic-form.sql @@ -76,7 +76,7 @@ CREATE TABLE `form_snapshot` ( -- ---------------------------- -- 菜单(扩展段顶级 ID=8,基础段 1-7 见 youlai-admin.sql) --- 目录与视图目录/URL 全链路对齐:views/dynamic-form + /dynamic-form(route_path/component 字段) +-- 顶级目录 route_path 为 /dynamic-form,子菜单 component 指向 views/dynamic-form 下的视图且 route_path 只填当前段 -- ---------------------------- -- params 携 {"badge":"NEW"}:挂顶级"动态表单"一级可见(二级折叠态角标不可见),宣传期过后清空 params 即消失 INSERT IGNORE INTO `sys_menu` (`id`, `parent_id`, `tree_path`, `name`, `type`, `route_name`, `route_path`, `component`, `perm`, `keep_alive`, `visible`, `sort`, `icon`, `redirect`, `create_time`, `update_time`, `params`) VALUES (8, 0, '0', '动态表单', 'C', '', '/dynamic-form', 'Layout', NULL, NULL, 1, 8, 'el-icon-Document', '/dynamic-form/definition', now(), now(), '{\"badge\":\"NEW\"}'); diff --git a/src/main/java/com/youlai/boot/codegen/controller/CodegenController.java b/src/main/java/com/youlai/boot/codegen/controller/CodegenController.java index 77a14631..77d4433c 100644 --- a/src/main/java/com/youlai/boot/codegen/controller/CodegenController.java +++ b/src/main/java/com/youlai/boot/codegen/controller/CodegenController.java @@ -8,6 +8,7 @@ import com.youlai.boot.codegen.property.CodegenProperties; import com.youlai.boot.common.enums.ActionTypeEnum; import com.youlai.boot.common.enums.LogModuleEnum; import com.youlai.boot.codegen.service.CodegenService; +import com.youlai.boot.codegen.model.form.AiFillConfigForm; import com.youlai.boot.codegen.model.form.GenConfigForm; import com.youlai.boot.codegen.model.query.TableQuery; import com.youlai.boot.codegen.model.vo.CodegenPreviewVO; @@ -111,4 +112,16 @@ public class CodegenController { throw new BusinessException("代码生成压缩包响应写入失败", e); } } + + // AI 辅助:未开启 AI 时调用返回业务异常 + + @Operation(summary = "AI 填充代码生成配置") + @PostMapping("/{tableName}/ai-config") + public Result fillConfig( + @Parameter(description = "表名", example = "sys_user") @PathVariable String tableName, + @RequestBody(required = false) @Valid AiFillConfigForm form + ) { + String requirement = form == null ? null : form.getRequirement(); + return Result.success(genTableService.aiFillConfig(tableName, requirement)); + } } diff --git a/src/main/java/com/youlai/boot/codegen/model/entity/GenTable.java b/src/main/java/com/youlai/boot/codegen/model/entity/GenTable.java index 51f5f289..fe9801eb 100644 --- a/src/main/java/com/youlai/boot/codegen/model/entity/GenTable.java +++ b/src/main/java/com/youlai/boot/codegen/model/entity/GenTable.java @@ -53,7 +53,7 @@ public class GenTable extends BaseEntity { private String author; /** - * 页面类型 classic|curd + * 页面类型 classic|crud */ private String pageType; diff --git a/src/main/java/com/youlai/boot/codegen/model/form/AiFillConfigForm.java b/src/main/java/com/youlai/boot/codegen/model/form/AiFillConfigForm.java new file mode 100644 index 00000000..b444604b --- /dev/null +++ b/src/main/java/com/youlai/boot/codegen/model/form/AiFillConfigForm.java @@ -0,0 +1,18 @@ +package com.youlai.boot.codegen.model.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * AI 填充生成配置入参 + * + * @author Ray.Hao + * @since 0.1.0 + */ +@Schema(description = "AI 填充生成配置入参") +@Data +public class AiFillConfigForm { + + @Schema(description = "补充需求描述,可为空", example = "状态字段用下拉框,创建时间不出现在表单") + private String requirement; +} diff --git a/src/main/java/com/youlai/boot/codegen/model/form/GenConfigForm.java b/src/main/java/com/youlai/boot/codegen/model/form/GenConfigForm.java index 60fb6076..db1ab797 100644 --- a/src/main/java/com/youlai/boot/codegen/model/form/GenConfigForm.java +++ b/src/main/java/com/youlai/boot/codegen/model/form/GenConfigForm.java @@ -19,36 +19,36 @@ import java.util.List; @Data public class GenConfigForm { - @Schema(description = "主键",example = "1") + @Schema(description = "主键", example = "1") private Long id; - @Schema(description = "表名",example = "sys_user") + @Schema(description = "表名", example = "sys_user") @NotBlank(message = "表名不能为空") private String tableName; - @Schema(description = "业务名",example = "用户") + @Schema(description = "业务名", example = "用户") @NotBlank(message = "业务名不能为空") private String businessName; - @Schema(description = "模块名",example = "system") + @Schema(description = "模块名", example = "system") @NotBlank(message = "模块名不能为空") @Pattern(regexp = "[a-zA-Z][a-zA-Z0-9_]*", message = "模块名只能包含字母、数字和下划线,且以字母开头") private String moduleName; - @Schema(description = "包名",example = "com.youlai") + @Schema(description = "包名", example = "com.youlai") @NotBlank(message = "包名不能为空") @Pattern(regexp = "([a-zA-Z_][a-zA-Z0-9_]*\\.)*[a-zA-Z_][a-zA-Z0-9_]*", message = "包名格式不正确") private String packageName; - @Schema(description = "实体名",example = "User") + @Schema(description = "实体名", example = "User") @NotBlank(message = "实体名不能为空") @Pattern(regexp = "[a-zA-Z][a-zA-Z0-9]*", message = "实体名只能包含字母和数字,且以字母开头") private String entityName; - @Schema(description = "作者",example = "youlaitech") + @Schema(description = "作者", example = "youlaitech") private String author; - @Schema(description = "上级菜单ID",example = "1") + @Schema(description = "上级菜单ID", example = "1") private Long parentMenuId; @Schema(description = "字段配置列表") @@ -60,7 +60,7 @@ public class GenConfigForm { @Schema(description = "前端应用名") private String frontendAppName; - @Schema(description = "页面类型 classic|curd", example = "classic") + @Schema(description = "页面类型 classic|crud", example = "classic") private String pageType; @Schema(description = "要移除的表前缀,如: sys_", example = "sys_") diff --git a/src/main/java/com/youlai/boot/codegen/model/vo/GenConfigAiVO.java b/src/main/java/com/youlai/boot/codegen/model/vo/GenConfigAiVO.java new file mode 100644 index 00000000..86b04871 --- /dev/null +++ b/src/main/java/com/youlai/boot/codegen/model/vo/GenConfigAiVO.java @@ -0,0 +1,55 @@ +package com.youlai.boot.codegen.model.vo; + +import lombok.Data; + +import java.util.List; + +/** + * AI 推断的代码生成配置 + * + * @author Ray.Hao + * @since 0.1.0 + */ +@Data +public class GenConfigAiVO { + + /** 业务名 */ + private String businessName; + + /** 字段配置列表 */ + private List fieldConfigs; + + /** + * AI 推断的字段配置 + */ + @Data + public static class FieldAiVO { + + /** 列名,须与入参一致 */ + private String columnName; + + /** 字段描述 */ + private String fieldComment; + + /** 表单类型枚举名 */ + private String formType; + + /** 查询类型枚举名 */ + private String queryType; + + /** 是否必填 */ + private Integer isRequired; + + /** 是否在列表显示 */ + private Integer isShowInList; + + /** 是否在表单显示 */ + private Integer isShowInForm; + + /** 是否在查询条件显示 */ + private Integer isShowInQuery; + + /** 字典类型编码 */ + private String dictType; + } +} diff --git a/src/main/java/com/youlai/boot/codegen/property/CodegenProperties.java b/src/main/java/com/youlai/boot/codegen/property/CodegenProperties.java index ca8bd685..70e44d91 100644 --- a/src/main/java/com/youlai/boot/codegen/property/CodegenProperties.java +++ b/src/main/java/com/youlai/boot/codegen/property/CodegenProperties.java @@ -58,7 +58,7 @@ public class CodegenProperties { public static class TemplateConfig { /** - * 模板路径 (e.g. /templates/codegen/controller.java.vm) + * 模板路径,相对模板根 classpath:codegen (e.g. backend/controller.java.vm) */ private String templatePath; diff --git a/src/main/java/com/youlai/boot/codegen/service/GenTableService.java b/src/main/java/com/youlai/boot/codegen/service/GenTableService.java index 44dbaa7c..934f705a 100644 --- a/src/main/java/com/youlai/boot/codegen/service/GenTableService.java +++ b/src/main/java/com/youlai/boot/codegen/service/GenTableService.java @@ -36,4 +36,13 @@ public interface GenTableService extends IService { */ void deleteGenConfig(String tableName); + /** + * 用 AI 推断代码生成配置,未推断出的字段沿用原有默认值 + * + * @param tableName 表名 + * @param requirement 补充需求描述,可为空 + * @return 回填了 AI 推断结果的配置 + */ + GenConfigForm aiFillConfig(String tableName, String requirement); + } diff --git a/src/main/java/com/youlai/boot/codegen/service/impl/CodegenServiceImpl.java b/src/main/java/com/youlai/boot/codegen/service/impl/CodegenServiceImpl.java index dc09e96d..06d4502f 100644 --- a/src/main/java/com/youlai/boot/codegen/service/impl/CodegenServiceImpl.java +++ b/src/main/java/com/youlai/boot/codegen/service/impl/CodegenServiceImpl.java @@ -85,10 +85,10 @@ public class CodegenServiceImpl implements CodegenService { return templateConfig.getTemplatePath(); } if ("API".equals(templateName)) { - return "codegen/frontend/js/api.js.vm"; + return "frontend/js/api.js.vm"; } if ("VIEW".equals(templateName)) { - return "codegen/frontend/js/index.js.vue.vm"; + return "frontend/js/index.js.vue.vm"; } return templateConfig.getTemplatePath(); } @@ -352,14 +352,14 @@ public class CodegenServiceImpl implements CodegenService { bindMap.put("hasBigDecimal", hasBigDecimal); bindMap.put("hasRequiredField", hasRequiredField); - TemplateEngine templateEngine = TemplateUtil.createEngine(new TemplateConfig("templates", TemplateConfig.ResourceMode.CLASSPATH)); - // 根据 ui 选择不同的前端页面模板:默认 index.vue.vm;封装版使用 index.curd.vue.vm + TemplateEngine templateEngine = TemplateUtil.createEngine(new TemplateConfig("codegen", TemplateConfig.ResourceMode.CLASSPATH)); + // 根据 ui 选择不同的前端页面模板:默认 index.vue.vm;封装版使用 index.crud.vue.vm String path = templatePath; - if ("curd".equalsIgnoreCase(pageType)) { + if ("crud".equalsIgnoreCase(pageType)) { if (path.endsWith("index.js.vue.vm")) { - path = path.replace("index.js.vue.vm", "index.curd.js.vue.vm"); + path = path.replace("index.js.vue.vm", "index.crud.js.vue.vm"); } else if (path.endsWith("index.vue.vm")) { - path = path.replace("index.vue.vm", "index.curd.vue.vm"); + path = path.replace("index.vue.vm", "index.crud.vue.vm"); } } Template template = templateEngine.getTemplate(path); diff --git a/src/main/java/com/youlai/boot/codegen/service/impl/GenTableServiceImpl.java b/src/main/java/com/youlai/boot/codegen/service/impl/GenTableServiceImpl.java index 287218a4..aaf0261e 100644 --- a/src/main/java/com/youlai/boot/codegen/service/impl/GenTableServiceImpl.java +++ b/src/main/java/com/youlai/boot/codegen/service/impl/GenTableServiceImpl.java @@ -3,6 +3,7 @@ package com.youlai.boot.codegen.service.impl; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.youlai.boot.YouLaiBootApplication; @@ -20,8 +21,11 @@ import com.youlai.boot.codegen.model.vo.TableMetaVO; import com.youlai.boot.codegen.model.entity.GenTable; import com.youlai.boot.codegen.model.entity.GenTableColumn; import com.youlai.boot.codegen.model.form.GenConfigForm; +import com.youlai.boot.codegen.model.vo.GenConfigAiVO; import com.youlai.boot.codegen.service.GenTableService; import com.youlai.boot.codegen.service.GenTableColumnService; +import com.youlai.boot.support.ai.AiChatService; +import com.youlai.boot.support.ai.PromptLoader; import com.youlai.boot.system.model.dto.MenuCreateDTO; import com.youlai.boot.system.service.MenuService; import lombok.RequiredArgsConstructor; @@ -29,9 +33,14 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.util.ArrayList; +import java.util.Arrays; import java.util.Comparator; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; /** * 数据库服务实现类 @@ -43,10 +52,15 @@ import java.util.Objects; @RequiredArgsConstructor public class GenTableServiceImpl extends ServiceImpl implements GenTableService { + /** 系统提示词名称,对应 resources/prompts 下的相对路径 */ + private static final String SYSTEM_PROMPT_PATH = "codegen/system.md"; + private final DatabaseMapper databaseMapper; private final CodegenProperties codegenProperties; private final GenTableColumnService genTableColumnService; private final CodegenConverter codegenConverter; + private final AiChatService aiChatService; + private final PromptLoader promptLoader; @Value("${spring.profiles.active}") private String springProfilesActive; @@ -229,6 +243,137 @@ public class GenTableServiceImpl extends ServiceImpl i } } + //--------------------------------------------------- + // AI 辅助:未开启 AI 时调用返回业务异常 + //--------------------------------------------------- + /** + * 用 AI 推断代码生成配置 + * + * @param tableName 表名 eg: sys_user + * @param requirement 补充需求描述,可为空 + * @return 回填了 AI 推断结果的配置 + */ + @Override + public GenConfigForm aiFillConfig(String tableName, String requirement) { + GenConfigForm config = getGenTableFormData(tableName); + String systemPrompt = promptLoader.render(SYSTEM_PROMPT_PATH, buildAiPromptVariables()); + GenConfigAiVO aiConfig = aiChatService.chatEntity(systemPrompt, buildAiUserPrompt(config, requirement), GenConfigAiVO.class); + + if (StrUtil.isNotBlank(aiConfig.getBusinessName())) { + config.setBusinessName(aiConfig.getBusinessName().trim()); + } + applyAiFieldConfigs(config, aiConfig.getFieldConfigs()); + return config; + } + + /** + * 构造系统提示词的变量值,表单类型与查询类型清单取自枚举,新增枚举值无需改动提示词 + * + * @return 变量值 + */ + private Map buildAiPromptVariables() { + String formTypes = Arrays.stream(FormTypeEnum.values()) + .map(type -> type.name() + "(" + type.getLabel() + ")") + .collect(Collectors.joining("|")); + String queryTypes = Arrays.stream(QueryTypeEnum.values()) + .map(Enum::name) + .collect(Collectors.joining("|")); + return Map.of("formTypes", formTypes, "queryTypes", queryTypes); + } + + /** + * 按列名匹配回填字段配置 + * + * @param config 原配置 + * @param aiFields AI 返回的字段列表 + */ + private void applyAiFieldConfigs(GenConfigForm config, List aiFields) { + if (CollectionUtil.isEmpty(aiFields) || CollectionUtil.isEmpty(config.getFieldConfigs())) { + return; + } + Map aiFieldMap = aiFields.stream() + .filter(field -> StrUtil.isNotBlank(field.getColumnName())) + .collect(Collectors.toMap(GenConfigAiVO.FieldAiVO::getColumnName, Function.identity(), + (existing, replacement) -> existing, LinkedHashMap::new)); + + config.getFieldConfigs().forEach(field -> { + GenConfigAiVO.FieldAiVO aiField = aiFieldMap.get(field.getColumnName()); + if (aiField == null) { + return; + } + if (StrUtil.isNotBlank(aiField.getFieldComment())) { + field.setFieldComment(aiField.getFieldComment().trim()); + } + if (StrUtil.isNotBlank(aiField.getDictType())) { + field.setDictType(aiField.getDictType().trim()); + } + field.setFormType(parseAiEnum(aiField.getFormType(), FormTypeEnum::valueOf, field.getFormType())); + field.setQueryType(parseAiEnum(aiField.getQueryType(), QueryTypeEnum::valueOf, field.getQueryType())); + field.setIsRequired(resolveAiValue(aiField.getIsRequired(), field.getIsRequired())); + field.setIsShowInList(resolveAiValue(aiField.getIsShowInList(), field.getIsShowInList())); + field.setIsShowInForm(resolveAiValue(aiField.getIsShowInForm(), field.getIsShowInForm())); + field.setIsShowInQuery(resolveAiValue(aiField.getIsShowInQuery(), field.getIsShowInQuery())); + }); + } + + /** + * 解析枚举名,非法或未给出时沿用原值 + * + * @param name 枚举名 + * @param parser 枚举解析器 + * @param fallback 原值 + * @return 枚举或原值 + */ + private T parseAiEnum(String name, Function parser, T fallback) { + if (StrUtil.isBlank(name)) { + return fallback; + } + try { + return parser.apply(name.trim()); + } catch (IllegalArgumentException e) { + return fallback; + } + } + + /** + * AI 值缺失时沿用原值 + * + * @param aiValue AI 值 + * @param fallback 原值 + * @return 生效值 + */ + private T resolveAiValue(T aiValue, T fallback) { + return aiValue != null ? aiValue : fallback; + } + + /** + * 构造用户提示词 + * + * @param config 当前配置 + * @param requirement 补充需求描述 + * @return 用户提示词 + */ + private String buildAiUserPrompt(GenConfigForm config, String requirement) { + List> columns = new ArrayList<>(); + if (CollectionUtil.isNotEmpty(config.getFieldConfigs())) { + config.getFieldConfigs().forEach(field -> { + Map column = new LinkedHashMap<>(); + column.put("columnName", field.getColumnName()); + column.put("columnType", field.getColumnType()); + column.put("fieldComment", field.getFieldComment()); + column.put("isRequired", field.getIsRequired()); + columns.add(column); + }); + } + return """ + 表名:%s + 当前业务名:%s + 补充说明:%s + 字段列表: + %s + """.formatted(config.getTableName(), config.getBusinessName(), + StrUtil.blankToDefault(requirement, "无"), JSONUtil.toJsonStr(columns)); + } } diff --git a/src/main/java/com/youlai/boot/framework/web/util/IPUtils.java b/src/main/java/com/youlai/boot/framework/web/util/IPUtils.java index b43d2cab..c52683a7 100644 --- a/src/main/java/com/youlai/boot/framework/web/util/IPUtils.java +++ b/src/main/java/com/youlai/boot/framework/web/util/IPUtils.java @@ -25,7 +25,7 @@ import java.nio.file.StandardCopyOption; @Component public class IPUtils { - private static final String DB_PATH = "/data/ip2region.xdb"; + private static final String DB_PATH = "/ip2region/ip2region.xdb"; private static Searcher searcher; @PostConstruct diff --git a/src/main/java/com/youlai/boot/system/service/impl/RoleMenuServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/RoleMenuServiceImpl.java index 077b8881..1d3a33ab 100644 --- a/src/main/java/com/youlai/boot/system/service/impl/RoleMenuServiceImpl.java +++ b/src/main/java/com/youlai/boot/system/service/impl/RoleMenuServiceImpl.java @@ -28,7 +28,7 @@ import java.util.stream.Collectors; public class RoleMenuServiceImpl extends ServiceImpl implements RoleMenuService { /** - * 权限缓存TTL:与 Caffeine 菜单缓存 expireAfterWrite=600s 对齐。 + * 权限缓存 TTL(秒)。 * Hash 写入默认永不过期,直接改库(SQL 脚本/手工修数)不触发任何刷新点时会永久陈旧; * 加 TTL 后过期整 Key 消失,Read-Through 按角色逐个回源重建,实现最终一致自愈 */ diff --git a/src/main/resources/codegen.yml b/src/main/resources/codegen.yml index 9ce6490e..ff07ff42 100644 --- a/src/main/resources/codegen.yml +++ b/src/main/resources/codegen.yml @@ -17,45 +17,45 @@ codegen: ## 模板配置 templateConfigs: API: - templatePath: codegen/frontend/ts/api.ts.vm + templatePath: frontend/ts/api.ts.vm subpackageName: api extension: .ts API_TYPES: - templatePath: codegen/frontend/ts/api-types.ts.vm + templatePath: frontend/ts/api-types.ts.vm subpackageName: types extension: .ts VIEW: - templatePath: codegen/frontend/ts/index.vue.vm + templatePath: frontend/ts/index.vue.vm subpackageName: views extension: .vue Controller: - templatePath: codegen/backend/controller.java.vm + templatePath: backend/controller.java.vm subpackageName: controller Service: - templatePath: codegen/backend/service.java.vm + templatePath: backend/service.java.vm subpackageName: service ServiceImpl: - templatePath: codegen/backend/serviceImpl.java.vm + templatePath: backend/serviceImpl.java.vm subpackageName: service.impl Mapper: - templatePath: codegen/backend/mapper.java.vm + templatePath: backend/mapper.java.vm subpackageName: mapper MapperXml: - templatePath: codegen/backend/mapper.xml.vm + templatePath: backend/mapper.xml.vm subpackageName: mapper extension: .xml Converter: - templatePath: codegen/backend/converter.java.vm + templatePath: backend/converter.java.vm subpackageName: converter Query: - templatePath: codegen/backend/query.java.vm + templatePath: backend/query.java.vm subpackageName: model.query Form: - templatePath: codegen/backend/form.java.vm + templatePath: backend/form.java.vm subpackageName: model.form VO: - templatePath: codegen/backend/vo.java.vm + templatePath: backend/vo.java.vm subpackageName: model.vo Entity: - templatePath: codegen/backend/entity.java.vm + templatePath: backend/entity.java.vm subpackageName: model.entity diff --git a/src/main/resources/templates/codegen/backend/controller.java.vm b/src/main/resources/codegen/backend/controller.java.vm similarity index 100% rename from src/main/resources/templates/codegen/backend/controller.java.vm rename to src/main/resources/codegen/backend/controller.java.vm diff --git a/src/main/resources/templates/codegen/backend/converter.java.vm b/src/main/resources/codegen/backend/converter.java.vm similarity index 100% rename from src/main/resources/templates/codegen/backend/converter.java.vm rename to src/main/resources/codegen/backend/converter.java.vm diff --git a/src/main/resources/templates/codegen/backend/entity.java.vm b/src/main/resources/codegen/backend/entity.java.vm similarity index 100% rename from src/main/resources/templates/codegen/backend/entity.java.vm rename to src/main/resources/codegen/backend/entity.java.vm diff --git a/src/main/resources/templates/codegen/backend/form.java.vm b/src/main/resources/codegen/backend/form.java.vm similarity index 100% rename from src/main/resources/templates/codegen/backend/form.java.vm rename to src/main/resources/codegen/backend/form.java.vm diff --git a/src/main/resources/templates/codegen/backend/mapper.java.vm b/src/main/resources/codegen/backend/mapper.java.vm similarity index 100% rename from src/main/resources/templates/codegen/backend/mapper.java.vm rename to src/main/resources/codegen/backend/mapper.java.vm diff --git a/src/main/resources/templates/codegen/backend/mapper.xml.vm b/src/main/resources/codegen/backend/mapper.xml.vm similarity index 100% rename from src/main/resources/templates/codegen/backend/mapper.xml.vm rename to src/main/resources/codegen/backend/mapper.xml.vm diff --git a/src/main/resources/templates/codegen/backend/query.java.vm b/src/main/resources/codegen/backend/query.java.vm similarity index 100% rename from src/main/resources/templates/codegen/backend/query.java.vm rename to src/main/resources/codegen/backend/query.java.vm diff --git a/src/main/resources/templates/codegen/backend/service.java.vm b/src/main/resources/codegen/backend/service.java.vm similarity index 100% rename from src/main/resources/templates/codegen/backend/service.java.vm rename to src/main/resources/codegen/backend/service.java.vm diff --git a/src/main/resources/templates/codegen/backend/serviceImpl.java.vm b/src/main/resources/codegen/backend/serviceImpl.java.vm similarity index 100% rename from src/main/resources/templates/codegen/backend/serviceImpl.java.vm rename to src/main/resources/codegen/backend/serviceImpl.java.vm diff --git a/src/main/resources/templates/codegen/backend/vo.java.vm b/src/main/resources/codegen/backend/vo.java.vm similarity index 100% rename from src/main/resources/templates/codegen/backend/vo.java.vm rename to src/main/resources/codegen/backend/vo.java.vm diff --git a/src/main/resources/templates/codegen/frontend/js/api.js.vm b/src/main/resources/codegen/frontend/js/api.js.vm similarity index 100% rename from src/main/resources/templates/codegen/frontend/js/api.js.vm rename to src/main/resources/codegen/frontend/js/api.js.vm diff --git a/src/main/resources/templates/codegen/frontend/js/index.curd.js.vue.vm b/src/main/resources/codegen/frontend/js/index.crud.js.vue.vm similarity index 97% rename from src/main/resources/templates/codegen/frontend/js/index.curd.js.vue.vm rename to src/main/resources/codegen/frontend/js/index.crud.js.vue.vm index 2909cfe2..6a5f6d94 100644 --- a/src/main/resources/templates/codegen/frontend/js/index.curd.js.vue.vm +++ b/src/main/resources/codegen/frontend/js/index.crud.js.vue.vm @@ -1,7 +1,7 @@ @@ -67,7 +67,7 @@ defineOptions({ }); import ${entityName}API from "@/api/${moduleName}/${entityKebab}"; -import usePage from "@/components/CURD/usePage"; +import useCrudPage from "@/components/Crud/useCrudPage"; // 组合式 CRUD const { @@ -83,7 +83,7 @@ const { handleExportClick, handleSearchClick, handleFilterChange, -} = usePage(); +} = useCrudPage(); // 搜索配置 const searchConfig = reactive({ diff --git a/src/main/resources/templates/codegen/frontend/js/index.js.vue.vm b/src/main/resources/codegen/frontend/js/index.js.vue.vm similarity index 100% rename from src/main/resources/templates/codegen/frontend/js/index.js.vue.vm rename to src/main/resources/codegen/frontend/js/index.js.vue.vm diff --git a/src/main/resources/templates/codegen/frontend/ts/api-types.ts.vm b/src/main/resources/codegen/frontend/ts/api-types.ts.vm similarity index 100% rename from src/main/resources/templates/codegen/frontend/ts/api-types.ts.vm rename to src/main/resources/codegen/frontend/ts/api-types.ts.vm diff --git a/src/main/resources/templates/codegen/frontend/ts/api.ts.vm b/src/main/resources/codegen/frontend/ts/api.ts.vm similarity index 100% rename from src/main/resources/templates/codegen/frontend/ts/api.ts.vm rename to src/main/resources/codegen/frontend/ts/api.ts.vm diff --git a/src/main/resources/templates/codegen/frontend/ts/index.curd.vue.vm b/src/main/resources/codegen/frontend/ts/index.crud.vue.vm similarity index 95% rename from src/main/resources/templates/codegen/frontend/ts/index.curd.vue.vm rename to src/main/resources/codegen/frontend/ts/index.crud.vue.vm index c10a31f9..5db65ff9 100644 --- a/src/main/resources/templates/codegen/frontend/ts/index.curd.vue.vm +++ b/src/main/resources/codegen/frontend/ts/index.crud.vue.vm @@ -1,7 +1,7 @@ @@ -68,8 +68,8 @@ defineOptions({ import ${entityName}API from "@/api/${moduleName}/${entityKebab}"; import type { ${entityName}Form, ${entityName}QueryParams } from "@/api/${moduleName}/${entityKebab}"; -import type { IObject, IOperateData, IModalConfig, IContentConfig, ISearchConfig } from "@/components/CURD/types"; -import usePage from "@/components/CURD/usePage"; +import type { IObject, IOperateData, IModalConfig, ICrudTableConfig, ISearchConfig } from "@/components/Crud/types"; +import useCrudPage from "@/components/Crud/useCrudPage"; // 组合式 CRUD const { @@ -85,7 +85,7 @@ const { handleExportClick, handleSearchClick, handleFilterChange, -} = usePage(); +} = useCrudPage(); // 搜索配置 const searchConfig: ISearchConfig = reactive({ @@ -138,7 +138,7 @@ const searchConfig: ISearchConfig = reactive({ }); // 列表配置 -const contentConfig: IContentConfig<${entityName}QueryParams> = reactive({ +const contentConfig: ICrudTableConfig<${entityName}QueryParams> = reactive({ // 权限前缀 permPrefix: "${moduleName}:${entityKebab}", table: { diff --git a/src/main/resources/templates/codegen/frontend/ts/index.vue.vm b/src/main/resources/codegen/frontend/ts/index.vue.vm similarity index 100% rename from src/main/resources/templates/codegen/frontend/ts/index.vue.vm rename to src/main/resources/codegen/frontend/ts/index.vue.vm diff --git a/src/main/resources/templates/excel/用户导入模板.xlsx b/src/main/resources/excel/用户导入模板.xlsx similarity index 100% rename from src/main/resources/templates/excel/用户导入模板.xlsx rename to src/main/resources/excel/用户导入模板.xlsx diff --git a/src/main/resources/data/ip2region.xdb b/src/main/resources/ip2region/ip2region.xdb similarity index 100% rename from src/main/resources/data/ip2region.xdb rename to src/main/resources/ip2region/ip2region.xdb diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml index 9391b1a8..1c9ba4f2 100644 --- a/src/main/resources/logback-spring.xml +++ b/src/main/resources/logback-spring.xml @@ -47,17 +47,14 @@ - - - - - - + + + + - + - diff --git a/src/main/resources/mapper/system/DictItemMapper.xml b/src/main/resources/mapper/system/DictItemMapper.xml index ab6cfec2..2b4ad820 100644 --- a/src/main/resources/mapper/system/DictItemMapper.xml +++ b/src/main/resources/mapper/system/DictItemMapper.xml @@ -11,11 +11,13 @@ dict_code, label, value, + tag_type, sort, status FROM sys_dict_item + is_deleted = 0 AND ( value LIKE CONCAT('%', #{queryParams.keywords} ,'%') @@ -27,5 +29,6 @@ AND dict_code = #{queryParams.dictCode} + ORDER BY sort ASC, id ASC diff --git a/src/main/resources/mapper/system/DictMapper.xml b/src/main/resources/mapper/system/DictMapper.xml index 670694b4..2628f549 100644 --- a/src/main/resources/mapper/system/DictMapper.xml +++ b/src/main/resources/mapper/system/DictMapper.xml @@ -3,7 +3,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> - + + diff --git a/src/main/resources/mapper/system/MenuMapper.xml b/src/main/resources/mapper/system/MenuMapper.xml index 2f5afaae..63eb81ee 100644 --- a/src/main/resources/mapper/system/MenuMapper.xml +++ b/src/main/resources/mapper/system/MenuMapper.xml @@ -24,7 +24,6 @@ t1.visible, t1.redirect, t1.type, - t1.always_show, t1.keep_alive, t1.params FROM