mirror of
https://github.com/dromara/RuoYi-Vue-Plus.git
synced 2026-07-17 18:16:11 +00:00
fix 修复 一些发现的小bug
- 短信验证码限流 key 改为 #phoneNumber,验证码改为发送成功后再写 Redis,失败消息避免 NPE - 邮箱验证码改为邮件发送成功后再写 Redis - ValidatorUtils 增加 NPE 校验 - 部门导入路径不存在时明确报错,不再静默转成 null - 重复提交切面完整扫描数组、集合、Map 中的过滤对象 - Excel 级联下拉补齐一级选项的 Excel 名称规则校验和重复校验
This commit is contained in:
@@ -58,7 +58,7 @@ public class CaptchaController {
|
||||
* @param phoneNumber 用户手机号
|
||||
* @return 操作结果
|
||||
*/
|
||||
@RateLimiter(key = "#phonenumber", time = 60, count = 1)
|
||||
@RateLimiter(key = "#phoneNumber", time = 60, count = 1)
|
||||
@GetMapping("/resource/sms/code")
|
||||
public R<Void> smsCode(@NotBlank(message = "{user.phonenumber.not.blank}") String phoneNumber) {
|
||||
if (!RegexValidator.isMobile(phoneNumber)) {
|
||||
@@ -66,7 +66,6 @@ public class CaptchaController {
|
||||
}
|
||||
String key = GlobalConstants.CAPTCHA_CODE_KEY + phoneNumber;
|
||||
String code = RandomUtil.randomNumbers(4);
|
||||
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
|
||||
// 验证码模板id 自行处理 (查数据库或写死均可)
|
||||
String templateId = "";
|
||||
LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
|
||||
@@ -75,8 +74,10 @@ public class CaptchaController {
|
||||
SmsResponse smsResponse = smsBlend.sendMessage(phoneNumber, templateId, map);
|
||||
if (!smsResponse.isSuccess()) {
|
||||
log.error("验证码短信发送异常 => {}", smsResponse);
|
||||
return R.fail(smsResponse.getData().toString());
|
||||
Object data = smsResponse.getData();
|
||||
return R.fail(data == null ? "验证码短信发送失败" : data.toString());
|
||||
}
|
||||
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@@ -107,13 +108,13 @@ public class CaptchaController {
|
||||
public void emailCodeImpl(String email) {
|
||||
String key = GlobalConstants.CAPTCHA_CODE_KEY + email;
|
||||
String code = RandomUtil.randomNumbers(4);
|
||||
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
|
||||
try {
|
||||
MailBuilder.of()
|
||||
.to(email)
|
||||
.subject("登录验证码")
|
||||
.text("您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。")
|
||||
.send();
|
||||
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
|
||||
} catch (Exception e) {
|
||||
log.error("验证码短信发送异常 => {}", e.getMessage());
|
||||
throw new ServiceException(e.getMessage());
|
||||
|
||||
+3
@@ -26,6 +26,9 @@ public class ValidatorUtils {
|
||||
* @throws ConstraintViolationException 如果校验不通过,则抛出参数校验异常
|
||||
*/
|
||||
public static <T> void validate(T object, Class<?>... groups) {
|
||||
if (object == null) {
|
||||
throw new RuntimeException("请求参数不能为空");
|
||||
}
|
||||
Set<ConstraintViolation<T>> validate = VALID.validate(object, groups);
|
||||
if (!validate.isEmpty()) {
|
||||
throw new ConstraintViolationException("参数校验异常", validate);
|
||||
|
||||
+25
-4
@@ -51,6 +51,9 @@ public class DropDownOptions {
|
||||
* 分隔符
|
||||
*/
|
||||
private static final String DELIMITER = "_";
|
||||
private static final String OPTION_PART_REGEX = "^[A-Za-z0-9\\u4e00-\\u9fa5]+$";
|
||||
private static final String EXCEL_NAME_REGEX = "^[A-Za-z_\\u4e00-\\u9fa5][A-Za-z0-9_\\u4e00-\\u9fa5]*$";
|
||||
private static final String CELL_REFERENCE_REGEX = "^[A-Za-z]{1,3}[1-9][0-9]*$";
|
||||
|
||||
/**
|
||||
* 创建只有一级的下拉选
|
||||
@@ -69,10 +72,9 @@ public class DropDownOptions {
|
||||
*/
|
||||
public static String createOptionValue(Object... vars) {
|
||||
StringBuilder stringBuffer = new StringBuilder();
|
||||
String regex = "^[\\S\\d\\u4e00-\\u9fa5]+$";
|
||||
for (int i = 0; i < vars.length; i++) {
|
||||
String var = StrUtil.trimToEmpty(Convert.toStr(vars[i]));
|
||||
if (!var.matches(regex)) {
|
||||
if (!var.matches(OPTION_PART_REGEX)) {
|
||||
throw new ServiceException("选项数据不符合规则,仅允许使用中英文字符以及数字");
|
||||
}
|
||||
stringBuffer.append(var);
|
||||
@@ -81,10 +83,29 @@ public class DropDownOptions {
|
||||
stringBuffer.append(DELIMITER);
|
||||
}
|
||||
}
|
||||
if (stringBuffer.toString().matches("^\\d_*$")) {
|
||||
String optionValue = stringBuffer.toString();
|
||||
validateOptionValue(optionValue);
|
||||
return optionValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验级联下拉选项值是否可作为 Excel 名称管理器名称。
|
||||
*
|
||||
* @param optionValue 选项值
|
||||
*/
|
||||
public static void validateOptionValue(String optionValue) {
|
||||
if (StrUtil.isBlank(optionValue)) {
|
||||
throw new ServiceException("选项数据不能为空");
|
||||
}
|
||||
if (optionValue.matches("^[0-9].*")) {
|
||||
throw new ServiceException("禁止以数字开头");
|
||||
}
|
||||
return stringBuffer.toString();
|
||||
if (!optionValue.matches(EXCEL_NAME_REGEX)) {
|
||||
throw new ServiceException("选项数据不符合Excel名称规则,仅允许中英文、数字或下划线,且不能以数字开头");
|
||||
}
|
||||
if (optionValue.matches(CELL_REFERENCE_REGEX)) {
|
||||
throw new ServiceException("选项数据不能为Excel单元格引用");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+14
@@ -202,6 +202,7 @@ public class ExcelDownHandler implements SheetWriteHandler {
|
||||
if (CollUtil.isEmpty(firstOptions)) {
|
||||
return;
|
||||
}
|
||||
validateLinkedOptionNames(firstOptions);
|
||||
Map<String, List<String>> secoundOptionsMap = new HashMap<>();
|
||||
options.getNextOptions().forEach((k, v) -> secoundOptionsMap.put(k, new ArrayList<>(v)));
|
||||
|
||||
@@ -313,6 +314,19 @@ public class ExcelDownHandler implements SheetWriteHandler {
|
||||
currentLinkedOptionsSheetIndex++;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验级联下拉一级选项可作为 Excel 名称,且不能重复。
|
||||
*/
|
||||
private void validateLinkedOptionNames(List<String> firstOptions) {
|
||||
Set<String> names = new HashSet<>();
|
||||
for (String firstOption : firstOptions) {
|
||||
DropDownOptions.validateOptionValue(firstOption);
|
||||
if (!names.add(firstOption)) {
|
||||
throw new ServiceException("级联下拉一级选项重复:" + firstOption);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <h2>额外表格形式的普通下拉框</h2>
|
||||
* 由于下拉框可选值数量过多,为提升Excel打开效率,使用额外表格形式做下拉
|
||||
|
||||
+18
-3
@@ -24,6 +24,7 @@ import org.dromara.common.redis.utils.RedisUtils;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
@@ -150,16 +151,30 @@ public class RepeatSubmitAspect {
|
||||
public boolean isFilterObject(final Object o) {
|
||||
Class<?> clazz = o.getClass();
|
||||
if (clazz.isArray()) {
|
||||
return MultipartFile.class.isAssignableFrom(clazz.getComponentType());
|
||||
if (MultipartFile.class.isAssignableFrom(clazz.getComponentType())) {
|
||||
return true;
|
||||
}
|
||||
int length = Array.getLength(o);
|
||||
for (int i = 0; i < length; i++) {
|
||||
Object value = Array.get(o, i);
|
||||
if (ObjectUtil.isNotNull(value) && isFilterObject(value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else if (Collection.class.isAssignableFrom(clazz)) {
|
||||
Collection collection = (Collection) o;
|
||||
for (Object value : collection) {
|
||||
return value instanceof MultipartFile;
|
||||
if (ObjectUtil.isNotNull(value) && isFilterObject(value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (Map.class.isAssignableFrom(clazz)) {
|
||||
Map map = (Map) o;
|
||||
for (Object value : map.values()) {
|
||||
return value instanceof MultipartFile;
|
||||
if (ObjectUtil.isNotNull(value) && isFilterObject(value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
|
||||
|
||||
+6
-1
@@ -10,6 +10,7 @@ import org.apache.fesod.sheet.metadata.GlobalConfiguration;
|
||||
import org.apache.fesod.sheet.metadata.data.ReadCellData;
|
||||
import org.apache.fesod.sheet.metadata.data.WriteCellData;
|
||||
import org.apache.fesod.sheet.metadata.property.ExcelContentProperty;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.core.utils.TreeBuildUtils;
|
||||
@@ -93,7 +94,11 @@ public class DeptExcelConverter implements Converter<Long> {
|
||||
if (StringUtils.isBlank(deptName)) {
|
||||
return null;
|
||||
}
|
||||
return getDeptMaps().nameToId().get(deptName);
|
||||
Long deptId = getDeptMaps().nameToId().get(deptName);
|
||||
if (deptId == null) {
|
||||
throw new ServiceException("部门不存在:" + deptName);
|
||||
}
|
||||
return deptId;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user