This commit is contained in:
xiao12feng8 2026-01-30 18:23:58 +08:00
parent 30bcc9b0ef
commit 015d10b3b5
49 changed files with 659 additions and 115 deletions

View File

@ -31,7 +31,7 @@ npm run build:prod
## 说明
本项目基于RuoYi-Vue框架定制开发,已进行以下定制化改造:
- 项目包名com.ruoyi.* → com.ddnai.*
- 模块命名:ruoyi-* → ry-study-*
本项目基于通用后台管理框架定制开发,已进行以下定制化改造:
- 项目包名com.example.* → com.ddnai.*
- 模块命名:framework-* → ry-study-*
- 品牌标识:全面更换为"国语教育平台"

View File

@ -13,7 +13,7 @@
<description>国语教育平台</description>
<properties>
<ruoyi.version>1.0.0</ruoyi.version>
<app.version>1.0.0</app.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
@ -187,35 +187,35 @@
<dependency>
<groupId>com.ddnai</groupId>
<artifactId>ry-study-quartz</artifactId>
<version>${ruoyi.version}</version>
<version>${app.version}</version>
</dependency>
<!-- 代码生成-->
<dependency>
<groupId>com.ddnai</groupId>
<artifactId>ry-study-generator</artifactId>
<version>${ruoyi.version}</version>
<version>${app.version}</version>
</dependency>
<!-- 核心模块-->
<dependency>
<groupId>com.ddnai</groupId>
<artifactId>ry-study-framework</artifactId>
<version>${ruoyi.version}</version>
<version>${app.version}</version>
</dependency>
<!-- 系统模块-->
<dependency>
<groupId>com.ddnai</groupId>
<artifactId>ry-study-system</artifactId>
<version>${ruoyi.version}</version>
<version>${app.version}</version>
</dependency>
<!-- 通用工具-->
<dependency>
<groupId>com.ddnai</groupId>
<artifactId>ry-study-common</artifactId>
<version>${ruoyi.version}</version>
<version>${app.version}</version>
</dependency>
</dependencies>

View File

