(Arrays.asList(info.getAlarmEmail().split(",")));
- for (String email: emailSet) {
+ for (String email : emailSet) {
// make mail
try {
@@ -88,28 +89,28 @@ public class EmailJobAlarm implements JobAlarm {
*
* @return
*/
- private static final String loadEmailJobAlarmTemplate(){
+ private static final String loadEmailJobAlarmTemplate() {
String mailBodyTemplate = "" + I18nUtil.getString("jobconf_monitor_detail") + ":" +
- " \n" +
- " " +
- " \n" +
- " "+ I18nUtil.getString("jobinfo_field_jobgroup") +" \n" +
- " "+ I18nUtil.getString("jobinfo_field_id") +" \n" +
- " "+ I18nUtil.getString("jobinfo_field_jobdesc") +" \n" +
- " "+ I18nUtil.getString("jobconf_monitor_alarm_title") +" \n" +
- " "+ I18nUtil.getString("jobconf_monitor_alarm_content") +" \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- " {0} \n" +
- " {1} \n" +
- " {2} \n" +
- " "+ I18nUtil.getString("jobconf_monitor_alarm_type") +" \n" +
- " {3} \n" +
- " \n" +
- " \n" +
- "
";
+ "\n" +
+ " " +
+ " \n" +
+ " " + I18nUtil.getString("jobinfo_field_jobgroup") + " \n" +
+ " " + I18nUtil.getString("jobinfo_field_id") + " \n" +
+ " " + I18nUtil.getString("jobinfo_field_jobdesc") + " \n" +
+ " " + I18nUtil.getString("jobconf_monitor_alarm_title") + " \n" +
+ " " + I18nUtil.getString("jobconf_monitor_alarm_content") + " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " {0} \n" +
+ " {1} \n" +
+ " {2} \n" +
+ " " + I18nUtil.getString("jobconf_monitor_alarm_type") + " \n" +
+ " {3} \n" +
+ " \n" +
+ " \n" +
+ "
";
return mailBodyTemplate;
}
diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/complete/XxlJobCompleter.java b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/complete/XxlJobCompleter.java
index b9ac59a38..83399336b 100644
--- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/complete/XxlJobCompleter.java
+++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/complete/XxlJobCompleter.java
@@ -32,7 +32,7 @@ public class XxlJobCompleter {
// text最大64kb 避免长度过长
if (xxlJobLog.getHandleMsg().length() > 15000) {
- xxlJobLog.setHandleMsg( xxlJobLog.getHandleMsg().substring(0, 15000) );
+ xxlJobLog.setHandleMsg(xxlJobLog.getHandleMsg().substring(0, 15000));
}
// fresh handle
@@ -43,18 +43,18 @@ public class XxlJobCompleter {
/**
* do somethind to finish job
*/
- private static void finishJob(XxlJobLog xxlJobLog){
+ private static void finishJob(XxlJobLog xxlJobLog) {
// 1、handle success, to trigger child job
String triggerChildMsg = null;
- if (XxlJobContext.HANDLE_COCE_SUCCESS == xxlJobLog.getHandleCode()) {
+ if (XxlJobContext.HANDLE_CODE_SUCCESS == xxlJobLog.getHandleCode()) {
XxlJobInfo xxlJobInfo = XxlJobAdminConfig.getAdminConfig().getXxlJobInfoDao().loadById(xxlJobLog.getJobId());
- if (xxlJobInfo!=null && xxlJobInfo.getChildJobId()!=null && xxlJobInfo.getChildJobId().trim().length()>0) {
- triggerChildMsg = " >>>>>>>>>>>"+ I18nUtil.getString("jobconf_trigger_child_run") +"<<<<<<<<<<< ";
+ if (xxlJobInfo != null && xxlJobInfo.getChildJobId() != null && xxlJobInfo.getChildJobId().trim().length() > 0) {
+ triggerChildMsg = " >>>>>>>>>>>" + I18nUtil.getString("jobconf_trigger_child_run") + "<<<<<<<<<<< ";
String[] childJobIds = xxlJobInfo.getChildJobId().split(",");
for (int i = 0; i < childJobIds.length; i++) {
- int childJobId = (childJobIds[i]!=null && childJobIds[i].trim().length()>0 && isNumeric(childJobIds[i]))?Integer.valueOf(childJobIds[i]):-1;
+ int childJobId = (childJobIds[i] != null && childJobIds[i].trim().length() > 0 && isNumeric(childJobIds[i])) ? Integer.valueOf(childJobIds[i]) : -1;
if (childJobId > 0) {
JobTriggerPoolHelper.trigger(childJobId, TriggerTypeEnum.PARENT, -1, null, null, null);
@@ -62,16 +62,16 @@ public class XxlJobCompleter {
// add msg
triggerChildMsg += MessageFormat.format(I18nUtil.getString("jobconf_callback_child_msg1"),
- (i+1),
- childJobIds.length,
- childJobIds[i],
- (triggerChildResult.getCode()==ReturnT.SUCCESS_CODE?I18nUtil.getString("system_success"):I18nUtil.getString("system_fail")),
- triggerChildResult.getMsg());
+ (i + 1),
+ childJobIds.length,
+ childJobIds[i],
+ (triggerChildResult.getCode() == ReturnT.SUCCESS_CODE ? I18nUtil.getString("system_success") : I18nUtil.getString("system_fail")),
+ triggerChildResult.getMsg());
} else {
triggerChildMsg += MessageFormat.format(I18nUtil.getString("jobconf_callback_child_msg2"),
- (i+1),
- childJobIds.length,
- childJobIds[i]);
+ (i + 1),
+ childJobIds.length,
+ childJobIds[i]);
}
}
@@ -79,7 +79,7 @@ public class XxlJobCompleter {
}
if (triggerChildMsg != null) {
- xxlJobLog.setHandleMsg( xxlJobLog.getHandleMsg() + triggerChildMsg );
+ xxlJobLog.setHandleMsg(xxlJobLog.getHandleMsg() + triggerChildMsg);
}
// 2、fix_delay trigger next
@@ -87,7 +87,7 @@ public class XxlJobCompleter {
}
- private static boolean isNumeric(String str){
+ private static boolean isNumeric(String str) {
try {
int result = Integer.valueOf(str);
return true;
diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/conf/XxlJobAdminConfig.java b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/conf/XxlJobAdminConfig.java
index 380b8a596..6e40cb760 100644
--- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/conf/XxlJobAdminConfig.java
+++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/conf/XxlJobAdminConfig.java
@@ -23,6 +23,7 @@ import java.util.Arrays;
public class XxlJobAdminConfig implements InitializingBean, DisposableBean {
private static XxlJobAdminConfig adminConfig = null;
+
public static XxlJobAdminConfig getAdminConfig() {
return adminConfig;
}
diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/cron/CronExpression.java b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/cron/CronExpression.java
index fce23524d..2ce373eeb 100644
--- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/cron/CronExpression.java
+++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/core/cron/CronExpression.java
@@ -1,18 +1,18 @@
/*
* All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy
- * of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
* under the License.
- *
+ *
*/
package com.xxl.job.admin.core.cron;
@@ -31,14 +31,14 @@ import java.util.TimeZone;
import java.util.TreeSet;
/**
- * Provides a parser and evaluator for unix-like cron expressions. Cron
+ * Provides a parser and evaluator for unix-like cron expressions. Cron
* expressions provide the ability to specify complex time combinations such as
- * "At 8:00am every Monday through Friday" or "At 1:30am every
- * last Friday of the month".
+ * "At 8:00am every Monday through Friday" or "At 1:30am every
+ * last Friday of the month".
*
* Cron expressions are comprised of 6 required fields and one optional field
* separated by white space. The fields respectively are described as follows:
- *
+ *
*
*
* Field Name
@@ -98,7 +98,7 @@ import java.util.TreeSet;
*
*
*
- * The '*' character is used to specify all values. For example, "*"
+ * The '*' character is used to specify all values. For example, "*"
* in the minute field means "every minute".
*
* The '?' character is allowed for the day-of-month and day-of-week fields. It
@@ -113,55 +113,55 @@ import java.util.TreeSet;
* Wednesday, and Friday".
*
* The '/' character is used to specify increments. For example "0/15"
- * in the seconds field means "the seconds 0, 15, 30, and 45". And
+ * in the seconds field means "the seconds 0, 15, 30, and 45". And
* "5/15" in the seconds field means "the seconds 5, 20, 35, and
* 50". Specifying '*' before the '/' is equivalent to specifying 0 is
* the value to start with. Essentially, for each field in the expression, there
- * is a set of numbers that can be turned on or off. For seconds and minutes,
+ * is a set of numbers that can be turned on or off. For seconds and minutes,
* the numbers range from 0 to 59. For hours 0 to 23, for days of the month 0 to
* 31, and for months 0 to 11 (JAN to DEC). The "/" character simply helps you turn
* on every "nth" value in the given set. Thus "7/6" in the
- * month field only turns on month "7", it does NOT mean every 6th
- * month, please note that subtlety.
+ * month field only turns on month "7", it does NOT mean every 6th
+ * month, please note that subtlety.
*
* The 'L' character is allowed for the day-of-month and day-of-week fields.
- * This character is short-hand for "last", but it has different
- * meaning in each of the two fields. For example, the value "L" in
- * the day-of-month field means "the last day of the month" - day 31
- * for January, day 28 for February on non-leap years. If used in the
- * day-of-week field by itself, it simply means "7" or
+ * This character is short-hand for "last", but it has different
+ * meaning in each of the two fields. For example, the value "L" in
+ * the day-of-month field means "the last day of the month" - day 31
+ * for January, day 28 for February on non-leap years. If used in the
+ * day-of-week field by itself, it simply means "7" or
* "SAT". But if used in the day-of-week field after another value, it
* means "the last xxx day of the month" - for example "6L"
- * means "the last friday of the month". You can also specify an offset
- * from the last day of the month, such as "L-3" which would mean the third-to-last
- * day of the calendar month. When using the 'L' option, it is important not to
+ * means "the last friday of the month". You can also specify an offset
+ * from the last day of the month, such as "L-3" which would mean the third-to-last
+ * day of the calendar month. When using the 'L' option, it is important not to
* specify lists, or ranges of values, as you'll get confusing/unexpected results.
*
- * The 'W' character is allowed for the day-of-month field. This character
- * is used to specify the weekday (Monday-Friday) nearest the given day. As an
- * example, if you were to specify "15W" as the value for the
+ * The 'W' character is allowed for the day-of-month field. This character
+ * is used to specify the weekday (Monday-Friday) nearest the given day. As an
+ * example, if you were to specify "15W" as the value for the
* day-of-month field, the meaning is: "the nearest weekday to the 15th of
- * the month". So if the 15th is a Saturday, the trigger will fire on
+ * the month". So if the 15th is a Saturday, the trigger will fire on
* Friday the 14th. If the 15th is a Sunday, the trigger will fire on Monday the
- * 16th. If the 15th is a Tuesday, then it will fire on Tuesday the 15th.
+ * 16th. If the 15th is a Tuesday, then it will fire on Tuesday the 15th.
* However if you specify "1W" as the value for day-of-month, and the
- * 1st is a Saturday, the trigger will fire on Monday the 3rd, as it will not
- * 'jump' over the boundary of a month's days. The 'W' character can only be
+ * 1st is a Saturday, the trigger will fire on Monday the 3rd, as it will not
+ * 'jump' over the boundary of a month's days. The 'W' character can only be
* specified when the day-of-month is a single day, not a range or list of days.
*
- * The 'L' and 'W' characters can also be combined for the day-of-month
- * expression to yield 'LW', which translates to "last weekday of the
+ * The 'L' and 'W' characters can also be combined for the day-of-month
+ * expression to yield 'LW', which translates to "last weekday of the
* month".
*
* The '#' character is allowed for the day-of-week field. This character is
- * used to specify "the nth" XXX day of the month. For example, the
- * value of "6#3" in the day-of-week field means the third Friday of
- * the month (day 6 = Friday and "#3" = the 3rd one in the month).
- * Other examples: "2#1" = the first Monday of the month and
+ * used to specify "the nth" XXX day of the month. For example, the
+ * value of "6#3" in the day-of-week field means the third Friday of
+ * the month (day 6 = Friday and "#3" = the 3rd one in the month).
+ * Other examples: "2#1" = the first Monday of the month and
* "4#5" = the fifth Wednesday of the month. Note that if you specify
* "#5" and there is not 5 of the given day-of-week in the month, then
* no firing will occur that month. If the '#' character is used, there can
- * only be one expression in the day-of-week field ("3#1,6#3" is
+ * only be one expression in the day-of-week field ("3#1,6#3" is
* not valid, since there are two expressions).
*
*
-
- org.apache.poi
- poi-ooxml
-
-
com.alibaba
easyexcel
-
+
- com.sun.xml.bind
- jaxb-impl
+ org.yaml
+ snakeyaml
@@ -179,6 +173,11 @@
hutool-extra
+
+ com.sun.mail
+ jakarta.mail
+
+
org.projectlombok
lombok
@@ -225,36 +224,27 @@
tlog-xxljob-spring-boot-starter
-
- com.qiniu
- qiniu-java-sdk
- ${qiniu.version}
-
- com.aliyun.oss
- aliyun-sdk-oss
- ${aliyun.oss.version}
+ com.amazonaws
+ aws-java-sdk-s3
+
+
- com.qcloud
- cos_api
- ${qcloud.cos.version}
+ com.aliyun
+ dysmsapi20170525
+
+
+
+ com.tencentcloudapi
+ tencentcloud-sdk-java
- org.slf4j
- slf4j-log4j12
-
-
- org.bouncycastle
- bcprov-jdk15on
+ com.squareup.okio
+ okio
-
- io.minio
- minio
- ${minio.version}
-
diff --git a/ruoyi/src/main/java/com/ruoyi/common/annotation/Anonymous.java b/ruoyi/src/main/java/com/ruoyi/common/annotation/Anonymous.java
new file mode 100644
index 000000000..fe2810083
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/common/annotation/Anonymous.java
@@ -0,0 +1,18 @@
+package com.ruoyi.common.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 匿名访问不鉴权注解
+ *
+ * @author ruoyi
+ */
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Anonymous {
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/common/annotation/CellMerge.java b/ruoyi/src/main/java/com/ruoyi/common/annotation/CellMerge.java
new file mode 100644
index 000000000..4af822eda
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/common/annotation/CellMerge.java
@@ -0,0 +1,24 @@
+package com.ruoyi.common.annotation;
+
+import com.ruoyi.common.excel.CellMergeStrategy;
+
+import java.lang.annotation.*;
+
+/**
+ * excel 列单元格合并(合并列相同项)
+ *
+ * 需搭配 {@link CellMergeStrategy} 策略使用
+ *
+ * @author Lion Li
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface CellMerge {
+
+ /**
+ * col index
+ */
+ int index() default -1;
+
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java b/ruoyi/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java
index 6885e95cf..9aa75f7a4 100644
--- a/ruoyi/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java
+++ b/ruoyi/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java
@@ -108,12 +108,6 @@ public class SysUser extends BaseEntity {
)
private String password;
- @JsonIgnore
- @JsonProperty
- public String getPassword() {
- return password;
- }
-
/**
* 帐号状态(0正常 1停用)
*/
diff --git a/ruoyi/src/main/java/com/ruoyi/common/excel/CellMergeStrategy.java b/ruoyi/src/main/java/com/ruoyi/common/excel/CellMergeStrategy.java
new file mode 100644
index 000000000..04a1bbb8a
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/common/excel/CellMergeStrategy.java
@@ -0,0 +1,114 @@
+package com.ruoyi.common.excel;
+
+import com.alibaba.excel.metadata.Head;
+import com.alibaba.excel.write.merge.AbstractMergeStrategy;
+import com.ruoyi.common.annotation.CellMerge;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.util.CellRangeAddress;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 列值重复合并策略
+ *
+ * @author Lion Li
+ */
+@AllArgsConstructor
+@Slf4j
+public class CellMergeStrategy extends AbstractMergeStrategy {
+
+ private List> list;
+ private boolean hasTitle;
+
+ @Override
+ protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
+ List cellList = handle(list, hasTitle);
+ // judge the list is not null
+ if (CollectionUtils.isNotEmpty(cellList)) {
+ // the judge is necessary
+ if (cell.getRowIndex() == 1 && cell.getColumnIndex() == 0) {
+ for (CellRangeAddress item : cellList) {
+ sheet.addMergedRegion(item);
+ }
+ }
+ }
+ }
+
+ @SneakyThrows
+ private static List handle(List> list, boolean hasTitle) {
+ List cellList = new ArrayList<>();
+ if (CollectionUtils.isEmpty(list)) {
+ return cellList;
+ }
+ Class> clazz = list.get(0).getClass();
+ Field[] fields = clazz.getDeclaredFields();
+ // 有注解的字段
+ List mergeFields = new ArrayList<>();
+ List mergeFieldsIndex = new ArrayList<>();
+ for (int i = 0; i < fields.length; i++) {
+ Field field = fields[i];
+ if (field.isAnnotationPresent(CellMerge.class)) {
+ CellMerge cm = field.getAnnotation(CellMerge.class);
+ mergeFields.add(field);
+ mergeFieldsIndex.add(cm.index() == -1 ? i : cm.index());
+ }
+ }
+ // 行合并开始下标
+ int rowIndex = hasTitle ? 1 : 0;
+ Map map = new HashMap<>();
+ // 生成两两合并单元格
+ for (int i = 0; i < list.size(); i++) {
+ for (int j = 0; j < mergeFields.size(); j++) {
+ Field field = mergeFields.get(j);
+ String name = field.getName();
+ String methodName = "get" + name.substring(0, 1).toUpperCase() + name.substring(1);
+ Method readMethod = clazz.getMethod(methodName);
+ Object val = readMethod.invoke(list.get(i));
+
+ int colNum = mergeFieldsIndex.get(j);
+ if (!map.containsKey(field)) {
+ map.put(field, new RepeatCell(val, i));
+ } else {
+ RepeatCell repeatCell = map.get(field);
+ Object cellValue = repeatCell.getValue();
+ if (cellValue == null || "".equals(cellValue)) {
+ // 空值跳过不合并
+ continue;
+ }
+ if (cellValue != val) {
+ if (i - repeatCell.getCurrent() > 1) {
+ cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum));
+ }
+ map.put(field, new RepeatCell(val, i));
+ } else if (i == list.size() - 1) {
+ if (i > repeatCell.getCurrent()) {
+ cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum));
+ }
+ }
+ }
+ }
+ }
+ return cellList;
+ }
+
+ @Data
+ @AllArgsConstructor
+ static class RepeatCell {
+
+ private Object value;
+
+ private int current;
+
+ }
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/common/helper/DataPermissionHelper.java b/ruoyi/src/main/java/com/ruoyi/common/helper/DataPermissionHelper.java
index ec4d56767..0e60485ad 100644
--- a/ruoyi/src/main/java/com/ruoyi/common/helper/DataPermissionHelper.java
+++ b/ruoyi/src/main/java/com/ruoyi/common/helper/DataPermissionHelper.java
@@ -1,11 +1,11 @@
package com.ruoyi.common.helper;
+import cn.dev33.satoken.context.SaHolder;
+import cn.dev33.satoken.context.model.SaStorage;
import cn.hutool.core.util.ObjectUtil;
-import com.ruoyi.common.utils.ServletUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
-import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
@@ -33,11 +33,11 @@ public class DataPermissionHelper {
}
public static Map getContext() {
- HttpServletRequest request = ServletUtils.getRequest();
- Object attribute = request.getAttribute(DATA_PERMISSION_KEY);
+ SaStorage saStorage = SaHolder.getStorage();
+ Object attribute = saStorage.get(DATA_PERMISSION_KEY);
if (ObjectUtil.isNull(attribute)) {
- request.setAttribute(DATA_PERMISSION_KEY, new HashMap<>());
- attribute = request.getAttribute(DATA_PERMISSION_KEY);
+ saStorage.set(DATA_PERMISSION_KEY, new HashMap<>());
+ attribute = saStorage.get(DATA_PERMISSION_KEY);
}
if (attribute instanceof Map) {
return (Map) attribute;
diff --git a/ruoyi/src/main/java/com/ruoyi/common/helper/LoginHelper.java b/ruoyi/src/main/java/com/ruoyi/common/helper/LoginHelper.java
index 7875c5630..09e1dc757 100644
--- a/ruoyi/src/main/java/com/ruoyi/common/helper/LoginHelper.java
+++ b/ruoyi/src/main/java/com/ruoyi/common/helper/LoginHelper.java
@@ -1,5 +1,6 @@
package com.ruoyi.common.helper;
+import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.ObjectUtil;
import com.ruoyi.common.constant.UserConstants;
@@ -13,7 +14,7 @@ import lombok.NoArgsConstructor;
/**
* 登录鉴权助手
- *
+ *
* user_type 为 用户类型 同一个用户表 可以有多种用户类型 例如 pc,app
* deivce 为 设备类型 同一个用户类型 可以有 多种设备类型 例如 web,ios
* 可以组成 用户类型与设备类型多对多的 权限灵活控制
@@ -29,15 +30,13 @@ public class LoginHelper {
public static final String JOIN_CODE = ":";
public static final String LOGIN_USER_KEY = "loginUser";
- private static final ThreadLocal LOGIN_CACHE = new ThreadLocal<>();
-
/**
* 登录系统
*
* @param loginUser 登录用户信息
*/
public static void login(LoginUser loginUser) {
- LOGIN_CACHE.set(loginUser);
+ SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser);
StpUtil.login(loginUser.getLoginId());
setLoginUser(loginUser);
}
@@ -49,7 +48,7 @@ public class LoginHelper {
* @param loginUser 登录用户信息
*/
public static void loginByDevice(LoginUser loginUser, DeviceType deviceType) {
- LOGIN_CACHE.set(loginUser);
+ SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser);
StpUtil.login(loginUser.getLoginId(), deviceType.getDevice());
setLoginUser(loginUser);
}
@@ -65,18 +64,13 @@ public class LoginHelper {
* 获取用户(多级缓存)
*/
public static LoginUser getLoginUser() {
- LoginUser loginUser = LOGIN_CACHE.get();
+ LoginUser loginUser = (LoginUser) SaHolder.getStorage().get(LOGIN_USER_KEY);
if (loginUser != null) {
return loginUser;
}
- return (LoginUser) StpUtil.getTokenSession().get(LOGIN_USER_KEY);
- }
-
- /**
- * 清除一级缓存 防止内存问题
- */
- public static void clearCache() {
- LOGIN_CACHE.remove();
+ loginUser = (LoginUser) StpUtil.getTokenSession().get(LOGIN_USER_KEY);
+ SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser);
+ return loginUser;
}
/**
diff --git a/ruoyi/src/main/java/com/ruoyi/common/jackson/SensitiveJsonSerializer.java b/ruoyi/src/main/java/com/ruoyi/common/jackson/SensitiveJsonSerializer.java
index 55c4e6a7d..404f393fe 100644
--- a/ruoyi/src/main/java/com/ruoyi/common/jackson/SensitiveJsonSerializer.java
+++ b/ruoyi/src/main/java/com/ruoyi/common/jackson/SensitiveJsonSerializer.java
@@ -27,9 +27,9 @@ public class SensitiveJsonSerializer extends JsonSerializer implements C
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
SensitiveService sensitiveService = SpringUtils.getBean(SensitiveService.class);
if (sensitiveService.isSensitive()) {
- gen.writeString(value);
- } else {
gen.writeString(strategy.desensitizer().apply(value));
+ } else {
+ gen.writeString(value);
}
}
diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/email/MailUtils.java b/ruoyi/src/main/java/com/ruoyi/common/utils/email/MailUtils.java
new file mode 100644
index 000000000..32a3f8252
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/common/utils/email/MailUtils.java
@@ -0,0 +1,468 @@
+package com.ruoyi.common.utils.email;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.CharUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.extra.mail.*;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.spring.SpringUtils;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
+import javax.mail.Authenticator;
+import javax.mail.Session;
+import java.io.File;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * 邮件工具类
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class MailUtils {
+
+ private static final MailAccount ACCOUNT = SpringUtils.getBean(MailAccount.class);
+
+ /**
+ * 获取邮件发送实例
+ */
+ public static MailAccount getMailAccount() {
+ return ACCOUNT;
+ }
+
+ /**
+ * 获取邮件发送实例 (自定义发送人以及授权码)
+ *
+ * @param user 发送人
+ * @param pass 授权码
+ */
+ public static MailAccount getMailAccount(String from, String user, String pass) {
+ ACCOUNT.setFrom(StringUtils.blankToDefault(from, ACCOUNT.getFrom()));
+ ACCOUNT.setUser(StringUtils.blankToDefault(user, ACCOUNT.getUser()));
+ ACCOUNT.setPass(StringUtils.blankToDefault(pass, ACCOUNT.getPass()));
+ return ACCOUNT;
+ }
+
+ /**
+ * 使用配置文件中设置的账户发送文本邮件,发送给单个或多个收件人
+ * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔
+ *
+ * @param to 收件人
+ * @param subject 标题
+ * @param content 正文
+ * @param files 附件列表
+ * @return message-id
+ * @since 3.2.0
+ */
+ public static String sendText(String to, String subject, String content, File... files) {
+ return send(to, subject, content, false, files);
+ }
+
+ /**
+ * 使用配置文件中设置的账户发送HTML邮件,发送给单个或多个收件人
+ * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔
+ *
+ * @param to 收件人
+ * @param subject 标题
+ * @param content 正文
+ * @param files 附件列表
+ * @return message-id
+ * @since 3.2.0
+ */
+ public static String sendHtml(String to, String subject, String content, File... files) {
+ return send(to, subject, content, true, files);
+ }
+
+ /**
+ * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
+ * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔
+ *
+ * @param to 收件人
+ * @param subject 标题
+ * @param content 正文
+ * @param isHtml 是否为HTML
+ * @param files 附件列表
+ * @return message-id
+ */
+ public static String send(String to, String subject, String content, boolean isHtml, File... files) {
+ return send(splitAddress(to), subject, content, isHtml, files);
+ }
+
+ /**
+ * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
+ * 多个收件人、抄送人、密送人可以使用逗号“,”分隔,也可以通过分号“;”分隔
+ *
+ * @param to 收件人,可以使用逗号“,”分隔,也可以通过分号“;”分隔
+ * @param cc 抄送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔
+ * @param bcc 密送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔
+ * @param subject 标题
+ * @param content 正文
+ * @param isHtml 是否为HTML
+ * @param files 附件列表
+ * @return message-id
+ * @since 4.0.3
+ */
+ public static String send(String to, String cc, String bcc, String subject, String content, boolean isHtml, File... files) {
+ return send(splitAddress(to), splitAddress(cc), splitAddress(bcc), subject, content, isHtml, files);
+ }
+
+ /**
+ * 使用配置文件中设置的账户发送文本邮件,发送给多人
+ *
+ * @param tos 收件人列表
+ * @param subject 标题
+ * @param content 正文
+ * @param files 附件列表
+ * @return message-id
+ */
+ public static String sendText(Collection tos, String subject, String content, File... files) {
+ return send(tos, subject, content, false, files);
+ }
+
+ /**
+ * 使用配置文件中设置的账户发送HTML邮件,发送给多人
+ *
+ * @param tos 收件人列表
+ * @param subject 标题
+ * @param content 正文
+ * @param files 附件列表
+ * @return message-id
+ * @since 3.2.0
+ */
+ public static String sendHtml(Collection tos, String subject, String content, File... files) {
+ return send(tos, subject, content, true, files);
+ }
+
+ /**
+ * 使用配置文件中设置的账户发送邮件,发送给多人
+ *
+ * @param tos 收件人列表
+ * @param subject 标题
+ * @param content 正文
+ * @param isHtml 是否为HTML
+ * @param files 附件列表
+ * @return message-id
+ */
+ public static String send(Collection tos, String subject, String content, boolean isHtml, File... files) {
+ return send(tos, null, null, subject, content, isHtml, files);
+ }
+
+ /**
+ * 使用配置文件中设置的账户发送邮件,发送给多人
+ *
+ * @param tos 收件人列表
+ * @param ccs 抄送人列表,可以为null或空
+ * @param bccs 密送人列表,可以为null或空
+ * @param subject 标题
+ * @param content 正文
+ * @param isHtml 是否为HTML
+ * @param files 附件列表
+ * @return message-id
+ * @since 4.0.3
+ */
+ public static String send(Collection tos, Collection ccs, Collection bccs, String subject, String content, boolean isHtml, File... files) {
+ return send(getMailAccount(), true, tos, ccs, bccs, subject, content, null, isHtml, files);
+ }
+
+ // ------------------------------------------------------------------------------------------------------------------------------- Custom MailAccount
+
+ /**
+ * 发送邮件给多人
+ *
+ * @param mailAccount 邮件认证对象
+ * @param to 收件人,多个收件人逗号或者分号隔开
+ * @param subject 标题
+ * @param content 正文
+ * @param isHtml 是否为HTML格式
+ * @param files 附件列表
+ * @return message-id
+ * @since 3.2.0
+ */
+ public static String send(MailAccount mailAccount, String to, String subject, String content, boolean isHtml, File... files) {
+ return send(mailAccount, splitAddress(to), subject, content, isHtml, files);
+ }
+
+ /**
+ * 发送邮件给多人
+ *
+ * @param mailAccount 邮件帐户信息
+ * @param tos 收件人列表
+ * @param subject 标题
+ * @param content 正文
+ * @param isHtml 是否为HTML格式
+ * @param files 附件列表
+ * @return message-id
+ */
+ public static String send(MailAccount mailAccount, Collection tos, String subject, String content, boolean isHtml, File... files) {
+ return send(mailAccount, tos, null, null, subject, content, isHtml, files);
+ }
+
+ /**
+ * 发送邮件给多人
+ *
+ * @param mailAccount 邮件帐户信息
+ * @param tos 收件人列表
+ * @param ccs 抄送人列表,可以为null或空
+ * @param bccs 密送人列表,可以为null或空
+ * @param subject 标题
+ * @param content 正文
+ * @param isHtml 是否为HTML格式
+ * @param files 附件列表
+ * @return message-id
+ * @since 4.0.3
+ */
+ public static String send(MailAccount mailAccount, Collection tos, Collection ccs, Collection bccs, String subject, String content, boolean isHtml, File... files) {
+ return send(mailAccount, false, tos, ccs, bccs, subject, content, null, isHtml, files);
+ }
+
+ /**
+ * 使用配置文件中设置的账户发送HTML邮件,发送给单个或多个收件人
+ * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔
+ *
+ * @param to 收件人
+ * @param subject 标题
+ * @param content 正文
+ * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER
+ * @param files 附件列表
+ * @return message-id
+ * @since 3.2.0
+ */
+ public static String sendHtml(String to, String subject, String content, Map imageMap, File... files) {
+ return send(to, subject, content, imageMap, true, files);
+ }
+
+ /**
+ * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
+ * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔
+ *
+ * @param to 收件人
+ * @param subject 标题
+ * @param content 正文
+ * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER
+ * @param isHtml 是否为HTML
+ * @param files 附件列表
+ * @return message-id
+ */
+ public static String send(String to, String subject, String content, Map imageMap, boolean isHtml, File... files) {
+ return send(splitAddress(to), subject, content, imageMap, isHtml, files);
+ }
+
+ /**
+ * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
+ * 多个收件人、抄送人、密送人可以使用逗号“,”分隔,也可以通过分号“;”分隔
+ *
+ * @param to 收件人,可以使用逗号“,”分隔,也可以通过分号“;”分隔
+ * @param cc 抄送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔
+ * @param bcc 密送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔
+ * @param subject 标题
+ * @param content 正文
+ * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER
+ * @param isHtml 是否为HTML
+ * @param files 附件列表
+ * @return message-id
+ * @since 4.0.3
+ */
+ public static String send(String to, String cc, String bcc, String subject, String content, Map imageMap, boolean isHtml, File... files) {
+ return send(splitAddress(to), splitAddress(cc), splitAddress(bcc), subject, content, imageMap, isHtml, files);
+ }
+
+ /**
+ * 使用配置文件中设置的账户发送HTML邮件,发送给多人
+ *
+ * @param tos 收件人列表
+ * @param subject 标题
+ * @param content 正文
+ * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER
+ * @param files 附件列表
+ * @return message-id
+ * @since 3.2.0
+ */
+ public static String sendHtml(Collection tos, String subject, String content, Map imageMap, File... files) {
+ return send(tos, subject, content, imageMap, true, files);
+ }
+
+ /**
+ * 使用配置文件中设置的账户发送邮件,发送给多人
+ *
+ * @param tos 收件人列表
+ * @param subject 标题
+ * @param content 正文
+ * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER
+ * @param isHtml 是否为HTML
+ * @param files 附件列表
+ * @return message-id
+ */
+ public static String send(Collection tos, String subject, String content, Map imageMap, boolean isHtml, File... files) {
+ return send(tos, null, null, subject, content, imageMap, isHtml, files);
+ }
+
+ /**
+ * 使用配置文件中设置的账户发送邮件,发送给多人
+ *
+ * @param tos 收件人列表
+ * @param ccs 抄送人列表,可以为null或空
+ * @param bccs 密送人列表,可以为null或空
+ * @param subject 标题
+ * @param content 正文
+ * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER
+ * @param isHtml 是否为HTML
+ * @param files 附件列表
+ * @return message-id
+ * @since 4.0.3
+ */
+ public static String send(Collection tos, Collection ccs, Collection bccs, String subject, String content, Map imageMap, boolean isHtml, File... files) {
+ return send(getMailAccount(), true, tos, ccs, bccs, subject, content, imageMap, isHtml, files);
+ }
+
+ // ------------------------------------------------------------------------------------------------------------------------------- Custom MailAccount
+
+ /**
+ * 发送邮件给多人
+ *
+ * @param mailAccount 邮件认证对象
+ * @param to 收件人,多个收件人逗号或者分号隔开
+ * @param subject 标题
+ * @param content 正文
+ * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER
+ * @param isHtml 是否为HTML格式
+ * @param files 附件列表
+ * @return message-id
+ * @since 3.2.0
+ */
+ public static String send(MailAccount mailAccount, String to, String subject, String content, Map imageMap, boolean isHtml, File... files) {
+ return send(mailAccount, splitAddress(to), subject, content, imageMap, isHtml, files);
+ }
+
+ /**
+ * 发送邮件给多人
+ *
+ * @param mailAccount 邮件帐户信息
+ * @param tos 收件人列表
+ * @param subject 标题
+ * @param content 正文
+ * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER
+ * @param isHtml 是否为HTML格式
+ * @param files 附件列表
+ * @return message-id
+ * @since 4.6.3
+ */
+ public static String send(MailAccount mailAccount, Collection tos, String subject, String content, Map imageMap, boolean isHtml, File... files) {
+ return send(mailAccount, tos, null, null, subject, content, imageMap, isHtml, files);
+ }
+
+ /**
+ * 发送邮件给多人
+ *
+ * @param mailAccount 邮件帐户信息
+ * @param tos 收件人列表
+ * @param ccs 抄送人列表,可以为null或空
+ * @param bccs 密送人列表,可以为null或空
+ * @param subject 标题
+ * @param content 正文
+ * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER
+ * @param isHtml 是否为HTML格式
+ * @param files 附件列表
+ * @return message-id
+ * @since 4.6.3
+ */
+ public static String send(MailAccount mailAccount, Collection tos, Collection ccs, Collection bccs, String subject, String content, Map imageMap,
+ boolean isHtml, File... files) {
+ return send(mailAccount, false, tos, ccs, bccs, subject, content, imageMap, isHtml, files);
+ }
+
+ /**
+ * 根据配置文件,获取邮件客户端会话
+ *
+ * @param mailAccount 邮件账户配置
+ * @param isSingleton 是否单例(全局共享会话)
+ * @return {@link Session}
+ * @since 5.5.7
+ */
+ public static Session getSession(MailAccount mailAccount, boolean isSingleton) {
+ Authenticator authenticator = null;
+ if (mailAccount.isAuth()) {
+ authenticator = new UserPassAuthenticator(mailAccount.getUser(), mailAccount.getPass());
+ }
+
+ return isSingleton ? Session.getDefaultInstance(mailAccount.getSmtpProps(), authenticator) //
+ : Session.getInstance(mailAccount.getSmtpProps(), authenticator);
+ }
+
+ // ------------------------------------------------------------------------------------------------------------------------ Private method start
+
+ /**
+ * 发送邮件给多人
+ *
+ * @param mailAccount 邮件帐户信息
+ * @param useGlobalSession 是否全局共享Session
+ * @param tos 收件人列表
+ * @param ccs 抄送人列表,可以为null或空
+ * @param bccs 密送人列表,可以为null或空
+ * @param subject 标题
+ * @param content 正文
+ * @param imageMap 图片与占位符,占位符格式为cid:${cid}
+ * @param isHtml 是否为HTML格式
+ * @param files 附件列表
+ * @return message-id
+ * @since 4.6.3
+ */
+ private static String send(MailAccount mailAccount, boolean useGlobalSession, Collection tos, Collection ccs, Collection bccs, String subject, String content,
+ Map imageMap, boolean isHtml, File... files) {
+ final Mail mail = Mail.create(mailAccount).setUseGlobalSession(useGlobalSession);
+
+ // 可选抄送人
+ if (CollUtil.isNotEmpty(ccs)) {
+ mail.setCcs(ccs.toArray(new String[0]));
+ }
+ // 可选密送人
+ if (CollUtil.isNotEmpty(bccs)) {
+ mail.setBccs(bccs.toArray(new String[0]));
+ }
+
+ mail.setTos(tos.toArray(new String[0]));
+ mail.setTitle(subject);
+ mail.setContent(content);
+ mail.setHtml(isHtml);
+ mail.setFiles(files);
+
+ // 图片
+ if (MapUtil.isNotEmpty(imageMap)) {
+ for (Map.Entry entry : imageMap.entrySet()) {
+ mail.addImage(entry.getKey(), entry.getValue());
+ // 关闭流
+ IoUtil.close(entry.getValue());
+ }
+ }
+
+ return mail.send();
+ }
+
+ /**
+ * 将多个联系人转为列表,分隔符为逗号或者分号
+ *
+ * @param addresses 多个联系人,如果为空返回null
+ * @return 联系人列表
+ */
+ private static List splitAddress(String addresses) {
+ if (StrUtil.isBlank(addresses)) {
+ return null;
+ }
+
+ List result;
+ if (StrUtil.contains(addresses, CharUtil.COMMA)) {
+ result = StrUtil.splitTrim(addresses, CharUtil.COMMA);
+ } else if (StrUtil.contains(addresses, ';')) {
+ result = StrUtil.splitTrim(addresses, ';');
+ } else {
+ result = CollUtil.newArrayList(addresses);
+ }
+ return result;
+ }
+ // ------------------------------------------------------------------------------------------------------------------------ Private method end
+
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java b/ruoyi/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java
new file mode 100644
index 000000000..6ca97fe60
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java
@@ -0,0 +1,40 @@
+package com.ruoyi.common.utils.file;
+
+/**
+ * 媒体类型工具类
+ *
+ * @author ruoyi
+ */
+public class MimeTypeUtils {
+ public static final String IMAGE_PNG = "image/png";
+
+ public static final String IMAGE_JPG = "image/jpg";
+
+ public static final String IMAGE_JPEG = "image/jpeg";
+
+ public static final String IMAGE_BMP = "image/bmp";
+
+ public static final String IMAGE_GIF = "image/gif";
+
+ public static final String[] IMAGE_EXTENSION = {"bmp", "gif", "jpg", "jpeg", "png"};
+
+ public static final String[] FLASH_EXTENSION = {"swf", "flv"};
+
+ public static final String[] MEDIA_EXTENSION = {"swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg",
+ "asf", "rm", "rmvb"};
+
+ public static final String[] VIDEO_EXTENSION = {"mp4", "avi", "rmvb"};
+
+ public static final String[] DEFAULT_ALLOWED_EXTENSION = {
+ // 图片
+ "bmp", "gif", "jpg", "jpeg", "png",
+ // word excel powerpoint
+ "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt",
+ // 压缩文件
+ "rar", "zip", "gz", "bz2",
+ // 视频格式
+ "mp4", "avi", "rmvb",
+ // pdf
+ "pdf"};
+
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java b/ruoyi/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
index 36dc67fa1..e6a67b26b 100644
--- a/ruoyi/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
+++ b/ruoyi/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
@@ -1,9 +1,17 @@
package com.ruoyi.common.utils.poi;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.io.resource.ClassPathResource;
import cn.hutool.core.util.IdUtil;
import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.ExcelWriter;
+import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
+import com.alibaba.excel.write.metadata.WriteSheet;
+import com.alibaba.excel.write.metadata.fill.FillConfig;
+import com.alibaba.excel.write.metadata.fill.FillWrapper;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.ruoyi.common.convert.ExcelBigNumberConvert;
+import com.ruoyi.common.excel.CellMergeStrategy;
import com.ruoyi.common.excel.DefaultExcelListener;
import com.ruoyi.common.excel.ExcelListener;
import com.ruoyi.common.excel.ExcelResult;
@@ -16,7 +24,10 @@ import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.Collection;
import java.util.List;
+import java.util.Map;
/**
* Excel相关处理
@@ -69,27 +80,125 @@ public class ExcelUtil {
*
* @param list 导出数据集合
* @param sheetName 工作表的名称
- * @return 结果
+ * @param clazz 实体类
+ * @param response 响应体
*/
public static void exportExcel(List list, String sheetName, Class clazz, HttpServletResponse response) {
+ exportExcel(list, sheetName, clazz, false, response);
+ }
+
+ /**
+ * 导出excel
+ *
+ * @param list 导出数据集合
+ * @param sheetName 工作表的名称
+ * @param clazz 实体类
+ * @param merge 是否合并单元格
+ * @param response 响应体
+ */
+ public static void exportExcel(List list, String sheetName, Class clazz, boolean merge, HttpServletResponse response) {
try {
- String filename = encodingFilename(sheetName);
- response.reset();
- FileUtils.setAttachmentResponseHeader(response, filename);
- response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8");
+ resetResponse(sheetName, response);
ServletOutputStream os = response.getOutputStream();
- EasyExcel.write(os, clazz)
+ ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz)
.autoCloseStream(false)
// 自动适配
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
// 大数值自动转换 防止失真
.registerConverter(new ExcelBigNumberConvert())
- .sheet(sheetName).doWrite(list);
+ .sheet(sheetName);
+ if (merge) {
+ // 合并处理器
+ builder.registerWriteHandler(new CellMergeStrategy(list, true));
+ }
+ builder.doWrite(list);
} catch (IOException e) {
throw new RuntimeException("导出Excel异常");
}
}
+ /**
+ * 单表多数据模板导出 模板格式为 {.属性}
+ *
+ * @param filename 文件名
+ * @param templatePath 模板路径 resource 目录下的路径包括模板文件名
+ * 例如: excel/temp.xlsx
+ * 重点: 模板文件必须放置到启动类对应的 resource 目录下
+ * @param data 模板需要的数据
+ */
+ public static void exportTemplate(List data, String filename, String templatePath, HttpServletResponse response) {
+ try {
+ resetResponse(filename, response);
+ ClassPathResource templateResource = new ClassPathResource(templatePath);
+ ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
+ .withTemplate(templateResource.getStream())
+ .autoCloseStream(false)
+ // 大数值自动转换 防止失真
+ .registerConverter(new ExcelBigNumberConvert())
+ .build();
+ WriteSheet writeSheet = EasyExcel.writerSheet().build();
+ if (CollUtil.isEmpty(data)) {
+ throw new IllegalArgumentException("数据为空");
+ }
+ // 单表多数据导出 模板格式为 {.属性}
+ for (Object d : data) {
+ excelWriter.fill(d, writeSheet);
+ }
+ excelWriter.finish();
+ } catch (IOException e) {
+ throw new RuntimeException("导出Excel异常");
+ }
+ }
+
+ /**
+ * 多表多数据模板导出 模板格式为 {key.属性}
+ *
+ * @param filename 文件名
+ * @param templatePath 模板路径 resource 目录下的路径包括模板文件名
+ * 例如: excel/temp.xlsx
+ * 重点: 模板文件必须放置到启动类对应的 resource 目录下
+ * @param data 模板需要的数据
+ */
+ public static void exportTemplateMultiList(Map data, String filename, String templatePath, HttpServletResponse response) {
+ try {
+ resetResponse(filename, response);
+ ClassPathResource templateResource = new ClassPathResource(templatePath);
+ ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
+ .withTemplate(templateResource.getStream())
+ .autoCloseStream(false)
+ // 大数值自动转换 防止失真
+ .registerConverter(new ExcelBigNumberConvert())
+ .build();
+ WriteSheet writeSheet = EasyExcel.writerSheet().build();
+ if (CollUtil.isEmpty(data)) {
+ throw new IllegalArgumentException("数据为空");
+ }
+ for (Map.Entry map : data.entrySet()) {
+ // 设置列表后续还有数据
+ FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
+ if (map.getValue() instanceof Collection) {
+ // 多表导出必须使用 FillWrapper
+ excelWriter.fill(new FillWrapper(map.getKey(), (Collection>) map.getValue()), fillConfig, writeSheet);
+ } else {
+ excelWriter.fill(map.getValue(), writeSheet);
+ }
+ }
+ excelWriter.finish();
+ } catch (IOException e) {
+ throw new RuntimeException("导出Excel异常");
+ }
+ }
+
+ /**
+ * 重置响应体
+ */
+ private static void resetResponse(String sheetName, HttpServletResponse response) throws UnsupportedEncodingException {
+ String filename = encodingFilename(sheetName);
+ response.reset();
+ FileUtils.setAttachmentResponseHeader(response, filename);
+ response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8");
+ }
+
/**
* 解析导出值 0=男,1=女,2=未知
*
@@ -103,7 +212,7 @@ public class ExcelUtil {
String[] convertSource = converterExp.split(",");
for (String item : convertSource) {
String[] itemArray = item.split("=");
- if (StringUtils.containsAny(separator, propertyValue)) {
+ if (StringUtils.containsAny(propertyValue, separator)) {
for (String value : propertyValue.split(separator)) {
if (itemArray[0].equals(value)) {
propertyString.append(itemArray[1] + separator);
@@ -132,7 +241,7 @@ public class ExcelUtil {
String[] convertSource = converterExp.split(",");
for (String item : convertSource) {
String[] itemArray = item.split("=");
- if (StringUtils.containsAny(separator, propertyValue)) {
+ if (StringUtils.containsAny(propertyValue, separator)) {
for (String value : propertyValue.split(separator)) {
if (itemArray[1].equals(value)) {
propertyString.append(itemArray[0] + separator);
diff --git a/ruoyi/src/main/java/com/ruoyi/common/utils/redis/RedisUtils.java b/ruoyi/src/main/java/com/ruoyi/common/utils/redis/RedisUtils.java
index 5a15d46b7..7ed3b2827 100644
--- a/ruoyi/src/main/java/com/ruoyi/common/utils/redis/RedisUtils.java
+++ b/ruoyi/src/main/java/com/ruoyi/common/utils/redis/RedisUtils.java
@@ -6,11 +6,11 @@ import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.redisson.api.*;
+import java.time.Duration;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
/**
@@ -107,7 +107,7 @@ public class RedisUtils {
} catch (Exception e) {
long timeToLive = bucket.remainTimeToLive();
bucket.set(value);
- bucket.expire(timeToLive, TimeUnit.MILLISECONDS);
+ bucket.expire(Duration.ofMillis(timeToLive));
}
} else {
bucket.set(value);
@@ -119,13 +119,12 @@ public class RedisUtils {
*
* @param key 缓存的键值
* @param value 缓存的值
- * @param timeout 时间
- * @param timeUnit 时间颗粒度
+ * @param duration 时间
*/
- public static void setCacheObject(final String key, final T value, final long timeout, final TimeUnit timeUnit) {
+ public static void setCacheObject(final String key, final T value, final Duration duration) {
RBucket result = CLIENT.getBucket(key);
result.set(value);
- result.expire(timeout, timeUnit);
+ result.expire(duration);
}
/**
@@ -149,20 +148,19 @@ public class RedisUtils {
* @return true=设置成功;false=设置失败
*/
public static boolean expire(final String key, final long timeout) {
- return expire(key, timeout, TimeUnit.SECONDS);
+ return expire(key, Duration.ofSeconds(timeout));
}
/**
* 设置有效时间
*
- * @param key Redis键
- * @param timeout 超时时间
- * @param unit 时间单位
+ * @param key Redis键
+ * @param duration 超时时间
* @return true=设置成功;false=设置失败
*/
- public static boolean expire(final String key, final long timeout, final TimeUnit unit) {
+ public static boolean expire(final String key, final Duration duration) {
RBucket rBucket = CLIENT.getBucket(key);
- return rBucket.expire(timeout, unit);
+ return rBucket.expire(duration);
}
/**
@@ -366,6 +364,50 @@ public class RedisUtils {
return rMap.getAll(hKeys);
}
+ /**
+ * 设置原子值
+ *
+ * @param key Redis键
+ * @param value 值
+ */
+ public static void setAtomicValue(String key, long value) {
+ RAtomicLong atomic = CLIENT.getAtomicLong(key);
+ atomic.set(value);
+ }
+
+ /**
+ * 获取原子值
+ *
+ * @param key Redis键
+ * @return 当前值
+ */
+ public static long getAtomicValue(String key) {
+ RAtomicLong atomic = CLIENT.getAtomicLong(key);
+ return atomic.get();
+ }
+
+ /**
+ * 递增原子值
+ *
+ * @param key Redis键
+ * @return 当前值
+ */
+ public static long incrAtomicValue(String key) {
+ RAtomicLong atomic = CLIENT.getAtomicLong(key);
+ return atomic.incrementAndGet();
+ }
+
+ /**
+ * 递减原子值
+ *
+ * @param key Redis键
+ * @return 当前值
+ */
+ public static long decrAtomicValue(String key) {
+ RAtomicLong atomic = CLIENT.getAtomicLong(key);
+ return atomic.decrementAndGet();
+ }
+
/**
* 获得缓存的基本对象列表
*
diff --git a/ruoyi/src/main/java/com/ruoyi/demo/controller/MailController.java b/ruoyi/src/main/java/com/ruoyi/demo/controller/MailController.java
new file mode 100644
index 000000000..6dabe5132
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/demo/controller/MailController.java
@@ -0,0 +1,48 @@
+package com.ruoyi.demo.controller;
+
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.utils.email.MailUtils;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.io.File;
+
+
+/**
+ * 邮件发送案例
+ *
+ * @author Michelle.Chung
+ */
+@Validated
+@Api(value = "邮件发送案例", tags = {"邮件发送案例"})
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/demo/mail")
+public class MailController {
+
+ @ApiOperation("发送邮件")
+ @GetMapping("/sendSimpleMessage")
+ public R sendSimpleMessage(@ApiParam("接收人") String to,
+ @ApiParam("标题") String subject,
+ @ApiParam("内容") String text) {
+ MailUtils.sendText(to, subject, text);
+ return R.ok();
+ }
+
+ @ApiOperation("发送邮件(带附件)")
+ @GetMapping("/sendMessageWithAttachment")
+ public R sendMessageWithAttachment(@ApiParam("接收人") String to,
+ @ApiParam("标题") String subject,
+ @ApiParam("内容") String text,
+ @ApiParam("附件路径") String filePath) {
+ MailUtils.sendText(to, subject, text, new File(filePath));
+ return R.ok();
+ }
+
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/demo/controller/RedisCacheController.java b/ruoyi/src/main/java/com/ruoyi/demo/controller/RedisCacheController.java
index 98cca18c0..9fc93074f 100644
--- a/ruoyi/src/main/java/com/ruoyi/demo/controller/RedisCacheController.java
+++ b/ruoyi/src/main/java/com/ruoyi/demo/controller/RedisCacheController.java
@@ -12,7 +12,7 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
-import java.util.concurrent.TimeUnit;
+import java.time.Duration;
/**
* spring-cache 演示案例
@@ -87,7 +87,7 @@ public class RedisCacheController {
@GetMapping("/test6")
public R test6(String key, String value) {
RedisUtils.setCacheObject(key, value);
- boolean flag = RedisUtils.expire(key, 10, TimeUnit.SECONDS);
+ boolean flag = RedisUtils.expire(key, Duration.ofSeconds(10));
System.out.println("***********" + flag);
try {
Thread.sleep(11 * 1000);
diff --git a/ruoyi/src/main/java/com/ruoyi/demo/controller/SmsController.java b/ruoyi/src/main/java/com/ruoyi/demo/controller/SmsController.java
new file mode 100644
index 000000000..b92078624
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/demo/controller/SmsController.java
@@ -0,0 +1,72 @@
+package com.ruoyi.demo.controller;
+
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.utils.spring.SpringUtils;
+import com.ruoyi.sms.config.properties.SmsProperties;
+import com.ruoyi.sms.core.SmsTemplate;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 短信演示案例
+ * 请先阅读文档 否则无法使用
+ *
+ * @author Lion Li
+ * @version 4.2.0
+ */
+@Validated
+@Api(value = "短信演示案例", tags = {"短信演示案例"})
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/demo/sms")
+public class SmsController {
+
+ private final SmsProperties smsProperties;
+// private final SmsTemplate smsTemplate; // 可以使用spring注入
+// private final AliyunSmsTemplate smsTemplate; // 也可以注入某个厂家的模板工具
+
+ @ApiOperation("发送短信Aliyun")
+ @GetMapping("/sendAliyun")
+ public R sendAliyun(@ApiParam("电话号") String phones,
+ @ApiParam("模板ID") String templateId) {
+ if (!smsProperties.getEnabled()) {
+ return R.fail("当前系统没有开启短信功能!");
+ }
+ if (!SpringUtils.containsBean("aliyunSmsTemplate")) {
+ return R.fail("阿里云依赖未引入!");
+ }
+ SmsTemplate smsTemplate = SpringUtils.getBean(SmsTemplate.class);
+ Map map = new HashMap<>(1);
+ map.put("code", "1234");
+ Object send = smsTemplate.send(phones, templateId, map);
+ return R.ok(send);
+ }
+
+ @ApiOperation("发送短信Tencent")
+ @GetMapping("/sendTencent")
+ public R sendTencent(@ApiParam("电话号") String phones,
+ @ApiParam("模板ID") String templateId) {
+ if (!smsProperties.getEnabled()) {
+ return R.fail("当前系统没有开启短信功能!");
+ }
+ if (!SpringUtils.containsBean("tencentSmsTemplate")) {
+ return R.fail("腾讯云依赖未引入!");
+ }
+ SmsTemplate smsTemplate = SpringUtils.getBean(SmsTemplate.class);
+ Map map = new HashMap<>(1);
+// map.put("2", "测试测试");
+ map.put("1", "1234");
+ Object send = smsTemplate.send(phones, templateId, map);
+ return R.ok(send);
+ }
+
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/demo/controller/TestDemoController.java b/ruoyi/src/main/java/com/ruoyi/demo/controller/TestDemoController.java
index d1f994e5d..9f00c0770 100644
--- a/ruoyi/src/main/java/com/ruoyi/demo/controller/TestDemoController.java
+++ b/ruoyi/src/main/java/com/ruoyi/demo/controller/TestDemoController.java
@@ -29,6 +29,7 @@ import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
+import java.io.File;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -70,7 +71,7 @@ public class TestDemoController extends BaseController {
@ApiOperation("导入测试-校验")
@ApiImplicitParams({
- @ApiImplicitParam(name = "file", value = "导入文件", dataType = "java.io.File", required = true),
+ @ApiImplicitParam(name = "file", value = "导入文件", paramType = "query", dataTypeClass = File.class, required = true),
})
@Log(title = "测试单表", businessType = BusinessType.IMPORT)
@SaCheckPermission("demo:demo:import")
diff --git a/ruoyi/src/main/java/com/ruoyi/demo/controller/TestExcelController.java b/ruoyi/src/main/java/com/ruoyi/demo/controller/TestExcelController.java
new file mode 100644
index 000000000..a318b4658
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/demo/controller/TestExcelController.java
@@ -0,0 +1,102 @@
+package com.ruoyi.demo.controller;
+
+import cn.hutool.core.collection.CollUtil;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 测试Excel功能
+ *
+ * @author Lion Li
+ */
+@Api(value = "测试Excel功能", tags = {"测试Excel功能"})
+@RestController
+@RequestMapping("/demo/excel")
+public class TestExcelController {
+
+ /**
+ * 单列表多数据
+ */
+ @ApiOperation(value = "单列表多数据")
+ @GetMapping("/exportTemplateOne")
+ public void exportTemplateOne(HttpServletResponse response) {
+ Map map = new HashMap<>();
+ map.put("title","单列表多数据");
+ map.put("test1","数据测试1");
+ map.put("test2","数据测试2");
+ map.put("test3","数据测试3");
+ map.put("test4","数据测试4");
+ map.put("testTest","666");
+ List list = new ArrayList<>();
+ list.add(new TestObj("单列表测试1", "列表测试1", "列表测试2", "列表测试3", "列表测试4"));
+ list.add(new TestObj("单列表测试2", "列表测试5", "列表测试6", "列表测试7", "列表测试8"));
+ list.add(new TestObj("单列表测试3", "列表测试9", "列表测试10", "列表测试11", "列表测试12"));
+ ExcelUtil.exportTemplate(CollUtil.newArrayList(map,list),"单列表.xlsx", "excel/单列表.xlsx", response);
+ }
+
+ /**
+ * 多列表多数据
+ */
+ @ApiOperation(value = "多列表多数据")
+ @GetMapping("/exportTemplateMuliti")
+ public void exportTemplateMuliti(HttpServletResponse response) {
+ Map map = new HashMap<>();
+ map.put("title1","标题1");
+ map.put("title2","标题2");
+ map.put("title3","标题3");
+ map.put("title4","标题4");
+ map.put("author","Lion Li");
+ List list1 = new ArrayList<>();
+ list1.add(new TestObj1("list1测试1", "list1测试2", "list1测试3"));
+ list1.add(new TestObj1("list1测试4", "list1测试5", "list1测试6"));
+ list1.add(new TestObj1("list1测试7", "list1测试8", "list1测试9"));
+ List list2 = new ArrayList<>();
+ list2.add(new TestObj1("list2测试1", "list2测试2", "list2测试3"));
+ list2.add(new TestObj1("list2测试4", "list2测试5", "list2测试6"));
+ List list3 = new ArrayList<>();
+ list3.add(new TestObj1("list3测试1", "list3测试2", "list3测试3"));
+ List list4 = new ArrayList<>();
+ list4.add(new TestObj1("list4测试1", "list4测试2", "list4测试3"));
+ list4.add(new TestObj1("list4测试4", "list4测试5", "list4测试6"));
+ list4.add(new TestObj1("list4测试7", "list4测试8", "list4测试9"));
+ list4.add(new TestObj1("list4测试10", "list4测试11", "list4测试12"));
+ Map multiListMap = new HashMap<>();
+ multiListMap.put("map",map);
+ multiListMap.put("data1",list1);
+ multiListMap.put("data2",list2);
+ multiListMap.put("data3",list3);
+ multiListMap.put("data4",list4);
+ ExcelUtil.exportTemplateMultiList(multiListMap, "多列表.xlsx", "excel/多列表.xlsx", response);
+ }
+
+ @Data
+ @AllArgsConstructor
+ static class TestObj1 {
+ private String test1;
+ private String test2;
+ private String test3;
+ }
+
+ @Data
+ @AllArgsConstructor
+ static class TestObj {
+ private String name;
+ private String list1;
+ private String list2;
+ private String list3;
+ private String list4;
+ }
+
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/demo/domain/bo/TestDemoBo.java b/ruoyi/src/main/java/com/ruoyi/demo/domain/bo/TestDemoBo.java
index 5c41204d7..61c10ab16 100644
--- a/ruoyi/src/main/java/com/ruoyi/demo/domain/bo/TestDemoBo.java
+++ b/ruoyi/src/main/java/com/ruoyi/demo/domain/bo/TestDemoBo.java
@@ -49,7 +49,7 @@ public class TestDemoBo extends BaseEntity {
*/
@ApiModelProperty("排序号")
@NotNull(message = "排序号不能为空", groups = {AddGroup.class, EditGroup.class})
- private Long orderNum;
+ private Integer orderNum;
/**
* key键
diff --git a/ruoyi/src/main/java/com/ruoyi/demo/domain/vo/TestDemoVo.java b/ruoyi/src/main/java/com/ruoyi/demo/domain/vo/TestDemoVo.java
index ef26ff325..1e896500d 100644
--- a/ruoyi/src/main/java/com/ruoyi/demo/domain/vo/TestDemoVo.java
+++ b/ruoyi/src/main/java/com/ruoyi/demo/domain/vo/TestDemoVo.java
@@ -48,7 +48,7 @@ public class TestDemoVo {
*/
@ExcelProperty(value = "排序号")
@ApiModelProperty("排序号")
- private Long orderNum;
+ private Integer orderNum;
/**
* key键
diff --git a/ruoyi/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java b/ruoyi/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java
index b1b80d26e..11a3d50e2 100644
--- a/ruoyi/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java
+++ b/ruoyi/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java
@@ -54,5 +54,5 @@ public interface TestDemoMapper extends BaseMapperPlus idList);
+ int deleteBatchIds(@Param(Constants.COLL) Collection> idList);
}
diff --git a/ruoyi/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java b/ruoyi/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java
index aedc4431c..b77acb40e 100644
--- a/ruoyi/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java
+++ b/ruoyi/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java
@@ -25,9 +25,9 @@ import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import java.time.Duration;
import java.util.Collection;
import java.util.Map;
-import java.util.concurrent.TimeUnit;
/**
* 防止重复提交(参考美团GTIS防重系统)
@@ -66,7 +66,7 @@ public class RepeatSubmitAspect {
String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + url + submitKey;
String key = RedisUtils.getCacheObject(cacheRepeatKey);
if (key == null) {
- RedisUtils.setCacheObject(cacheRepeatKey, "", interval, TimeUnit.MILLISECONDS);
+ RedisUtils.setCacheObject(cacheRepeatKey, "", Duration.ofMillis(interval));
KEY_CACHE.set(cacheRepeatKey);
} else {
String message = repeatSubmit.message();
diff --git a/ruoyi/src/main/java/com/ruoyi/framework/config/JacksonConfig.java b/ruoyi/src/main/java/com/ruoyi/framework/config/JacksonConfig.java
index a5d637165..eace537a5 100644
--- a/ruoyi/src/main/java/com/ruoyi/framework/config/JacksonConfig.java
+++ b/ruoyi/src/main/java/com/ruoyi/framework/config/JacksonConfig.java
@@ -1,17 +1,14 @@
package com.ruoyi.framework.config;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.ruoyi.framework.jackson.BigNumberSerializer;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.boot.autoconfigure.jackson.JacksonProperties;
+import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Primary;
-import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import java.math.BigDecimal;
import java.math.BigInteger;
@@ -28,23 +25,22 @@ import java.util.TimeZone;
@Configuration
public class JacksonConfig {
- @Primary
@Bean
- public ObjectMapper getObjectMapper(Jackson2ObjectMapperBuilder builder, JacksonProperties jacksonProperties) {
- ObjectMapper objectMapper = builder.createXmlMapper(false).build();
- // 全局配置序列化返回 JSON 处理
- SimpleModule simpleModule = new SimpleModule();
- simpleModule.addSerializer(Long.class, BigNumberSerializer.INSTANCE);
- simpleModule.addSerializer(Long.TYPE, BigNumberSerializer.INSTANCE);
- simpleModule.addSerializer(BigInteger.class, BigNumberSerializer.INSTANCE);
- simpleModule.addSerializer(BigDecimal.class, ToStringSerializer.instance);
- DateTimeFormatter formatter = DateTimeFormatter.ofPattern(jacksonProperties.getDateFormat());
- simpleModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter));
- simpleModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter));
- objectMapper.registerModule(simpleModule);
- objectMapper.setTimeZone(TimeZone.getDefault());
- log.info("初始化 jackson 配置");
- return objectMapper;
+ public Jackson2ObjectMapperBuilderCustomizer customizer() {
+ return builder -> {
+ // 全局配置序列化返回 JSON 处理
+ JavaTimeModule javaTimeModule = new JavaTimeModule();
+ javaTimeModule.addSerializer(Long.class, BigNumberSerializer.INSTANCE);
+ javaTimeModule.addSerializer(Long.TYPE, BigNumberSerializer.INSTANCE);
+ javaTimeModule.addSerializer(BigInteger.class, BigNumberSerializer.INSTANCE);
+ javaTimeModule.addSerializer(BigDecimal.class, ToStringSerializer.instance);
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+ javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter));
+ javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter));
+ builder.modules(javaTimeModule);
+ builder.timeZone(TimeZone.getDefault());
+ log.info("初始化 jackson 配置");
+ };
}
}
diff --git a/ruoyi/src/main/java/com/ruoyi/framework/config/MailConfig.java b/ruoyi/src/main/java/com/ruoyi/framework/config/MailConfig.java
new file mode 100644
index 000000000..20769aa19
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/framework/config/MailConfig.java
@@ -0,0 +1,35 @@
+package com.ruoyi.framework.config;
+
+import cn.hutool.extra.mail.MailAccount;
+import com.ruoyi.framework.config.properties.MailProperties;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * JavaMail 配置
+ *
+ * @author Michelle.Chung
+ */
+@Configuration
+public class MailConfig {
+
+ @Bean
+ @ConditionalOnProperty(value = "mail.enabled", havingValue = "true")
+ public MailAccount mailAccount(MailProperties mailProperties) {
+ MailAccount account = new MailAccount();
+ account.setHost(mailProperties.getHost());
+ account.setPort(mailProperties.getPort());
+ account.setAuth(mailProperties.getAuth());
+ account.setFrom(mailProperties.getFrom());
+ account.setUser(mailProperties.getUser());
+ account.setPass(mailProperties.getPass());
+ account.setSocketFactoryPort(mailProperties.getPort());
+ account.setStarttlsEnable(mailProperties.getStarttlsEnable());
+ account.setSslEnable(mailProperties.getSslEnable());
+ account.setTimeout(mailProperties.getTimeout());
+ account.setConnectionTimeout(mailProperties.getConnectionTimeout());
+ return account;
+ }
+
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/framework/config/RedisConfig.java b/ruoyi/src/main/java/com/ruoyi/framework/config/RedisConfig.java
index da18cfd17..38a871895 100644
--- a/ruoyi/src/main/java/com/ruoyi/framework/config/RedisConfig.java
+++ b/ruoyi/src/main/java/com/ruoyi/framework/config/RedisConfig.java
@@ -1,28 +1,25 @@
package com.ruoyi.framework.config;
import cn.hutool.core.util.ObjectUtil;
-import com.ruoyi.common.utils.StringUtils;
+import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.framework.config.properties.RedissonProperties;
import lombok.extern.slf4j.Slf4j;
-import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.codec.JsonJacksonCodec;
-import org.redisson.config.Config;
import org.redisson.spring.cache.CacheConfig;
import org.redisson.spring.cache.RedissonSpringCacheManager;
+import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Primary;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.stream.Collectors;
/**
* redis配置
@@ -32,72 +29,49 @@ import java.util.stream.Collectors;
@Slf4j
@Configuration
@EnableCaching
+@EnableConfigurationProperties(RedissonProperties.class)
public class RedisConfig extends CachingConfigurerSupport {
- private static final String REDIS_PROTOCOL_PREFIX = "redis://";
- private static final String REDISS_PROTOCOL_PREFIX = "rediss://";
-
- @Autowired
- private RedisProperties redisProperties;
-
@Autowired
private RedissonProperties redissonProperties;
- @Primary
- @Bean(destroyMethod = "shutdown")
- public RedissonClient redisson() {
- String prefix = REDIS_PROTOCOL_PREFIX;
- if (redisProperties.isSsl()) {
- prefix = REDISS_PROTOCOL_PREFIX;
- }
- Config config = new Config();
- config.setThreads(redissonProperties.getThreads())
- .setNettyThreads(redissonProperties.getNettyThreads())
- .setCodec(JsonJacksonCodec.INSTANCE);
+ @Autowired
+ private ObjectMapper objectMapper;
- RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig();
- if (ObjectUtil.isNotNull(singleServerConfig)) {
- // 使用单机模式
- config.useSingleServer()
- .setAddress(prefix + redisProperties.getHost() + ":" + redisProperties.getPort())
- .setConnectTimeout(((Long) redisProperties.getTimeout().toMillis()).intValue())
- .setDatabase(redisProperties.getDatabase())
- .setPassword(StringUtils.isNotBlank(redisProperties.getPassword()) ? redisProperties.getPassword() : null)
- .setTimeout(singleServerConfig.getTimeout())
- .setClientName(singleServerConfig.getClientName())
- .setIdleConnectionTimeout(singleServerConfig.getIdleConnectionTimeout())
- .setSubscriptionConnectionPoolSize(singleServerConfig.getSubscriptionConnectionPoolSize())
- .setConnectionMinimumIdleSize(singleServerConfig.getConnectionMinimumIdleSize())
- .setConnectionPoolSize(singleServerConfig.getConnectionPoolSize());
- }
- // 集群配置方式 参考下方注释
- RedissonProperties.ClusterServersConfig clusterServersConfig = redissonProperties.getClusterServersConfig();
- if (ObjectUtil.isNotNull(clusterServersConfig)) {
- // 使用集群模式
- String finalPrefix = prefix;
- List nodes = redisProperties.getCluster().getNodes()
- .stream()
- .map(node -> finalPrefix + node)
- .collect(Collectors.toList());
-
- config.useClusterServers()
- .setConnectTimeout(((Long) redisProperties.getTimeout().toMillis()).intValue())
- .setPassword(StringUtils.isNotBlank(redisProperties.getPassword()) ? redisProperties.getPassword() : null)
- .setTimeout(clusterServersConfig.getTimeout())
- .setClientName(clusterServersConfig.getClientName())
- .setIdleConnectionTimeout(clusterServersConfig.getIdleConnectionTimeout())
- .setSubscriptionConnectionPoolSize(clusterServersConfig.getSubscriptionConnectionPoolSize())
- .setMasterConnectionMinimumIdleSize(clusterServersConfig.getMasterConnectionMinimumIdleSize())
- .setMasterConnectionPoolSize(clusterServersConfig.getMasterConnectionPoolSize())
- .setSlaveConnectionMinimumIdleSize(clusterServersConfig.getSlaveConnectionMinimumIdleSize())
- .setSlaveConnectionPoolSize(clusterServersConfig.getSlaveConnectionPoolSize())
- .setReadMode(clusterServersConfig.getReadMode())
- .setSubscriptionMode(clusterServersConfig.getSubscriptionMode())
- .setNodeAddresses(nodes);
- }
- RedissonClient redissonClient = Redisson.create(config);
- log.info("初始化 redis 配置");
- return redissonClient;
+ @Bean
+ public RedissonAutoConfigurationCustomizer redissonCustomizer() {
+ return config -> {
+ config.setThreads(redissonProperties.getThreads())
+ .setNettyThreads(redissonProperties.getNettyThreads())
+ .setCodec(new JsonJacksonCodec(objectMapper));
+ RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig();
+ if (ObjectUtil.isNotNull(singleServerConfig)) {
+ // 使用单机模式
+ config.useSingleServer()
+ .setTimeout(singleServerConfig.getTimeout())
+ .setClientName(singleServerConfig.getClientName())
+ .setIdleConnectionTimeout(singleServerConfig.getIdleConnectionTimeout())
+ .setSubscriptionConnectionPoolSize(singleServerConfig.getSubscriptionConnectionPoolSize())
+ .setConnectionMinimumIdleSize(singleServerConfig.getConnectionMinimumIdleSize())
+ .setConnectionPoolSize(singleServerConfig.getConnectionPoolSize());
+ }
+ // 集群配置方式 参考下方注释
+ RedissonProperties.ClusterServersConfig clusterServersConfig = redissonProperties.getClusterServersConfig();
+ if (ObjectUtil.isNotNull(clusterServersConfig)) {
+ config.useClusterServers()
+ .setTimeout(clusterServersConfig.getTimeout())
+ .setClientName(clusterServersConfig.getClientName())
+ .setIdleConnectionTimeout(clusterServersConfig.getIdleConnectionTimeout())
+ .setSubscriptionConnectionPoolSize(clusterServersConfig.getSubscriptionConnectionPoolSize())
+ .setMasterConnectionMinimumIdleSize(clusterServersConfig.getMasterConnectionMinimumIdleSize())
+ .setMasterConnectionPoolSize(clusterServersConfig.getMasterConnectionPoolSize())
+ .setSlaveConnectionMinimumIdleSize(clusterServersConfig.getSlaveConnectionMinimumIdleSize())
+ .setSlaveConnectionPoolSize(clusterServersConfig.getSlaveConnectionPoolSize())
+ .setReadMode(clusterServersConfig.getReadMode())
+ .setSubscriptionMode(clusterServersConfig.getSubscriptionMode());
+ }
+ log.info("初始化 redis 配置");
+ };
}
/**
@@ -112,7 +86,7 @@ public class RedisConfig extends CachingConfigurerSupport {
cacheConfig.setMaxSize(group.getMaxSize());
config.put(group.getGroupId(), cacheConfig);
}
- return new RedissonSpringCacheManager(redissonClient, config, JsonJacksonCodec.INSTANCE);
+ return new RedissonSpringCacheManager(redissonClient, config, new JsonJacksonCodec(objectMapper));
}
/**
diff --git a/ruoyi/src/main/java/com/ruoyi/framework/config/SaTokenConfig.java b/ruoyi/src/main/java/com/ruoyi/framework/config/SaTokenConfig.java
index 671400f6f..a5be2678e 100644
--- a/ruoyi/src/main/java/com/ruoyi/framework/config/SaTokenConfig.java
+++ b/ruoyi/src/main/java/com/ruoyi/framework/config/SaTokenConfig.java
@@ -2,11 +2,12 @@ package com.ruoyi.framework.config;
import cn.dev33.satoken.interceptor.SaAnnotationInterceptor;
import cn.dev33.satoken.interceptor.SaRouteInterceptor;
-import cn.dev33.satoken.jwt.StpLogicJwtForStyle;
+import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil;
-import com.ruoyi.common.helper.LoginHelper;
+import com.ruoyi.common.utils.spring.SpringUtils;
+import com.ruoyi.framework.config.properties.ExcludeUrlProperties;
import com.ruoyi.framework.config.properties.SecurityProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -15,9 +16,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
/**
* sa-token 配置
*
@@ -37,12 +35,14 @@ public class SaTokenConfig implements WebMvcConfigurer {
public void addInterceptors(InterceptorRegistry registry) {
// 注册路由拦截器,自定义验证规则
registry.addInterceptor(new SaRouteInterceptor((request, response, handler) -> {
+ ExcludeUrlProperties excludeUrlProperties = SpringUtils.getBean(ExcludeUrlProperties.class);
// 登录验证 -- 排除多个路径
SaRouter
// 获取所有的
.match("/**")
// 排除下不需要拦截的
.notMatch(securityProperties.getExcludes())
+ .notMatch(excludeUrlProperties.getExcludes())
// 对未排除的路径进行检查
.check(() -> {
// 检查是否登录 是否有token
@@ -55,20 +55,14 @@ public class SaTokenConfig implements WebMvcConfigurer {
// }
});
- }) {
- @SuppressWarnings("all")
- @Override
- public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
- LoginHelper.clearCache();
- }
- }).addPathPatterns("/**");
+ })).addPathPatterns("/**");
registry.addInterceptor(new SaAnnotationInterceptor()).addPathPatterns("/**");
}
@Bean
public StpLogic getStpLogicJwt() {
- // Sa-Token 整合 jwt (Style模式)
- return new StpLogicJwtForStyle();
+ // Sa-Token 整合 jwt (简单模式)
+ return new StpLogicJwtForSimple();
}
}
diff --git a/ruoyi/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java b/ruoyi/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java
index 22ef7c1b5..2424981c1 100644
--- a/ruoyi/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java
+++ b/ruoyi/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java
@@ -109,7 +109,7 @@ public class SwaggerConfig {
* 安全模式,这里指定token通过Authorization头请求头传递
*/
private List securitySchemes() {
- List apiKeyList = new ArrayList();
+ List apiKeyList = new ArrayList<>();
String header = saTokenConfig.getTokenName();
apiKeyList.add(new ApiKey(header, header, In.HEADER.toValue()));
return apiKeyList;
diff --git a/ruoyi/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java b/ruoyi/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java
index ebf236c87..a85ad1e57 100644
--- a/ruoyi/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java
+++ b/ruoyi/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java
@@ -33,8 +33,8 @@ public class ThreadPoolConfig {
@ConditionalOnProperty(prefix = "thread-pool", name = "enabled", havingValue = "true")
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
- executor.setMaxPoolSize(core);
- executor.setCorePoolSize(core * 2);
+ executor.setCorePoolSize(core);
+ executor.setMaxPoolSize(core * 2);
executor.setQueueCapacity(threadPoolProperties.getQueueCapacity());
executor.setKeepAliveSeconds(threadPoolProperties.getKeepAliveSeconds());
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
diff --git a/ruoyi/src/main/java/com/ruoyi/framework/config/UndertowConfig.java b/ruoyi/src/main/java/com/ruoyi/framework/config/UndertowConfig.java
new file mode 100644
index 000000000..64e745a86
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/framework/config/UndertowConfig.java
@@ -0,0 +1,30 @@
+package com.ruoyi.framework.config;
+
+import io.undertow.server.DefaultByteBufferPool;
+import io.undertow.websockets.jsr.WebSocketDeploymentInfo;
+import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
+import org.springframework.boot.web.server.WebServerFactoryCustomizer;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Undertow 自定义配置
+ *
+ * @author Lion Li
+ */
+@Configuration
+public class UndertowConfig implements WebServerFactoryCustomizer {
+
+ /**
+ * 设置 Undertow 的 websocket 缓冲池
+ */
+ @Override
+ public void customize(UndertowServletWebServerFactory factory) {
+ // 默认不直接分配内存 如果项目中使用了 websocket 建议直接分配
+ factory.addDeploymentInfoCustomizers(deploymentInfo -> {
+ WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo();
+ webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(false, 512));
+ deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo", webSocketDeploymentInfo);
+ });
+ }
+
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/framework/config/properties/ExcludeUrlProperties.java b/ruoyi/src/main/java/com/ruoyi/framework/config/properties/ExcludeUrlProperties.java
new file mode 100644
index 000000000..b4e3eae6d
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/framework/config/properties/ExcludeUrlProperties.java
@@ -0,0 +1,61 @@
+package com.ruoyi.framework.config.properties;
+
+import cn.hutool.core.util.ReUtil;
+import com.ruoyi.common.annotation.Anonymous;
+import com.ruoyi.common.utils.spring.SpringUtils;
+import lombok.Getter;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.stereotype.Component;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
+import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
+
+import java.util.*;
+import java.util.regex.Pattern;
+
+/**
+ * 设置注解允许匿名访问的url
+ *
+ * @author Lion Li
+ */
+@Lazy
+@Component
+public class ExcludeUrlProperties implements InitializingBean {
+
+ private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}");
+
+ @Getter
+ private final List excludes = new ArrayList<>();
+
+ @Override
+ public void afterPropertiesSet() {
+ String asterisk = "*";
+ RequestMappingHandlerMapping mapping = SpringUtils.getBean(RequestMappingHandlerMapping.class);
+ Map map = mapping.getHandlerMethods();
+
+ map.keySet().forEach(info -> {
+ HandlerMethod handlerMethod = map.get(info);
+
+ // 获取方法上边的注解 替代path variable 为 *
+ Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class);
+ Optional.ofNullable(method).ifPresent(anonymous -> {
+ Set patterns = info.getPatternsCondition().getPatterns();
+ patterns.forEach(url -> {
+ excludes.add(ReUtil.replaceAll(url, PATTERN, asterisk));
+ });
+ });
+
+ // 获取类上边的注解, 替代path variable 为 *
+ Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class);
+ Optional.ofNullable(controller).ifPresent(anonymous -> {
+ Set patterns = info.getPatternsCondition().getPatterns();
+ patterns.forEach(url -> {
+ excludes.add(ReUtil.replaceAll(url, PATTERN, asterisk));
+ });
+ });
+ });
+ }
+
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/framework/config/properties/MailProperties.java b/ruoyi/src/main/java/com/ruoyi/framework/config/properties/MailProperties.java
new file mode 100644
index 000000000..95e6cb8ba
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/framework/config/properties/MailProperties.java
@@ -0,0 +1,71 @@
+package com.ruoyi.framework.config.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * JavaMail 配置属性
+ *
+ * @author Michelle.Chung
+ */
+@Data
+@Component
+@ConfigurationProperties(prefix = "mail")
+public class MailProperties {
+
+ /**
+ * 过滤开关
+ */
+ private Boolean enabled;
+
+ /**
+ * SMTP服务器域名
+ */
+ private String host;
+
+ /**
+ * SMTP服务端口
+ */
+ private Integer port;
+
+ /**
+ * 是否需要用户名密码验证
+ */
+ private Boolean auth;
+
+ /**
+ * 用户名
+ */
+ private String user;
+
+ /**
+ * 密码
+ */
+ private String pass;
+
+ /**
+ * 发送方,遵循RFC-822标准
+ */
+ private String from;
+
+ /**
+ * 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。它将纯文本连接升级为加密连接(TLS或SSL), 而不是使用一个单独的加密通信端口。
+ */
+ private Boolean starttlsEnable;
+
+ /**
+ * 使用 SSL安全连接
+ */
+ private Boolean sslEnable;
+
+ /**
+ * SMTP超时时长,单位毫秒,缺省值不超时
+ */
+ private Long timeout;
+
+ /**
+ * Socket连接超时值,单位毫秒,缺省值不超时
+ */
+ private Long connectionTimeout;
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/framework/listener/UserActionListener.java b/ruoyi/src/main/java/com/ruoyi/framework/listener/UserActionListener.java
index 75277c257..dcb9b1996 100644
--- a/ruoyi/src/main/java/com/ruoyi/framework/listener/UserActionListener.java
+++ b/ruoyi/src/main/java/com/ruoyi/framework/listener/UserActionListener.java
@@ -3,7 +3,6 @@ package com.ruoyi.framework.listener;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.listener.SaTokenListener;
import cn.dev33.satoken.stp.SaLoginModel;
-import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import com.ruoyi.common.constant.Constants;
@@ -18,7 +17,7 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
-import java.util.concurrent.TimeUnit;
+import java.time.Duration;
/**
* 用户行为 侦听器的实现
@@ -36,13 +35,12 @@ public class UserActionListener implements SaTokenListener {
* 每次登录时触发
*/
@Override
- public void doLogin(String loginType, Object loginId, SaLoginModel loginModel) {
+ public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) {
UserType userType = UserType.getUserType(loginId.toString());
if (userType == UserType.SYS_USER) {
UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
String ip = ServletUtils.getClientIP();
LoginUser user = LoginHelper.getLoginUser();
- String tokenValue = StpUtil.getTokenValueByLoginId(loginId);
UserOnlineDTO dto = new UserOnlineDTO();
dto.setIpaddr(ip);
dto.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
@@ -52,7 +50,7 @@ public class UserActionListener implements SaTokenListener {
dto.setTokenId(tokenValue);
dto.setUserName(user.getUsername());
dto.setDeptName(user.getDeptName());
- RedisUtils.setCacheObject(Constants.ONLINE_TOKEN_KEY + tokenValue, dto, tokenConfig.getTimeout(), TimeUnit.SECONDS);
+ RedisUtils.setCacheObject(Constants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(tokenConfig.getTimeout()));
log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue);
} else if (userType == UserType.APP_USER) {
// app端 自行根据业务编写
diff --git a/ruoyi/src/main/java/com/ruoyi/framework/satoken/dao/PlusSaTokenDao.java b/ruoyi/src/main/java/com/ruoyi/framework/satoken/dao/PlusSaTokenDao.java
index f78e81415..68df5a646 100644
--- a/ruoyi/src/main/java/com/ruoyi/framework/satoken/dao/PlusSaTokenDao.java
+++ b/ruoyi/src/main/java/com/ruoyi/framework/satoken/dao/PlusSaTokenDao.java
@@ -5,10 +5,10 @@ import cn.dev33.satoken.util.SaFoxUtil;
import com.ruoyi.common.utils.redis.RedisUtils;
import org.springframework.stereotype.Component;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
-import java.util.concurrent.TimeUnit;
/**
* Sa-Token持久层接口(使用框架自带RedisUtils实现 协议统一)
@@ -38,7 +38,7 @@ public class PlusSaTokenDao implements SaTokenDao {
if (timeout == SaTokenDao.NEVER_EXPIRE) {
RedisUtils.setCacheObject(key, value);
} else {
- RedisUtils.setCacheObject(key, value, timeout, TimeUnit.SECONDS);
+ RedisUtils.setCacheObject(key, value, Duration.ofSeconds(timeout));
}
}
@@ -68,7 +68,8 @@ public class PlusSaTokenDao implements SaTokenDao {
*/
@Override
public long getTimeout(String key) {
- return RedisUtils.getTimeToLive(key) / 1000;
+ long timeout = RedisUtils.getTimeToLive(key);
+ return timeout < 0 ? timeout : timeout / 1000;
}
/**
@@ -87,7 +88,7 @@ public class PlusSaTokenDao implements SaTokenDao {
}
return;
}
- RedisUtils.expire(key, timeout, TimeUnit.SECONDS);
+ RedisUtils.expire(key, Duration.ofSeconds(timeout));
}
@@ -111,7 +112,7 @@ public class PlusSaTokenDao implements SaTokenDao {
if (timeout == SaTokenDao.NEVER_EXPIRE) {
RedisUtils.setCacheObject(key, object);
} else {
- RedisUtils.setCacheObject(key, object, timeout, TimeUnit.SECONDS);
+ RedisUtils.setCacheObject(key, object, Duration.ofSeconds(timeout));
}
}
@@ -141,7 +142,8 @@ public class PlusSaTokenDao implements SaTokenDao {
*/
@Override
public long getObjectTimeout(String key) {
- return RedisUtils.getTimeToLive(key) / 1000;
+ long timeout = RedisUtils.getTimeToLive(key);
+ return timeout < 0 ? timeout : timeout / 1000;
}
/**
@@ -160,7 +162,7 @@ public class PlusSaTokenDao implements SaTokenDao {
}
return;
}
- RedisUtils.expire(key, timeout, TimeUnit.SECONDS);
+ RedisUtils.expire(key, Duration.ofSeconds(timeout));
}
diff --git a/ruoyi/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java b/ruoyi/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java
index 9dccab2d1..2a99a2e65 100644
--- a/ruoyi/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java
+++ b/ruoyi/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java
@@ -33,6 +33,7 @@ import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -135,6 +136,7 @@ public class GenTableServiceImpl implements IGenTableService {
* @param genTable 业务信息
* @return 结果
*/
+ @Transactional(rollbackFor = Exception.class)
@Override
public void updateGenTable(GenTable genTable) {
String options = JsonUtils.toJsonString(genTable.getParams());
@@ -153,6 +155,7 @@ public class GenTableServiceImpl implements IGenTableService {
* @param tableIds 需要删除的数据ID
* @return 结果
*/
+ @Transactional(rollbackFor = Exception.class)
@Override
public void deleteGenTableByIds(Long[] tableIds) {
List ids = Arrays.asList(tableIds);
@@ -165,6 +168,7 @@ public class GenTableServiceImpl implements IGenTableService {
*
* @param tableList 导入表列表
*/
+ @Transactional(rollbackFor = Exception.class)
@Override
public void importGenTable(List tableList) {
String operName = LoginHelper.getUsername();
@@ -284,6 +288,7 @@ public class GenTableServiceImpl implements IGenTableService {
*
* @param tableName 表名称
*/
+ @Transactional(rollbackFor = Exception.class)
@Override
public void synchDb(String tableName) {
GenTable table = baseMapper.selectGenTableByName(tableName);
diff --git a/ruoyi/src/main/java/com/ruoyi/oss/constant/OssConstant.java b/ruoyi/src/main/java/com/ruoyi/oss/constant/OssConstant.java
index 6c00cd8db..1d1a77708 100644
--- a/ruoyi/src/main/java/com/ruoyi/oss/constant/OssConstant.java
+++ b/ruoyi/src/main/java/com/ruoyi/oss/constant/OssConstant.java
@@ -35,6 +35,11 @@ public interface OssConstant {
*/
List SYSTEM_DATA_IDS = Arrays.asList(1, 2, 3, 4);
+ /**
+ * 云服务商
+ */
+ String[] CLOUD_SERVICE = new String[] {"aliyun", "qcloud", "qiniu"};
+
/**
* https 状态
*/
diff --git a/ruoyi/src/main/java/com/ruoyi/oss/core/OssClient.java b/ruoyi/src/main/java/com/ruoyi/oss/core/OssClient.java
new file mode 100644
index 000000000..6f6be8f8c
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/oss/core/OssClient.java
@@ -0,0 +1,188 @@
+package com.ruoyi.oss.core;
+
+import cn.hutool.core.util.IdUtil;
+import com.amazonaws.ClientConfiguration;
+import com.amazonaws.Protocol;
+import com.amazonaws.auth.AWSCredentials;
+import com.amazonaws.auth.AWSCredentialsProvider;
+import com.amazonaws.auth.AWSStaticCredentialsProvider;
+import com.amazonaws.auth.BasicAWSCredentials;
+import com.amazonaws.client.builder.AwsClientBuilder;
+import com.amazonaws.services.s3.AmazonS3;
+import com.amazonaws.services.s3.AmazonS3Client;
+import com.amazonaws.services.s3.model.CannedAccessControlList;
+import com.amazonaws.services.s3.model.CreateBucketRequest;
+import com.amazonaws.services.s3.model.ObjectMetadata;
+import com.amazonaws.services.s3.model.PutObjectRequest;
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.oss.constant.OssConstant;
+import com.ruoyi.oss.entity.UploadResult;
+import com.ruoyi.oss.enumd.PolicyType;
+import com.ruoyi.oss.exception.OssException;
+import com.ruoyi.oss.properties.OssProperties;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+/**
+ * S3 存储协议 所有兼容S3协议的云厂商均支持
+ * 阿里云 腾讯云 七牛云 minio
+ *
+ * @author Lion Li
+ */
+public class OssClient {
+
+ private final String configKey;
+
+ private final OssProperties properties;
+
+ private final AmazonS3 client;
+
+ public OssClient(String configKey, OssProperties ossProperties) {
+ this.configKey = configKey;
+ this.properties = ossProperties;
+ try {
+ AwsClientBuilder.EndpointConfiguration endpointConfig =
+ new AwsClientBuilder.EndpointConfiguration(properties.getEndpoint(), properties.getRegion());
+
+ AWSCredentials credentials = new BasicAWSCredentials(properties.getAccessKey(), properties.getSecretKey());
+ AWSCredentialsProvider credentialsProvider = new AWSStaticCredentialsProvider(credentials);
+ ClientConfiguration clientConfig = new ClientConfiguration();
+ if (OssConstant.IS_HTTPS.equals(properties.getIsHttps())) {
+ clientConfig.setProtocol(Protocol.HTTPS);
+ } else {
+ clientConfig.setProtocol(Protocol.HTTP);
+ }
+ this.client = AmazonS3Client.builder()
+ .withEndpointConfiguration(endpointConfig)
+ .withClientConfiguration(clientConfig)
+ .withCredentials(credentialsProvider)
+ .disableChunkedEncoding()
+ .build();
+
+ createBucket();
+ } catch (Exception e) {
+ if (e instanceof OssException) {
+ throw e;
+ }
+ throw new OssException("配置错误! 请检查系统配置:[" + e.getMessage() + "]");
+ }
+ }
+
+ public void createBucket() {
+ try {
+ String bucketName = properties.getBucketName();
+ if (client.doesBucketExistV2(bucketName)) {
+ return;
+ }
+ CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
+ createBucketRequest.setCannedAcl(CannedAccessControlList.PublicRead);
+ client.createBucket(createBucketRequest);
+ client.setBucketPolicy(bucketName, getPolicy(bucketName, PolicyType.READ));
+ } catch (Exception e) {
+ throw new OssException("创建Bucket失败, 请核对配置信息:[" + e.getMessage() + "]");
+ }
+ }
+
+ public UploadResult upload(byte[] data, String path, String contentType) {
+ return upload(new ByteArrayInputStream(data), path, contentType);
+ }
+
+ public UploadResult upload(InputStream inputStream, String path, String contentType) {
+ try {
+ ObjectMetadata metadata = new ObjectMetadata();
+ metadata.setContentType(contentType);
+ metadata.setContentLength(inputStream.available());
+ client.putObject(new PutObjectRequest(properties.getBucketName(), path, inputStream, metadata));
+ } catch (Exception e) {
+ throw new OssException("上传文件失败,请检查配置信息:[" + e.getMessage() + "]");
+ }
+ return UploadResult.builder().url(getUrl() + "/" + path).filename(path).build();
+ }
+
+ public void delete(String path) {
+ path = path.replace(getUrl() + "/", "");
+ try {
+ client.deleteObject(properties.getBucketName(), path);
+ } catch (Exception e) {
+ throw new OssException("上传文件失败,请检查配置信息:[" + e.getMessage() + "]");
+ }
+ }
+
+ public UploadResult uploadSuffix(byte[] data, String suffix, String contentType) {
+ return upload(data, getPath(properties.getPrefix(), suffix), contentType);
+ }
+
+ public UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType) {
+ return upload(inputStream, getPath(properties.getPrefix(), suffix), contentType);
+ }
+
+ public String getUrl() {
+ String domain = properties.getDomain();
+ if (StringUtils.isNotBlank(domain)) {
+ return domain;
+ }
+ String endpoint = properties.getEndpoint();
+ String header = OssConstant.IS_HTTPS.equals(properties.getIsHttps()) ? "https://" : "http://";
+ // 云服务商直接返回
+ if (StringUtils.containsAny(endpoint, OssConstant.CLOUD_SERVICE)){
+ return header + properties.getBucketName() + "." + endpoint;
+ }
+ // minio 单独处理
+ return header + endpoint + "/" + properties.getBucketName();
+ }
+
+ public String getPath(String prefix, String suffix) {
+ // 生成uuid
+ String uuid = IdUtil.fastSimpleUUID();
+ // 文件路径
+ String path = DateUtils.datePath() + "/" + uuid;
+ if (StringUtils.isNotBlank(prefix)) {
+ path = prefix + "/" + path;
+ }
+ return path + suffix;
+ }
+
+
+ public String getConfigKey() {
+ return configKey;
+ }
+
+ private static String getPolicy(String bucketName, PolicyType policyType) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("{\n\"Statement\": [\n{\n\"Action\": [\n");
+ if (policyType == PolicyType.WRITE) {
+ builder.append("\"s3:GetBucketLocation\",\n\"s3:ListBucketMultipartUploads\"\n");
+ } else if (policyType == PolicyType.READ_WRITE) {
+ builder.append("\"s3:GetBucketLocation\",\n\"s3:ListBucket\",\n\"s3:ListBucketMultipartUploads\"\n");
+ } else {
+ builder.append("\"s3:GetBucketLocation\"\n");
+ }
+ builder.append("],\n\"Effect\": \"Allow\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::");
+ builder.append(bucketName);
+ builder.append("\"\n},\n");
+ if (policyType == PolicyType.READ) {
+ builder.append("{\n\"Action\": [\n\"s3:ListBucket\"\n],\n\"Effect\": \"Deny\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::");
+ builder.append(bucketName);
+ builder.append("\"\n},\n");
+ }
+ builder.append("{\n\"Action\": ");
+ switch (policyType) {
+ case WRITE:
+ builder.append("[\n\"s3:AbortMultipartUpload\",\n\"s3:DeleteObject\",\n\"s3:ListMultipartUploadParts\",\n\"s3:PutObject\"\n],\n");
+ break;
+ case READ_WRITE:
+ builder.append("[\n\"s3:AbortMultipartUpload\",\n\"s3:DeleteObject\",\n\"s3:GetObject\",\n\"s3:ListMultipartUploadParts\",\n\"s3:PutObject\"\n],\n");
+ break;
+ default:
+ builder.append("\"s3:GetObject\",\n");
+ break;
+ }
+ builder.append("\"Effect\": \"Allow\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::");
+ builder.append(bucketName);
+ builder.append("/*\"\n}\n],\n\"Version\": \"2012-10-17\"\n}\n");
+ return builder.toString();
+ }
+
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/oss/enumd/OssEnumd.java b/ruoyi/src/main/java/com/ruoyi/oss/enumd/OssEnumd.java
deleted file mode 100644
index e16a67338..000000000
--- a/ruoyi/src/main/java/com/ruoyi/oss/enumd/OssEnumd.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.ruoyi.oss.enumd;
-
-import com.ruoyi.oss.service.impl.AliyunOssStrategy;
-import com.ruoyi.oss.service.impl.MinioOssStrategy;
-import com.ruoyi.oss.service.impl.QcloudOssStrategy;
-import com.ruoyi.oss.service.impl.QiniuOssStrategy;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-
-/**
- * 对象存储服务商枚举
- *
- * @author Lion Li
- */
-@Getter
-@AllArgsConstructor
-public enum OssEnumd {
-
- /**
- * 七牛云
- */
- QINIU("qiniu", QiniuOssStrategy.class),
-
- /**
- * 阿里云
- */
- ALIYUN("aliyun", AliyunOssStrategy.class),
-
- /**
- * 腾讯云
- */
- QCLOUD("qcloud", QcloudOssStrategy.class),
-
- /**
- * minio
- */
- MINIO("minio", MinioOssStrategy.class);
-
- private final String value;
-
- private final Class> beanClass;
-
- public static OssEnumd find(String value) {
- for (OssEnumd enumd : values()) {
- if (enumd.getValue().equals(value)) {
- return enumd;
- }
- }
- return null;
- }
-
-}
diff --git a/ruoyi/src/main/java/com/ruoyi/oss/enumd/PolicyType.java b/ruoyi/src/main/java/com/ruoyi/oss/enumd/PolicyType.java
index e1925dcfb..606f0f484 100644
--- a/ruoyi/src/main/java/com/ruoyi/oss/enumd/PolicyType.java
+++ b/ruoyi/src/main/java/com/ruoyi/oss/enumd/PolicyType.java
@@ -1,19 +1,3 @@
-/*
- * Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * Neither the name of the dreamlu.net developer nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- * Author: Chill 庄骞 (smallchill@163.com)
- */
package com.ruoyi.oss.enumd;
import lombok.AllArgsConstructor;
diff --git a/ruoyi/src/main/java/com/ruoyi/oss/factory/OssFactory.java b/ruoyi/src/main/java/com/ruoyi/oss/factory/OssFactory.java
index 9ac887e65..7065c4a4a 100644
--- a/ruoyi/src/main/java/com/ruoyi/oss/factory/OssFactory.java
+++ b/ruoyi/src/main/java/com/ruoyi/oss/factory/OssFactory.java
@@ -3,15 +3,15 @@ package com.ruoyi.oss.factory;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.redis.RedisUtils;
-import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.oss.constant.OssConstant;
-import com.ruoyi.oss.enumd.OssEnumd;
+import com.ruoyi.oss.core.OssClient;
import com.ruoyi.oss.exception.OssException;
import com.ruoyi.oss.properties.OssProperties;
-import com.ruoyi.oss.service.IOssStrategy;
-import com.ruoyi.oss.service.abstractd.AbstractOssStrategy;
import lombok.extern.slf4j.Slf4j;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
/**
* 文件上传Factory
*
@@ -20,17 +20,19 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
public class OssFactory {
+ private static final Map CLIENT_CACHE = new ConcurrentHashMap<>();
+
/**
* 初始化工厂
*/
public static void init() {
log.info("初始化OSS工厂");
- RedisUtils.subscribe(OssConstant.CACHE_CONFIG_KEY, String.class, type -> {
- AbstractOssStrategy strategy = getStrategy(type);
+ RedisUtils.subscribe(OssConstant.CACHE_CONFIG_KEY, String.class, configKey -> {
+ OssClient client = getClient(configKey);
// 未初始化不处理
- if (strategy.isInit) {
- refresh(type);
- log.info("订阅刷新OSS配置 => " + type);
+ if (client != null) {
+ refresh(configKey);
+ log.info("订阅刷新OSS配置 => " + configKey);
}
});
}
@@ -38,42 +40,38 @@ public class OssFactory {
/**
* 获取默认实例
*/
- public static IOssStrategy instance() {
+ public static OssClient instance() {
// 获取redis 默认类型
- String type = RedisUtils.getCacheObject(OssConstant.CACHE_CONFIG_KEY);
- if (StringUtils.isEmpty(type)) {
+ String configKey = RedisUtils.getCacheObject(OssConstant.CACHE_CONFIG_KEY);
+ if (StringUtils.isEmpty(configKey)) {
throw new OssException("文件存储服务类型无法找到!");
}
- return instance(type);
+ return instance(configKey);
}
/**
* 根据类型获取实例
*/
- public static IOssStrategy instance(String type) {
- OssEnumd enumd = OssEnumd.find(type);
- if (enumd == null) {
- throw new OssException("文件存储服务类型无法找到!");
+ public static OssClient instance(String configKey) {
+ OssClient client = getClient(configKey);
+ if (client == null) {
+ refresh(configKey);
+ return getClient(configKey);
}
- AbstractOssStrategy strategy = getStrategy(type);
- if (!strategy.isInit) {
- refresh(type);
- }
- return strategy;
+ return client;
}
- private static void refresh(String type) {
- Object json = RedisUtils.getCacheObject(OssConstant.SYS_OSS_KEY + type);
+ private static void refresh(String configKey) {
+ Object json = RedisUtils.getCacheObject(OssConstant.SYS_OSS_KEY + configKey);
OssProperties properties = JsonUtils.parseObject(json.toString(), OssProperties.class);
if (properties == null) {
- throw new OssException("系统异常, '" + type + "'配置信息不存在!");
+ throw new OssException("系统异常, '" + configKey + "'配置信息不存在!");
}
- getStrategy(type).init(properties);
+ CLIENT_CACHE.put(configKey, new OssClient(configKey, properties));
}
- private static AbstractOssStrategy getStrategy(String type) {
- OssEnumd enumd = OssEnumd.find(type);
- return (AbstractOssStrategy) SpringUtils.getBean(enumd.getBeanClass());
+ private static OssClient getClient(String configKey) {
+ return CLIENT_CACHE.get(configKey);
}
}
diff --git a/ruoyi/src/main/java/com/ruoyi/oss/properties/OssProperties.java b/ruoyi/src/main/java/com/ruoyi/oss/properties/OssProperties.java
index d09bfdb69..a01777901 100644
--- a/ruoyi/src/main/java/com/ruoyi/oss/properties/OssProperties.java
+++ b/ruoyi/src/main/java/com/ruoyi/oss/properties/OssProperties.java
@@ -11,10 +11,15 @@ import lombok.Data;
public class OssProperties {
/**
- * 域名
+ * 访问站点
*/
private String endpoint;
+ /**
+ * 自定义域名
+ */
+ private String domain;
+
/**
* 前缀
*/
diff --git a/ruoyi/src/main/java/com/ruoyi/oss/service/IOssStrategy.java b/ruoyi/src/main/java/com/ruoyi/oss/service/IOssStrategy.java
deleted file mode 100644
index 981c23e9e..000000000
--- a/ruoyi/src/main/java/com/ruoyi/oss/service/IOssStrategy.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package com.ruoyi.oss.service;
-
-import com.ruoyi.oss.entity.UploadResult;
-import com.ruoyi.oss.enumd.OssEnumd;
-
-import java.io.InputStream;
-
-/**
- * 对象存储策略
- *
- * @author Lion Li
- */
-public interface IOssStrategy {
-
- /**
- * 创建存储桶
- */
- void createBucket();
-
- /**
- * 获取服务商类型
- * @return 对象存储服务商枚举
- */
- OssEnumd getServiceType();
-
- /**
- * 文件上传
- *
- * @param data 文件字节数组
- * @param path 文件路径,包含文件名
- * @param contentType 文件类型
- * @return 返回http地址
- */
- UploadResult upload(byte[] data, String path, String contentType);
-
- /**
- * 文件删除
- *
- * @param path 文件路径,包含文件名
- */
- void delete(String path);
-
- /**
- * 文件上传
- *
- * @param data 文件字节数组
- * @param suffix 后缀
- * @param contentType 文件类型
- * @return 返回http地址
- */
- UploadResult uploadSuffix(byte[] data, String suffix, String contentType);
-
- /**
- * 文件上传
- *
- * @param inputStream 字节流
- * @param path 文件路径,包含文件名
- * @param contentType 文件类型
- * @return 返回http地址
- */
- UploadResult upload(InputStream inputStream, String path, String contentType);
-
- /**
- * 文件上传
- *
- * @param inputStream 字节流
- * @param suffix 后缀
- * @param contentType 文件类型
- * @return 返回http地址
- */
- UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType);
-
-}
diff --git a/ruoyi/src/main/java/com/ruoyi/oss/service/abstractd/AbstractOssStrategy.java b/ruoyi/src/main/java/com/ruoyi/oss/service/abstractd/AbstractOssStrategy.java
deleted file mode 100644
index ae17c19de..000000000
--- a/ruoyi/src/main/java/com/ruoyi/oss/service/abstractd/AbstractOssStrategy.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package com.ruoyi.oss.service.abstractd;
-
-import cn.hutool.core.io.IoUtil;
-import cn.hutool.core.util.IdUtil;
-import com.ruoyi.common.utils.DateUtils;
-import com.ruoyi.common.utils.StringUtils;
-import com.ruoyi.oss.entity.UploadResult;
-import com.ruoyi.oss.enumd.OssEnumd;
-import com.ruoyi.oss.properties.OssProperties;
-import com.ruoyi.oss.service.IOssStrategy;
-
-import java.io.InputStream;
-
-/**
- * 对象存储策略(支持七牛、阿里云、腾讯云、minio)
- *
- * @author Lion Li
- */
-public abstract class AbstractOssStrategy implements IOssStrategy {
-
- protected OssProperties properties;
- public boolean isInit = false;
-
- public void init(OssProperties properties) {
- this.properties = properties;
- }
-
- @Override
- public abstract void createBucket();
-
- @Override
- public abstract OssEnumd getServiceType();
-
- public String getPath(String prefix, String suffix) {
- // 生成uuid
- String uuid = IdUtil.fastSimpleUUID();
- // 文件路径
- String path = DateUtils.datePath() + "/" + uuid;
- if (StringUtils.isNotBlank(prefix)) {
- path = prefix + "/" + path;
- }
- return path + suffix;
- }
-
- @Override
- public abstract UploadResult upload(byte[] data, String path, String contentType);
-
- @Override
- public abstract void delete(String path);
-
- @Override
- public UploadResult upload(InputStream inputStream, String path, String contentType) {
- byte[] data = IoUtil.readBytes(inputStream);
- return this.upload(data, path, contentType);
- }
-
- @Override
- public abstract UploadResult uploadSuffix(byte[] data, String suffix, String contentType);
-
- @Override
- public abstract UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType);
-
- /**
- * 获取域名访问链接
- *
- * @return 域名访问链接
- */
- public abstract String getEndpointLink();
-}
diff --git a/ruoyi/src/main/java/com/ruoyi/oss/service/impl/AliyunOssStrategy.java b/ruoyi/src/main/java/com/ruoyi/oss/service/impl/AliyunOssStrategy.java
deleted file mode 100644
index 62c29228f..000000000
--- a/ruoyi/src/main/java/com/ruoyi/oss/service/impl/AliyunOssStrategy.java
+++ /dev/null
@@ -1,120 +0,0 @@
-package com.ruoyi.oss.service.impl;
-
-import com.aliyun.oss.ClientConfiguration;
-import com.aliyun.oss.OSSClient;
-import com.aliyun.oss.common.auth.DefaultCredentialProvider;
-import com.aliyun.oss.common.comm.Protocol;
-import com.aliyun.oss.model.CannedAccessControlList;
-import com.aliyun.oss.model.CreateBucketRequest;
-import com.aliyun.oss.model.ObjectMetadata;
-import com.aliyun.oss.model.PutObjectRequest;
-import com.ruoyi.common.utils.StringUtils;
-import com.ruoyi.oss.constant.OssConstant;
-import com.ruoyi.oss.entity.UploadResult;
-import com.ruoyi.oss.enumd.OssEnumd;
-import com.ruoyi.oss.exception.OssException;
-import com.ruoyi.oss.properties.OssProperties;
-import com.ruoyi.oss.service.abstractd.AbstractOssStrategy;
-import org.springframework.stereotype.Component;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-
-/**
- * 阿里云存储策略
- *
- * @author Lion Li
- */
-@Component
-public class AliyunOssStrategy extends AbstractOssStrategy {
-
- private OSSClient client;
-
- @Override
- public void init(OssProperties ossProperties) {
- super.init(ossProperties);
- try {
- ClientConfiguration configuration = new ClientConfiguration();
- if (OssConstant.IS_HTTPS.equals(ossProperties.getIsHttps())) {
- configuration.setProtocol(Protocol.HTTPS);
- }
- DefaultCredentialProvider credentialProvider = new DefaultCredentialProvider(
- properties.getAccessKey(), properties.getSecretKey());
- client = new OSSClient(properties.getEndpoint(), credentialProvider, configuration);
- createBucket();
- } catch (Exception e) {
- throw new OssException("阿里云存储配置错误! 请检查系统配置:[" + e.getMessage() + "]");
- }
- isInit = true;
- }
-
- @Override
- public void createBucket() {
- try {
- String bucketName = properties.getBucketName();
- if (client.doesBucketExist(bucketName)) {
- return;
- }
- CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
- createBucketRequest.setCannedACL(CannedAccessControlList.PublicRead);
- client.createBucket(createBucketRequest);
- } catch (Exception e) {
- throw new OssException("创建Bucket失败, 请核对阿里云配置信息:[" + e.getMessage() + "]");
- }
- }
-
- @Override
- public OssEnumd getServiceType() {
- return OssEnumd.ALIYUN;
- }
-
- @Override
- public UploadResult upload(byte[] data, String path, String contentType) {
- return upload(new ByteArrayInputStream(data), path, contentType);
- }
-
- @Override
- public UploadResult upload(InputStream inputStream, String path, String contentType) {
- try {
- ObjectMetadata metadata = new ObjectMetadata();
- metadata.setContentType(contentType);
- client.putObject(new PutObjectRequest(properties.getBucketName(), path, inputStream, metadata));
- } catch (Exception e) {
- throw new OssException("上传文件失败,请检查阿里云配置信息:[" + e.getMessage() + "]");
- }
- return UploadResult.builder().url(getEndpointLink() + "/" + path).filename(path).build(); }
-
- @Override
- public void delete(String path) {
- path = path.replace(getEndpointLink() + "/", "");
- try {
- client.deleteObject(properties.getBucketName(), path);
- } catch (Exception e) {
- throw new OssException("上传文件失败,请检查阿里云配置信息:[" + e.getMessage() + "]");
- }
- }
-
- @Override
- public UploadResult uploadSuffix(byte[] data, String suffix, String contentType) {
- return upload(data, getPath(properties.getPrefix(), suffix), contentType);
- }
-
- @Override
- public UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType) {
- return upload(inputStream, getPath(properties.getPrefix(), suffix), contentType);
- }
-
- @Override
- public String getEndpointLink() {
- String endpoint = properties.getEndpoint();
- StringBuilder sb = new StringBuilder(endpoint);
- if (StringUtils.containsAnyIgnoreCase(endpoint, "http://")) {
- sb.insert(7, properties.getBucketName() + ".");
- } else if (StringUtils.containsAnyIgnoreCase(endpoint, "https://")) {
- sb.insert(8, properties.getBucketName() + ".");
- } else {
- throw new OssException("Endpoint配置错误");
- }
- return sb.toString();
- }
-}
diff --git a/ruoyi/src/main/java/com/ruoyi/oss/service/impl/MinioOssStrategy.java b/ruoyi/src/main/java/com/ruoyi/oss/service/impl/MinioOssStrategy.java
deleted file mode 100644
index f5be957fa..000000000
--- a/ruoyi/src/main/java/com/ruoyi/oss/service/impl/MinioOssStrategy.java
+++ /dev/null
@@ -1,194 +0,0 @@
-package com.ruoyi.oss.service.impl;
-
-import com.ruoyi.common.utils.StringUtils;
-import com.ruoyi.oss.constant.OssConstant;
-import com.ruoyi.oss.entity.UploadResult;
-import com.ruoyi.oss.enumd.OssEnumd;
-import com.ruoyi.oss.enumd.PolicyType;
-import com.ruoyi.oss.exception.OssException;
-import com.ruoyi.oss.properties.OssProperties;
-import com.ruoyi.oss.service.abstractd.AbstractOssStrategy;
-import io.minio.*;
-import io.minio.http.HttpUtils;
-import okhttp3.HttpUrl;
-import org.springframework.http.MediaType;
-import org.springframework.stereotype.Component;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-
-/**
- * minio存储策略
- *
- * @author Lion Li
- */
-@Component
-public class MinioOssStrategy extends AbstractOssStrategy {
-
- private MinioClient minioClient;
-
- @Override
- public void init(OssProperties ossProperties) {
- super.init(ossProperties);
- try {
- MinioClient.Builder builder = MinioClient.builder();
- if (OssConstant.IS_HTTPS.equals(ossProperties.getIsHttps())) {
- HttpUrl url = HttpUtils.getBaseUrl(properties.getEndpoint())
- .newBuilder().scheme("https").build();
- builder.endpoint(url);
- } else {
- builder.endpoint(properties.getEndpoint());
- }
- minioClient = builder.credentials(properties.getAccessKey(), properties.getSecretKey()).build();
- createBucket();
- } catch (Exception e) {
- throw new OssException("Minio存储配置错误! 请检查系统配置:[" + e.getMessage() + "]");
- }
- isInit = true;
- }
-
- @Override
- public void createBucket() {
- try {
- String bucketName = properties.getBucketName();
- boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
- if (exists) {
- return;
- }
- // 不存在就创建桶
- minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
- minioClient.setBucketPolicy(SetBucketPolicyArgs.builder()
- .bucket(bucketName)
- .config(getPolicy(bucketName, PolicyType.READ))
- .build());
- } catch (Exception e) {
- throw new OssException("创建Bucket失败, 请核对Minio配置信息:[" + e.getMessage() + "]");
- }
- }
-
- @Override
- public OssEnumd getServiceType() {
- return OssEnumd.MINIO;
- }
-
- @Override
- public UploadResult upload(byte[] data, String path, String contentType) {
- return upload(new ByteArrayInputStream(data), path, contentType);
- }
-
- @Override
- public UploadResult upload(InputStream inputStream, String path, String contentType) {
- try {
- // 解决 inputStream.available() 再 socket 下传输延迟问题 导致获取数值不精确
- Thread.sleep(1000);
- minioClient.putObject(PutObjectArgs.builder()
- .bucket(properties.getBucketName())
- .object(path)
- .contentType(StringUtils.blankToDefault(contentType, MediaType.APPLICATION_OCTET_STREAM_VALUE))
- .stream(inputStream, inputStream.available(), -1)
- .build());
- } catch (Exception e) {
- throw new OssException("上传文件失败,请核对Minio配置信息:[" + e.getMessage() + "]");
- }
- return UploadResult.builder().url(getEndpointLink() + "/" + path).filename(path).build();
- }
-
- @Override
- public void delete(String path) {
- path = path.replace(getEndpointLink() + "/", "");
- try {
- minioClient.removeObject(RemoveObjectArgs.builder()
- .bucket(properties.getBucketName())
- .object(path)
- .build());
- } catch (Exception e) {
- throw new OssException(e.getMessage());
- }
- }
-
- @Override
- public UploadResult uploadSuffix(byte[] data, String suffix, String contentType) {
- return upload(data, getPath(properties.getPrefix(), suffix), contentType);
- }
-
- @Override
- public UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType) {
- return upload(inputStream, getPath(properties.getPrefix(), suffix), contentType);
- }
-
- @Override
- public String getEndpointLink() {
- return properties.getEndpoint() + "/" + properties.getBucketName();
- }
-
- private String getPolicy(String bucketName, PolicyType policyType) {
- StringBuilder builder = new StringBuilder();
- builder.append("{\n");
- builder.append(" \"Statement\": [\n");
- builder.append(" {\n");
- builder.append(" \"Action\": [\n");
- if (policyType == PolicyType.WRITE) {
- builder.append(" \"s3:GetBucketLocation\",\n");
- builder.append(" \"s3:ListBucketMultipartUploads\"\n");
- } else if (policyType == PolicyType.READ_WRITE) {
- builder.append(" \"s3:GetBucketLocation\",\n");
- builder.append(" \"s3:ListBucket\",\n");
- builder.append(" \"s3:ListBucketMultipartUploads\"\n");
- } else {
- builder.append(" \"s3:GetBucketLocation\"\n");
- }
- builder.append(" ],\n");
- builder.append(" \"Effect\": \"Allow\",\n");
- builder.append(" \"Principal\": \"*\",\n");
- builder.append(" \"Resource\": \"arn:aws:s3:::");
- builder.append(bucketName);
- builder.append("\"\n");
- builder.append(" },\n");
- if (PolicyType.READ.equals(policyType)) {
- builder.append(" {\n");
- builder.append(" \"Action\": [\n");
- builder.append(" \"s3:ListBucket\"\n");
- builder.append(" ],\n");
- builder.append(" \"Effect\": \"Deny\",\n");
- builder.append(" \"Principal\": \"*\",\n");
- builder.append(" \"Resource\": \"arn:aws:s3:::");
- builder.append(bucketName);
- builder.append("\"\n");
- builder.append(" },\n");
- }
- builder.append(" {\n");
- builder.append(" \"Action\": ");
- switch (policyType) {
- case WRITE:
- builder.append("[\n");
- builder.append(" \"s3:AbortMultipartUpload\",\n");
- builder.append(" \"s3:DeleteObject\",\n");
- builder.append(" \"s3:ListMultipartUploadParts\",\n");
- builder.append(" \"s3:PutObject\"\n");
- builder.append(" ],\n");
- break;
- case READ_WRITE:
- builder.append("[\n");
- builder.append(" \"s3:AbortMultipartUpload\",\n");
- builder.append(" \"s3:DeleteObject\",\n");
- builder.append(" \"s3:GetObject\",\n");
- builder.append(" \"s3:ListMultipartUploadParts\",\n");
- builder.append(" \"s3:PutObject\"\n");
- builder.append(" ],\n");
- break;
- default:
- builder.append("\"s3:GetObject\",\n");
- break;
- }
- builder.append(" \"Effect\": \"Allow\",\n");
- builder.append(" \"Principal\": \"*\",\n");
- builder.append(" \"Resource\": \"arn:aws:s3:::");
- builder.append(bucketName);
- builder.append("/*\"\n");
- builder.append(" }\n");
- builder.append(" ],\n");
- builder.append(" \"Version\": \"2012-10-17\"\n");
- builder.append("}\n");
- return builder.toString();
- }
-}
diff --git a/ruoyi/src/main/java/com/ruoyi/oss/service/impl/QcloudOssStrategy.java b/ruoyi/src/main/java/com/ruoyi/oss/service/impl/QcloudOssStrategy.java
deleted file mode 100644
index 756462e4d..000000000
--- a/ruoyi/src/main/java/com/ruoyi/oss/service/impl/QcloudOssStrategy.java
+++ /dev/null
@@ -1,125 +0,0 @@
-package com.ruoyi.oss.service.impl;
-
-import com.qcloud.cos.COSClient;
-import com.qcloud.cos.ClientConfig;
-import com.qcloud.cos.auth.BasicCOSCredentials;
-import com.qcloud.cos.auth.COSCredentials;
-import com.qcloud.cos.http.HttpProtocol;
-import com.qcloud.cos.model.*;
-import com.qcloud.cos.region.Region;
-import com.ruoyi.common.utils.StringUtils;
-import com.ruoyi.oss.constant.OssConstant;
-import com.ruoyi.oss.entity.UploadResult;
-import com.ruoyi.oss.enumd.OssEnumd;
-import com.ruoyi.oss.exception.OssException;
-import com.ruoyi.oss.properties.OssProperties;
-import com.ruoyi.oss.service.abstractd.AbstractOssStrategy;
-import org.springframework.stereotype.Component;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-
-/**
- * 腾讯云存储策略
- *
- * @author Lion Li
- */
-@Component
-public class QcloudOssStrategy extends AbstractOssStrategy {
-
- private COSClient client;
-
- @Override
- public void init(OssProperties ossProperties) {
- super.init(ossProperties);
- try {
- COSCredentials credentials = new BasicCOSCredentials(
- properties.getAccessKey(), properties.getSecretKey());
- // 初始化客户端配置
- ClientConfig clientConfig = new ClientConfig();
- // 设置bucket所在的区域,华南:gz 华北:tj 华东:sh
- clientConfig.setRegion(new Region(properties.getRegion()));
- if (OssConstant.IS_HTTPS.equals(properties.getIsHttps())) {
- clientConfig.setHttpProtocol(HttpProtocol.https);
- } else {
- clientConfig.setHttpProtocol(HttpProtocol.http);
- }
- client = new COSClient(credentials, clientConfig);
- createBucket();
- } catch (Exception e) {
- throw new OssException("腾讯云存储配置错误! 请检查系统配置:[" + e.getMessage() + "]");
- }
- isInit = true;
- }
-
- @Override
- public void createBucket() {
- try {
- String bucketName = properties.getBucketName();
- if (client.doesBucketExist(bucketName)) {
- return;
- }
- CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
- createBucketRequest.setCannedAcl(CannedAccessControlList.PublicRead);
- client.createBucket(createBucketRequest);
- } catch (Exception e) {
- throw new OssException("创建Bucket失败, 请核对腾讯云配置信息:[" + e.getMessage() + "]");
- }
- }
-
- @Override
- public OssEnumd getServiceType() {
- return OssEnumd.QCLOUD;
- }
-
- @Override
- public UploadResult upload(byte[] data, String path, String contentType) {
- return upload(new ByteArrayInputStream(data), path, contentType);
- }
-
- @Override
- public UploadResult upload(InputStream inputStream, String path, String contentType) {
- try {
- ObjectMetadata metadata = new ObjectMetadata();
- metadata.setContentType(contentType);
- client.putObject(new PutObjectRequest(properties.getBucketName(), path, inputStream, metadata));
- } catch (Exception e) {
- throw new OssException("上传文件失败,请检查腾讯云配置信息:[" + e.getMessage() + "]");
- }
- return UploadResult.builder().url(getEndpointLink() + "/" + path).filename(path).build();
- }
-
- @Override
- public void delete(String path) {
- path = path.replace(getEndpointLink() + "/", "");
- try {
- client.deleteObject(new DeleteObjectRequest(properties.getBucketName(), path));
- } catch (Exception e) {
- throw new OssException("上传文件失败,请检腾讯云查配置信息:[" + e.getMessage() + "]");
- }
- }
-
- @Override
- public UploadResult uploadSuffix(byte[] data, String suffix, String contentType) {
- return upload(data, getPath(properties.getPrefix(), suffix), contentType);
- }
-
- @Override
- public UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType) {
- return upload(inputStream, getPath(properties.getPrefix(), suffix), contentType);
- }
-
- @Override
- public String getEndpointLink() {
- String endpoint = properties.getEndpoint();
- StringBuilder sb = new StringBuilder(endpoint);
- if (StringUtils.containsAnyIgnoreCase(endpoint, "http://")) {
- sb.insert(7, properties.getBucketName() + ".");
- } else if (StringUtils.containsAnyIgnoreCase(endpoint, "https://")) {
- sb.insert(8, properties.getBucketName() + ".");
- } else {
- throw new OssException("Endpoint配置错误");
- }
- return sb.toString();
- }
-}
diff --git a/ruoyi/src/main/java/com/ruoyi/oss/service/impl/QiniuOssStrategy.java b/ruoyi/src/main/java/com/ruoyi/oss/service/impl/QiniuOssStrategy.java
deleted file mode 100644
index 20f13eca1..000000000
--- a/ruoyi/src/main/java/com/ruoyi/oss/service/impl/QiniuOssStrategy.java
+++ /dev/null
@@ -1,127 +0,0 @@
-package com.ruoyi.oss.service.impl;
-
-import cn.hutool.core.util.ArrayUtil;
-import com.qiniu.http.Response;
-import com.qiniu.storage.BucketManager;
-import com.qiniu.storage.Configuration;
-import com.qiniu.storage.Region;
-import com.qiniu.storage.UploadManager;
-import com.qiniu.util.Auth;
-import com.ruoyi.oss.constant.OssConstant;
-import com.ruoyi.oss.entity.UploadResult;
-import com.ruoyi.oss.enumd.OssEnumd;
-import com.ruoyi.oss.exception.OssException;
-import com.ruoyi.oss.properties.OssProperties;
-import com.ruoyi.oss.service.abstractd.AbstractOssStrategy;
-import org.springframework.stereotype.Component;
-
-import java.io.InputStream;
-
-/**
- * 七牛云存储策略
- *
- * @author Lion Li
- */
-@Component
-public class QiniuOssStrategy extends AbstractOssStrategy {
-
- private UploadManager uploadManager;
- private BucketManager bucketManager;
- private Auth auth;
-
-
- @Override
- public void init(OssProperties ossProperties) {
- super.init(ossProperties);
- try {
- Configuration config = new Configuration(getRegion(properties.getRegion()));
- // https设置
- config.useHttpsDomains = OssConstant.IS_HTTPS.equals(properties.getIsHttps());
- uploadManager = new UploadManager(config);
- auth = Auth.create(properties.getAccessKey(), properties.getSecretKey());
- bucketManager = new BucketManager(auth, config);
- createBucket();
- } catch (Exception e) {
- throw new OssException("七牛云存储配置错误! 请检查系统配置:[" + e.getMessage() + "]");
- }
- isInit = true;
- }
-
- @Override
- public void createBucket() {
- try {
- String bucketName = properties.getBucketName();
- if (ArrayUtil.contains(bucketManager.buckets(), bucketName)) {
- return;
- }
- bucketManager.createBucket(bucketName, properties.getRegion());
- } catch (Exception e) {
- throw new OssException("创建Bucket失败, 请核对七牛云配置信息:[" + e.getMessage() + "]");
- }
- }
-
- @Override
- public OssEnumd getServiceType() {
- return OssEnumd.QINIU;
- }
-
- @Override
- public UploadResult upload(byte[] data, String path, String contentType) {
- try {
- String token = auth.uploadToken(properties.getBucketName());
- Response res = uploadManager.put(data, path, token, null, contentType, false);
- if (!res.isOK()) {
- throw new RuntimeException("上传七牛出错:" + res.error);
- }
- } catch (Exception e) {
- throw new OssException("上传文件失败,请核对七牛配置信息:[" + e.getMessage() + "]");
- }
- return UploadResult.builder().url(getEndpointLink() + "/" + path).filename(path).build();
- }
-
- @Override
- public void delete(String path) {
- try {
- path = path.replace(getEndpointLink() + "/", "");
- Response res = bucketManager.delete(properties.getBucketName(), path);
- if (!res.isOK()) {
- throw new RuntimeException("删除七牛文件出错:" + res.error);
- }
- } catch (Exception e) {
- throw new OssException(e.getMessage());
- }
- }
-
- @Override
- public UploadResult uploadSuffix(byte[] data, String suffix, String contentType) {
- return upload(data, getPath(properties.getPrefix(), suffix), contentType);
- }
-
- @Override
- public UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType) {
- return upload(inputStream, getPath(properties.getPrefix(), suffix), contentType);
- }
-
- @Override
- public String getEndpointLink() {
- return properties.getEndpoint();
- }
-
- private Region getRegion(String region) {
- switch (region) {
- case "z0":
- return Region.region0();
- case "z1":
- return Region.region1();
- case "z2":
- return Region.region2();
- case "na0":
- return Region.regionNa0();
- case "as0":
- return Region.regionAs0();
- default:
- return Region.autoRegion();
- }
- }
-
-}
diff --git a/ruoyi/src/main/java/com/ruoyi/sms/config/SmsConfig.java b/ruoyi/src/main/java/com/ruoyi/sms/config/SmsConfig.java
new file mode 100644
index 000000000..e9ce2b4de
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/sms/config/SmsConfig.java
@@ -0,0 +1,46 @@
+package com.ruoyi.sms.config;
+
+import com.ruoyi.sms.config.properties.SmsProperties;
+import com.ruoyi.sms.core.AliyunSmsTemplate;
+import com.ruoyi.sms.core.SmsTemplate;
+import com.ruoyi.sms.core.TencentSmsTemplate;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 短信配置类
+ * 需要哪个 打开哪个的注释即可
+ *
+ * @author Lion Li
+ * @version 4.2.0
+ */
+@Configuration
+public class SmsConfig {
+
+// @Configuration
+// @ConditionalOnProperty(value = "sms.enabled", havingValue = "true")
+// @ConditionalOnClass(com.aliyun.dysmsapi20170525.Client.class)
+// static class AliyunSmsConfig {
+//
+// @Bean
+// public SmsTemplate aliyunSmsTemplate(SmsProperties smsProperties) {
+// return new AliyunSmsTemplate(smsProperties);
+// }
+//
+// }
+
+ @Configuration
+ @ConditionalOnProperty(value = "sms.enabled", havingValue = "true")
+ @ConditionalOnClass(com.tencentcloudapi.sms.v20190711.SmsClient.class)
+ static class TencentSmsConfig {
+
+ @Bean
+ public SmsTemplate tencentSmsTemplate(SmsProperties smsProperties) {
+ return new TencentSmsTemplate(smsProperties);
+ }
+
+ }
+
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/sms/config/properties/SmsProperties.java b/ruoyi/src/main/java/com/ruoyi/sms/config/properties/SmsProperties.java
new file mode 100644
index 000000000..39359cdfd
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/sms/config/properties/SmsProperties.java
@@ -0,0 +1,47 @@
+package com.ruoyi.sms.config.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * SMS短信 配置属性
+ *
+ * @author Lion Li
+ * @version 4.2.0
+ */
+@Data
+@Component
+@ConfigurationProperties(prefix = "sms")
+public class SmsProperties {
+
+ private Boolean enabled;
+
+ /**
+ * 配置节点
+ * 阿里云 dysmsapi.aliyuncs.com
+ * 腾讯云 sms.tencentcloudapi.com
+ */
+ private String endpoint;
+
+ /**
+ * key
+ */
+ private String accessKeyId;
+
+ /**
+ * 密匙
+ */
+ private String accessKeySecret;
+
+ /*
+ * 短信签名
+ */
+ private String signName;
+
+ /**
+ * 短信应用ID (腾讯专属)
+ */
+ private String sdkAppId;
+
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/sms/core/AliyunSmsTemplate.java b/ruoyi/src/main/java/com/ruoyi/sms/core/AliyunSmsTemplate.java
new file mode 100644
index 000000000..eede376e5
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/sms/core/AliyunSmsTemplate.java
@@ -0,0 +1,66 @@
+package com.ruoyi.sms.core;
+
+import com.aliyun.dysmsapi20170525.Client;
+import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
+import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
+import com.aliyun.teaopenapi.models.Config;
+import com.ruoyi.common.utils.JsonUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.sms.config.properties.SmsProperties;
+import com.ruoyi.sms.entity.SmsResult;
+import com.ruoyi.sms.exception.SmsException;
+import lombok.SneakyThrows;
+
+import java.util.Map;
+
+/**
+ * Aliyun 短信模板
+ *
+ * @author Lion Li
+ * @version 4.2.0
+ */
+public class AliyunSmsTemplate implements SmsTemplate {
+
+ private SmsProperties properties;
+
+ private Client client;
+
+ @SneakyThrows(Exception.class)
+ public AliyunSmsTemplate(SmsProperties smsProperties) {
+ this.properties = smsProperties;
+ Config config = new Config()
+ // 您的AccessKey ID
+ .setAccessKeyId(smsProperties.getAccessKeyId())
+ // 您的AccessKey Secret
+ .setAccessKeySecret(smsProperties.getAccessKeySecret())
+ // 访问的域名
+ .setEndpoint(smsProperties.getEndpoint());
+ this.client = new Client(config);
+ }
+
+ @Override
+ public SmsResult send(String phones, String templateId, Map param) {
+ if (StringUtils.isBlank(phones)) {
+ throw new SmsException("手机号不能为空");
+ }
+ if (StringUtils.isBlank(templateId)) {
+ throw new SmsException("模板ID不能为空");
+ }
+ SendSmsRequest req = new SendSmsRequest()
+ .setPhoneNumbers(phones)
+ .setSignName(properties.getSignName())
+ .setTemplateCode(templateId)
+ .setTemplateParam(JsonUtils.toJsonString(param));
+ try {
+ SendSmsResponse resp = client.sendSms(req);
+ return SmsResult.builder()
+ .isSuccess("OK".equals(resp.getBody().getCode()))
+ .message(resp.getBody().getMessage())
+ .response(resp)
+ .build();
+ } catch (Exception e) {
+ throw new SmsException(e.getMessage());
+ }
+ }
+
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/sms/core/SmsTemplate.java b/ruoyi/src/main/java/com/ruoyi/sms/core/SmsTemplate.java
new file mode 100644
index 000000000..0aec3ddbe
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/sms/core/SmsTemplate.java
@@ -0,0 +1,26 @@
+package com.ruoyi.sms.core;
+
+import com.ruoyi.sms.entity.SmsResult;
+
+import java.util.Map;
+
+/**
+ * 短信模板
+ *
+ * @author Lion Li
+ * @version 4.2.0
+ */
+public interface SmsTemplate {
+
+ /**
+ * 发送短信
+ *
+ * @param phones 电话号(多个逗号分割)
+ * @param templateId 模板id
+ * @param param 模板对应参数
+ * 阿里 需使用 模板变量名称对应内容 例如: code=1234
+ * 腾讯 需使用 模板变量顺序对应内容 例如: 1=1234, 1为模板内第一个参数
+ */
+ SmsResult send(String phones, String templateId, Map param);
+
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/sms/core/TencentSmsTemplate.java b/ruoyi/src/main/java/com/ruoyi/sms/core/TencentSmsTemplate.java
new file mode 100644
index 000000000..1de8eae14
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/sms/core/TencentSmsTemplate.java
@@ -0,0 +1,81 @@
+package com.ruoyi.sms.core;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ArrayUtil;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.sms.config.properties.SmsProperties;
+import com.ruoyi.sms.entity.SmsResult;
+import com.ruoyi.sms.exception.SmsException;
+import com.tencentcloudapi.common.Credential;
+import com.tencentcloudapi.common.profile.ClientProfile;
+import com.tencentcloudapi.common.profile.HttpProfile;
+import com.tencentcloudapi.sms.v20190711.SmsClient;
+import com.tencentcloudapi.sms.v20190711.models.SendSmsRequest;
+import com.tencentcloudapi.sms.v20190711.models.SendSmsResponse;
+import com.tencentcloudapi.sms.v20190711.models.SendStatus;
+import lombok.SneakyThrows;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Tencent 短信模板
+ *
+ * @author Lion Li
+ * @version 4.2.0
+ */
+public class TencentSmsTemplate implements SmsTemplate {
+
+ private SmsProperties properties;
+
+ private SmsClient client;
+
+ @SneakyThrows(Exception.class)
+ public TencentSmsTemplate(SmsProperties smsProperties) {
+ this.properties = smsProperties;
+ Credential credential = new Credential(smsProperties.getAccessKeyId(), smsProperties.getAccessKeySecret());
+ HttpProfile httpProfile = new HttpProfile();
+ httpProfile.setEndpoint(smsProperties.getEndpoint());
+ ClientProfile clientProfile = new ClientProfile();
+ clientProfile.setHttpProfile(httpProfile);
+ this.client = new SmsClient(credential, "", clientProfile);
+ }
+
+ @Override
+ public SmsResult send(String phones, String templateId, Map param) {
+ if (StringUtils.isBlank(phones)) {
+ throw new SmsException("手机号不能为空");
+ }
+ if (StringUtils.isBlank(templateId)) {
+ throw new SmsException("模板ID不能为空");
+ }
+ SendSmsRequest req = new SendSmsRequest();
+ Set set = Arrays.stream(phones.split(",")).map(p -> "+86" + p).collect(Collectors.toSet());
+ req.setPhoneNumberSet(ArrayUtil.toArray(set, String.class));
+ if (CollUtil.isNotEmpty(param)) {
+ req.setTemplateParamSet(ArrayUtil.toArray(param.values(), String.class));
+ }
+ req.setTemplateID(templateId);
+ req.setSign(properties.getSignName());
+ req.setSmsSdkAppid(properties.getSdkAppId());
+ try {
+ SendSmsResponse resp = client.SendSms(req);
+ SmsResult.SmsResultBuilder builder = SmsResult.builder()
+ .isSuccess(true)
+ .message("send success")
+ .response(resp);
+ for (SendStatus sendStatus : resp.getSendStatusSet()) {
+ if (!"Ok".equals(sendStatus.getCode())) {
+ builder.isSuccess(false).message(sendStatus.getMessage());
+ break;
+ }
+ }
+ return builder.build();
+ } catch (Exception e) {
+ throw new SmsException(e.getMessage());
+ }
+ }
+
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/sms/entity/SmsResult.java b/ruoyi/src/main/java/com/ruoyi/sms/entity/SmsResult.java
new file mode 100644
index 000000000..3f13b2774
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/sms/entity/SmsResult.java
@@ -0,0 +1,29 @@
+package com.ruoyi.sms.entity;
+
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * 上传返回体
+ *
+ * @author Lion Li
+ */
+@Data
+@Builder
+public class SmsResult {
+
+ /**
+ * 是否成功
+ */
+ private boolean isSuccess;
+
+ /**
+ * 响应消息
+ */
+ private String message;
+
+ /**
+ * 实际响应体
+ */
+ private Object response;
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/sms/exception/SmsException.java b/ruoyi/src/main/java/com/ruoyi/sms/exception/SmsException.java
new file mode 100644
index 000000000..28632a375
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/sms/exception/SmsException.java
@@ -0,0 +1,16 @@
+package com.ruoyi.sms.exception;
+
+/**
+ * Sms异常类
+ *
+ * @author Lion Li
+ */
+public class SmsException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ public SmsException(String msg) {
+ super(msg);
+ }
+
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/system/domain/SysOssConfig.java b/ruoyi/src/main/java/com/ruoyi/system/domain/SysOssConfig.java
index a8340df77..577f17fb8 100644
--- a/ruoyi/src/main/java/com/ruoyi/system/domain/SysOssConfig.java
+++ b/ruoyi/src/main/java/com/ruoyi/system/domain/SysOssConfig.java
@@ -5,7 +5,6 @@ import com.baomidou.mybatisplus.annotation.TableName;
import com.ruoyi.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
-import lombok.experimental.Accessors;
/**
* 对象存储配置对象 sys_oss_config
@@ -53,6 +52,11 @@ public class SysOssConfig extends BaseEntity {
*/
private String endpoint;
+ /**
+ * 自定义域名
+ */
+ private String domain;
+
/**
* 是否https(0否 1是)
*/
diff --git a/ruoyi/src/main/java/com/ruoyi/system/domain/bo/SysOssConfigBo.java b/ruoyi/src/main/java/com/ruoyi/system/domain/bo/SysOssConfigBo.java
index 9a66e384b..5ac4e96e4 100644
--- a/ruoyi/src/main/java/com/ruoyi/system/domain/bo/SysOssConfigBo.java
+++ b/ruoyi/src/main/java/com/ruoyi/system/domain/bo/SysOssConfigBo.java
@@ -35,8 +35,8 @@ public class SysOssConfigBo extends BaseEntity {
/**
* 配置key
*/
- @ApiModelProperty(value = "configKey", required = true)
- @NotBlank(message = "configKey不能为空", groups = {AddGroup.class, EditGroup.class})
+ @ApiModelProperty(value = "配置key", required = true)
+ @NotBlank(message = "配置key不能为空", groups = {AddGroup.class, EditGroup.class})
@Size(min = 2, max = 100, message = "configKey长度必须介于2和20 之间")
private String configKey;
@@ -59,8 +59,8 @@ public class SysOssConfigBo extends BaseEntity {
/**
* 桶名称
*/
- @ApiModelProperty(value = "bucketName", required = true)
- @NotBlank(message = "bucketName不能为空", groups = {AddGroup.class, EditGroup.class})
+ @ApiModelProperty(value = "桶名称", required = true)
+ @NotBlank(message = "桶名称不能为空", groups = {AddGroup.class, EditGroup.class})
@Size(min = 2, max = 100, message = "bucketName长度必须介于2和100之间")
private String bucketName;
@@ -73,11 +73,17 @@ public class SysOssConfigBo extends BaseEntity {
/**
* 访问站点
*/
- @ApiModelProperty(value = "endpoint", required = true)
- @NotBlank(message = "endpoint不能为空", groups = {AddGroup.class, EditGroup.class})
+ @ApiModelProperty(value = "访问站点", required = true)
+ @NotBlank(message = "访问站点不能为空", groups = {AddGroup.class, EditGroup.class})
@Size(min = 2, max = 100, message = "endpoint长度必须介于2和100之间")
private String endpoint;
+ /**
+ * 自定义域名
+ */
+ @ApiModelProperty("自定义域名")
+ private String domain;
+
/**
* 是否https(Y=是,N=否)
*/
@@ -93,7 +99,7 @@ public class SysOssConfigBo extends BaseEntity {
/**
* 域
*/
- @ApiModelProperty(value = "region")
+ @ApiModelProperty(value = "域")
private String region;
/**
@@ -102,4 +108,10 @@ public class SysOssConfigBo extends BaseEntity {
@ApiModelProperty(value = "扩展字段")
private String ext1;
+ /**
+ * 备注
+ */
+ @ApiModelProperty(value = "备注")
+ private String remark;
+
}
diff --git a/ruoyi/src/main/java/com/ruoyi/system/domain/vo/SysOssConfigVo.java b/ruoyi/src/main/java/com/ruoyi/system/domain/vo/SysOssConfigVo.java
index 0fb08dd00..20edacaa6 100644
--- a/ruoyi/src/main/java/com/ruoyi/system/domain/vo/SysOssConfigVo.java
+++ b/ruoyi/src/main/java/com/ruoyi/system/domain/vo/SysOssConfigVo.java
@@ -62,6 +62,12 @@ public class SysOssConfigVo {
@ApiModelProperty("访问站点")
private String endpoint;
+ /**
+ * 自定义域名
+ */
+ @ApiModelProperty("自定义域名")
+ private String domain;
+
/**
* 是否https(Y=是,N=否)
*/
diff --git a/ruoyi/src/main/java/com/ruoyi/system/service/ISysOssService.java b/ruoyi/src/main/java/com/ruoyi/system/service/ISysOssService.java
index b444e6d06..c55e5bc12 100644
--- a/ruoyi/src/main/java/com/ruoyi/system/service/ISysOssService.java
+++ b/ruoyi/src/main/java/com/ruoyi/system/service/ISysOssService.java
@@ -8,6 +8,7 @@ import com.ruoyi.system.domain.vo.SysOssVo;
import org.springframework.web.multipart.MultipartFile;
import java.util.Collection;
+import java.util.List;
/**
* 文件上传 服务层
@@ -18,6 +19,8 @@ public interface ISysOssService {
TableDataInfo queryPageList(SysOssBo sysOss, PageQuery pageQuery);
+ List listByIds(Collection ossIds);
+
SysOss getById(Long ossId);
SysOss upload(MultipartFile file);
diff --git a/ruoyi/src/main/java/com/ruoyi/system/service/SysLoginService.java b/ruoyi/src/main/java/com/ruoyi/system/service/SysLoginService.java
index e0b53c1fb..1a672e59c 100644
--- a/ruoyi/src/main/java/com/ruoyi/system/service/SysLoginService.java
+++ b/ruoyi/src/main/java/com/ruoyi/system/service/SysLoginService.java
@@ -27,8 +27,8 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
+import java.time.Duration;
import java.util.List;
-import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
/**
@@ -79,7 +79,7 @@ public class SysLoginService {
SysUser user = loadUserByPhonenumber(phonenumber);
HttpServletRequest request = ServletUtils.getRequest();
- checkLogin(LoginType.SMS, user.getUserName(), () -> !validateSmsCode(phonenumber, smsCode));
+ checkLogin(LoginType.SMS, user.getUserName(), () -> !validateSmsCode(phonenumber, smsCode, request));
// 此处可根据登录用户的数据不同 自行创建 loginUser
LoginUser loginUser = buildLoginUser(user);
// 生成token
@@ -121,9 +121,13 @@ public class SysLoginService {
/**
* 校验短信验证码
*/
- private boolean validateSmsCode(String phonenumber, String smsCode) {
- // todo 此处使用手机号查询redis验证码与参数验证码是否一致 用户自行实现
- return true;
+ private boolean validateSmsCode(String phonenumber, String smsCode, HttpServletRequest request) {
+ String code = RedisUtils.getCacheObject(Constants.CAPTCHA_CODE_KEY + phonenumber);
+ if (StringUtils.isBlank(code)) {
+ asyncService.recordLogininfor(phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"), request);
+ throw new CaptchaExpireException();
+ }
+ return code.equals(smsCode);
}
/**
@@ -205,7 +209,7 @@ public class SysLoginService {
loginUser.setUserType(user.getUserType());
loginUser.setMenuPermission(permissionService.getMenuPermission(user));
loginUser.setRolePermission(permissionService.getRolePermission(user));
- loginUser.setDeptName(user.getDept().getDeptName());
+ loginUser.setDeptName(ObjectUtil.isNull(user.getDept()) ? "" : user.getDept().getDeptName());
List roles = BeanUtil.copyToList(user.getRoles(), RoleDTO.class);
loginUser.setRoles(roles);
return loginUser;
@@ -248,7 +252,7 @@ public class SysLoginService {
errorNumber = ObjectUtil.isNull(errorNumber) ? 1 : errorNumber + 1;
// 达到规定错误次数 则锁定登录
if (errorNumber.equals(setErrorNumber)) {
- RedisUtils.setCacheObject(errorKey, errorNumber, errorLimitTime, TimeUnit.MINUTES);
+ RedisUtils.setCacheObject(errorKey, errorNumber, Duration.ofMinutes(errorLimitTime));
asyncService.recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), errorLimitTime), request);
throw new UserException(loginType.getRetryLimitExceed(), errorLimitTime);
} else {
diff --git a/ruoyi/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java b/ruoyi/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java
index 2f9d0020a..6525d31e9 100644
--- a/ruoyi/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java
+++ b/ruoyi/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java
@@ -68,7 +68,7 @@ public class SysMenuServiceImpl implements ISysMenuService {
.orderByAsc(SysMenu::getOrderNum));
} else {
QueryWrapper wrapper = Wrappers.query();
- wrapper.eq("ur.user_id", userId)
+ wrapper.eq("sur.user_id", userId)
.like(StringUtils.isNotBlank(menu.getMenuName()), "m.menu_name", menu.getMenuName())
.eq(StringUtils.isNotBlank(menu.getVisible()), "m.visible", menu.getVisible())
.eq(StringUtils.isNotBlank(menu.getStatus()), "m.status", menu.getStatus())
diff --git a/ruoyi/src/main/java/com/ruoyi/system/service/impl/SysOssConfigServiceImpl.java b/ruoyi/src/main/java/com/ruoyi/system/service/impl/SysOssConfigServiceImpl.java
index f78be1e83..015c156ab 100644
--- a/ruoyi/src/main/java/com/ruoyi/system/service/impl/SysOssConfigServiceImpl.java
+++ b/ruoyi/src/main/java/com/ruoyi/system/service/impl/SysOssConfigServiceImpl.java
@@ -95,9 +95,10 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService {
SysOssConfig config = BeanUtil.toBean(bo, SysOssConfig.class);
validEntityBeforeSave(config);
LambdaUpdateWrapper luw = new LambdaUpdateWrapper<>();
- luw.set(StringUtils.isBlank(config.getPrefix()), SysOssConfig::getPrefix, "");
- luw.set(StringUtils.isBlank(config.getRegion()), SysOssConfig::getRegion, "");
- luw.set(StringUtils.isBlank(config.getExt1()), SysOssConfig::getExt1, "");
+ luw.set(ObjectUtil.isNull(config.getPrefix()), SysOssConfig::getPrefix, "");
+ luw.set(ObjectUtil.isNull(config.getRegion()), SysOssConfig::getRegion, "");
+ luw.set(ObjectUtil.isNull(config.getExt1()), SysOssConfig::getExt1, "");
+ luw.set(ObjectUtil.isNull(config.getRemark()), SysOssConfig::getRemark, "");
luw.eq(SysOssConfig::getOssConfigId, config.getOssConfigId());
return setConfigCache(baseMapper.update(config, luw) > 0, config);
}
diff --git a/ruoyi/src/main/java/com/ruoyi/system/service/impl/SysOssServiceImpl.java b/ruoyi/src/main/java/com/ruoyi/system/service/impl/SysOssServiceImpl.java
index 19423f404..03a6cfca8 100644
--- a/ruoyi/src/main/java/com/ruoyi/system/service/impl/SysOssServiceImpl.java
+++ b/ruoyi/src/main/java/com/ruoyi/system/service/impl/SysOssServiceImpl.java
@@ -1,5 +1,6 @@
package com.ruoyi.system.service.impl;
+import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -7,9 +8,11 @@ import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.redis.RedisUtils;
+import com.ruoyi.oss.constant.OssConstant;
+import com.ruoyi.oss.core.OssClient;
import com.ruoyi.oss.entity.UploadResult;
import com.ruoyi.oss.factory.OssFactory;
-import com.ruoyi.oss.service.IOssStrategy;
import com.ruoyi.system.domain.SysOss;
import com.ruoyi.system.domain.bo.SysOssBo;
import com.ruoyi.system.domain.vo.SysOssVo;
@@ -20,6 +23,8 @@ import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
+import java.time.Duration;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -42,6 +47,21 @@ public class SysOssServiceImpl implements ISysOssService {
return TableDataInfo.build(result);
}
+ @Override
+ public List listByIds(Collection ossIds) {
+ List list = new ArrayList<>();
+ for (Long id : ossIds) {
+ String key = OssConstant.SYS_OSS_KEY + id;
+ SysOssVo vo = RedisUtils.getCacheObject(key);
+ if (ObjectUtil.isNull(vo)) {
+ vo = baseMapper.selectVoById(id);
+ RedisUtils.setCacheObject(key, vo, Duration.ofDays(30));
+ }
+ list.add(vo);
+ }
+ return list;
+ }
+
private LambdaQueryWrapper buildQueryWrapper(SysOssBo bo) {
Map params = bo.getParams();
LambdaQueryWrapper lqw = Wrappers.lambdaQuery();
@@ -65,7 +85,7 @@ public class SysOssServiceImpl implements ISysOssService {
public SysOss upload(MultipartFile file) {
String originalfileName = file.getOriginalFilename();
String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());
- IOssStrategy storage = OssFactory.instance();
+ OssClient storage = OssFactory.instance();
UploadResult uploadResult;
try {
uploadResult = storage.uploadSuffix(file.getBytes(), suffix, file.getContentType());
@@ -78,7 +98,7 @@ public class SysOssServiceImpl implements ISysOssService {
oss.setFileSuffix(suffix);
oss.setFileName(uploadResult.getFilename());
oss.setOriginalName(originalfileName);
- oss.setService(storage.getServiceType().getValue());
+ oss.setService(storage.getConfigKey());
baseMapper.insert(oss);
return oss;
}
@@ -90,7 +110,7 @@ public class SysOssServiceImpl implements ISysOssService {
}
List list = baseMapper.selectBatchIds(ids);
for (SysOss sysOss : list) {
- IOssStrategy storage = OssFactory.instance(sysOss.getService());
+ OssClient storage = OssFactory.instance(sysOss.getService());
storage.delete(sysOss.getUrl());
}
return baseMapper.deleteBatchIds(ids) > 0;
diff --git a/ruoyi/src/main/java/com/ruoyi/system/service/impl/SysSensitiveServiceImpl.java b/ruoyi/src/main/java/com/ruoyi/system/service/impl/SysSensitiveServiceImpl.java
index 08d4cc4f9..fe142ca3c 100644
--- a/ruoyi/src/main/java/com/ruoyi/system/service/impl/SysSensitiveServiceImpl.java
+++ b/ruoyi/src/main/java/com/ruoyi/system/service/impl/SysSensitiveServiceImpl.java
@@ -20,7 +20,7 @@ public class SysSensitiveServiceImpl implements SensitiveService {
*/
@Override
public boolean isSensitive() {
- return LoginHelper.isAdmin();
+ return !LoginHelper.isAdmin();
}
}
diff --git a/ruoyi/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java b/ruoyi/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
index 2ef63881c..ed24ab4e9 100644
--- a/ruoyi/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
+++ b/ruoyi/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
@@ -1,6 +1,7 @@
package com.ruoyi.system.service.impl;
import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -115,11 +116,11 @@ public class SysUserServiceImpl implements ISysUserService {
*/
@Override
public TableDataInfo selectUnallocatedList(SysUser user, PageQuery pageQuery) {
- List userId = userRoleMapper.selectUserIdsByRoleId(user.getRoleId());
+ List userIds = userRoleMapper.selectUserIdsByRoleId(user.getRoleId());
QueryWrapper wrapper = Wrappers.query();
wrapper.eq("u.del_flag", UserConstants.USER_NORMAL)
.and(w -> w.ne("r.role_id", user.getRoleId()).or().isNull("r.role_id"))
- .notIn("u.user_id", userId)
+ .notIn(CollUtil.isNotEmpty(userIds), "u.user_id", userIds)
.like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName())
.like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber());
Page page = baseMapper.selectUnallocatedList(pageQuery.build(), wrapper);
@@ -402,20 +403,7 @@ public class SysUserServiceImpl implements ISysUserService {
* @param user 用户对象
*/
public void insertUserRole(SysUser user) {
- Long[] roles = user.getRoleIds();
- if (ObjectUtil.isNotNull(roles)) {
- // 新增用户与角色管理
- List list = new ArrayList();
- for (Long roleId : roles) {
- SysUserRole ur = new SysUserRole();
- ur.setUserId(user.getUserId());
- ur.setRoleId(roleId);
- list.add(ur);
- }
- if (list.size() > 0) {
- userRoleMapper.insertBatch(list);
- }
- }
+ this.insertUserRole(user.getUserId(), user.getRoleIds());
}
/**
@@ -425,18 +413,16 @@ public class SysUserServiceImpl implements ISysUserService {
*/
public void insertUserPost(SysUser user) {
Long[] posts = user.getPostIds();
- if (ObjectUtil.isNotNull(posts)) {
+ if (ArrayUtil.isNotEmpty(posts)) {
// 新增用户与岗位管理
- List list = new ArrayList();
+ List list = new ArrayList<>(posts.length);
for (Long postId : posts) {
SysUserPost up = new SysUserPost();
up.setUserId(user.getUserId());
up.setPostId(postId);
list.add(up);
}
- if (list.size() > 0) {
- userPostMapper.insertBatch(list);
- }
+ userPostMapper.insertBatch(list);
}
}
@@ -447,18 +433,16 @@ public class SysUserServiceImpl implements ISysUserService {
* @param roleIds 角色组
*/
public void insertUserRole(Long userId, Long[] roleIds) {
- if (ObjectUtil.isNotNull(roleIds)) {
+ if (ArrayUtil.isNotEmpty(roleIds)) {
// 新增用户与角色管理
- List list = new ArrayList();
+ List list = new ArrayList<>(roleIds.length);
for (Long roleId : roleIds) {
SysUserRole ur = new SysUserRole();
ur.setUserId(userId);
ur.setRoleId(roleId);
list.add(ur);
}
- if (list.size() > 0) {
- userRoleMapper.insertBatch(list);
- }
+ userRoleMapper.insertBatch(list);
}
}
diff --git a/ruoyi/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java b/ruoyi/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java
index a19be65ea..0889aabc7 100644
--- a/ruoyi/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java
+++ b/ruoyi/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java
@@ -4,6 +4,8 @@ import cn.hutool.captcha.AbstractCaptcha;
import cn.hutool.captcha.generator.CodeGenerator;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.RandomUtil;
+import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.enums.CaptchaType;
@@ -12,30 +14,68 @@ import com.ruoyi.common.utils.redis.RedisUtils;
import com.ruoyi.common.utils.reflect.ReflectUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.framework.config.properties.CaptchaProperties;
+import com.ruoyi.sms.config.properties.SmsProperties;
+import com.ruoyi.sms.core.SmsTemplate;
+import com.ruoyi.sms.entity.SmsResult;
import com.ruoyi.system.service.ISysConfigService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
+import javax.validation.constraints.NotBlank;
+import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
-import java.util.concurrent.TimeUnit;
/**
* 验证码操作处理
*
* @author Lion Li
*/
+@Anonymous
+@Slf4j
+@Validated
@Api(value = "验证码操作处理", tags = {"验证码管理"})
@RequiredArgsConstructor
@RestController
public class CaptchaController {
private final CaptchaProperties captchaProperties;
+ private final SmsProperties smsProperties;
private final ISysConfigService configService;
+ /**
+ * 短信验证码
+ */
+ @ApiOperation("短信验证码")
+ @GetMapping("/captchaSms")
+ public R smsCaptcha(@ApiParam("用户手机号")
+ @NotBlank(message = "{user.phonenumber.not.blank}")
+ String phonenumber) {
+ if (smsProperties.getEnabled()) {
+ R.fail("当前系统没有开启短信功能!");
+ }
+ String key = Constants.CAPTCHA_CODE_KEY + phonenumber;
+ String code = RandomUtil.randomNumbers(4);
+ RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
+ // 验证码模板id 自行处理 (查数据库或写死均可)
+ String templateId = "";
+ Map map = new HashMap<>(1);
+ map.put("code", code);
+ SmsTemplate smsTemplate = SpringUtils.getBean(SmsTemplate.class);
+ SmsResult result = smsTemplate.send(phonenumber, templateId, map);
+ if (!result.isSuccess()) {
+ log.error("验证码短信发送异常 => {}", result);
+ return R.fail(result.getMessage());
+ }
+ return R.ok();
+ }
+
/**
* 生成验证码
*/
@@ -60,7 +100,7 @@ public class CaptchaController {
captcha.setGenerator(codeGenerator);
captcha.createCode();
String code = isMath ? getCodeResult(captcha.getCode()) : captcha.getCode();
- RedisUtils.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
+ RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
ajax.put("uuid", uuid);
ajax.put("img", captcha.getImageBase64());
return R.ok(ajax);
diff --git a/ruoyi/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java b/ruoyi/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java
index facf6ce99..cdb1d6cd6 100644
--- a/ruoyi/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java
+++ b/ruoyi/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java
@@ -2,6 +2,7 @@ package com.ruoyi.web.controller.system;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.stp.StpUtil;
+import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.domain.entity.SysMenu;
@@ -51,6 +52,7 @@ public class SysLoginController {
* @param loginBody 登录信息
* @return 结果
*/
+ @Anonymous
@ApiOperation("登录方法")
@PostMapping("/login")
public R> login(@Validated @RequestBody LoginBody loginBody) {
@@ -68,6 +70,7 @@ public class SysLoginController {
* @param smsLoginBody 登录信息
* @return 结果
*/
+ @Anonymous
@ApiOperation("短信登录(示例)")
@PostMapping("/smsLogin")
public R> smsLogin(@Validated @RequestBody SmsLoginBody smsLoginBody) {
@@ -84,6 +87,7 @@ public class SysLoginController {
* @param xcxCode 小程序code
* @return 结果
*/
+ @Anonymous
@ApiOperation("小程序登录(示例)")
@PostMapping("/xcxLogin")
public R> xcxLogin(@NotBlank(message = "{xcx.code.not.blank}") String xcxCode) {
@@ -94,12 +98,14 @@ public class SysLoginController {
return R.ok(ajax);
}
+ @Anonymous
@ApiOperation("登出方法")
@PostMapping("/logout")
public R logout() {
try {
+ String username = LoginHelper.getUsername();
StpUtil.logout();
- loginService.logout(LoginHelper.getUsername());
+ loginService.logout(username);
} catch (NotLoginException e) {
}
return R.ok("退出成功");
diff --git a/ruoyi/src/main/java/com/ruoyi/web/controller/system/SysOssController.java b/ruoyi/src/main/java/com/ruoyi/web/controller/system/SysOssController.java
index a128a15ff..d4e52acc6 100644
--- a/ruoyi/src/main/java/com/ruoyi/web/controller/system/SysOssController.java
+++ b/ruoyi/src/main/java/com/ruoyi/web/controller/system/SysOssController.java
@@ -32,6 +32,7 @@ import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
/**
@@ -58,6 +59,19 @@ public class SysOssController extends BaseController {
return iSysOssService.queryPageList(bo, pageQuery);
}
+ /**
+ * 查询OSS对象基于id串
+ */
+ @ApiOperation("查询OSS对象基于ID")
+ @SaCheckPermission("system:oss:list")
+ @GetMapping("/listByIds/{ossIds}")
+ public R> listByIds(@ApiParam("OSS对象ID串")
+ @NotEmpty(message = "主键不能为空")
+ @PathVariable Long[] ossIds) {
+ List list = iSysOssService.listByIds(Arrays.asList(ossIds));
+ return R.ok(list);
+ }
+
/**
* 上传OSS对象存储
*/
diff --git a/ruoyi/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java b/ruoyi/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java
index 84d4c0b3d..22b506f36 100644
--- a/ruoyi/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java
+++ b/ruoyi/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java
@@ -1,6 +1,7 @@
package com.ruoyi.web.controller.system;
import cn.dev33.satoken.secure.BCrypt;
+import cn.hutool.core.io.FileUtil;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.controller.BaseController;
@@ -9,6 +10,7 @@ import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.file.MimeTypeUtils;
import com.ruoyi.system.domain.SysOss;
import com.ruoyi.system.service.ISysOssService;
import com.ruoyi.system.service.ISysUserService;
@@ -22,6 +24,7 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@@ -117,6 +120,10 @@ public class SysProfileController extends BaseController {
public R> avatar(@RequestPart("avatarfile") MultipartFile file) {
Map ajax = new HashMap<>();
if (!file.isEmpty()) {
+ String extension = FileUtil.extName(file.getOriginalFilename());
+ if (!StringUtils.equalsAnyIgnoreCase(extension, MimeTypeUtils.IMAGE_EXTENSION)) {
+ return R.fail("文件格式不正确,请上传" + Arrays.toString(MimeTypeUtils.IMAGE_EXTENSION) + "格式");
+ }
SysOss oss = iSysOssService.upload(file);
String avatar = oss.getUrl();
if (userService.updateUserAvatar(getUsername(), avatar)) {
diff --git a/ruoyi/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java b/ruoyi/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java
index 6595f06df..b8cedd67a 100644
--- a/ruoyi/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java
+++ b/ruoyi/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java
@@ -1,5 +1,6 @@
package com.ruoyi.web.controller.system;
+import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.domain.model.RegisterBody;
@@ -27,6 +28,7 @@ public class SysRegisterController extends BaseController {
private final SysRegisterService registerService;
private final ISysConfigService configService;
+ @Anonymous
@ApiOperation("用户注册")
@PostMapping("/register")
public R register(@Validated @RequestBody RegisterBody user) {
diff --git a/ruoyi/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java b/ruoyi/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java
index 5d9a20615..9b7cb268b 100644
--- a/ruoyi/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java
+++ b/ruoyi/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java
@@ -148,7 +148,7 @@ public class SysRoleController extends BaseController {
@SaCheckPermission("system:role:remove")
@Log(title = "角色管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{roleIds}")
- public R remove(@ApiParam("岗位ID串") @PathVariable Long[] roleIds) {
+ public R remove(@ApiParam("角色ID串") @PathVariable Long[] roleIds) {
return toAjax(roleService.deleteRoleByIds(roleIds));
}
diff --git a/ruoyi/src/main/resources/application-dev.yml b/ruoyi/src/main/resources/application-dev.yml
index 45eb5be97..f9327f779 100644
--- a/ruoyi/src/main/resources/application-dev.yml
+++ b/ruoyi/src/main/resources/application-dev.yml
@@ -130,8 +130,8 @@ spring:
port: 6379
# 数据库索引
database: 0
- # 密码
- password:
+ # 密码(如没有密码请注释掉)
+ # password:
# 连接超时时间
timeout: 10s
# 是否开启ssl
@@ -156,3 +156,37 @@ redisson:
timeout: 3000
# 发布和订阅连接池大小
subscriptionConnectionPoolSize: 50
+
+--- # mail 邮件发送
+mail:
+ enabled: false
+ host: smtp.163.com
+ port: 465
+ # 是否需要用户名密码验证
+ auth: true
+ # 发送方,遵循RFC-822标准
+ from: xxx@163.com
+ # 用户名(注意:如果使用foxmail邮箱,此处user为qq号)
+ user: xxx@163.com
+ # 密码(注意,某些邮箱需要为SMTP服务单独设置密码,详情查看相关帮助)
+ pass: xxxxxxxxxx
+ # 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。
+ starttlsEnable: true
+ # 使用SSL安全连接
+ sslEnable: true
+ # SMTP超时时长,单位毫秒,缺省值不超时
+ timeout: 0
+ # Socket连接超时值,单位毫秒,缺省值不超时
+ connectionTimeout: 0
+
+--- # sms 短信
+sms:
+ enabled: false
+ # 阿里云 dysmsapi.aliyuncs.com
+ # 腾讯云 sms.tencentcloudapi.com
+ endpoint: "dysmsapi.aliyuncs.com"
+ accessKeyId: xxxxxxx
+ accessKeySecret: xxxxxx
+ signName: 测试
+ # 腾讯专用
+ sdkAppId:
diff --git a/ruoyi/src/main/resources/application-prod.yml b/ruoyi/src/main/resources/application-prod.yml
index 6a28bccfb..def5f1666 100644
--- a/ruoyi/src/main/resources/application-prod.yml
+++ b/ruoyi/src/main/resources/application-prod.yml
@@ -133,8 +133,8 @@ spring:
port: 6379
# 数据库索引
database: 0
- # 密码
- password:
+ # 密码(如没有密码请注释掉)
+ # password:
# 连接超时时间
timeout: 10s
# 是否开启ssl
@@ -159,3 +159,37 @@ redisson:
timeout: 3000
# 发布和订阅连接池大小
subscriptionConnectionPoolSize: 50
+
+--- # mail 邮件发送
+mail:
+ enabled: false
+ host: smtp.163.com
+ port: 465
+ # 是否需要用户名密码验证
+ auth: true
+ # 发送方,遵循RFC-822标准
+ from: xxx@163.com
+ # 用户名(注意:如果使用foxmail邮箱,此处user为qq号)
+ user: xxx@163.com
+ # 密码(注意,某些邮箱需要为SMTP服务单独设置密码,详情查看相关帮助)
+ pass: xxxxxxxxxx
+ # 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。
+ starttlsEnable: true
+ # 使用SSL安全连接
+ sslEnable: true
+ # SMTP超时时长,单位毫秒,缺省值不超时
+ timeout: 0
+ # Socket连接超时值,单位毫秒,缺省值不超时
+ connectionTimeout: 0
+
+--- # sms 短信
+sms:
+ enabled: false
+ # 阿里云 dysmsapi.aliyuncs.com
+ # 腾讯云 sms.tencentcloudapi.com
+ endpoint: "dysmsapi.aliyuncs.com"
+ accessKeyId: xxxxxxx
+ accessKeySecret: xxxxxx
+ signName: 测试
+ # 腾讯专用
+ sdkAppId:
diff --git a/ruoyi/src/main/resources/application.yml b/ruoyi/src/main/resources/application.yml
index c692fc267..ceaaf9561 100644
--- a/ruoyi/src/main/resources/application.yml
+++ b/ruoyi/src/main/resources/application.yml
@@ -118,12 +118,6 @@ sa-token:
security:
# 排除路径
excludes:
- - /login
- - /smsLogin
- - /xcxLogin
- - /logout
- - /register
- - /captchaImage
# 静态资源
- /*.html
- /**/*.html
diff --git a/ruoyi/src/main/resources/excel/单列表.xlsx b/ruoyi/src/main/resources/excel/单列表.xlsx
new file mode 100644
index 0000000000000000000000000000000000000000..0f7347d652c06ae29eca9fa6cd1055ec6ae6c6e3
GIT binary patch
literal 10787
zcmeHtWmH{Dvn}o}K|-)>B)Gei4Fn7B?(V@|gFC@JxVyW%1-AgfJ-9wdzI$>G_uM<)
z-#5nVAFMT?qnl
zi!0;4<^%-+F#!VsLHWCwwv`o)qq$j1RJ$lRJqGa7`w^wWlF=NPttSJ4>VQDf6Dt*E@B%Uq_WIa3_bX!b*-C?*_06=6bE&i}$6fXrPG^duN
zFG~9=*XOM$muU3h1g^*q6jw^%J$EX*s*fOIsxQpCY=b~iZirL0x9E*_nS>SG9z~$G
zotQze03}=bZVs4_Su!91VH$=sz*Wi;+*mxBtvSnF^;u!b0eOd4Hai2blG{4+^_8sizVF|lllka
zftOpowKI)Yu!im7hAI1{7u7r{(H2CXm%3R9Z6I{NSY?z$cxdQNe5Y-T=CSHUv3@(^
z3h9}}vl3TT+2f%aS+!E&lG}Fa^V!VfeFZwY1THMmjcX
zRJ4l@rRT`|=qXT>ybU78jp-F+SP+UB$O0k;4#5(*QAV|_N8XO85a*lVj%QwKZitmj
z7V6`{5rfBWKL=Sm0rxnx$&^wYw7?*+QDGz)l?pX+89OfAI?ldve;A=48NMKsvAi^M
z3xHftm7G2d5-MrdzWdVA~qUKvYt2-Z;}iwJutSjd;Co;
zn796`kYllLP^(3RIJ2ANfZ$pff}nW5<7~%ZcL&Vrbq|#p($mRYtkqSF)z&j}AvE11
z+>=g*MFj(enWX;11gn$Z+oc%`l7hQS$kWZr)4Yuss|xY9+U)oC?+A7(!}9+@{sceL6a47^fd7-Nou&D60KP`5i*?dr
z_#Z(&3ii7thEpFf%!i%p5^9jMIcQGUQ8Ilnp4dToY{an*!dl+`@x6(Q>xjL7SN^0biK0Z~W5)6n=&|9MTA)
zm~RoAi2TA>Y4u8#fn4>^^*Z4kb50_$x1=Q1f*9*Cf&Cm=tH*xeSj(r|4?$)jYzuQs
z7fg;zRM);~-U{_4AKF#>gDUjm`BhH9(8O)}47vOrz37LH%fIjKzJcgu7w1nm{ZY7r
z$vMi=pwHW+#A-lQLR_;fM-_D9Dq)qX7&2rf2jtp9)dXyFCZ!j4@|qhcCf(*jYgQ!q
z4(66v+S}X_3DajqM2vYb0RX9^9+muCvg-H=ZXnP5`rmm5{0Ue`vtP*ny8zN}0S8MP
zQ(Gf_eY=-zr^xzj>Knb~JWn1C0l
zcz))-Iy@EYt-IKdg6O*LC?#i+L>ZOSP^h&&pJ5FCin
zuj2CEzO4;#sO`q+v|1jPl)P%^$x#SYS=^KiRqw`E1oDs2s3O{=WL6iGw=|Y=)*#
z1h#b_hqlb~qx=!GL=QJuI{IGLbnSTgok>8(5y
z1&7VedXGP7*Xz8LT)5u`THg+?j%q61z$Mx!R5n}5DmWH1aSm`ENd{MnPz$3+%WFNj
z7A$+M!+E=8vSYT&*EJIo)3_(*SSJqWb`V8H-a
zNiKV2Re?O-+$!lx(2g8*>JycN-^>Zd%N>=R?X4$8*%!%t+%;PXaECA=O`-I*3zX#_
z1rR&oXNEr@PL0zt2e}3iHED;OxbQCf6eQ6u_V?kZ*`Su+kqvjzzjw&%ZK>D<&j`o@
zp@(FzuG
z55+JAiW9PU=|MOWnn^+9IHz#Xfqb$en7a1oNJ8BX$um5=(M1;#@+8Hg$c)|-bS5Q5
zh7r{`a-IRRA~+jCWMD4z!)b8nns&kG-P#0*
z;RC4wIBy#u*od=zfvY7OI!SEt^MZO+VHrQiw*VCpvjTdpfa0peRx_#Myqa&u$P?bj
zJ|;mXWcs->Cce7Xe8Qpei`odTSViBN%1>^Sn5pJrxs+B$Ne&Ui(lHB;yFnGhr*i9n*
zMNYg|GvPv&(8v54)Yrs=?BJ@pS!Rl-nhp!Ww8@ZBHIcZ~F?X{ZT$WW?RbynhlV+4UKn5RLM}zm5HY{(Yd;&~fWLC?K
zPRrQaaV#qHVT=;XU|#npsmA&bl;#mJDSU1$NojBB8gW##=OsI_GI0
zWz)PgaN;7}J*==$_cQBLCW2C|;&8&zD4=RJAL8)mV$8JzyhJ~Uw`x$X~k;|Zp(f#~*
z$@r`R3f*E{leNZz7F?$3vCtBxQhQkQ{8A+29_94?S2Y
z@)?TO*ApYLiy36Jn4y`ZvnUQ9nlWc5~SXc)Dy7*l91)7dMJwGHtpslB6=o%pyKZ0p4IDE0S%{Wl-
z@&wA;8hYH_-{0SuZmoUgL>0V!FEX&6qsXm!f)eP)lWjeAW^vZIHO&RU3=4=qiDznl
z7rFd3^m3)~^NhR*vA32R)r|Ede5~m7m$+=?PNmc>*g$QIE}$=FQ;?Q1`7!8N=*{=GCV}GAN~%gT%xQM&4O(m(EL=dk
zz66EF>K?!1NTtkrAOMY2Ou}FjBMg?-wIe|l&Pe_E5fVU?^N`wYwGt`5&PEmY_C*IE0;7r9Yg7z6xv=L`t_I$cfXeE
zGvZ1WG!3wCe0Pq=ux8eX(E@c`Eee;GL5O?0JwYYJr4Xz`-BDY=4aNtKqrfC&k{E${
zK@`8hh6@wK4BjMZrQL&cZDS$wQ(yv%Nm}Bl6GBjDXuH|{;$YA4c-QKg%Fm-W{jI98G=}u?xjZ=f?N4=%Fjw&?A(r%_Pv-Eovhgqaj+7&H!&>
z=`KQ;Auc(wfFsA(ZyStW2rFuq633wa?n)AZw~Dk?)D6rBEiux7guf)SmKsqL531u1
zxg2z?wcf9*Vf>J8V!*#M+wulYcu3t}*AE;G-R}&K09__?iCXkcahc@*^lgm
z#;YiPC39JR+W~X_c2sd2SnqoA$w?t7Lv+u->ta6VXV@#2<+lS$A0=?=DI|`lwZU=i
zwWUfK4L?Q^bVZ@ye<@h*;C`D!V$O$Fx6
zgv@QLLHTJ?mMt4PrxIL*&1v5eD1KfD1r5K+Rs+PXH$#0=3rjcTw!tSI$_*m^jv_Jy
z#?4!9a?FwgI{K2R#Q}&=ZuVR~CXg105*O5UDP13c_SKO43`fnOUn*cyonZ6cNC>~e
zijMByPTyhP9sGoR&1UTUmW`rQH^y`3s|6`N{OWwaVu$~IdB0hO>*lafPJ9xCeBFJS
zeVXiSK6qe+3H;!gzkhrUt}`CHr0lWS@cY=UhQ?YhP2eJa=E
zNr)r|izUaYqNx`#velq|Dxe_SQN}KjJ2=aW52Qs>bkjYg^$SeW0S7
zNSHXQlOhVBi1D2|f-}MsKkzj2qST1@Pxme~`AOZ^@=(KWXdGYVADum)1&W4o@fH(K
zz&?X2VVl!miFv7!>O&kF1FAfVBB{bY=p0#5sp)FL@&Y%8KwgqbR2--eC$7h#OCrAy
zA1iyet1X3c%jG=YRU;=s(olMgYJh}b^c*i1y(O|iV}ZJ5oS}=QG%7c}X{Cv>0!U?t
z!kEb910s;^hk_~PbaKz&xj5VLT2l{QJnMT{H2~2)@&zx2tB^SM9%A+AWqJ+q)x7|^
zPnlO9kKt?NZ20Uqczo-6(B^c;GSgMYd)dWt=F^6YJDaZI*s5aoXdY=3vre{~6}C1f
zRG}#rF<1syk)_fLLx2dn@v^rv{st}FVKmIeR!BGE5
zTJ?11?aG3HApSn&Z0(%P^lhIrnQv-)mJ8FEk33qB;O$zsC_`~qW0cj&zzSFdqX4je
zPtXOFF*JSjbdk_ZhDJfJD&yS}K`4ZsxMK3M5@4L0Qd9Bknsn0%4^o-^kM~&ihs_&q
zB41*Xl`!_BR-
zgsrkH(jr1<=sE;^&O3@dv#D3vT_`EFu)NM{`4;XkN``$cEz7*~*3b3ShlXoP-p|Su
zTHVt|_WE#CnEz~^Rg)Kh^(9tprO__mRn|%3bo?VL0}tTr^#(!C%ccMkoljJukYgcsKH|Llr>)-j{GkVPk%v>BNp*aOtvQVGh4ix>Y94*SO_{`Id
zepP(!J}bB^+BD)se9=%IL3OaGu~il1wru1xSqLBKg7b~dYYT20QePUKMtS730~T4Z
z7@jFbgC;ePUQl-j!eE&R)^0h1_H^~Uq?1!5uc;{fqnaL9!(26oiw%?9_*(`$7q;
z>PU$O4L~Z;FK*!oj1Hf3&1RP7^JdWJoIbYrMXj7k^#Aciv-5&(;iheh=U^&n9hyEU}OG$>zHPq))d5ZI(
z=`o4o$nXbDQeMsbgwlo3yHCYl#d|8%8l|k_f}Q!TcXcfa{H@nl)dP2lr6pCw#pET%
zYRU?YDdEMoaGLS1s>b=={UzV}a%q;#nQd&|DZWyai8C#%_>|m|T&gI;Q>Qel=#FOE
z0NHvSgDg4Ys!Kgxt-e%!x@%!=9a~yrt!C>~jdneFXe`BSi(_wBv~>Gt0=X3qFG~0+DyZF=ae3VqOx}M|Mln1hY}fv2|1Xs|0ciV%tBI20ECq(fzCF
zVB0>9(TWSkq?A(T5mm|rCC?`4C4T?$9*~^%ln055Qy$0WEg%ZHqzZBqHMNi<*Z
zRBx%F1p>Dwk`)ybf>k(fBnlyI$qmkv1qsJ(kh+1}@dpw(AYnT6Z`i#dKW&h|`erR@
zA&%*yqGPmYmbvq)(jd;%09cBaYuF!_^vWooBWz1uSYjaDrK+CcwbU?@%c2m%Y@WBm
z*?13%9c<bcy$5xBJAN|`RODpfanI{1+%zl-Ls0CG01;-s;UrJK`|o+J
zTfVQBgF8_+;L#fJ5J=P*W9Z(uk;Y`HdGClv#J~U8-fXtU&0~reLHD4O3RP?9$g$$+
zdp!X#UajE6mT0;+SyBs=p(u%?Yt#U^*!ZFK$is?cfa&*sjdd^EQ(Elfj2C3E#!
zBjAH(C4^w}II|DJC%zzT`!vIiBp`UM4-)*v0BKZBv`kVL_t~jyryo
z2FrF1gj!=Q``2El!DIM~R!G{EAaj^ZmAQOjnt5TSpLq>$vxa?k6K8Mkl-lF_l`4+y
zf0S+Fb?(@AW5SzY)Vhc)a=xcb^lq8Y$`4E9Vs|^kS7g2rDuFgs7YX$q{x}Jd`{>T(
zeme4TfLF|jP@g>!cm5lTMAnzqEHm=;(iX1!-jx2M@ZNBOWVeg{ozfPmHj8XEB16V@
zDE64-eUG4wRGvoOmRq_EK2k%BG5{b_tc(;RuU{9#k#U!4?4+4c{=3@hr#xjJW$0Kd8W&Mjrx`TwA
zLjvEi9(o+6E_twABc8*|&vZ$DaJyOyA8l)hZuN+1>ju{(g(E;st{%Y3CDTmfW^+0@
zami(r=^l$r{B&B{n)=2=1Q?V7UPh7;ZSodHSm2fAp;jNFv646sH_z=pGMul5IKUr`
zeV3#7XlK1O#N(C-Ft8EbDlI_ox<(6=XhRh#^q~vI+leDEH8-3GdQ#kiHxZ4@UmA!j
zTy9e}+*9c>L_CHX2NM#QhjXICBMd-1le3hXa|HCwwrw-3vlMdZ?6-+W`jX4De4vl?z8a9;fk9pNX=?1j)Zutjg#pmqBQ9O62PW>Kos!gTBcv
zs6xtF(=zKK1P2$TikP3KXVGbDS4s4hpuokY@yE8@P%X(R)ThAhF`(Gp(2f}A^y*2x
zL~4u5dwYUZj56kZil9YX=*wTgjs2RQM?1X6SK`;Jfp*eY-_ZAOaEpy}NoOXKWPF~z
zfS`^FnM_Ze4$gk2Hx-qY%0!|zjW7p8GCs^M=$!pLt`f|FI(sB^vi3LRo@#duzoJqv|FV|Ar2^MG^xJ3c;XoH{1Y|lmJr^^{zBW)XfJy|;&V++GyS+4m+yJ#mL
z3e;-jDyMIAlEefd@dsxVSMM7TF$Na+eo_OnOkWLoa!Ax5=#Qzp{TIX-C)VO2mf9is
zT|?HbX-n5Q)2}tN1Fez#;u201%vFX!FbyK*WYbc_!!D8HGofQ$HS`NLF
zxS@w76FkAFKLtlVVGB}JCHzQl*kGcR@mea6w@JYwBz^;~m1xylap6Rfts@j43+J|3
zH$)!GMfrUji|Uo53KTvRrl`1dDb6nVH7sf%6;{>+o8_2oFKYc3aL|
zhSU2>v7a8rC`|K=QEw83nHs@MEDslY$($k257u<6aYC45MYNBl^hu-4<~XG*9Wo7%
zIx~+JoEjVU6zO-SPYijEcw~=XTzOkz<8kpk!O8XnC-R?qn2x2T>93p5NC~kgB>j)P
zAGPNiB@(NEV$t4tv%wfs&Ki}`20QNMjm+mOoQ>^r+uf6{&0I~4l`3g50L|4Vx`xcj
z50bFkJysc)m~I{}4fS8UpmW%xg#>}E?USF}+^Tm&IbCsW31P-sGSy-16ZBhl_~?1*
zUolCltYg3{HMngSgZH>Wqr?maacSxW!Ad}POxy=(&nqw;Yb9oZy@C1+-VZi^nNpTI
zU8o>3i})JV-~e8B`xfvnawtHU2JAQ}PAL(8B$S=x(C8>!VC}4A2tM)KdX#DaaM7MD
z{c_8l>H{qk>wUm#Mxl{(3_RD|m~eEQqUQ#j!NlA(E)EdEoyN7-zVaKJ>Jv!f;6m(X+bj2C|#
zBSnu=K4@v?m!_I(fDTW%-b`9?)oXmcM5sZZ6V~MYN!pvwvd_Ln`qE0M-HL^Ma2fxh(I;Jc-f%EA8M`ER0+Lmk_15{>BHB2lapH=?UCcERf8fNz9gA@
zj{t_=u8lY+9%fAun^WpShyNMN|J>yM8=Qn!hd8%SFit%cba0=ZPh|01?72StUwix)
zy3NspCY{eE9lr;kcsJ?=V?o%+%6EQd3uoZI-0mo<42e6P9xLs^b^fas;w;_?{FkZY
zz}wD`GX=B=Q;vp&fjUy#HkwQ&49C{aZjMkuGj(m%qp>zmh=BbEi%0)r{rdNxCZo|X
zg`rF~-E*0LFx$rwuVh7k*by{T9+28u-ZWIrkX&~x?9@c;Gf3Q3!iMS9f!}31Tf1wm
zp&cac#Y*$A-@Xd+%bACwT4wKa26V8<^+6dNh@?+^tn_WwpGl!QX3(PJ8LQYELp5Hgtgj_ekd9Qv{Ytb46%Zj+1x%A6ZsHmo
zmsIV9A!xD85#^YKuMta2p`v5K#q;L1dV)|E!MDia$G~Lw+Q!5!nqPVOyAPy9Fw#RV
zAK4$(RgrqarbwBVeP4yOp)VCUZOUFTCBvuC>tA=)9v+CdDxvravk^lZGM{C~;u}M;
zj!PvPz^dnS;^XVQ>Cl#Q7@B$V)mx9s)e$q%%u4^qtZG0q!q;ATq}eJb4N=0-`WzUU
zJXM8sjxa!b0Gc}&fxg&Y2D@vLwE9v!Td&q9+f9v5hJj>QO$9vo@BGcsAIZ>EC&!&o
z;NU&e3+;qmT;!0tHEv-F;b<%fgHj2h8|2#7YiBq-$!)=|^zZj4=ao%o-REPR3xLKW
ztG(0$^KJ#kE9WJV4XxyhW5SOeUU%Pm4?J#HPpk>t!KK7N!7zTEcfXseUPkFDP#~Zn
zgik-G-~MaNejE7rQphtI{T|Q#S#(5ysOg_hUy{?a&Tpr3f9d@CnEp3s&tL2B@$Bq*
z{H<^7pZ;EUjy((i_DA-2wg2>yy@cpxJJcWTh^OuSm(%}hj(Vy7vYqFTI`R{l{89g_
zx#xdd^pnPa#`B_$ApX#(Qb!Wn%Tm
zjy?Xrr&%v8yu5k*W8upalRPJCf89g=Z(#j%?G;arI)4@5UV404sr%z`?(b@U>UJ*;
zyi9-o80aSW<@fKf{cn?g?mPNDo+BLiJHr3yLi*?4e%Xigtou7^)&EWRPgl~*>Ax&s
z|FJRw^VjtM(+BnI@vD8||K3W+)9!o%|8MK~W#v~h|?xCZy&?(UG_4#5c$2oM6ikeqY&
z4$pqh`o5pU9tTK%L-_Ie?}6~}!N~Tlf`hG{Ba^}d
z8P>xW@W03sD+R9koJ@y_EL6%k-d_0)@MxOlr?8
zNdZ!PUOUxTnhB>M@P#zLG08I$l=blg?@`H+EDX%ryj8F(s?mx3*nxojHe)#}7japJfhBd0S2&d-K&
znKFuy#0}b1@HnU-`UZFG$Iee=qeVXOf_vxlG|sFE-i-@uGr{~VO%F3nW8!V-}zralyv1iVYV(b_r&C3ulv%i`+BD?(N%r
z=jM^IEa0=UlGbSW-9gCH``xLnDZN{If&=3lp{wgO49j
zdJ8}QG0)(>>P4qwR2>^H3`GxVjAl~)^bHojEtlOh$ODVzPpy-zFx2Lqw9Tzj#1d=+
zlg?AH#M#z}sADKHL8aW1s*4j9wW~EIDz)D>8zl0r`N(8mP*K*3Vr{~Q4)W%%odiSS
zuAJ@Og;_;#FU~K2XLnnszYfX_P-`rg>eLzx`)HITqIn95A?q+;${*t9&oXLWwSKTq
z1l`ReE0SgTrDPSGZ=AQuSg1vv%Y?q1ylzE>KJ3&>&MxCk#E6{=kbehVk7AcEHLIjs
z$lByh>P-Qh-pAyi;ev8o7l)r@5-hnfF%!P*6cicbzSSZ-$~wf!-Vl$5{pVN%^x
zt6x#(p9xTZ6S&$sSUQ>+8#_IXNZ)|9Y@H8MrjQ^Y@cswwFBX?cT}-TR%F{G?Od5RxjC|#if>Uzq8m=A
zlcb^@&q5^~N|5MbdJ->;w=H#x#cduE3YtTzyG4^0O}{=_8Nuv>fS3D|4Y
z&vTKd7%qan+wYXU*4Y>BR=9p_tFOsB+V?e!)(9-iH$?}$g>={-jJpj<0U97#YSTR6sW3-x
zbJ=Le8~m?z_;A>A`>{qKJYR~`AbOUT${5?U&lYyk?A|_xJTe@^S!&Fz94GWW;m>?v
zm5tK0{OqNaN15mi+=(~g4EJ|Ik%nK%#O8YOR_afQt(UzLXx_qkj5(8^9rI5Y;H_@X
ze2*>^V)5ZOC#^e{_iNK{&pmjJwY%g5P?_;6eEpZ1~?AYVOxCSCY
zeo!l?A~jEI_%dw5^Rs>!x6{IHlo2kGllLdrFs@=Hp>FlCZOWIERdMQ04?F)6!RrYr
zeHbo|%0+3#7rST^UB#}LfRjR63xZ^;#F+k1E%)#cZ#&`H)%05rbFAI`gYr-ej{)*#
z)fD!yT0pX(JwyMMZ&eTwRl-S(ILSR{=;18nr|83|EQU~fsisg6`cTDAR%+?gC8H}6
zOA+Xy(G5p{7o3%Q?wk-1ob^zg2V>y3n*d9#5>B+SHBIXzxVb4WdS!&o^w;0kUgW!<
zptlb~S(#^`Kv1dLbImtKH;}x*AQAb&N=#{Zi$eqfk!~OwQ^bqPx(jc5!UyT$>~p+5
zl6DMF!ZI2ZeI-Y=Huo?IKXV<~fTIfGA7*v(V7i#LD)B~A=I{|VR`@LFy`z+PSr5Ba
zeb~rJ^`B#;F*96{P2DKP&1l@^wXOE7vF15S4lq+C?1^imqTnlP;ypBcS9a}g*{J9(
zf_&Q=mLjfk`6RM5*zE&L)ZA867RNpn>Z!{zq+N07%U;?f{sn_q*(zx=6TUF3xnsO|
z`XlJvwH2I$`zhY7QdY*YrxXEFG^})8U$K$Vu?0T17xAAdWv3DOiD3!5%5VkEc{0#i
z1{1jB5QLO}--E7Zf?`;6jGh#MUltSwY+i=$IR?{evRwE0b{3+flT(0R&Z5?{lm~r^
zbFfzDF^!%HC!3esY`%Z?XK-HU4YzN42+rmYNB@60_N>2-J$Tq=kr@EIg!zF6eTn{M
z#(G~^Nnwt80qCD4mCC^oNzp|CqU=|ks$c>FqmWEv6Sm2Sm-q;AW~0Tc;Z8)d8Lr8P
zd7!l3=32cu({o)6XG(*KtBWOIi2sQI*J$Wz$lkHyxjXY8>Rf5)rLD;0l1Wxm7s#_%HZwQ{DvE`DOHkFOmr`e9CE
z`czm(+_DB>0?8CF#LAAW#PEa
zIK1dqs}mq^$hmTfYA?ju#zi?U0Wd4G{IT8IKKi|P#b6UaQyfyNgT1j?S
z=>pS~?*n%@&%vhjEA0gSn*oh*NyoF1Rv2FH?GI4CC3k(e0M+a__SaKm2}{}3OxTe*
zRCAn)hO|-9AAZ37|MoZA|MU0%?f(Af6yWiFz@goS`edj2~{
z`w!VK;$Ij4k;ba+a}GjZi4EV%uilnZR65EG=_%|Q1_x>!^Ap7B;f_1Ku7dOO%`I|^
zn-mm1!k!|pT2B1@q*9?VHZ!x|njxFtL+UNMzISxYK2r1d1FAZj`u@DVz5QXivysP#
zE_x#-IkcJoMnLZr5b7=X!G7Z0=Dc}lhMxjE3Y2u3#NPTcb|o+Ja<%#0tg0k=fW9~V
zto<}ng7nP$#1ClQ>KQu-p$0ZRz##0FFnx2HlV=l=b5jm^ADgfY2s~-}&|_K!2^2H4
zc7*QE&q^fPm%Jf-SH$~ryZsbPKMC<7Y@j#rwN)yulhC$L7RC5~5LgZnH-d!H)|nDJ+bT7q1fUtSQhZdf&A
zwZWd$N+V`wlMxl!?XtZMiftJhe_1hcJWm|nsTJRz_Y8aB9
z_w68w-V(4~==$t)_xRB1$9MM^&2ZzscQu6Y`0U`_-koS)&mUL4xzTz)-zuToUY=yZ
zA5XuNbc!U*5+L@s>0>V6G9s0)&ml3~FYO?kU?f{s%!X>=>@7u}B`-g={lYhSHme5H13*MzzcG7-;aKk(4bHO{O^Ue1Ku-FIQM22-Ch`j;lH=B%jhx?(TfjKA3h
zHhO(`c2^6_mOcpS`M!|?iOX!X%+dsI9>DHU3^;kMExQ+cikDm2CdX?P+eBw1S2
zuQvsp;M<65B0vWtT|7DXL0uzI|Em831Yc7*WiI-u>g0Sdx;5WTN}`gWqS$%Y`ZI#c
z(7En4AEM%{@fhibQ)XdhodGWp>$`AK>(!xGj9ZP;&W
z05m7Mgk>664^`Qr%vjo9=DW{18Cj;r6
z_Y(bqz*jmnrD}%rsxL`)A&>$r=3VY(!DC(v?H!Gm3vHKFv!Mmg`NqUj=Ay=V#@$;h
zzR%Qd`cp5vbY(xkvesBuF&aE=78$=lTR6M4)qKvNK_FhYTHV^=x^NcC7GGiQduyrH
zz1!nxL&z&n@eIj#E)gwvch{nf^N^Sq%CiBVkVo+66k3-*QKz|^!gJ!_sO9J6IsR{^
zvGNq3&y_RT+Fs}SbG!>t;=;RD)Z(0y!MOgyC(vLvfU!P<-~sU^@q9EhB4GD|sohNs
zRTo_Dbuff&tgL7wRe|JlJK6Vh72!R~ne!2W$=Q)soVLtKm41zv{x&)K{Xl0D+N*R}
z^i&AtR;pgae$)4BC8j5GvZ){Qak;;?oLW@5*4O{=f1O@hv}WvN>}h$G=(PBq^iIX-
z&}I!L8%qSw|NSZUDlzv~)aiVM9>1sGF>g&bnHUM#Cln`?eqFDfj*CEmPANLRa$Ni<
zZvr;)VZHIN(g{hEEQ~Ii&SJMK;#R1}IBoU`KU;iQ!!gOZ8t*lqOQK}1`;Z}%n8M{Qfgd~m
z;n?A`7HK7n$UH3_jHLUJ@kCnoAWuaww_}3q;F#kMUxj)0YlD3j4j!qHyjiBP4A}!E
zSysEL%8IaaVruJ|q{8?;nTw1h#r;9Ohdkfl
zZ?E8Hk9qo0&wHOYG^@Ua*nxRZiL@g$RVA!pPu>+nYA~~eo5Dv%opfBu8cM{8~bK?(XJ=c+(P1^6y>HC^+
zdZ`}6R%UAsFz*94ymgc$vCHHR8xNe%UsB5?(=gPN_r`V`?Mah^V)e@J%o8=$3Y4QN
zlEYdswAjvuYcH%4G()`{dsQ*eYQmWMMjdkSU+ZC2peU2RiDQ?2U$tZ-mc}kSQBGl=
zR1R$XK@Fo>y{0TL$=0STRm#$)8(CJ?N>shu$Nb&0pvp9X1FOl2i4ZNrprMTae4I-@FgyT_QN4)@dDJ9k*S58nKhdR)5
z7^+|=3brFrcD<)>zSoy#*K*fHUfM=lI38k1PV|H+1Z@DX*YV*6S;>Z3{n=r|5(RK$
z93>$1-4Z+Yv?`(CB$+NcRWg9f*QicP|5BHE;WCZgzF|?1A?z6znQSR9CAoPFebUeV
zZ&@*OG>f>VGF>ss%q9}1?ei+*QY;5EFyzo7+QRl4M7`p<)ji}4xKkthjWJ?bd#_T4
z_i0&|qe29y^E@+z@WV6&aNhyU8vV#CqP)V!w)#|>RmFZ7he{S)AhVa5v!mphf2xJ8
za@W1s<}vcA3_BcF*m5KjjhPNc?&T-i9D_aAX^Pre7EX^KIiE(2FnHC;|COy-Lw^=+
zf!N<=K1)>H_Z^y)X>gjA?R*C71{-Moav
zN+`@(m6HQ3V0Rw%tKa0e0|Zo$y;0_OX*NNwEHATGjI&vB{00kqAerBEY5I}gu9#IG
z2jQ&Diam=DNB5(|GiVTI>2kIbV(EiC6z4Q93l2wV(XwF33d>atR^)c(SYqq{{3YZC
zgiKeJCLT%)_@RsyHDsb4s9Grg`Mfb@S!o1Gw&ZXK^}^nMQ5^8503{$Y#vI-+{hPS(
zUY{`{HYO_VtW5yaHWuy5HcX8IvKUoGjf{Hjl4jV3>e91R%f64(VJsy>a~BvUI>wb<
zgewag_}0F9)gAgxQ`-7jb#Ha$)&d&mQv56uCQ7lp2DWey7YCOKRB~;y^GnIwOpXRn
zQnx{G8(mZkZeJouMv8{+9}>#oq*2R~WP((GBw@BBobTdkWyi_iNS$;K$@Z!U5Cvog
z6e}fFHA3`%hED#xU;u&Py}VR%A4sJWbrw=(j
zRa*-GFgGJvc8W6il6jbyGXE+Dia}WKMa_iSo97I(C4Q_b%$haPin`K-Dui^VvqPqM
zpt>{szU*Ldz+29hZxj)l+DF-KY+4mpk#Ec`NweRN)O}yT3WT+T#;a@kyHyW@cf(>dZ7Jkh>14A&V5cKm1AJO5H9*B;@XPdJ5WU|Dhrs
z8B72{t4>`JD9IV!t1zA^gtzJ${|+c#fW_x&&PpDR+Ort1`*U$>a2tgsE1`VWbLyOByk3VL
zlG+g5$}amVk!Un?@ih8@#j|^+p|&SjXu(LkKnhb|ANTLflrB0?QTRIXy}J7N%Cj^XvWGcKbN!d
z>B01SO1IM>h_}WDun{BzGRHgN=&~P?#I%>Lufq#oD`5x*u^Ye}YvO22(}%wor%@86
z(a@h~zdMadXZ7PydUh^L7>`0?q%Zs|b)+upl0S>*R;^oNuS%x}P}GKP8*d
zq!txof;!Vy5ZGs|mZ0eUc_EKKU_)vuBtAQY!C@Y#fiv#zr?#@gtsMOIk^WE#yAr(+
zEKavDJA~ySYBuqxh175!el$hH=jC%j6BGkP#7|Pg1V+z6qDg$o`wE<1H8a8TV=ADJ
z>$$a2>9VHwS(YU8C!g?rO39BFMZGT4+62yWQum^*lZkzCg*Fg;qK`eMTZzPyfLi(e
zwplxpQq?Hc#j_U^ee3F{*7KlVG7ZMox3^wxeb1(y7hpF_lXJ%kO7kDpwmD?{!ZrJb+BA*KJoR8oL-P6s
z>1y%ji%`kZ%-W>0fWkHaWHlshq<=~0_w5;ug#5d?L+BZlT|y*?Vrg{HdQo9bRhn^m
zZT#6)TJ=myGY;du@IZoCE=5rkasog=N<=!$-~H~}b<@3D__jTNH2A(@m}8HIR5%4k
zoHb)hf;D-lI~6x%qc-h*30S5PU(o7(>6aGm0e2q9BZKBqrvtKjjbZo3m~Rd#H+LSZ
zLhVjXd0ia2g}d!pKc2LP?eGZs1s`n3{a`z^zdBxqwzihPDxk4)GTp*}kmG=RgZXB;
zlp3H6IG}JY9P5*ZZZ+6s&!?)H<6@Psxl?7gciO9!zlE(@Gc%q-Z>@#7DQ8+r9$~l7
zF8dPO+t;(HF|P+Mp9d^13T*G3cJ}tJ-H+t+B5)*ypJ>a`fp
zM>jb_Qr^9xco{nal3;{92}@K@As&n5q5Nug9Q|tJynF;H<@08o76`cHLY;NF<3lgS
z#LjgKTFWjmQ;bLApP%rAtvZI>FaZ+V&y$EXLFFh>@Fsgj)+cXtzFo>f8(CJ;-{ABj
zK)~JKh$i@^n0}~NXnznJd0of|Zf1Wag909%iN%Wxoww2*0dHLHU7$T~(|@i)K2#YY
z^c;+>o;Dyoxm{ZBAAVbXaK}Rr@?SKJkCbI`+RH4FQew?%cQz2`B5za&WZuTwgWD0iYH!f>dOI=
zrLCW#+*Rzlpquy43~z!0xB2527%5_Pyp$$bL3}U4=97CW3c{Iu9KZq|p|>vrIa_TM
zY=+Ln`!b0Y7tkG-X>FJ%W&e%zw0X
z7P-ur1m1MNo-JlV{^Vv#8fvJp>!8P8&U#|+;q3+sw9+xqI-cn8g9aVm+1!Ve88?bK
zPlGYAC15SKeF`|faJVFrujYcK_C!rJh7|TzwoSFN7Oj7pM@!|TWh=P$t0ukZ|bP;_ys48WQkNxl=&1G#Hrhk(>{9dqywC~x{X
zjx<=i2EFMo+^A%bQ#tV4yt$Pl2Qd5dfN_bkoHERNkp@$gNn3r)?~~3CnM~UM45qg3
zFyxL80fFdYi#`Gsv_x%foQ!Rp^ip^oz4sJj05vBydOijp9^k;Z})fR%y%`1KT(y>`T%j-wu2zh0EdpNzIVL9is5bse8
zG#^{*XLz;XU2ML3Q6AgWPO~&2ao^?t^K<`^@6Fn&J&Di5h4%~+>(}M>8#?vWOAn5M
zcm_fG@VDi~Kl<#qfd4%hJqq}Z!onf@ci4HV^l5N<A$5e%+rt)BhLJ>DS{IjN$*gl&pu{`QW#|t>c%JU(Im^8JNc`=HZgSgaALxS)cOp
GzyAY;y@`_m
literal 0
HcmV?d00001
diff --git a/ruoyi/src/main/resources/logback.xml b/ruoyi/src/main/resources/logback.xml
index 4d6b910d3..b78044b35 100644
--- a/ruoyi/src/main/resources/logback.xml
+++ b/ruoyi/src/main/resources/logback.xml
@@ -97,17 +97,9 @@
-
-
-
-
-
-
-
-
-
+
diff --git a/ruoyi/src/main/resources/mapper/system/SysMenuMapper.xml b/ruoyi/src/main/resources/mapper/system/SysMenuMapper.xml
index 08838c599..2f69197b3 100644
--- a/ruoyi/src/main/resources/mapper/system/SysMenuMapper.xml
+++ b/ruoyi/src/main/resources/mapper/system/SysMenuMapper.xml
@@ -32,8 +32,8 @@
m.perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
from sys_menu m
left join sys_role_menu rm on m.menu_id = rm.menu_id
- left join sys_user_role ur on rm.role_id = ur.role_id
- left join sys_role ro on ur.role_id = ro.role_id
+ left join sys_user_role sur on rm.role_id = sur.role_id
+ left join sys_role ro on sur.role_id = ro.role_id
${ew.getCustomSqlSegment}
@@ -55,9 +55,9 @@
m.create_time
from sys_menu m
left join sys_role_menu rm on m.menu_id = rm.menu_id
- left join sys_user_role ur on rm.role_id = ur.role_id
- left join sys_role ro on ur.role_id = ro.role_id
- left join sys_user u on ur.user_id = u.user_id
+ left join sys_user_role sur on rm.role_id = sur.role_id
+ left join sys_role ro on sur.role_id = ro.role_id
+ left join sys_user u on sur.user_id = u.user_id
where u.user_id = #{userId}
and m.menu_type in ('M', 'C')
and m.status = '0'
@@ -81,18 +81,18 @@
select distinct m.perms
from sys_menu m
left join sys_role_menu rm on m.menu_id = rm.menu_id
- left join sys_user_role ur on rm.role_id = ur.role_id
+ left join sys_user_role sur on rm.role_id = sur.role_id
diff --git a/ruoyi/src/main/resources/mapper/system/SysRoleMapper.xml b/ruoyi/src/main/resources/mapper/system/SysRoleMapper.xml
index dc54c7e9f..90dff5051 100644
--- a/ruoyi/src/main/resources/mapper/system/SysRoleMapper.xml
+++ b/ruoyi/src/main/resources/mapper/system/SysRoleMapper.xml
@@ -34,8 +34,8 @@
r.create_time,
r.remark
from sys_role r
- left join sys_user_role ur on ur.role_id = r.role_id
- left join sys_user u on u.user_id = ur.user_id
+ left join sys_user_role sur on sur.role_id = r.role_id
+ left join sys_user u on u.user_id = sur.user_id
left join sys_dept d on u.dept_id = d.dept_id
@@ -51,14 +51,14 @@
- WHERE r.del_flag = '0' and ur.user_id = #{userId}
+ WHERE r.del_flag = '0' and sur.user_id = #{userId}
select r.role_id
from sys_role r
- left join sys_user_role ur on ur.role_id = r.role_id
- left join sys_user u on u.user_id = ur.user_id
+ left join sys_user_role sur on sur.role_id = r.role_id
+ left join sys_user u on u.user_id = sur.user_id
where u.user_id = #{userId}
diff --git a/ruoyi/src/main/resources/mapper/system/SysUserMapper.xml b/ruoyi/src/main/resources/mapper/system/SysUserMapper.xml
index 0b388d136..dacdcd8a6 100644
--- a/ruoyi/src/main/resources/mapper/system/SysUserMapper.xml
+++ b/ruoyi/src/main/resources/mapper/system/SysUserMapper.xml
@@ -80,12 +80,12 @@
r.status as role_status
from sys_user u
left join sys_dept d on u.dept_id = d.dept_id
- left join sys_user_role ur on u.user_id = ur.user_id
- left join sys_role r on r.role_id = ur.role_id
+ left join sys_user_role sur on u.user_id = sur.user_id
+ left join sys_role r on r.role_id = sur.role_id
- select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.password, u.sex,
+ select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex,
u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, d.dept_name, d.leader from
sys_user u
left join sys_dept d on u.dept_id = d.dept_id
@@ -93,7 +93,7 @@
- select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.password, u.sex,
+ select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex,
u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, d.dept_name, d.leader from
sys_user u
left join sys_dept d on u.dept_id = d.dept_id
@@ -104,8 +104,8 @@
select distinct u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_time
from sys_user u
left join sys_dept d on u.dept_id = d.dept_id
- left join sys_user_role ur on u.user_id = ur.user_id
- left join sys_role r on r.role_id = ur.role_id
+ left join sys_user_role sur on u.user_id = sur.user_id
+ left join sys_role r on r.role_id = sur.role_id
${ew.getCustomSqlSegment}
@@ -113,8 +113,8 @@
select distinct u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_time
from sys_user u
left join sys_dept d on u.dept_id = d.dept_id
- left join sys_user_role ur on u.user_id = ur.user_id
- left join sys_role r on r.role_id = ur.role_id
+ left join sys_user_role sur on u.user_id = sur.user_id
+ left join sys_role r on r.role_id = sur.role_id
${ew.getCustomSqlSegment}
diff --git a/ruoyi/src/main/resources/mapper/system/SysUserRoleMapper.xml b/ruoyi/src/main/resources/mapper/system/SysUserRoleMapper.xml
index 63a846d30..5a55fcb1e 100644
--- a/ruoyi/src/main/resources/mapper/system/SysUserRoleMapper.xml
+++ b/ruoyi/src/main/resources/mapper/system/SysUserRoleMapper.xml
@@ -10,8 +10,8 @@
select u.user_id from sys_user u
- inner join sys_user_role ur
- on u.user_id = ur.user_id and ur.role_id = #{roleId}
+ inner join sys_user_role sur
+ on u.user_id = sur.user_id and sur.role_id = #{roleId}
diff --git a/ruoyi/src/main/resources/vm/java/controller.java.vm b/ruoyi/src/main/resources/vm/java/controller.java.vm
index 20580d860..6b2fc6582 100644
--- a/ruoyi/src/main/resources/vm/java/controller.java.vm
+++ b/ruoyi/src/main/resources/vm/java/controller.java.vm
@@ -53,11 +53,11 @@ public class ${ClassName}Controller extends BaseController {
@SaCheckPermission("${permissionPrefix}:list")
@GetMapping("/list")
#if($table.crud || $table.sub)
- public TableDataInfo<${ClassName}Vo> list(@Validated(QueryGroup.class) ${ClassName}Bo bo, PageQuery pageQuery) {
+ public TableDataInfo<${ClassName}Vo> list(${ClassName}Bo bo, PageQuery pageQuery) {
return i${ClassName}Service.queryPageList(bo, pageQuery);
}
#elseif($table.tree)
- public R> list(@Validated(QueryGroup.class) ${ClassName}Bo bo) {
+ public R> list(${ClassName}Bo bo) {
List<${ClassName}Vo> list = i${ClassName}Service.queryList(bo);
return R.ok(list);
}
@@ -70,7 +70,7 @@ public class ${ClassName}Controller extends BaseController {
@SaCheckPermission("${permissionPrefix}:export")
@Log(title = "${functionName}", businessType = BusinessType.EXPORT)
@PostMapping("/export")
- public void export(@Validated ${ClassName}Bo bo, HttpServletResponse response) {
+ public void export(${ClassName}Bo bo, HttpServletResponse response) {
List<${ClassName}Vo> list = i${ClassName}Service.queryList(bo);
ExcelUtil.exportExcel(list, "${functionName}", ${ClassName}Vo.class, response);
}
@@ -82,8 +82,8 @@ public class ${ClassName}Controller extends BaseController {
@SaCheckPermission("${permissionPrefix}:query")
@GetMapping("/{${pkColumn.javaField}}")
public R<${ClassName}Vo> getInfo(@ApiParam("主键")
- @NotNull(message = "主键不能为空")
- @PathVariable("${pkColumn.javaField}") ${pkColumn.javaType} ${pkColumn.javaField}) {
+ @NotNull(message = "主键不能为空")
+ @PathVariable("${pkColumn.javaField}") ${pkColumn.javaType} ${pkColumn.javaField}) {
return R.ok(i${ClassName}Service.queryById(${pkColumn.javaField}));
}
diff --git a/ruoyi/src/main/resources/vm/java/domain.java.vm b/ruoyi/src/main/resources/vm/java/domain.java.vm
index 8fde4adea..28ca34c0f 100644
--- a/ruoyi/src/main/resources/vm/java/domain.java.vm
+++ b/ruoyi/src/main/resources/vm/java/domain.java.vm
@@ -2,7 +2,7 @@ package ${packageName}.domain;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
-import lombok.NoArgsConstructor;
+import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.util.Date;
import java.math.BigDecimal;
@@ -28,6 +28,7 @@ import com.ruoyi.common.core.domain.TreeEntity;
#set($Entity="TreeEntity<${ClassName}>")
#end
@Data
+@EqualsAndHashCode(callSuper = true)
@TableName("${tableName}")
public class ${ClassName} extends ${Entity} {
diff --git a/ruoyi/src/main/resources/vm/java/service.java.vm b/ruoyi/src/main/resources/vm/java/service.java.vm
index 309328ca3..0f9137a5d 100644
--- a/ruoyi/src/main/resources/vm/java/service.java.vm
+++ b/ruoyi/src/main/resources/vm/java/service.java.vm
@@ -21,52 +21,33 @@ public interface I${ClassName}Service {
/**
* 查询${functionName}
- *
- * @param ${pkColumn.javaField} ${functionName}主键
- * @return ${functionName}
*/
${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField});
#if($table.crud || $table.sub)
/**
* 查询${functionName}列表
- *
- * @param ${className} ${functionName}
- * @return ${functionName}集合
*/
TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}Bo bo, PageQuery pageQuery);
#end
/**
* 查询${functionName}列表
- *
- * @param ${className} ${functionName}
- * @return ${functionName}集合
*/
List<${ClassName}Vo> queryList(${ClassName}Bo bo);
/**
* 修改${functionName}
- *
- * @param ${className} ${functionName}
- * @return 结果
*/
Boolean insertByBo(${ClassName}Bo bo);
/**
* 修改${functionName}
- *
- * @param ${className} ${functionName}
- * @return 结果
*/
Boolean updateByBo(${ClassName}Bo bo);
/**
* 校验并批量删除${functionName}信息
- *
- * @param ${pkColumn.javaField}s 需要删除的${functionName}主键集合
- * @param isValid 是否校验,true-删除前校验,false-不校验
- * @return 结果
*/
Boolean deleteWithValidByIds(Collection<${pkColumn.javaType}> ids, Boolean isValid);
}
diff --git a/ruoyi/src/main/resources/vm/java/serviceImpl.java.vm b/ruoyi/src/main/resources/vm/java/serviceImpl.java.vm
index 005c84a9f..776738219 100644
--- a/ruoyi/src/main/resources/vm/java/serviceImpl.java.vm
+++ b/ruoyi/src/main/resources/vm/java/serviceImpl.java.vm
@@ -35,9 +35,6 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service {
/**
* 查询${functionName}
- *
- * @param ${pkColumn.javaField} ${functionName}主键
- * @return ${functionName}
*/
@Override
public ${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField}){
@@ -47,9 +44,6 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service {
#if($table.crud || $table.sub)
/**
* 查询${functionName}列表
- *
- * @param bo ${functionName}
- * @return ${functionName}
*/
@Override
public TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}Bo bo, PageQuery pageQuery) {
@@ -61,9 +55,6 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service {
/**
* 查询${functionName}列表
- *
- * @param bo ${functionName}
- * @return ${functionName}
*/
@Override
public List<${ClassName}Vo> queryList(${ClassName}Bo bo) {
@@ -100,9 +91,6 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service {
/**
* 新增${functionName}
- *
- * @param bo ${functionName}
- * @return 结果
*/
@Override
public Boolean insertByBo(${ClassName}Bo bo) {
@@ -118,9 +106,6 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service {
/**
* 修改${functionName}
- *
- * @param bo ${functionName}
- * @return 结果
*/
@Override
public Boolean updateByBo(${ClassName}Bo bo) {
@@ -131,8 +116,6 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service {
/**
* 保存前的数据校验
- *
- * @param entity 实体类数据
*/
private void validEntityBeforeSave(${ClassName} entity){
//TODO 做一些数据校验,如唯一约束
@@ -140,9 +123,6 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service {
/**
* 批量删除${functionName}
- *
- * @param ${pkColumn.javaField}s 需要删除的${functionName}主键
- * @return 结果
*/
@Override
public Boolean deleteWithValidByIds(Collection<${pkColumn.javaType}> ids, Boolean isValid) {
diff --git a/ruoyi/src/main/resources/vm/vue/v3/index-tree.vue.vm b/ruoyi/src/main/resources/vm/vue/v3/index-tree.vue.vm
index 6776687d9..5db196710 100644
--- a/ruoyi/src/main/resources/vm/vue/v3/index-tree.vue.vm
+++ b/ruoyi/src/main/resources/vm/vue/v3/index-tree.vue.vm
@@ -173,11 +173,13 @@
#set($dictType=$column.dictType)
#if("" != $treeParentCode && $column.javaField == $treeParentCode)
-