Compare commits
8 Commits
14f96acd60
...
14f9193703
| Author | SHA1 | Date | |
|---|---|---|---|
| 14f9193703 | |||
| 060da45f84 | |||
| 1cb5d841d9 | |||
| 00a1c1037c | |||
| 9e192f5197 | |||
| 4875594bcb | |||
| 90f8142d0b | |||
| 3a6d62d695 |
@@ -11,18 +11,20 @@ RUN mkdir -p /ruoyi/server/logs \
|
||||
|
||||
WORKDIR /ruoyi/server
|
||||
|
||||
ENV SERVER_PORT=8080 SNAIL_PORT=28080 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS=""
|
||||
ENV SERVER_PORT=8080 SNAIL_JOB_PORT=28080 SNAIL_AI_PORT=38080 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS=""
|
||||
|
||||
EXPOSE ${SERVER_PORT}
|
||||
# 暴露 snail job 客户端端口 用于定时任务调度中心通信
|
||||
EXPOSE ${SNAIL_PORT}
|
||||
# 暴露 snail 客户端端口 用于定时任务调度中心通信
|
||||
EXPOSE ${SNAIL_JOB_PORT}
|
||||
EXPOSE ${SNAIL_AI_PORT}
|
||||
|
||||
ADD ./target/ruoyi-admin.jar ./app.jar
|
||||
|
||||
SHELL ["/bin/bash", "-c"]
|
||||
|
||||
ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \
|
||||
-Dsnail-job.port=${SNAIL_PORT} \
|
||||
-Dsnail-job.port=${SNAIL_JOB_PORT} \
|
||||
-Dsnail-ai.port=${SNAIL_AI_PORT} \
|
||||
# 应用名称 如果想区分集群节点监控 改成不同的名称即可
|
||||
#-Dskywalking.agent.service_name=ruoyi-server \
|
||||
#-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar \
|
||||
|
||||
@@ -58,7 +58,7 @@ public class CaptchaController {
|
||||
* @param phoneNumber 用户手机号
|
||||
* @return 操作结果
|
||||
*/
|
||||
@RateLimiter(key = "#phonenumber", time = 60, count = 1)
|
||||
@RateLimiter(key = "#phoneNumber", time = 60, count = 1)
|
||||
@GetMapping("/resource/sms/code")
|
||||
public R<Void> smsCode(@NotBlank(message = "{user.phonenumber.not.blank}") String phoneNumber) {
|
||||
if (!RegexValidator.isMobile(phoneNumber)) {
|
||||
@@ -66,7 +66,6 @@ public class CaptchaController {
|
||||
}
|
||||
String key = GlobalConstants.CAPTCHA_CODE_KEY + phoneNumber;
|
||||
String code = RandomUtil.randomNumbers(4);
|
||||
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
|
||||
// 验证码模板id 自行处理 (查数据库或写死均可)
|
||||
String templateId = "";
|
||||
LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
|
||||
@@ -75,8 +74,10 @@ public class CaptchaController {
|
||||
SmsResponse smsResponse = smsBlend.sendMessage(phoneNumber, templateId, map);
|
||||
if (!smsResponse.isSuccess()) {
|
||||
log.error("验证码短信发送异常 => {}", smsResponse);
|
||||
return R.fail(smsResponse.getData().toString());
|
||||
Object data = smsResponse.getData();
|
||||
return R.fail(data == null ? "验证码短信发送失败" : data.toString());
|
||||
}
|
||||
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@@ -107,13 +108,13 @@ public class CaptchaController {
|
||||
public void emailCodeImpl(String email) {
|
||||
String key = GlobalConstants.CAPTCHA_CODE_KEY + email;
|
||||
String code = RandomUtil.randomNumbers(4);
|
||||
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
|
||||
try {
|
||||
MailBuilder.of()
|
||||
.to(email)
|
||||
.subject("登录验证码")
|
||||
.text("您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。")
|
||||
.send();
|
||||
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
|
||||
} catch (Exception e) {
|
||||
log.error("验证码短信发送异常 => {}", e.getMessage());
|
||||
throw new ServiceException(e.getMessage());
|
||||
|
||||
+3
@@ -26,6 +26,9 @@ public class ValidatorUtils {
|
||||
* @throws ConstraintViolationException 如果校验不通过,则抛出参数校验异常
|
||||
*/
|
||||
public static <T> void validate(T object, Class<?>... groups) {
|
||||
if (object == null) {
|
||||
throw new RuntimeException("请求参数不能为空");
|
||||
}
|
||||
Set<ConstraintViolation<T>> validate = VALID.validate(object, groups);
|
||||
if (!validate.isEmpty()) {
|
||||
throw new ConstraintViolationException("参数校验异常", validate);
|
||||
|
||||
+25
-4
@@ -51,6 +51,9 @@ public class DropDownOptions {
|
||||
* 分隔符
|
||||
*/
|
||||
private static final String DELIMITER = "_";
|
||||
private static final String OPTION_PART_REGEX = "^[A-Za-z0-9\\u4e00-\\u9fa5]+$";
|
||||
private static final String EXCEL_NAME_REGEX = "^[A-Za-z_\\u4e00-\\u9fa5][A-Za-z0-9_\\u4e00-\\u9fa5]*$";
|
||||
private static final String CELL_REFERENCE_REGEX = "^[A-Za-z]{1,3}[1-9][0-9]*$";
|
||||
|
||||
/**
|
||||
* 创建只有一级的下拉选
|
||||
@@ -69,10 +72,9 @@ public class DropDownOptions {
|
||||
*/
|
||||
public static String createOptionValue(Object... vars) {
|
||||
StringBuilder stringBuffer = new StringBuilder();
|
||||
String regex = "^[\\S\\d\\u4e00-\\u9fa5]+$";
|
||||
for (int i = 0; i < vars.length; i++) {
|
||||
String var = StrUtil.trimToEmpty(Convert.toStr(vars[i]));
|
||||
if (!var.matches(regex)) {
|
||||
if (!var.matches(OPTION_PART_REGEX)) {
|
||||
throw new ServiceException("选项数据不符合规则,仅允许使用中英文字符以及数字");
|
||||
}
|
||||
stringBuffer.append(var);
|
||||
@@ -81,10 +83,29 @@ public class DropDownOptions {
|
||||
stringBuffer.append(DELIMITER);
|
||||
}
|
||||
}
|
||||
if (stringBuffer.toString().matches("^\\d_*$")) {
|
||||
String optionValue = stringBuffer.toString();
|
||||
validateOptionValue(optionValue);
|
||||
return optionValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验级联下拉选项值是否可作为 Excel 名称管理器名称。
|
||||
*
|
||||
* @param optionValue 选项值
|
||||
*/
|
||||
public static void validateOptionValue(String optionValue) {
|
||||
if (StrUtil.isBlank(optionValue)) {
|
||||
throw new ServiceException("选项数据不能为空");
|
||||
}
|
||||
if (optionValue.matches("^[0-9].*")) {
|
||||
throw new ServiceException("禁止以数字开头");
|
||||
}
|
||||
return stringBuffer.toString();
|
||||
if (!optionValue.matches(EXCEL_NAME_REGEX)) {
|
||||
throw new ServiceException("选项数据不符合Excel名称规则,仅允许中英文、数字或下划线,且不能以数字开头");
|
||||
}
|
||||
if (optionValue.matches(CELL_REFERENCE_REGEX)) {
|
||||
throw new ServiceException("选项数据不能为Excel单元格引用");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+14
@@ -202,6 +202,7 @@ public class ExcelDownHandler implements SheetWriteHandler {
|
||||
if (CollUtil.isEmpty(firstOptions)) {
|
||||
return;
|
||||
}
|
||||
validateLinkedOptionNames(firstOptions);
|
||||
Map<String, List<String>> secoundOptionsMap = new HashMap<>();
|
||||
options.getNextOptions().forEach((k, v) -> secoundOptionsMap.put(k, new ArrayList<>(v)));
|
||||
|
||||
@@ -313,6 +314,19 @@ public class ExcelDownHandler implements SheetWriteHandler {
|
||||
currentLinkedOptionsSheetIndex++;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验级联下拉一级选项可作为 Excel 名称,且不能重复。
|
||||
*/
|
||||
private void validateLinkedOptionNames(List<String> firstOptions) {
|
||||
Set<String> names = new HashSet<>();
|
||||
for (String firstOption : firstOptions) {
|
||||
DropDownOptions.validateOptionValue(firstOption);
|
||||
if (!names.add(firstOption)) {
|
||||
throw new ServiceException("级联下拉一级选项重复:" + firstOption);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <h2>额外表格形式的普通下拉框</h2>
|
||||
* 由于下拉框可选值数量过多,为提升Excel打开效率,使用额外表格形式做下拉
|
||||
|
||||
+18
-3
@@ -24,6 +24,7 @@ import org.dromara.common.redis.utils.RedisUtils;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
@@ -150,16 +151,30 @@ public class RepeatSubmitAspect {
|
||||
public boolean isFilterObject(final Object o) {
|
||||
Class<?> clazz = o.getClass();
|
||||
if (clazz.isArray()) {
|
||||
return MultipartFile.class.isAssignableFrom(clazz.getComponentType());
|
||||
if (MultipartFile.class.isAssignableFrom(clazz.getComponentType())) {
|
||||
return true;
|
||||
}
|
||||
int length = Array.getLength(o);
|
||||
for (int i = 0; i < length; i++) {
|
||||
Object value = Array.get(o, i);
|
||||
if (ObjectUtil.isNotNull(value) && isFilterObject(value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else if (Collection.class.isAssignableFrom(clazz)) {
|
||||
Collection collection = (Collection) o;
|
||||
for (Object value : collection) {
|
||||
return value instanceof MultipartFile;
|
||||
if (ObjectUtil.isNotNull(value) && isFilterObject(value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (Map.class.isAssignableFrom(clazz)) {
|
||||
Map map = (Map) o;
|
||||
for (Object value : map.values()) {
|
||||
return value instanceof MultipartFile;
|
||||
if (ObjectUtil.isNotNull(value) && isFilterObject(value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
|
||||
|
||||
+8
-1
@@ -45,7 +45,14 @@ public class SaTokenExceptionHandler {
|
||||
public R<Void> handleNotLoginException(NotLoginException e, HttpServletRequest request) {
|
||||
String requestURI = request.getRequestURI();
|
||||
log.error("请求地址'{}',认证失败'{}',无法访问系统资源", requestURI, e.getMessage());
|
||||
return R.fail(HttpStatus.HTTP_UNAUTHORIZED, "认证失败,无法访问系统资源");
|
||||
String msg = switch (e.getType()) {
|
||||
case NotLoginException.TOKEN_TIMEOUT -> "登录已过期,请重新登录";
|
||||
case NotLoginException.BE_REPLACED -> "当前账号已在其他设备登录,您已被强制下线";
|
||||
case NotLoginException.KICK_OUT -> "账号已被管理员强制下线";
|
||||
case NotLoginException.TOKEN_FREEZE -> "账号已被冻结,请联系管理员处理";
|
||||
default -> "登录状态异常,请重新登录";
|
||||
};
|
||||
return R.fail(HttpStatus.HTTP_UNAUTHORIZED, msg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -618,7 +618,7 @@ const { resetQuery } = useSearchReset({
|
||||
});
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = (row?: ${BusinessName}VO) => {
|
||||
const handleAdd = (row?: Partial<${BusinessName}VO>) => {
|
||||
openDialog('添加${functionName}');
|
||||
getTreeselect();
|
||||
if (row != null && row.${treeCode}) {
|
||||
@@ -629,7 +629,7 @@ const handleAdd = (row?: ${BusinessName}VO) => {
|
||||
};
|
||||
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row: ${BusinessName}VO) => {
|
||||
const handleUpdate = async (row: Partial<${BusinessName}VO>) => {
|
||||
reset();
|
||||
await getTreeselect(row.${treeCode});
|
||||
if (row != null) {
|
||||
@@ -668,7 +668,7 @@ const submitForm = () => {
|
||||
};
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row: ${BusinessName}VO) => {
|
||||
const handleDelete = async (row: Partial<${BusinessName}VO>) => {
|
||||
await modal.confirm('是否确认删除${functionName}编号为"' + row.${pkColumn.javaField} + '"的数据项?');
|
||||
setLoading(true);
|
||||
await del${BusinessName}(row.${pkColumn.javaField}).finally(() => setLoading(false));
|
||||
@@ -687,7 +687,7 @@ const filterTreeOptions = (options: ${BusinessName}Option[], excludeId: string |
|
||||
|
||||
#if($enableStatus)
|
||||
/** 状态修改 */
|
||||
const handleStatusChange = async (row: ${BusinessName}VO) => {
|
||||
const handleStatusChange = async (row: Partial<${BusinessName}VO>) => {
|
||||
const text = row.${statusField} === ${statusField}ActiveValue ? '启用' : '停用';
|
||||
try {
|
||||
await modal.confirm('确认要"' + text + '"吗?');
|
||||
@@ -701,7 +701,7 @@ const handleStatusChange = async (row: ${BusinessName}VO) => {
|
||||
|
||||
#if($enableSort)
|
||||
/** 排序调整 */
|
||||
const handleSortChange = async (row: ${BusinessName}VO) => {
|
||||
const handleSortChange = async (row: Partial<${BusinessName}VO>) => {
|
||||
try {
|
||||
await update${BusinessName}Sort(row.${pkColumn.javaField}, row.${sortField});
|
||||
modal.msgSuccess('排序更新成功');
|
||||
|
||||
@@ -561,7 +561,7 @@ const handleAdd = () => {
|
||||
};
|
||||
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row?: ${BusinessName}VO) => {
|
||||
const handleUpdate = async (row?: Partial<${BusinessName}VO>) => {
|
||||
reset();
|
||||
const _${pkColumn.javaField} = row?.${pkColumn.javaField} || ids.value[0];
|
||||
const res = await get${BusinessName}(_${pkColumn.javaField});
|
||||
@@ -597,7 +597,7 @@ const submitForm = () => {
|
||||
};
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row?: ${BusinessName}VO) => {
|
||||
const handleDelete = async (row?: Partial<${BusinessName}VO>) => {
|
||||
const _${pkColumn.javaField}s = row?.${pkColumn.javaField} || ids.value;
|
||||
await modal.confirm('是否确认删除${functionName}编号为"' + _${pkColumn.javaField}s + '"的数据项?');
|
||||
await del${BusinessName}(_${pkColumn.javaField}s);
|
||||
@@ -620,7 +620,7 @@ const handleExport = () => {
|
||||
|
||||
#if($enableStatus)
|
||||
/** 状态修改 */
|
||||
const handleStatusChange = async (row: ${BusinessName}VO) => {
|
||||
const handleStatusChange = async (row: Partial<${BusinessName}VO>) => {
|
||||
const text = row.${statusField} === ${statusField}ActiveValue ? '启用' : '停用';
|
||||
try {
|
||||
await modal.confirm('确认要"' + text + '"吗?');
|
||||
@@ -634,7 +634,7 @@ const handleStatusChange = async (row: ${BusinessName}VO) => {
|
||||
|
||||
#if($enableSort)
|
||||
/** 排序调整 */
|
||||
const handleSortChange = async (row: ${BusinessName}VO) => {
|
||||
const handleSortChange = async (row: Partial<${BusinessName}VO>) => {
|
||||
try {
|
||||
await update${BusinessName}Sort(row.${pkColumn.javaField}, row.${sortField});
|
||||
modal.msgSuccess('排序更新成功');
|
||||
|
||||
@@ -10,7 +10,6 @@ import org.dromara.common.core.constant.SystemConstants;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -25,8 +24,6 @@ import java.util.List;
|
||||
@TableName("sys_menu")
|
||||
public class SysMenu extends BaseEntity {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 6967394714839655267L;
|
||||
/**
|
||||
* 菜单ID
|
||||
*/
|
||||
|
||||
+6
-1
@@ -10,6 +10,7 @@ import org.apache.fesod.sheet.metadata.GlobalConfiguration;
|
||||
import org.apache.fesod.sheet.metadata.data.ReadCellData;
|
||||
import org.apache.fesod.sheet.metadata.data.WriteCellData;
|
||||
import org.apache.fesod.sheet.metadata.property.ExcelContentProperty;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.core.utils.TreeBuildUtils;
|
||||
@@ -93,7 +94,11 @@ public class DeptExcelConverter implements Converter<Long> {
|
||||
if (StringUtils.isBlank(deptName)) {
|
||||
return null;
|
||||
}
|
||||
return getDeptMaps().nameToId().get(deptName);
|
||||
Long deptId = getDeptMaps().nameToId().get(deptName);
|
||||
if (deptId == null) {
|
||||
throw new ServiceException("部门不存在:" + deptName);
|
||||
}
|
||||
return deptId;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+9
-9
@@ -27,10 +27,7 @@ import org.dromara.workflow.common.constant.FlowConstant;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -74,10 +71,12 @@ public class FlwChartExtServiceImpl implements ChartExtService {
|
||||
Map<String, List<FlowHisTask>> groupedByNode = StreamUtils.groupByKey(flowHisTasks, FlowHisTask::getNodeCode);
|
||||
|
||||
// 批量查询所有审批人的用户信息
|
||||
List<UserDTO> userDTOList = userService.selectListByIds(StreamUtils.toList(flowHisTasks, e -> Convert.toLong(e.getApprover())));
|
||||
List<UserDTO> userDTOList = userService.selectListByIds(StreamUtils.toSet(flowHisTasks, e -> Convert.toLong(e.getApprover())));
|
||||
|
||||
// 将查询到的用户列表转换为以用户ID为key的映射
|
||||
Map<Long, UserDTO> userMap = StreamUtils.toIdentityMap(userDTOList, UserDTO::getUserId);
|
||||
Set<Long> deptIds = StreamUtils.toSet(userDTOList, UserDTO::getDeptId);
|
||||
Map<Long, String> deptNameMap = deptService.selectDeptNamesByIds(deptIds);
|
||||
|
||||
Map<String, String> dictType = dictService.getAllDictByDictType(FlowConstant.WF_TASK_STATUS);
|
||||
|
||||
@@ -100,7 +99,7 @@ public class FlwChartExtServiceImpl implements ChartExtService {
|
||||
));
|
||||
|
||||
// 处理当前节点的扩展信息
|
||||
this.processNodeExtInfo(nodeJson, latestPerApprover, userMap, dictType);
|
||||
this.processNodeExtInfo(nodeJson, latestPerApprover, userMap, deptNameMap, dictType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,9 +167,11 @@ public class FlwChartExtServiceImpl implements ChartExtService {
|
||||
* @param nodeJson 当前流程节点对象,包含节点基础信息和提示内容容器
|
||||
* @param taskList 当前节点关联的历史审批任务列表,用于生成提示信息
|
||||
* @param userMap 用户信息映射表,key 为用户ID,value 为用户DTO对象,用于获取审批人信息
|
||||
* @param deptNameMap 部门名称映射表,key 为部门ID,value 为部门名称
|
||||
* @param dictType 数据字典映射表,key 为字典项编码,value 为对应显示值,用于翻译审批状态等
|
||||
*/
|
||||
private void processNodeExtInfo(NodeJson nodeJson, List<FlowHisTask> taskList, Map<Long, UserDTO> userMap, Map<String, String> dictType) {
|
||||
private void processNodeExtInfo(NodeJson nodeJson, List<FlowHisTask> taskList, Map<Long, UserDTO> userMap,
|
||||
Map<Long, String> deptNameMap, Map<String, String> dictType) {
|
||||
|
||||
// 获取节点提示内容对象中的 info 列表,用于追加提示项
|
||||
List<PromptContent.InfoItem> info = nodeJson.getPromptContent().getInfo();
|
||||
@@ -182,8 +183,7 @@ public class FlwChartExtServiceImpl implements ChartExtService {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 查询用户所属部门名称
|
||||
String deptName = deptService.selectDeptNameByIds(Convert.toStr(userDTO.getDeptId()));
|
||||
String deptName = deptNameMap.getOrDefault(userDTO.getDeptId(), StringUtils.EMPTY);
|
||||
|
||||
// 添加标题项,如:👤 张三(市场部)
|
||||
info.add(new PromptContent.InfoItem()
|
||||
|
||||
+7
-3
@@ -338,10 +338,14 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
|
||||
return;
|
||||
}
|
||||
// 添加抄送人记录
|
||||
FlowHisTask flowHisTask = flowHisTaskMapper.selectList(
|
||||
List<FlowHisTask> flowHisTasks = flowHisTaskMapper.selectList(
|
||||
QueryBuilder.lambda(FlowHisTask.class)
|
||||
.eq(FlowHisTask::getTaskId, task.getId())
|
||||
.build()).get(0);
|
||||
.build());
|
||||
if (CollUtil.isEmpty(flowHisTasks)) {
|
||||
throw new ServiceException("流程历史任务不存在,无法添加抄送记录");
|
||||
}
|
||||
FlowHisTask flowHisTask = flowHisTasks.get(0);
|
||||
FlowNode flowNode = new FlowNode();
|
||||
flowNode.setNodeCode(flowHisTask.getTargetNodeCode());
|
||||
flowNode.setNodeName(flowHisTask.getTargetNodeName());
|
||||
@@ -897,7 +901,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
|
||||
if (CollUtil.isEmpty(userList)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return userService.selectListByIds(StreamUtils.toList(userList, e -> Convert.toLong(e.getProcessedBy())));
|
||||
return userService.selectListByIds(StreamUtils.toSet(userList, e -> Convert.toLong(e.getProcessedBy())));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -103,7 +103,8 @@ services:
|
||||
# 时区上海
|
||||
TZ: Asia/Shanghai
|
||||
SERVER_PORT: 8080
|
||||
SNAIL_PORT: 28080
|
||||
SNAIL_JOB_PORT: 28080
|
||||
SNAIL_AI_PORT: 38080
|
||||
volumes:
|
||||
# 配置文件
|
||||
- /docker/server1/logs/:/ruoyi/server/logs/
|
||||
@@ -119,7 +120,8 @@ services:
|
||||
# 时区上海
|
||||
TZ: Asia/Shanghai
|
||||
SERVER_PORT: 8081
|
||||
SNAIL_PORT: 28081
|
||||
SNAIL_JOB_PORT: 28081
|
||||
SNAIL_AI_PORT: 38081
|
||||
volumes:
|
||||
# 配置文件
|
||||
- /docker/server2/logs/:/ruoyi/server/logs/
|
||||
@@ -153,3 +155,17 @@ services:
|
||||
- /docker/snailjob/logs/:/ruoyi/snailjob/logs
|
||||
privileged: true
|
||||
network_mode: "host"
|
||||
|
||||
ruoyi-snailai-server:
|
||||
image: ruoyi/ruoyi-snailai-server:5.5.3
|
||||
container_name: ruoyi-snailai-server
|
||||
environment:
|
||||
# 时区上海
|
||||
TZ: Asia/Shanghai
|
||||
ports:
|
||||
- "8900:8900"
|
||||
- "18888:18888"
|
||||
volumes:
|
||||
- /docker/snailai/logs/:/ruoyi/snailai/logs
|
||||
privileged: true
|
||||
network_mode: "host"
|
||||
|
||||
Reference in New Issue
Block a user