@ -11,7 +11,7 @@ import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.google.code.kaptcha.Producer;
import com.ddnai.common.config.RuoYiConfig;
import com.ddnai.common.config.AppConfig;
import com.ddnai.common.constant.CacheConstants;
import com.ddnai.common.constant.Constants;
import com.ddnai.common.core.domain.AjaxResult;
@ -61,7 +61,7 @@ public class CaptchaController
BufferedImage image = null;
// 生成验证码
String captchaType = RuoYiConfig.getCaptchaType();
String captchaType = AppConfig.getCaptchaType();
if ("math".equals(captchaType))
{
String capText = captchaProducerMath.createText();

View File

@ -13,7 +13,7 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.ddnai.common.config.RuoYiConfig;
import com.ddnai.common.config.AppConfig;
import com.ddnai.common.core.domain.AjaxResult;
import com.ddnai.common.utils.StringUtils;
import com.ddnai.common.utils.file.FileUploadUtils;
@ -52,7 +52,7 @@ public class CommonController
throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName));
}
String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);
String filePath = RuoYiConfig.getDownloadPath() + fileName;
String filePath = AppConfig.getDownloadPath() + fileName;
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
FileUtils.setAttachmentResponseHeader(response, realFileName);
@ -77,7 +77,7 @@ public class CommonController
try
{
// 上传文件路径必须带/upload否则数据库路径会错误
String filePath = RuoYiConfig.getProfile() + "/upload";
String filePath = AppConfig.getProfile() + "/upload";
// 上传并返回新文件名称
String fileName = FileUploadUtils.upload(filePath, file);
String baseUrl = serverConfig.getUrl();
@ -111,7 +111,7 @@ public class CommonController
try
{
// 上传文件路径必须带/upload否则数据库路径会错误
String filePath = RuoYiConfig.getProfile() + "/upload";
String filePath = AppConfig.getProfile() + "/upload";
String baseUrl = serverConfig.getUrl();
// 移除 /api 前缀文件访问不应该通过 /api 路径
if (baseUrl.endsWith("/api"))
@ -159,7 +159,7 @@ public class CommonController
throw new Exception(StringUtils.format("资源文件({})非法,不允许下载。 ", resource));
}
// 本地资源路径
String localPath = RuoYiConfig.getProfile();
String localPath = AppConfig.getProfile();
// 数据库资源地址
String downloadPath = localPath + FileUtils.stripPrefix(resource);
// 下载名称

View File

@ -14,7 +14,7 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import com.ddnai.common.annotation.Log;
import com.ddnai.common.config.RuoYiConfig;
import com.ddnai.common.config.AppConfig;
import com.ddnai.common.core.controller.BaseController;
import com.ddnai.common.core.domain.AjaxResult;
import com.ddnai.common.core.page.TableDataInfo;
@ -59,8 +59,8 @@ public class StudyScreenMonitorController extends BaseController
Long studentId = getUserId();
// 使用RuoYi的文件上传工具
String uploadPath = RuoYiConfig.getProfile() + "/upload/screenshots";
// 使用系统文件上传工具
String uploadPath = AppConfig.getProfile() + "/upload/screenshots";
String fileName = FileUploadUtils.upload(uploadPath, file, MimeTypeUtils.IMAGE_EXTENSION);
String baseUrl = serverConfig.getUrl();
// 移除 /api 前缀文件访问不应该通过 /api 路径

View File

@ -79,7 +79,7 @@ public class StudyVoiceEvaluationController extends BaseController
// 第1步保存上传的文件
testLog.append("【步骤1】保存上传文件... ");
String uploadPath = com.ddnai.common.config.RuoYiConfig.getProfile() + "/upload/test";
String uploadPath = com.ddnai.common.config.AppConfig.getProfile() + "/upload/test";
String[] audioExtensions = { "mp3", "m4a", "aac" };
String fileName = FileUploadUtils.upload(uploadPath, file, audioExtensions);
@ -87,7 +87,7 @@ public class StudyVoiceEvaluationController extends BaseController
testLog.append(" 上传返回路径: ").append(fileName).append("\n");
// 处理路径FileUploadUtils返回格式如 /profile/upload/xxx 或绝对路径
String profilePath = com.ddnai.common.config.RuoYiConfig.getProfile();
String profilePath = com.ddnai.common.config.AppConfig.getProfile();
String fullPath;
if (fileName.startsWith("/profile/")) {
// 相对路径需要替换/profile为实际路径
@ -329,7 +329,7 @@ public class StudyVoiceEvaluationController extends BaseController
}
// 上传音频文件
String uploadPath = com.ddnai.common.config.RuoYiConfig.getProfile() + "/upload/voice";
String uploadPath = com.ddnai.common.config.AppConfig.getProfile() + "/upload/voice";
String[] audioExtensions = { "mp3", "wav", "wma", "m4a", "aac" };
String fileName = FileUploadUtils.upload(uploadPath, file, audioExtensions);
@ -495,7 +495,7 @@ public class StudyVoiceEvaluationController extends BaseController
logger.info("✓ 音频格式: {}", format);
// 上传音频文件
String uploadPath = com.ddnai.common.config.RuoYiConfig.getProfile() + "/upload/voice";
String uploadPath = com.ddnai.common.config.AppConfig.getProfile() + "/upload/voice";
// 扩展支持的音频格式包含pcm
String[] audioExtensions = { "mp3", "wav", "wma", "m4a", "aac", "pcm" };
String fileName = FileUploadUtils.upload(uploadPath, file, audioExtensions);
@ -622,7 +622,7 @@ public class StudyVoiceEvaluationController extends BaseController
if (StringUtils.isEmpty(voiceEvaluation.getAudioPath()))
{
// 上传音频文件
String uploadPath = com.ddnai.common.config.RuoYiConfig.getProfile() + "/upload/voice";
String uploadPath = com.ddnai.common.config.AppConfig.getProfile() + "/upload/voice";
String[] audioExtensions = { "mp3", "wav", "wma", "m4a", "aac" };
String fileName = FileUploadUtils.upload(uploadPath, file, audioExtensions);
voiceEvaluation.setAudioPath(fileName);
@ -632,7 +632,7 @@ public class StudyVoiceEvaluationController extends BaseController
{
// 如果已有音频路径可以选择覆盖或提示
// 这里选择覆盖如果需要保留原音频可以注释掉下面的代码
String uploadPath = com.ddnai.common.config.RuoYiConfig.getProfile() + "/upload/voice";
String uploadPath = com.ddnai.common.config.AppConfig.getProfile() + "/upload/voice";
String[] audioExtensions = { "mp3", "wav", "wma", "m4a", "aac" };
String fileName = FileUploadUtils.upload(uploadPath, file, audioExtensions);
voiceEvaluation.setAudioPath(fileName);
@ -721,7 +721,7 @@ public class StudyVoiceEvaluationController extends BaseController
}
// 上传音频文件
String uploadPath = com.ddnai.common.config.RuoYiConfig.getProfile() + "/upload/voice";
String uploadPath = com.ddnai.common.config.AppConfig.getProfile() + "/upload/voice";
String[] audioExtensions = { "mp3", "wav", "wma", "m4a", "aac" };
String originalFileName = FileUploadUtils.upload(uploadPath, file, audioExtensions);
@ -731,7 +731,7 @@ public class StudyVoiceEvaluationController extends BaseController
String fileNameForRecognition = originalFileName;
// 🔄 自动转换音频格式为WAV百度API需要WAV格式
String profilePath = com.ddnai.common.config.RuoYiConfig.getProfile();
String profilePath = com.ddnai.common.config.AppConfig.getProfile();
String fullPath;
if (originalFileName.startsWith("/profile/")) {
fullPath = profilePath + originalFileName.substring(8);
@ -882,11 +882,11 @@ public class StudyVoiceEvaluationController extends BaseController
// 步骤1保存上传的文件
testLog.append("【步骤1】保存上传文件... ");
String uploadPath = com.ddnai.common.config.RuoYiConfig.getProfile() + "/upload/test";
String uploadPath = com.ddnai.common.config.AppConfig.getProfile() + "/upload/test";
String[] audioExtensions = { "mp3", "m4a", "aac" };
String fileName = FileUploadUtils.upload(uploadPath, file, audioExtensions);
String profilePath = com.ddnai.common.config.RuoYiConfig.getProfile();
String profilePath = com.ddnai.common.config.AppConfig.getProfile();
String fullPath;
if (fileName.startsWith("/profile/")) {
fullPath = profilePath + fileName.substring(8);

View File

@ -3,7 +3,7 @@ package com.ddnai.web.controller.system;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ddnai.common.config.RuoYiConfig;
import com.ddnai.common.config.AppConfig;
import com.ddnai.common.utils.StringUtils;
/**
@ -16,7 +16,7 @@ public class SysIndexController
{
/** 系统基础配置 */
@Autowired
private RuoYiConfig ruoyiConfig;
private AppConfig appConfig;
/**
* 访问首页提示语
@ -24,7 +24,7 @@ public class SysIndexController
@RequestMapping("/")
public String index()
{
return StringUtils.format("欢迎使用{}后台管理框架当前版本v{},请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion());
return StringUtils.format("{}服务运行正常当前版本v{}。", appConfig.getName(), appConfig.getVersion());
}
}

View File

@ -11,7 +11,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.ddnai.common.annotation.Log;
import com.ddnai.common.config.RuoYiConfig;
import com.ddnai.common.config.AppConfig;
import com.ddnai.common.core.controller.BaseController;
import com.ddnai.common.core.domain.AjaxResult;
import com.ddnai.common.core.domain.entity.SysUser;
@ -127,13 +127,13 @@ public class SysProfileController extends BaseController
if (!file.isEmpty())
{
LoginUser loginUser = getLoginUser();
String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION, true);
String avatar = FileUploadUtils.upload(AppConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION, true);
if (userService.updateUserAvatar(loginUser.getUserId(), avatar))
{
String oldAvatar = loginUser.getUser().getAvatar();
if (StringUtils.isNotEmpty(oldAvatar))
{
FileUtils.deleteFile(RuoYiConfig.getProfile() + FileUtils.stripPrefix(oldAvatar));
FileUtils.deleteFile(AppConfig.getProfile() + FileUtils.stripPrefix(oldAvatar));
}
AjaxResult ajax = AjaxResult.success();
ajax.put("imgUrl", avatar);

View File

@ -6,7 +6,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.ddnai.common.config.RuoYiConfig;
import com.ddnai.common.config.AppConfig;
import io.swagger.annotations.ApiOperation;
import io.swagger.models.auth.In;
import springfox.documentation.builders.ApiInfoBuilder;
@ -32,7 +32,7 @@ public class SwaggerConfig
{
/** 系统基础配置 */
@Autowired
private RuoYiConfig ruoyiConfig;
private AppConfig appConfig;
/** 是否开启swagger */
@Value("${swagger.enabled}")
@ -117,9 +117,9 @@ public class SwaggerConfig
// 描述
.description("描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...")
// 作者信息
.contact(new Contact(ruoyiConfig.getName(), null, null))
.contact(new Contact(appConfig.getName(), null, null))
// 版本
.version("版本号:" + ruoyiConfig.getVersion())
.version("版本号:" + appConfig.getVersion())
.build();
}
}

View File

@ -1,5 +1,5 @@
# 项目相关配置
ruoyi:
app:
# 名称
name: 国语教育平台
# 版本

View File

@ -1,3 +1,3 @@
Application Version: ${ruoyi.version}
Application Version: ${app.version}
Spring Boot Version: ${spring-boot.version}
// 国语教育平台 永不宕机 永无BUG //

View File

@ -9,8 +9,8 @@ import org.springframework.stereotype.Component;
* @author ddnai
*/
@Component
@ConfigurationProperties(prefix = "ruoyi")
public class RuoYiConfig
@ConfigurationProperties(prefix = "app")
public class AppConfig
{
/** 项目名称 */
private String name;
@ -67,7 +67,7 @@ public class RuoYiConfig
public void setProfile(String profile)
{
RuoYiConfig.profile = profile;
AppConfig.profile = profile;
}
public static boolean isAddressEnabled()
@ -77,7 +77,7 @@ public class RuoYiConfig
public void setAddressEnabled(boolean addressEnabled)
{
RuoYiConfig.addressEnabled = addressEnabled;
AppConfig.addressEnabled = addressEnabled;
}
public static String getCaptchaType() {
@ -85,7 +85,7 @@ public class RuoYiConfig
}
public void setCaptchaType(String captchaType) {
RuoYiConfig.captchaType = captchaType;
AppConfig.captchaType = captchaType;
}
/**

View File

@ -13,7 +13,7 @@ public class FileTypeUtils
/**
* 获取文件类型
* <p>
* 例如: ruoyi.txt, 返回: txt
* 例如: example.txt, 返回: txt
*
* @param file 文件名
* @return 后缀不含".")
@ -30,7 +30,7 @@ public class FileTypeUtils
/**
* 获取文件类型
* <p>
* 例如: ruoyi.txt, 返回: txt
* 例如: example.txt, 返回: txt
*
* @param fileName 文件名
* @return 后缀不含".")

View File

@ -6,7 +6,7 @@ import java.nio.file.Paths;
import java.util.Objects;
import org.apache.commons.io.FilenameUtils;
import org.springframework.web.multipart.MultipartFile;
import com.ddnai.common.config.RuoYiConfig;
import com.ddnai.common.config.AppConfig;
import com.ddnai.common.constant.Constants;
import com.ddnai.common.exception.file.FileNameLengthLimitExceededException;
import com.ddnai.common.exception.file.FileSizeLimitExceededException;
@ -36,7 +36,7 @@ public class FileUploadUtils
/**
* 默认上传的地址
*/
private static String defaultBaseDir = RuoYiConfig.getProfile();
private static String defaultBaseDir = AppConfig.getProfile();
public static void setDefaultBaseDir(String defaultBaseDir)
{
@ -170,7 +170,7 @@ public class FileUploadUtils
public static final String getPathFileName(String uploadDir, String fileName) throws IOException
{
int dirLastIndex = RuoYiConfig.getProfile().length() + 1;
int dirLastIndex = AppConfig.getProfile().length() + 1;
String currentDir = StringUtils.substring(uploadDir, dirLastIndex);
return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
}

View File

@ -14,7 +14,7 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import com.ddnai.common.config.RuoYiConfig;
import com.ddnai.common.config.AppConfig;
import com.ddnai.common.constant.Constants;
import com.ddnai.common.utils.DateUtils;
import com.ddnai.common.utils.StringUtils;
@ -74,7 +74,7 @@ public class FileUtils
*/
public static String writeImportBytes(byte[] data) throws IOException
{
return writeBytes(data, RuoYiConfig.getImportPath());
return writeBytes(data, AppConfig.getImportPath());
}
/**
@ -268,7 +268,7 @@ public class FileUtils
}
/**
* 获取文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi.png
* 获取文件名称 /profile/upload/2022/04/16/example.png -- example.png
*
* @param fileName 路径名称
* @return 没有文件路径的名称
@ -286,7 +286,7 @@ public class FileUtils
}
/**
* 获取不带后缀文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi
* 获取不带后缀文件名称 /profile/upload/2022/04/16/example.png -- example
*
* @param fileName 路径名称
* @return 没有文件路径和后缀的名称

View File

@ -9,7 +9,7 @@ import java.util.Arrays;
import org.apache.poi.util.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ddnai.common.config.RuoYiConfig;
import com.ddnai.common.config.AppConfig;
import com.ddnai.common.constant.Constants;
import com.ddnai.common.utils.StringUtils;
@ -79,7 +79,7 @@ public class ImageUtils
else
{
// 本机地址
String localPath = RuoYiConfig.getProfile();
String localPath = AppConfig.getProfile();
String downloadPath = localPath + StringUtils.substringAfter(url, Constants.RESOURCE_PREFIX);
in = new FileInputStream(downloadPath);
}

View File

@ -7,7 +7,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ddnai.common.config.RuoYiConfig;
import com.ddnai.common.config.AppConfig;
import com.ddnai.common.utils.StringUtils;
/**
@ -195,10 +195,10 @@ public class VideoDurationUtils
}
// 获取上传目录
String uploadPath = RuoYiConfig.getUploadPath();
String uploadPath = AppConfig.getUploadPath();
if (StringUtils.isEmpty(uploadPath))
{
uploadPath = RuoYiConfig.getProfile();
uploadPath = AppConfig.getProfile();
}
// 构建绝对路径

View File

@ -4,7 +4,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ddnai.common.config.RuoYiConfig;
import com.ddnai.common.config.AppConfig;
import com.ddnai.common.constant.Constants;
import com.ddnai.common.utils.StringUtils;
import com.ddnai.common.utils.http.HttpUtils;
@ -31,7 +31,7 @@ public class AddressUtils
{
return "内网IP";
}
if (RuoYiConfig.isAddressEnabled())
if (AppConfig.isAddressEnabled())
{
try
{

View File

@ -74,7 +74,7 @@ import com.ddnai.common.annotation.Excel;
import com.ddnai.common.annotation.Excel.ColumnType;
import com.ddnai.common.annotation.Excel.Type;
import com.ddnai.common.annotation.Excels;
import com.ddnai.common.config.RuoYiConfig;
import com.ddnai.common.config.AppConfig;
import com.ddnai.common.core.domain.AjaxResult;
import com.ddnai.common.core.text.Convert;
import com.ddnai.common.exception.UtilException;
@ -1459,7 +1459,7 @@ public class ExcelUtil<T>
*/
public String getAbsoluteFile(String filename)
{
String downloadPath = RuoYiConfig.getDownloadPath() + filename;
String downloadPath = AppConfig.getDownloadPath() + filename;
File desc = new File(downloadPath);
if (!desc.getParentFile().exists())
{

View File

@ -11,7 +11,7 @@ import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.ddnai.common.config.RuoYiConfig;
import com.ddnai.common.config.AppConfig;
import com.ddnai.common.constant.Constants;
import com.ddnai.framework.interceptor.RepeatSubmitInterceptor;
@ -31,7 +31,7 @@ public class ResourcesConfig implements WebMvcConfigurer
{
/** 本地文件上传路径 - 使用自定义解析器兼容 /upload/ 和无 /upload/ 两种路径 */
// 如果profile配置包含upload目录需要回退到父目录
String profilePath = RuoYiConfig.getProfile();
String profilePath = AppConfig.getProfile();
if (profilePath.endsWith("upload") || profilePath.endsWith("upload/") || profilePath.endsWith("upload\\")) {
profilePath = profilePath.substring(0, profilePath.lastIndexOf("upload"));
}

View File

@ -11,7 +11,7 @@ import com.ddnai.common.utils.StringUtils;
import com.ddnai.framework.security.context.PermissionContextHolder;
/**
* RuoYi首创 自定义权限实现ss取自SpringSecurity首字母
* 自定义权限实现ss取自SpringSecurity首字母
*
* @author ddnai
*/

View File

@ -255,7 +255,7 @@ public class GenController extends BaseController
response.reset();
response.addHeader("Access-Control-Allow-Origin", "*");
response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");
response.setHeader("Content-Disposition", "attachment; filename=\"ruoyi.zip\"");
response.setHeader("Content-Disposition", "attachment; filename=\"code.zip\"");
response.addHeader("Content-Length", "" + data.length);
response.setContentType("application/octet-stream; charset=UTF-8");
IOUtils.write(data, response.getOutputStream());

View File

@ -215,7 +215,7 @@ public class GenUtils
*/
public static String replaceText(String text)
{
return RegExUtils.replaceAll(text, "(?:表|若依)", "");
return RegExUtils.replaceAll(text, "(?:表)", "");
}
/**

View File

@ -22,7 +22,7 @@
//
// // quartz参数
// Properties prop = new Properties();
// prop.put("org.quartz.scheduler.instanceName", "RuoyiScheduler");
// prop.put("org.quartz.scheduler.instanceName", "AppScheduler");
// prop.put("org.quartz.scheduler.instanceId", "AUTO");
// // 线程池配置
// prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
@ -42,7 +42,7 @@
// prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
// factory.setQuartzProperties(prop);
//
// factory.setSchedulerName("RuoyiScheduler");
// factory.setSchedulerName("AppScheduler");
// // 延时启动
// factory.setStartupDelay(1);
// factory.setApplicationContextSchedulerContextKey("applicationContextKey");

View File

@ -357,7 +357,7 @@ public class StudyUserAuditServiceImpl implements IStudyUserAuditService
com.ddnai.common.core.domain.entity.SysDept rootDept = new com.ddnai.common.core.domain.entity.SysDept();
rootDept.setParentId(0L);
List<com.ddnai.common.core.domain.entity.SysDept> rootDepts = deptService.selectDeptList(rootDept);
Long parentId = 100L; // 默认父部门IDRuoYi默认根部门ID
Long parentId = 100L; // 默认父部门ID默认根部门ID
if (rootDepts != null && !rootDepts.isEmpty())
{
parentId = rootDepts.get(0).getDeptId();

View File

@ -222,7 +222,7 @@ public class VoiceEvaluationServiceImpl implements IVoiceEvaluationService
*/
private String convertToRealPath(String dbPath) {
// 如果已经是绝对路径包含盘符或以profile路径开头直接返回
String profilePath = com.ddnai.common.config.RuoYiConfig.getProfile();
String profilePath = com.ddnai.common.config.AppConfig.getProfile();
// Windows绝对路径判断包含冒号 D:
if (dbPath.contains(":") || dbPath.startsWith(profilePath)) {

View File

@ -1,5 +1,5 @@
import request from '@/utils/request'
import { parseStrEmpty } from "@/utils/ruoyi";
import { parseStrEmpty } from "@/utils/app";
// 查询用户列表
export function listUser(query) {

View File

@ -0,0 +1,288 @@
/**
* 通用css样式布局处理
* 国语教育平台
* Copyright (c) 2025 DDNAI
*/
/** 基础通用 **/
.pt5 {
padding-top: 5px;
}
.pr5 {
padding-right: 5px;
}
.pb5 {
padding-bottom: 5px;
}
.mt5 {
margin-top: 5px;
}
.mr5 {
margin-right: 5px;
}
.mb5 {
margin-bottom: 5px;
}
.mb8 {
margin-bottom: 8px;
}
.ml5 {
margin-left: 5px;
}
.mt10 {
margin-top: 10px;
}
.mr10 {
margin-right: 10px;
}
.mb10 {
margin-bottom: 10px;
}
.ml10 {
margin-left: 10px;
}
.mt20 {
margin-top: 20px;
}
.mr20 {
margin-right: 20px;
}
.mb20 {
margin-bottom: 20px;
}
.ml20 {
margin-left: 20px;
}
.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 {
font-family: inherit;
font-weight: 500;
line-height: 1.1;
color: inherit;
}
.el-message-box__status + .el-message-box__message{
word-break: break-word;
}
.el-dialog:not(.is-fullscreen) {
margin-top: 6vh !important;
}
.el-dialog__wrapper.scrollbar .el-dialog .el-dialog__body {
overflow: auto;
overflow-x: hidden;
max-height: 70vh;
padding: 10px 20px 0;
}
.el-table {
.el-table__header-wrapper, .el-table__fixed-header-wrapper {
th {
word-break: break-word;
background-color: #f8f8f9;
color: #515a6e;
height: 40px;
font-size: 13px;
}
}
.el-table__body-wrapper {
.el-button [class*="el-icon-"] + span {
margin-left: 1px;
}
}
}
/** 表单布局 **/
.form-header {
font-size: 15px;
color: #6379bb;
border-bottom: 1px solid #ddd;
margin: 8px 10px 25px 10px;
padding-bottom: 5px
}
/** 表格布局 **/
.pagination-container {
display: flex;
justify-content: flex-end;
margin-top: 20px;
}
/* tree border */
.tree-border {
margin-top: 5px;
border: 1px solid #e5e6e7;
background: #FFFFFF none;
border-radius: 4px;
}
@media (max-width: 768px) {
.pagination-container .el-pagination > .el-pagination__jump {
display: none !important;
}
.pagination-container .el-pagination > .el-pagination__sizes {
display: none !important;
}
}
.el-table .fixed-width .el-button--mini {
padding-left: 0;
padding-right: 0;
width: inherit;
}
/** 表格更多操作下拉样式 */
.el-table .el-dropdown-link,.el-table .el-dropdown-selfdefine {
cursor: pointer;
margin-left: 5px;
}
.el-table .el-dropdown, .el-icon-arrow-down {
font-size: 12px;
}
.el-tree-node__content > .el-checkbox {
margin-right: 8px;
}
.list-group-striped > .list-group-item {
border-left: 0;
border-right: 0;
border-radius: 0;
padding-left: 0;
padding-right: 0;
}
.list-group {
padding-left: 0px;
list-style: none;
}
.list-group-item {
border-bottom: 1px solid #e7eaec;
border-top: 1px solid #e7eaec;
margin-bottom: -1px;
padding: 11px 0px;
font-size: 13px;
}
.pull-right {
float: right !important;
}
.el-card__header {
padding: 14px 15px 7px;
min-height: 40px;
}
.el-card__body {
padding: 15px 20px 20px 20px;
}
.card-box {
margin-bottom: 10px;
}
/* button color */
.el-button--cyan.is-active,
.el-button--cyan:active {
background: #20B2AA;
border-color: #20B2AA;
color: #FFFFFF;
}
.el-button--cyan:focus,
.el-button--cyan:hover {
background: #48D1CC;
border-color: #48D1CC;
color: #FFFFFF;
}
.el-button--cyan {
background-color: #20B2AA;
border-color: #20B2AA;
color: #FFFFFF;
}
/* text color */
.text-navy {
color: #1ab394;
}
.text-primary {
color: inherit;
}
.text-success {
color: #1c84c6;
}
.text-info {
color: #23c6c8;
}
.text-warning {
color: #f8ac59;
}
.text-danger {
color: #ed5565;
}
.text-muted {
color: #888888;
}
/* image */
.img-circle {
border-radius: 50%;
}
.img-lg {
width: 120px;
height: 120px;
}
.avatar-upload-preview {
position: relative;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 200px;
height: 200px;
border-radius: 50%;
box-shadow: 0 0 4px #ccc;
overflow: hidden;
}
/* 拖拽列样式 */
.sortable-ghost {
opacity: .8;
color: #fff !important;
background: #42b983 !important;
}
.top-right-btn {
position: relative;
float: right;
}
/* 分割面板样式 */
.splitpanes.default-theme .splitpanes__pane {
background-color: #fff!important;
}

View File

@ -9,14 +9,6 @@
<template v-if="device!=='mobile'">
<search id="header-search" class="right-menu-item" />
<el-tooltip content="源码地址" effect="dark" placement="bottom">
<ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
</el-tooltip>
<el-tooltip content="文档地址" effect="dark" placement="bottom">
<ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" />
</el-tooltip>
<screenfull id="screenfull" class="right-menu-item hover-effect" />
<el-tooltip content="布局大小" effect="dark" placement="bottom">
@ -54,8 +46,6 @@ import Hamburger from '@/components/Hamburger'
import Screenfull from '@/components/Screenfull'
import SizeSelect from '@/components/SizeSelect'
import Search from '@/components/HeaderSearch'
import RuoYiGit from '@/components/RuoYi/Git'
import RuoYiDoc from '@/components/RuoYi/Doc'
export default {
emits: ['setLayout'],
@ -65,9 +55,7 @@ export default {
Hamburger,
Screenfull,
SizeSelect,
Search,
RuoYiGit,
RuoYiDoc
Search
},
computed: {
...mapGetters([

View File

@ -42,7 +42,7 @@ window.addEventListener('unhandledrejection', (event) => {
})
import '@/assets/styles/index.scss' // global css
import '@/assets/styles/ruoyi.scss' // ruoyi css
import '@/assets/styles/app.scss' // app css
import App from './App'
import store from './store'
import router from './router'
@ -55,7 +55,7 @@ import './permission' // permission control
import preventEnterSubmit from '@/mixins/preventEnterSubmit' // 全局防止回车键刷新
import { getDicts } from "@/api/system/dict/data"
import { getConfigKey } from "@/api/system/config"
import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, handleTree } from "@/utils/ruoyi"
import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, handleTree } from "@/utils/app"
// 分页组件
import Pagination from "@/components/Pagination"
// 自定义表格工具组件

View File

@ -3,7 +3,7 @@ import {Loading, Message} from 'element-ui'
import { saveAs } from 'file-saver'
import { getToken } from '@/utils/auth'
import errorCode from '@/utils/errorCode'
import { blobValidate } from "@/utils/ruoyi"
import { blobValidate } from "@/utils/app"
const baseURL = process.env.VUE_APP_BASE_API
let downloadLoadingInstance

View File

@ -3,7 +3,7 @@ import { MessageBox, } from 'element-ui'
import { login, logout, getInfo } from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth'
import { isHttp, isEmpty } from "@/utils/validate"
import { getFileUrl } from "@/utils/ruoyi"
import { getFileUrl } from "@/utils/app"
import defAva from '@/assets/images/profile.jpg'
const user = {

View File

@ -0,0 +1,268 @@
/**
* 通用js方法封装处理
* 国语教育平台
* Copyright (c) 2025 DDNAI
*/
// 日期格式化
export function parseTime(time, pattern) {
if (arguments.length === 0 || !time) {
return null
}
const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
let date
if (typeof time === 'object') {
date = time
} else {
if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
time = parseInt(time)
} else if (typeof time === 'string') {
time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\.[\d]{3}/gm), '')
}
if ((typeof time === 'number') && (time.toString().length === 10)) {
time = time * 1000
}
date = new Date(time)
}
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay()
}
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
let value = formatObj[key]
// Note: getDay() returns 0 on Sunday
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] }
if (result.length > 0 && value < 10) {
value = '0' + value
}
return value || 0
})
return time_str
}
// 表单重置
export function resetForm(refName) {
if (this.$refs[refName]) {
this.$refs[refName].resetFields()
}
}
// 添加日期范围
export function addDateRange(params, dateRange, propName) {
let search = params
search.params = typeof (search.params) === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {}
dateRange = Array.isArray(dateRange) ? dateRange : []
if (typeof (propName) === 'undefined') {
search.params['beginTime'] = dateRange[0]
search.params['endTime'] = dateRange[1]
} else {
search.params['begin' + propName] = dateRange[0]
search.params['end' + propName] = dateRange[1]
}
return search
}
// 回显数据字典
export function selectDictLabel(datas, value) {
if (value === undefined) {
return ""
}
var actions = []
Object.keys(datas).some((key) => {
if (datas[key].value == ('' + value)) {
actions.push(datas[key].label)
return true
}
})
if (actions.length === 0) {
actions.push(value)
}
return actions.join('')
}
// 回显数据字典(字符串、数组)
export function selectDictLabels(datas, value, separator) {
if (value === undefined || value.length ===0) {
return ""
}
if (Array.isArray(value)) {
value = value.join(",")
}
var actions = []
var currentSeparator = undefined === separator ? "," : separator
var temp = value.split(currentSeparator)
Object.keys(value.split(currentSeparator)).some((val) => {
var match = false
Object.keys(datas).some((key) => {
if (datas[key].value == ('' + temp[val])) {
actions.push(datas[key].label + currentSeparator)
match = true
}
})
if (!match) {
actions.push(temp[val] + currentSeparator)
}
})
return actions.join('').substring(0, actions.join('').length - 1)
}
// 字符串格式化(%s )
export function sprintf(str) {
var args = arguments, flag = true, i = 1
str = str.replace(/%s/g, function () {
var arg = args[i++]
if (typeof arg === 'undefined') {
flag = false
return ''
}
return arg
})
return flag ? str : ''
}
// 转换字符串undefined,null等转化为""
export function parseStrEmpty(str) {
if (!str || str == "undefined" || str == "null") {
return ""
}
return str
}
// 数据合并
export function mergeRecursive(source, target) {
for (var p in target) {
try {
if (target[p].constructor == Object) {
source[p] = mergeRecursive(source[p], target[p])
} else {
source[p] = target[p]
}
} catch (e) {
source[p] = target[p]
}
}
return source
}
/**
* 构造树型结构数据
* @param {*} data 数据源
* @param {*} id id字段 默认 'id'
* @param {*} parentId 父节点字段 默认 'parentId'
* @param {*} children 孩子节点字段 默认 'children'
*/
export function handleTree(data, id, parentId, children) {
let config = {
id: id || 'id',
parentId: parentId || 'parentId',
childrenList: children || 'children'
}
var childrenListMap = {}
var tree = []
for (let d of data) {
let id = d[config.id]
childrenListMap[id] = d
if (!d[config.childrenList]) {
d[config.childrenList] = []
}
}
for (let d of data) {
let parentId = d[config.parentId]
let parentObj = childrenListMap[parentId]
if (!parentObj) {
tree.push(d)
} else {
parentObj[config.childrenList].push(d)
}
}
return tree
}
/**
* 参数处理
* @param {*} params 参数
*/
export function tansParams(params) {
let result = ''
for (const propName of Object.keys(params)) {
const value = params[propName]
var part = encodeURIComponent(propName) + "="
if (value !== null && value !== "" && typeof (value) !== "undefined") {
if (typeof value === 'object') {
for (const key of Object.keys(value)) {
if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') {
let params = propName + '[' + key + ']'
var subPart = encodeURIComponent(params) + "="
result += subPart + encodeURIComponent(value[key]) + "&"
}
}
} else {
result += part + encodeURIComponent(value) + "&"
}
}
}
return result
}
// 返回项目路径
export function getNormalPath(p) {
if (p.length === 0 || !p || p == 'undefined') {
return p
}
let res = p.replace('//', '/')
if (res[res.length - 1] === '/') {
return res.slice(0, res.length - 1)
}
return res
}
// 验证是否为blob格式
export function blobValidate(data) {
return data.type !== 'application/json'
}
/**
* 获取文件访问的基础URL不包含/api前缀
* 文件访问通过代理转发到后端服务器
*/
export function getFileBaseUrl() {
// 优先使用环境变量 VUE_APP_FILE_BASE_URL
let fileBaseUrl = process.env.VUE_APP_FILE_BASE_URL
if (!fileBaseUrl) {
// 开发环境和生产环境都使用相对路径通过代理或Nginx转发
// 开发环境vue.config.js 中配置了 /profile 代理到后端
// 生产环境Nginx 配置 /profile 代理到后端
fileBaseUrl = ''
} else {
// 移除可能的引号
if (typeof fileBaseUrl === 'string') {
fileBaseUrl = fileBaseUrl.trim().replace(/^['"]|['"]$/g, '')
}
}
return fileBaseUrl
}
/**
* 获取完整的文件访问URL
* @param {string} filePath 文件路径/profile/upload/2025/11/17/xxx.mp4
* @returns {string} 完整的文件URL
*/
export function getFileUrl(filePath) {
if (!filePath) return ''
// 如果已经是完整的URL直接返回
if (filePath.startsWith('http://') || filePath.startsWith('https://')) {
return filePath
}
// 确保路径以 / 开头
const path = filePath.startsWith('/') ? filePath : '/' + filePath
return getFileBaseUrl() + path
}

View File

@ -1,5 +1,5 @@
import Vue from 'vue'
import { mergeRecursive } from "@/utils/ruoyi"
import { mergeRecursive } from "@/utils/app"
import DictMeta from './DictMeta'
import DictData from './DictData'

View File

@ -1,4 +1,4 @@
import { mergeRecursive } from "@/utils/ruoyi"
import { mergeRecursive } from "@/utils/app"
import DictOptions from './DictOptions'
/**

View File

@ -1,4 +1,4 @@
import { mergeRecursive } from "@/utils/ruoyi"
import { mergeRecursive } from "@/utils/app"
import dictConverter from './DictConverter'
export const options = {

View File

@ -1,4 +1,4 @@
import { parseTime } from './ruoyi'
import { parseTime } from './app'
/**
* 表格时间格式化

View File

@ -3,7 +3,7 @@ import { Notification, MessageBox, Message, Loading } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'
import errorCode from '@/utils/errorCode'
import { tansParams, blobValidate } from "@/utils/ruoyi"
import { tansParams, blobValidate } from "@/utils/app"
import cache from '@/plugins/cache'
import { saveAs } from 'file-saver'

View File

@ -179,7 +179,7 @@
<el-tooltip placement="top">
<div slot="content">
Bean调用示例ryTask.ryParams('ry')
<br />Class类调用示例com.ruoyi.quartz.task.RyTask.ryParams('ry')
<br />Class类调用示例com.example.quartz.task.Task.ryParams('ry')
<br />参数说明支持字符串布尔类型长整型浮点型整型
</div>
<i class="el-icon-question"></i>

View File

@ -342,7 +342,7 @@ import { listSubject } from "@/api/study/subject";
import { listCourse } from "@/api/study/course";
import { listClass } from "@/api/study/class";
import { getAllStudents, getClassStudents } from "@/api/study/classUser";
import { getFileUrl as getFileUrlUtil } from "@/utils/ruoyi";
import { getFileUrl as getFileUrlUtil } from "@/utils/app";
export default {
name: "Courseware",

View File

@ -176,7 +176,7 @@
<script>
import { getScore } from "@/api/study/exam";
import { parseTime } from "@/utils/ruoyi";
import { parseTime } from "@/utils/app";
export default {
name: "ScoreDetail",

View File

@ -276,7 +276,7 @@ export default {
//
let students = [];
if (response && response.code === 200) {
// RuoYi{code: 200, data: [...]}
// {code: 200, data: [...]}
students = response.data || [];
} else if (response && response.data) {
// data

View File

@ -332,7 +332,7 @@
import { listVoiceEvaluation, getVoiceEvaluation, delVoiceEvaluation, exportVoiceEvaluation,
listVoiceEvaluationContent, addVoiceEvaluationContent, updateVoiceEvaluationContent,
getVoiceEvaluationContent, delVoiceEvaluationContent } from "@/api/study/voiceEvaluation";
import { getFileUrl } from "@/utils/ruoyi";
import { getFileUrl } from "@/utils/app";
export default {
name: "VoiceEvaluation",

View File

@ -58,7 +58,7 @@ import store from "@/store"
import { VueCropper } from "vue-cropper"
import { uploadAvatar } from "@/api/system/user"
import { debounce } from '@/utils'
import { getFileUrl } from "@/utils/ruoyi"
import { getFileUrl } from "@/utils/app"
export default {
components: { VueCropper },

View File

@ -24,7 +24,7 @@
<el-form-item prop="packageName">
<span slot="label">
生成包路径
<el-tooltip content="生成在哪个java包下例如 com.ruoyi.system" placement="top">
<el-tooltip content="生成在哪个java包下例如 com.example.system" placement="top">
<i class="el-icon-question"></i>
</el-tooltip>
</span>

View File

@ -266,7 +266,7 @@ export default {
this.$modal.msgSuccess("成功生成到自定义路径:" + row.genPath)
})
} else {
this.$download.zip("/tool/gen/batchGenCode?tables=" + tableNames, "ruoyi.zip")
this.$download.zip("/tool/gen/batchGenCode?tables=" + tableNames, "code.zip")
}
},
/** 同步数据库操作 */

View File

@ -137,7 +137,7 @@ export default {
console.log('准备注册,数据:', registerData)
// RuoYi
//
// token
const response = await request.post('/register', registerData, {
loading: false, // 使loading

View File

@ -7,7 +7,7 @@ import request from './request.js'
export default {
/**
* 登录
* 适配RuoYi登录接口仅用户名登录不需要密码
* 适配后端登录接口仅用户名登录不需要密码
* @param {string} username - 用户名
* @param {string} userType - 用户类型固定为student
* @param {string} code - 验证码
@ -15,7 +15,7 @@ export default {
*/
async login(username, userType = 'student', code = '', uuid = '') {
try {
// RuoYi登录接口POST /login
// 登录接口POST /login
// 登录接口不需要token需要明确指定
// 仅用户名登录,不传密码
const response = await request.post('/login', {
@ -30,7 +30,7 @@ export default {
}
})
// RuoYi响应格式:{ code: 200, msg: "操作成功", token: "xxx" }
// 响应格式:{ code: 200, msg: "操作成功", token: "xxx" }
if (response.code === 200 && response.token) {
// 保存token
uni.setStorageSync(config.TOKEN_KEY, response.token)
@ -115,11 +115,11 @@ export default {
/**
* 登出
* 适配RuoYi登出接口
* 适配后端登出接口
*/
async logout() {
try {
// RuoYi登出接口POST /logout
// 登出接口POST /logout
await request.post('/logout')
} catch (error) {
console.error('登出失败:', error)

View File

@ -83,11 +83,11 @@ class Request {
// 检查是否需要token注册接口等不需要token
const isToken = requestConfig.header && requestConfig.header.isToken === false
// 添加tokenRuoYi使用Authorization Bearer格式)
// 添加token后端使用 Authorization Bearer 格式)
const token = uni.getStorageSync('token')
if (token && !isToken) {
requestConfig.header = requestConfig.header || {}
// RuoYi使用Authorization Bearer格式
// 后端使用 Authorization Bearer 格式
requestConfig.header['Authorization'] = `Bearer ${token}`
}
@ -127,7 +127,7 @@ class Request {
}
}
// 业务状态码检查(RuoYi使用code字段)
// 业务状态码检查(后端使用 code 字段)
if (responseData.code === 401) {
// Token过期清除登录信息并跳转到登录页
uni.removeStorageSync('token')
@ -139,7 +139,7 @@ class Request {
return
}
// RuoYi响应格式:{ code: 200, msg: "操作成功", data: {...} }
// 响应格式:{ code: 200, msg: "操作成功", data: {...} }
if (responseData.code !== 200) {
reject(new Error(responseData.msg || responseData.message || '请求失败'))
return
@ -354,7 +354,7 @@ class Request {
name: options.name || 'file',
formData: formData,
header: {
'Authorization': token ? `Bearer ${token}` : '' // RuoYi使用Authorization Bearer格式
'Authorization': token ? `Bearer ${token}` : '' // 后端使用 Authorization Bearer 格式
},
success: (response) => {
try {