From f9fb9b6eb95d1e7444f37b3b743a4be48435e7a9 Mon Sep 17 00:00:00 2001
From: "Ray.Hao" <1490493387@qq.com>
Date: Fri, 16 Jan 2026 23:23:14 +0800
Subject: [PATCH] =?UTF-8?q?refactor:=20=E4=BB=A3=E7=A0=81=E7=94=9F?=
=?UTF-8?q?=E6=88=90=E6=94=AF=E6=8C=81js=EF=BC=8C=E5=B7=B2=E7=9F=A5?=
=?UTF-8?q?=E9=97=AE=E9=A2=98=E4=BF=AE=E5=A4=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../config/property/CaptchaProperties.java | 2 +-
.../ai/controller/AiAssistantController.java | 36 +-
.../ai/service/AiAssistantRecordService.java | 14 -
.../impl/AiAssistantRecordServiceImpl.java | 22 -
.../codegen/controller/CodegenController.java | 10 +-
.../codegen/service/CodegenService.java | 4 +-
.../service/impl/CodegenServiceImpl.java | 93 +++-
.../file/controller/FileController.java | 2 +-
.../system/controller/ConfigController.java | 2 +-
.../boot/system/controller/LogController.java | 2 +-
.../system/controller/NoticeController.java | 2 +-
.../controller/StatisticsController.java | 2 +-
.../system/controller/UserController.java | 51 ++-
.../system/model/form/EmailUpdateForm.java | 6 +
.../system/model/form/MobileUpdateForm.java | 6 +
.../system/model/form/PasswordVerifyForm.java | 14 +
.../system/model/form/UserProfileForm.java | 14 -
.../boot/system/service/UserService.java | 16 +
.../system/service/impl/UserServiceImpl.java | 122 ++++-
.../resources/mapper/system/UserMapper.xml | 22 +-
.../resources/templates/codegen/api.js.vm | 63 +++
.../templates/codegen/index.curd.js.vue.vm | 369 ++++++++++++++++
.../templates/codegen/index.js.vue.vm | 417 ++++++++++++++++++
.../resources/templates/codegen/index.vue.vm | 4 +-
24 files changed, 1151 insertions(+), 144 deletions(-)
create mode 100644 src/main/java/com/youlai/boot/system/model/form/PasswordVerifyForm.java
create mode 100644 src/main/resources/templates/codegen/api.js.vm
create mode 100644 src/main/resources/templates/codegen/index.curd.js.vue.vm
create mode 100644 src/main/resources/templates/codegen/index.js.vue.vm
diff --git a/src/main/java/com/youlai/boot/config/property/CaptchaProperties.java b/src/main/java/com/youlai/boot/config/property/CaptchaProperties.java
index fae6c447..4067d51e 100644
--- a/src/main/java/com/youlai/boot/config/property/CaptchaProperties.java
+++ b/src/main/java/com/youlai/boot/config/property/CaptchaProperties.java
@@ -7,7 +7,7 @@ import org.springframework.stereotype.Component;
/**
* 验证码 属性配置
*
- * @author haoxr
+ * @author Ray.Hao
* @since 2023/11/24
*/
@Component
diff --git a/src/main/java/com/youlai/boot/platform/ai/controller/AiAssistantController.java b/src/main/java/com/youlai/boot/platform/ai/controller/AiAssistantController.java
index 040db52c..792ae741 100644
--- a/src/main/java/com/youlai/boot/platform/ai/controller/AiAssistantController.java
+++ b/src/main/java/com/youlai/boot/platform/ai/controller/AiAssistantController.java
@@ -10,27 +10,21 @@ import com.youlai.boot.platform.ai.model.query.AiAssistantQuery;
import com.youlai.boot.platform.ai.model.vo.AiAssistantRecordVO;
import com.youlai.boot.platform.ai.service.AiAssistantRecordService;
import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
-import java.util.Arrays;
-import java.util.List;
-import java.util.stream.Collectors;
-
/**
* AI 助手控制器
*
- * 负责 AI 命令的解析、执行、记录管理及回滚操作,
- * 表示一次 AI 助手完整的指令生命周期。
+ * 负责 AI 命令的解析、执行与记录查询。
*
* @author Ray.Hao
* @since 3.0.0
*/
-@Tag(name = "AI 助手接口")
+@Tag(name = "13.AI 助手接口")
@RestController
@RequestMapping("/api/v1/ai/assistant")
@RequiredArgsConstructor
@@ -81,29 +75,5 @@ public class AiAssistantController {
return PageResult.success(page);
}
- @Operation(summary = "删除 AI 命令记录")
- @DeleteMapping("/records/{ids}")
- public Result deleteRecords(
- @Parameter(description = "记录ID,多个以英文逗号(,)分割")
- @PathVariable String ids
- ) {
- List idList = Arrays.stream(ids.split(","))
- .filter(s -> s != null && !s.isBlank())
- .map(String::trim)
- .map(Long::valueOf)
- .collect(Collectors.toList());
-
- boolean removed = aiAssistantRecordService.deleteRecords(idList);
- return Result.judge(removed);
- }
-
- @Operation(summary = "撤销命令执行")
- @PostMapping("/records/{recordId}/rollback")
- public Result rollbackCommand(
- @Parameter(description = "记录ID")
- @PathVariable String recordId
- ) {
- aiAssistantRecordService.rollbackCommand(recordId);
- return Result.success();
- }
+ // 记录类接口按需扩展,当前开放 parse/execute/records
}
diff --git a/src/main/java/com/youlai/boot/platform/ai/service/AiAssistantRecordService.java b/src/main/java/com/youlai/boot/platform/ai/service/AiAssistantRecordService.java
index e542025c..38340bdb 100644
--- a/src/main/java/com/youlai/boot/platform/ai/service/AiAssistantRecordService.java
+++ b/src/main/java/com/youlai/boot/platform/ai/service/AiAssistantRecordService.java
@@ -49,18 +49,4 @@ public interface AiAssistantRecordService extends IService {
*/
IPage getRecordPage(AiAssistantQuery queryParams);
- /**
- * 删除 AI 助手行为记录。
- *
- * @param ids 记录ID列表
- * @return 是否删除成功
- */
- boolean deleteRecords(List ids);
-
- /**
- * 撤销命令执行
- *
- * @param logId 记录ID
- */
- void rollbackCommand(String logId);
}
diff --git a/src/main/java/com/youlai/boot/platform/ai/service/impl/AiAssistantRecordServiceImpl.java b/src/main/java/com/youlai/boot/platform/ai/service/impl/AiAssistantRecordServiceImpl.java
index 66b0a690..7791a323 100644
--- a/src/main/java/com/youlai/boot/platform/ai/service/impl/AiAssistantRecordServiceImpl.java
+++ b/src/main/java/com/youlai/boot/platform/ai/service/impl/AiAssistantRecordServiceImpl.java
@@ -299,26 +299,4 @@ public class AiAssistantRecordServiceImpl
return this.baseMapper.getRecordPage(page, queryParams);
}
- @Override
- public boolean deleteRecords(List ids) {
- if (ids == null || ids.isEmpty()) {
- return true;
- }
- return this.removeByIds(ids);
- }
-
- @Override
- public void rollbackCommand(String logId) {
- AiAssistantRecord commandRecord = this.getById(logId);
- if (commandRecord == null) {
- throw new RuntimeException("命令记录不存在");
- }
-
- if (commandRecord.getExecuteStatus() == null || commandRecord.getExecuteStatus() != 1) {
- throw new RuntimeException("只能撤销成功执行的命令");
- }
-
- log.info("撤销命令执行: logId={}, function={}", logId, commandRecord.getFunctionName());
- throw new UnsupportedOperationException("回滚功能尚未实现");
- }
}
diff --git a/src/main/java/com/youlai/boot/platform/codegen/controller/CodegenController.java b/src/main/java/com/youlai/boot/platform/codegen/controller/CodegenController.java
index de890da4..1f483e60 100644
--- a/src/main/java/com/youlai/boot/platform/codegen/controller/CodegenController.java
+++ b/src/main/java/com/youlai/boot/platform/codegen/controller/CodegenController.java
@@ -83,8 +83,9 @@ public class CodegenController {
@GetMapping("/{tableName}/preview")
@Log(value = "预览生成代码", module = LogModuleEnum.OTHER)
public Result> getTablePreviewData(@PathVariable String tableName,
- @RequestParam(value = "pageType", required = false, defaultValue = "classic") String pageType) {
- List list = codegenService.getCodegenPreviewData(tableName, pageType);
+ @RequestParam(value = "pageType", required = false, defaultValue = "classic") String pageType,
+ @RequestParam(value = "type", required = false, defaultValue = "ts") String type) {
+ List list = codegenService.getCodegenPreviewData(tableName, pageType, type);
return Result.success(list);
}
@@ -92,9 +93,10 @@ public class CodegenController {
@GetMapping("/{tableName}/download")
@Log(value = "下载代码", module = LogModuleEnum.OTHER)
public void downloadZip(HttpServletResponse response, @PathVariable String tableName,
- @RequestParam(value = "pageType", required = false, defaultValue = "classic") String pageType) {
+ @RequestParam(value = "pageType", required = false, defaultValue = "classic") String pageType,
+ @RequestParam(value = "type", required = false, defaultValue = "ts") String type) {
String[] tableNames = tableName.split(",");
- byte[] data = codegenService.downloadCode(tableNames, pageType);
+ byte[] data = codegenService.downloadCode(tableNames, pageType, type);
response.reset();
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(codegenProperties.getDownloadFileName(), StandardCharsets.UTF_8));
diff --git a/src/main/java/com/youlai/boot/platform/codegen/service/CodegenService.java b/src/main/java/com/youlai/boot/platform/codegen/service/CodegenService.java
index aa1f4a58..8a028158 100644
--- a/src/main/java/com/youlai/boot/platform/codegen/service/CodegenService.java
+++ b/src/main/java/com/youlai/boot/platform/codegen/service/CodegenService.java
@@ -29,12 +29,12 @@ public interface CodegenService {
* @param tableName 表名
* @return
*/
- List getCodegenPreviewData(String tableName, String pageType);
+ List getCodegenPreviewData(String tableName, String pageType, String type);
/**
* 下载代码
* @param tableNames 表名
* @return
*/
- byte[] downloadCode(String[] tableNames, String pageType);
+ byte[] downloadCode(String[] tableNames, String pageType, String type);
}
diff --git a/src/main/java/com/youlai/boot/platform/codegen/service/impl/CodegenServiceImpl.java b/src/main/java/com/youlai/boot/platform/codegen/service/impl/CodegenServiceImpl.java
index 20309ff5..6c2c6d44 100644
--- a/src/main/java/com/youlai/boot/platform/codegen/service/impl/CodegenServiceImpl.java
+++ b/src/main/java/com/youlai/boot/platform/codegen/service/impl/CodegenServiceImpl.java
@@ -70,6 +70,52 @@ public class CodegenServiceImpl implements CodegenService {
return databaseMapper.getTablePage(page, queryParams);
}
+ /**
+ * 解析前端模板路径
+ *
+ * @param templateName 模板标识
+ * @param templateConfig 模板配置
+ * @param frontendType 前端类型
+ * @return 模板路径
+ */
+ private String resolveFrontendTemplatePath(String templateName,
+ CodegenProperties.TemplateConfig templateConfig,
+ String frontendType) {
+ if (!"js".equals(frontendType)) {
+ return templateConfig.getTemplatePath();
+ }
+ if ("API".equals(templateName)) {
+ return "codegen/api.js.vm";
+ }
+ if ("VIEW".equals(templateName)) {
+ return "codegen/index.js.vue.vm";
+ }
+ if ("API_TYPES".equals(templateName)) {
+ return "codegen/api-types.js.vm";
+ }
+ return templateConfig.getTemplatePath();
+ }
+
+ /**
+ * 解析前端文件后缀
+ *
+ * @param templateName 模板标识
+ * @param templateConfig 模板配置
+ * @param frontendType 前端类型
+ * @return 文件后缀
+ */
+ private String resolveFrontendExtension(String templateName,
+ CodegenProperties.TemplateConfig templateConfig,
+ String frontendType) {
+ if (!"js".equals(frontendType)) {
+ return templateConfig.getExtension();
+ }
+ if ("API".equals(templateName) || "API_TYPES".equals(templateName)) {
+ return ".js";
+ }
+ return templateConfig.getExtension();
+ }
+
/**
* 获取预览生成代码
*
@@ -77,7 +123,7 @@ public class CodegenServiceImpl implements CodegenService {
* @return 预览数据
*/
@Override
- public List getCodegenPreviewData(String tableName, String pageType) {
+ public List getCodegenPreviewData(String tableName, String pageType, String type) {
List list = new ArrayList<>();
@@ -99,18 +145,25 @@ public class CodegenServiceImpl implements CodegenService {
// 遍历模板配置
Map templateConfigs = codegenProperties.getTemplateConfigs();
+ String frontendType = StrUtil.blankToDefault(type, "ts").toLowerCase();
for (Map.Entry templateConfigEntry : templateConfigs.entrySet()) {
CodegenPreviewVO previewVo = new CodegenPreviewVO();
CodegenProperties.TemplateConfig templateConfig = templateConfigEntry.getValue();
+ String templateName = templateConfigEntry.getKey();
+ if ("js".equals(frontendType) && "API_TYPES".equals(templateName)) {
+ continue;
+ }
+
+ String effectiveTemplatePath = resolveFrontendTemplatePath(templateName, templateConfig, frontendType);
+ String extension = resolveFrontendExtension(templateName, templateConfig, frontendType);
+
/* 1. 生成文件名 UserController */
// User Role Menu Dept
String entityName = genTable.getEntityName();
// Controller Service Mapper Entity
- String templateName = templateConfigEntry.getKey();
// .java .ts .vue
- String extension = templateConfig.getExtension();
// 文件名 UserController.java
String fileName = getFileName(entityName, templateName, extension);
@@ -131,7 +184,13 @@ public class CodegenServiceImpl implements CodegenService {
// 将模板文件中的变量替换为具体的值 生成代码内容
// 优先使用保存的 ui,没有则使用请求参数
String finalType = StrUtil.blankToDefault(genTable.getPageType(), pageType);
- String content = getCodeContent(templateConfig, genTable, fieldConfigs, finalType);
+ String content = getCodeContent(
+ effectiveTemplatePath,
+ templateConfig.getSubpackageName(),
+ genTable,
+ fieldConfigs,
+ finalType
+ );
previewVo.setContent(content);
list.add(previewVo);
@@ -232,7 +291,11 @@ public class CodegenServiceImpl implements CodegenService {
* @param pageType 前端页面类型
* @return 渲染后的代码内容
*/
- private String getCodeContent(CodegenProperties.TemplateConfig templateConfig, GenTable genTable, List fieldConfigs, String pageType) {
+ private String getCodeContent(String templatePath,
+ String subpackageName,
+ GenTable genTable,
+ List fieldConfigs,
+ String pageType) {
Map bindMap = new HashMap<>();
@@ -240,7 +303,7 @@ public class CodegenServiceImpl implements CodegenService {
bindMap.put("packageName", genTable.getPackageName());
bindMap.put("moduleName", genTable.getModuleName());
- bindMap.put("subpackageName", templateConfig.getSubpackageName());
+ bindMap.put("subpackageName", subpackageName);
bindMap.put("date", DateUtil.format(new Date(), "yyyy-MM-dd HH:mm"));
bindMap.put("entityName", entityName);
bindMap.put("tableName", genTable.getTableName());
@@ -282,9 +345,13 @@ public class CodegenServiceImpl implements CodegenService {
TemplateEngine templateEngine = TemplateUtil.createEngine(new TemplateConfig("templates", TemplateConfig.ResourceMode.CLASSPATH));
// 根据 ui 选择不同的前端页面模板:默认 index.vue.vm;封装版使用 index.curd.vue.vm
- String path = templateConfig.getTemplatePath();
- if ("curd".equalsIgnoreCase(pageType) && path.endsWith("index.vue.vm")) {
- path = path.replace("index.vue.vm", "index.curd.vue.vm");
+ String path = templatePath;
+ if ("curd".equalsIgnoreCase(pageType)) {
+ if (path.endsWith("index.js.vue.vm")) {
+ path = path.replace("index.js.vue.vm", "index.curd.js.vue.vm");
+ } else if (path.endsWith("index.vue.vm")) {
+ path = path.replace("index.vue.vm", "index.curd.vue.vm");
+ }
}
Template template = templateEngine.getTemplate(path);
@@ -299,13 +366,13 @@ public class CodegenServiceImpl implements CodegenService {
* @return zip 压缩文件字节数组
*/
@Override
- public byte[] downloadCode(String[] tableNames, String ui) {
+ public byte[] downloadCode(String[] tableNames, String ui, String type) {
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ZipOutputStream zip = new ZipOutputStream(outputStream)) {
// 遍历每个表名,生成对应的代码并压缩到 zip 文件中
for (String tableName : tableNames) {
- generateAndZipCode(tableName, zip, ui);
+ generateAndZipCode(tableName, zip, ui, type);
}
// 确保所有压缩数据写入输出流,避免数据残留在内存缓冲区引发的数据不完整
zip.finish();
@@ -324,8 +391,8 @@ public class CodegenServiceImpl implements CodegenService {
* @param zip 压缩文件输出流
* @param ui 页面类型
*/
- private void generateAndZipCode(String tableName, ZipOutputStream zip, String ui) {
- List codePreviewList = getCodegenPreviewData(tableName, ui);
+ private void generateAndZipCode(String tableName, ZipOutputStream zip, String ui, String type) {
+ List codePreviewList = getCodegenPreviewData(tableName, ui, type);
for (CodegenPreviewVO codePreview : codePreviewList) {
String fileName = codePreview.getFileName();
diff --git a/src/main/java/com/youlai/boot/platform/file/controller/FileController.java b/src/main/java/com/youlai/boot/platform/file/controller/FileController.java
index a6b63feb..55923b2a 100644
--- a/src/main/java/com/youlai/boot/platform/file/controller/FileController.java
+++ b/src/main/java/com/youlai/boot/platform/file/controller/FileController.java
@@ -19,7 +19,7 @@ import org.springframework.web.multipart.MultipartFile;
* @author Ray.Hao
* @since 2022/10/16
*/
-@Tag(name = "07.文件接口")
+@Tag(name = "10.文件接口")
@RestController
@RequestMapping("/api/v1/files")
@RequiredArgsConstructor
diff --git a/src/main/java/com/youlai/boot/system/controller/ConfigController.java b/src/main/java/com/youlai/boot/system/controller/ConfigController.java
index 280a4e32..113a6c59 100644
--- a/src/main/java/com/youlai/boot/system/controller/ConfigController.java
+++ b/src/main/java/com/youlai/boot/system/controller/ConfigController.java
@@ -28,7 +28,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
@Slf4j
@RestController
@RequiredArgsConstructor
-@Tag(name = "08.系统配置")
+@Tag(name = "07.系统配置")
@RequestMapping("/api/v1/configs")
public class ConfigController {
diff --git a/src/main/java/com/youlai/boot/system/controller/LogController.java b/src/main/java/com/youlai/boot/system/controller/LogController.java
index 89d0a85c..eae8cd31 100644
--- a/src/main/java/com/youlai/boot/system/controller/LogController.java
+++ b/src/main/java/com/youlai/boot/system/controller/LogController.java
@@ -16,7 +16,7 @@ import org.springframework.web.bind.annotation.*;
* @author Ray.Hao
* @since 2.10.0
*/
-@Tag(name = "10.日志接口")
+@Tag(name = "09.日志接口")
@RestController
@RequestMapping("/api/v1/logs")
@RequiredArgsConstructor
diff --git a/src/main/java/com/youlai/boot/system/controller/NoticeController.java b/src/main/java/com/youlai/boot/system/controller/NoticeController.java
index e2700d85..04bfeecf 100644
--- a/src/main/java/com/youlai/boot/system/controller/NoticeController.java
+++ b/src/main/java/com/youlai/boot/system/controller/NoticeController.java
@@ -25,7 +25,7 @@ import org.springframework.web.bind.annotation.*;
* @author youlaitech
* @since 2024-08-27 10:31
*/
-@Tag(name = "09.通知公告")
+@Tag(name = "08.通知公告")
@RestController
@RequestMapping("/api/v1/notices")
@RequiredArgsConstructor
diff --git a/src/main/java/com/youlai/boot/system/controller/StatisticsController.java b/src/main/java/com/youlai/boot/system/controller/StatisticsController.java
index 50184c5b..3014c230 100644
--- a/src/main/java/com/youlai/boot/system/controller/StatisticsController.java
+++ b/src/main/java/com/youlai/boot/system/controller/StatisticsController.java
@@ -18,7 +18,7 @@ import java.time.LocalDate;
* @author Ray.Hao
* @since 2025-12-15
*/
-@Tag(name = "11.统计分析")
+@Tag(name = "12.统计分析")
@RestController
@RequestMapping("/api/v1/statistics")
@RequiredArgsConstructor
diff --git a/src/main/java/com/youlai/boot/system/controller/UserController.java b/src/main/java/com/youlai/boot/system/controller/UserController.java
index 8705986c..a2909079 100644
--- a/src/main/java/com/youlai/boot/system/controller/UserController.java
+++ b/src/main/java/com/youlai/boot/system/controller/UserController.java
@@ -125,6 +125,17 @@ public class UserController {
return Result.judge(result);
}
+ @Operation(summary = "重置指定用户密码")
+ @PutMapping(value = "/{userId}/password/reset")
+ @PreAuthorize("@ss.hasPerm('sys:user:reset-password')")
+ public Result> resetUserPassword(
+ @Parameter(description = "用户ID") @PathVariable Long userId,
+ @RequestParam String password
+ ) {
+ boolean result = userService.resetUserPassword(userId, password);
+ return Result.judge(result);
+ }
+
@Operation(summary = "获取当前登录用户信息")
@GetMapping("/me")
@Log(value = "获取当前登录用户信息", module = LogModuleEnum.USER)
@@ -176,6 +187,13 @@ public class UserController {
.doWrite(exportUserList);
}
+ @Operation(summary = "获取用户下拉选项")
+ @GetMapping("/options")
+ public Result>> listUserOptions() {
+ List