@@ -250,6 +250,11 @@ | |||
<groupId>com.ningdatech</groupId> | |||
<artifactId>nd-flowable-starter</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.ningdatech</groupId> | |||
<artifactId>nd-yxt-starter</artifactId> | |||
<version>1.0.0</version> | |||
</dependency> | |||
<!--浙政钉--> | |||
<dependency> | |||
<groupId>com.alibaba.xxpt</groupId> | |||
@@ -0,0 +1,35 @@ | |||
package com.ningdatech.pmapi.sms.constant; | |||
/** | |||
* <p> | |||
* DatePattern | |||
* </p> | |||
* | |||
* @author WendyYang | |||
* @since 09:28 2022/5/10 | |||
*/ | |||
public interface DatePattern { | |||
String TIME_ZONE = "GMT+8"; | |||
String T = "'T'"; | |||
String GMT = "XXX"; | |||
String YMD_HMS = "yyyy-MM-dd HH:mm:ss"; | |||
String YMD_HMS_1 = "yyyyMMddHHmmss"; | |||
String YMD_HMS_S = "yyyy-MM-dd HH:mm:ss.SSS"; | |||
String YMD = "yyyy-MM-dd"; | |||
String YMD_0 = "yyyy/MM/dd"; | |||
String YMD_1 = "yyyyMMdd"; | |||
String HMS = "HH:mm:ss"; | |||
String HMS_0 = "HH/mm/ss"; | |||
String HMS_1 = "HHmmss"; | |||
String YMD_HMS_GMT = YMD + T + HMS + GMT; | |||
String EEE = "EEE"; | |||
} |
@@ -0,0 +1,68 @@ | |||
package com.ningdatech.pmapi.sms.constant; | |||
import io.swagger.annotations.ApiModel; | |||
import io.swagger.annotations.ApiModelProperty; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Getter; | |||
import lombok.NoArgsConstructor; | |||
import java.util.stream.Stream; | |||
/** | |||
* @author liuxinxin | |||
* @date 2023/2/16 下午4:50 | |||
*/ | |||
@Getter | |||
@NoArgsConstructor | |||
@AllArgsConstructor | |||
@ApiModel(value = "VerificationCodeType", description = "验证码类型") | |||
public enum VerificationCodeType { | |||
/** | |||
* 用户注册 | |||
*/ | |||
LOGIN("用户登录", 1, 5, 10); | |||
@ApiModelProperty(value = "描述") | |||
private String desc; | |||
@ApiModelProperty(value = "发送间隔(单位:分钟)") | |||
private Integer sendInterval; | |||
@ApiModelProperty(value = "过期时间(单位:分钟)") | |||
private Integer expireTime; | |||
/** | |||
* 小于等于0则不限制次数,超出次数限制则锁定验证码发送 | |||
*/ | |||
@ApiModelProperty(value = "每日发送次数") | |||
private Integer sendTimesByDay; | |||
public static VerificationCodeType match(String val, VerificationCodeType def) { | |||
return Stream.of(values()).filter(item -> item.name().equalsIgnoreCase(val)).findAny().orElse(def); | |||
} | |||
public static VerificationCodeType get(String val) { | |||
return match(val, null); | |||
} | |||
public boolean eq(VerificationCodeType val) { | |||
return val != null && this.name().equals(val.name()); | |||
} | |||
@ApiModelProperty(value = "编码") | |||
public String getCode() { | |||
return this.name(); | |||
} | |||
public static VerificationCodeType of(String key) { | |||
for (VerificationCodeType statusEnum : VerificationCodeType.values()) { | |||
if (statusEnum.name().equals(key)) { | |||
return statusEnum; | |||
} | |||
} | |||
throw new IllegalArgumentException(String.format("Illegal VerificationCodeType = %s", key)); | |||
} | |||
} |
@@ -0,0 +1,13 @@ | |||
package com.ningdatech.pmapi.sms.constant; | |||
/** | |||
* @author liuxinxin | |||
* @date 2022/8/8 下午5:05 | |||
*/ | |||
public interface YxtSmsTemplateConst { | |||
/** | |||
* 短信登陆验证码 | |||
*/ | |||
String SMS_LOGIN_TEMPLATE = "验证码:%s(有效期为%s分钟),请勿泄露给他人,如非本人操作,请忽略此信息。"; | |||
} |
@@ -0,0 +1,41 @@ | |||
package com.ningdatech.pmapi.sms.controller; | |||
import com.ningdatech.pmapi.sms.constant.VerificationCodeType; | |||
import com.ningdatech.pmapi.sms.manage.SmsManage; | |||
import com.ningdatech.pmapi.sms.model.po.ReqVerificationCodePO; | |||
import io.swagger.annotations.Api; | |||
import io.swagger.annotations.ApiOperation; | |||
import lombok.RequiredArgsConstructor; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.springframework.validation.annotation.Validated; | |||
import org.springframework.web.bind.annotation.PostMapping; | |||
import org.springframework.web.bind.annotation.RequestBody; | |||
import org.springframework.web.bind.annotation.RequestMapping; | |||
import org.springframework.web.bind.annotation.RestController; | |||
/** | |||
* @author liuxinxin | |||
* @date 2023/2/16 下午4:40 | |||
*/ | |||
@Slf4j | |||
@RestController | |||
@RequestMapping("/api/v1/verification") | |||
@Api(tags = "验证码相关接口") | |||
@RequiredArgsConstructor | |||
public class VerificationCodeController { | |||
private final SmsManage smsManage; | |||
/** | |||
* 通用的发送验证码功能 | |||
* 一个系统可能有很多地方需要发送验证码(注册、找回密码等) | |||
* 每增加一个场景{@link VerificationCodeType}就需要增加一个枚举值 | |||
*/ | |||
@ApiOperation(value = "发送验证码", notes = "发送验证码") | |||
@PostMapping(value = "/send") | |||
public void send(@Validated @RequestBody ReqVerificationCodePO request) { | |||
smsManage.sendVerificationCode(request); | |||
} | |||
} |
@@ -0,0 +1,43 @@ | |||
package com.ningdatech.pmapi.sms.helper; | |||
import com.baomidou.mybatisplus.core.toolkit.StringUtils; | |||
import com.ningdatech.cache.repository.CachePlusOps; | |||
import com.ningdatech.pmapi.sms.constant.VerificationCodeType; | |||
import com.ningdatech.pmapi.sms.model.dto.VerifyCodeCacheDTO; | |||
import com.ningdatech.pmapi.sms.utils.SmsRedisKeyUtils; | |||
import lombok.RequiredArgsConstructor; | |||
import org.springframework.stereotype.Component; | |||
import java.util.Objects; | |||
/** | |||
* @author liuxinxin | |||
* @date 2022/8/16 上午11:43 | |||
*/ | |||
@Component | |||
@RequiredArgsConstructor | |||
public class VerifyCodeCheckHelper { | |||
private final CachePlusOps cachePlusOps; | |||
/** | |||
* 对某种类型的验证码进行校验 | |||
* @param type | |||
* @param mobile | |||
* @param verificationCode | |||
* @return | |||
*/ | |||
public boolean verification(VerificationCodeType type, String mobile, String verificationCode) { | |||
if (StringUtils.isBlank(mobile) || Objects.isNull(type)) { | |||
return false; | |||
} | |||
String key = SmsRedisKeyUtils.smsCodeVerifyKey(type, mobile); | |||
Object cacheObj = cachePlusOps.get(key); | |||
if (Objects.isNull(cacheObj)) { | |||
return false; | |||
} | |||
VerifyCodeCacheDTO cache = (VerifyCodeCacheDTO) cacheObj; | |||
return verificationCode.trim().equals(cache.getCode()); | |||
} | |||
} |
@@ -0,0 +1,97 @@ | |||
package com.ningdatech.pmapi.sms.manage; | |||
import cn.hutool.core.util.PhoneUtil; | |||
import cn.hutool.core.util.RandomUtil; | |||
import com.ningdatech.basic.exception.BizException; | |||
import com.ningdatech.cache.model.cache.CacheKey; | |||
import com.ningdatech.cache.repository.CachePlusOps; | |||
import com.ningdatech.pmapi.sms.constant.VerificationCodeType; | |||
import com.ningdatech.pmapi.sms.constant.YxtSmsTemplateConst; | |||
import com.ningdatech.pmapi.sms.model.dto.VerifyCodeCacheDTO; | |||
import com.ningdatech.pmapi.sms.model.po.ReqVerificationCodePO; | |||
import com.ningdatech.pmapi.sms.utils.DateUtil; | |||
import com.ningdatech.pmapi.sms.utils.SmsRedisKeyUtils; | |||
import com.ningdatech.yxt.client.YxtClient; | |||
import com.ningdatech.yxt.constants.YxtSmsSignEnum; | |||
import com.ningdatech.yxt.model.cmd.SendSmsCmd; | |||
import lombok.RequiredArgsConstructor; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.springframework.stereotype.Component; | |||
import org.springframework.util.Assert; | |||
import java.time.Duration; | |||
import java.time.LocalDateTime; | |||
import java.util.Collections; | |||
/** | |||
* @author liuxinxin | |||
* @date 2023/2/16 下午4:42 | |||
*/ | |||
@Slf4j | |||
@Component | |||
@RequiredArgsConstructor | |||
public class SmsManage { | |||
private final YxtClient yxtClient; | |||
private final CachePlusOps cachePlusOps; | |||
public void sendVerificationCode(ReqVerificationCodePO request) { | |||
Assert.isTrue(PhoneUtil.isMobile(request.getMobile()), "手机号码格式不正确"); | |||
String verificationType = request.getVerificationType(); | |||
VerificationCodeType verificationCodeTypeEnum = VerificationCodeType.of(verificationType); | |||
// 验证是否被锁定 | |||
String lockKey = SmsRedisKeyUtils.smsSendLockKey(verificationCodeTypeEnum, request.getMobile()); | |||
if (StringUtils.isNotBlank(cachePlusOps.get(lockKey))) { | |||
throw BizException.wrap("今日" + verificationCodeTypeEnum.getDesc() + "的验证码发送次数过多,已被锁定"); | |||
} | |||
// 验证发送间隔 | |||
String cacheKey = SmsRedisKeyUtils.smsCodeVerifyKey(verificationCodeTypeEnum, request.getMobile()); | |||
VerifyCodeCacheDTO preCache = (VerifyCodeCacheDTO) cachePlusOps.get(cacheKey); | |||
if (preCache != null) { | |||
if (LocalDateTime.now().minusMinutes(verificationCodeTypeEnum.getSendInterval()) | |||
.isBefore(preCache.getSendTime())) { | |||
throw BizException.wrap(verificationCodeTypeEnum.getSendInterval() + "分钟之内已发送过验证码,请稍后重试"); | |||
} | |||
} | |||
String code = RandomUtil.randomNumbers(6); | |||
VerifyCodeCacheDTO cache = VerifyCodeCacheDTO.builder() | |||
.code(code) | |||
.sendTime(LocalDateTime.now()) | |||
.mobile(request.getMobile()) | |||
.build(); | |||
// 创建短信内容 | |||
SendSmsCmd sendSmsCmd = new SendSmsCmd(); | |||
switch (verificationCodeTypeEnum) { | |||
case LOGIN: | |||
SendSmsCmd.SendSmsContext sendSmsContext = new SendSmsCmd.SendSmsContext(); | |||
sendSmsContext.setReceiveNumber(request.getMobile()); | |||
sendSmsContext.setContent(String.format(YxtSmsTemplateConst.SMS_LOGIN_TEMPLATE, code, verificationCodeTypeEnum.getExpireTime())); | |||
sendSmsCmd.setContextList(Collections.singletonList(sendSmsContext)); | |||
sendSmsCmd.setSmsSignEnum(YxtSmsSignEnum.ZJS_ELECTRONIC_EXPERT_LIB); | |||
break; | |||
default: | |||
throw new IllegalArgumentException("非法的短信发送类型"); | |||
} | |||
// 发送 短信 | |||
yxtClient.submitSmsTask(sendSmsCmd); | |||
log.info("send verificationCode mobile = {},code = {}", request.getMobile(), code); | |||
cachePlusOps.set(new CacheKey(cacheKey, Duration.ofMinutes(verificationCodeTypeEnum.getExpireTime())), cache); | |||
String limitKey = SmsRedisKeyUtils.smsSendLimitKey(verificationCodeTypeEnum, request.getMobile()); | |||
if (StringUtils.isNotBlank(cachePlusOps.get(limitKey))) { | |||
long limitCount = cachePlusOps.incr(new CacheKey(limitKey, Duration.ofSeconds(DateUtil.restSecondsFromNowToNoon()))); | |||
// 超出单日发送次数之后直接锁定 | |||
if (limitCount >= verificationCodeTypeEnum.getSendTimesByDay().longValue()) { | |||
cachePlusOps.set(new CacheKey(lockKey, Duration.ofSeconds(DateUtil.restSecondsFromNowToNoon())), request.getMobile()); | |||
} | |||
} else { | |||
cachePlusOps.set(new CacheKey(limitKey, Duration.ofSeconds(DateUtil.restSecondsFromNowToNoon())), 1); | |||
} | |||
} | |||
} |
@@ -0,0 +1,27 @@ | |||
package com.ningdatech.pmapi.sms.model.dto; | |||
import lombok.Builder; | |||
import lombok.Data; | |||
import lombok.experimental.Tolerate; | |||
import java.time.LocalDateTime; | |||
/** | |||
* @author liuxinxin | |||
* @date 2023/2/16 下午4:42 | |||
*/ | |||
@Data | |||
@Builder | |||
public class VerifyCodeCacheDTO { | |||
@Tolerate | |||
public VerifyCodeCacheDTO() { | |||
} | |||
private String mobile; | |||
private LocalDateTime sendTime; | |||
private String code; | |||
} |
@@ -0,0 +1,26 @@ | |||
package com.ningdatech.pmapi.sms.model.po; | |||
import io.swagger.annotations.ApiModel; | |||
import io.swagger.annotations.ApiModelProperty; | |||
import lombok.Data; | |||
import javax.validation.constraints.NotBlank; | |||
import java.io.Serializable; | |||
/** | |||
* @author liuxinxin | |||
* @date 2023/2/16 下午4:40 | |||
*/ | |||
@Data | |||
@ApiModel(value = "VerificationCodeRequest", description = "验证码发送验证") | |||
public class ReqVerificationCodePO implements Serializable { | |||
@ApiModelProperty(value = "手机号") | |||
@NotBlank(message = "手机号不能为空") | |||
private String mobile; | |||
@ApiModelProperty(value = "短信类型", allowableValues = "LOGIN,RECOMMENDATION_PROOF_FILE_SUBMIT") | |||
@NotBlank(message = "短信类型不能为空") | |||
private String verificationType; | |||
} |
@@ -0,0 +1,206 @@ | |||
package com.ningdatech.pmapi.sms.utils; | |||
import com.ningdatech.pmapi.sms.constant.DatePattern; | |||
import java.time.*; | |||
import java.time.format.DateTimeFormatter; | |||
import java.time.temporal.ChronoUnit; | |||
import java.util.Date; | |||
/** | |||
* @author qinxianyun | |||
* JDK 8 新日期类 格式化与字符串转换 工具类 | |||
*/ | |||
public class DateUtil { | |||
public static final DateTimeFormatter DTF_YMD_HMS = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); | |||
public static final DateTimeFormatter DTF_YMD_HMS_COMPRESS = DateTimeFormatter.ofPattern("yyyyMMddHHmmss"); | |||
public static final DateTimeFormatter DTF_YMD_HM = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); | |||
public static final DateTimeFormatter DTF_YMD = DateTimeFormatter.ofPattern("yyyy-MM-dd"); | |||
public static final DateTimeFormatter DTF_YMD_SLASH = DateTimeFormatter.ofPattern("yyyy/MM/dd"); | |||
public static final DateTimeFormatter DTF_HMS = DateTimeFormatter.ofPattern("HH:mm:ss"); | |||
/** | |||
* 最大时间,三位小数 | |||
*/ | |||
public static final LocalTime LOCAL_TIME_3D = LocalTime.of(23, 59, 59, 999_000_00); | |||
/** | |||
* LocalDateTime 转时间戳 | |||
* | |||
* @param localDateTime / | |||
* @return / | |||
*/ | |||
public static Long getTimeStamp(LocalDateTime localDateTime) { | |||
return localDateTime.atZone(ZoneId.systemDefault()).toEpochSecond(); | |||
} | |||
/** | |||
* 时间戳转LocalDateTime | |||
* | |||
* @param timeStamp / | |||
* @return / | |||
*/ | |||
public static LocalDateTime fromTimeStamp(Long timeStamp) { | |||
return LocalDateTime.ofEpochSecond(timeStamp, 0, OffsetDateTime.now().getOffset()); | |||
} | |||
/** | |||
* LocalDateTime 转 Date | |||
* Jdk8 后 不推荐使用 {@link Date} Date | |||
* | |||
* @param localDateTime / | |||
* @return / | |||
*/ | |||
public static Date toDate(LocalDateTime localDateTime) { | |||
return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()); | |||
} | |||
/** | |||
* LocalDate 转 Date | |||
* Jdk8 后 不推荐使用 {@link Date} Date | |||
* | |||
* @param localDate / | |||
* @return / | |||
*/ | |||
public static Date toDate(LocalDate localDate) { | |||
return toDate(localDate.atTime(LocalTime.now(ZoneId.systemDefault()))); | |||
} | |||
/** | |||
* Date转 LocalDateTime | |||
* Jdk8 后 不推荐使用 {@link Date} Date | |||
* | |||
* @param date / | |||
* @return / | |||
*/ | |||
public static LocalDateTime toLocalDateTime(Date date) { | |||
return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()); | |||
} | |||
/** | |||
* 日期 格式化 | |||
* | |||
* @param localDateTime / | |||
* @param patten / | |||
* @return / | |||
*/ | |||
public static String localDateTimeFormat(LocalDateTime localDateTime, String patten) { | |||
DateTimeFormatter df = DateTimeFormatter.ofPattern(patten); | |||
return df.format(localDateTime); | |||
} | |||
/** | |||
* 日期 格式化 | |||
* | |||
* @param localDateTime / | |||
* @param df / | |||
* @return / | |||
*/ | |||
public static String localDateTimeFormat(LocalDateTime localDateTime, DateTimeFormatter df) { | |||
return df.format(localDateTime); | |||
} | |||
/** | |||
* 日期格式化 yyyy-MM-dd HH:mm:ss | |||
* | |||
* @param localDateTime / | |||
* @return / | |||
*/ | |||
public static String localDateTimeFormatyMdHms(LocalDateTime localDateTime) { | |||
return DTF_YMD_HMS.format(localDateTime); | |||
} | |||
/** | |||
* 日期格式化 yyyy-MM-dd | |||
* | |||
* @param localDateTime / | |||
* @return / | |||
*/ | |||
public String localDateTimeFormatyMd(LocalDateTime localDateTime) { | |||
return DTF_YMD.format(localDateTime); | |||
} | |||
/** | |||
* 字符串转 LocalDateTime ,字符串格式 yyyy-MM-dd | |||
* | |||
* @param localDateTime / | |||
* @return / | |||
*/ | |||
public static LocalDateTime parseLocalDateTimeFormat(String localDateTime, String pattern) { | |||
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern); | |||
return LocalDateTime.from(dateTimeFormatter.parse(localDateTime)); | |||
} | |||
/** | |||
* 字符串转 LocalDateTime ,字符串格式 yyyy-MM-dd | |||
* | |||
* @param localDateTime / | |||
* @return / | |||
*/ | |||
public static LocalDateTime parseLocalDateTimeFormat(String localDateTime, DateTimeFormatter dateTimeFormatter) { | |||
return LocalDateTime.from(dateTimeFormatter.parse(localDateTime)); | |||
} | |||
/** | |||
* 字符串转 LocalDateTime ,字符串格式 yyyy-MM-dd HH:mm:ss | |||
* | |||
* @param localDateTime / | |||
* @return / | |||
*/ | |||
public static LocalDateTime parseLocalDateTimeFormatyMdHms(String localDateTime) { | |||
return LocalDateTime.from(DTF_YMD_HMS.parse(localDateTime)); | |||
} | |||
public static String weekday(LocalDate date) { | |||
return date.format(DateTimeFormatter.ofPattern(DatePattern.EEE)); | |||
} | |||
public static String weekdayWithChou(LocalDate date) { | |||
return date.format(DateTimeFormatter.ofPattern(DatePattern.EEE)).replace("星期", "周"); | |||
} | |||
public static boolean between(LocalDate target, LocalDate start, LocalDate end) { | |||
return !(target.isBefore(start) || target.isAfter(end)); | |||
} | |||
public static boolean between(LocalDateTime target, LocalDateTime start, LocalDateTime end) { | |||
return !(target.isBefore(start) || target.isAfter(end)); | |||
} | |||
public static boolean between(LocalTime target, LocalTime start, LocalTime end) { | |||
return !(target.isBefore(start) || target.isAfter(end)); | |||
} | |||
public static boolean between(Date target, Date start, Date end) { | |||
return !(target.before(start) || target.after(end)); | |||
} | |||
public static LocalDateTime min(LocalDateTime time1, LocalDateTime time2) { | |||
return time1.isAfter(time2) ? time2 : time1; | |||
} | |||
public static LocalDateTime max(LocalDateTime time1, LocalDateTime time2) { | |||
return time1.isBefore(time2) ? time2 : time1; | |||
} | |||
public static long restSecondsFromNowToNoon() { | |||
return ChronoUnit.SECONDS.between(LocalDateTime.now(), LocalDate.now().atTime(LocalTime.MAX)); | |||
} | |||
public static boolean intersect(LocalDateTime startG1, LocalDateTime endG1, LocalDateTime startG2, LocalDateTime endG2) { | |||
if (!startG1.isBefore(startG2) && !startG1.isAfter(endG2)) { | |||
return Boolean.TRUE; | |||
} | |||
return !endG1.isBefore(startG2) && !endG1.isAfter(endG2); | |||
} | |||
} |
@@ -0,0 +1,35 @@ | |||
package com.ningdatech.pmapi.sms.utils; | |||
import cn.hutool.core.text.StrPool; | |||
import com.ningdatech.pmapi.sms.constant.VerificationCodeType; | |||
/** | |||
* <p> | |||
* SmsRedisKeyUtils | |||
* </p> | |||
* | |||
* @author WendyYang | |||
* @since 11:21 2022/7/25 | |||
*/ | |||
public class SmsRedisKeyUtils { | |||
private SmsRedisKeyUtils() { | |||
} | |||
private static final String SMS_CODE_VERIFY_PREFIX = "sms:verify:"; | |||
private static final String SMS_SEND_LIMIT = "sms:limit:"; | |||
private static final String SMS_SEND_LOCK = "sms:lock:"; | |||
public static String smsCodeVerifyKey(VerificationCodeType type, String mobile) { | |||
return SMS_CODE_VERIFY_PREFIX + StrPool.COLON + type.name() + StrPool.COLON + mobile; | |||
} | |||
public static String smsSendLimitKey(VerificationCodeType type, String mobile) { | |||
return SMS_SEND_LIMIT + StrPool.COLON + type.name() + StrPool.COLON + mobile; | |||
} | |||
public static String smsSendLockKey(VerificationCodeType type, String mobile) { | |||
return SMS_SEND_LOCK + StrPool.COLON + type.name() + StrPool.COLON + mobile; | |||
} | |||
} |
@@ -12,8 +12,8 @@ import com.ningdatech.pmapi.sys.model.entity.RoleMenu; | |||
import com.ningdatech.pmapi.sys.model.req.RolePageReq; | |||
import com.ningdatech.pmapi.sys.model.vo.MenuRoleVO; | |||
import com.ningdatech.pmapi.sys.model.vo.RoleVO; | |||
import com.ningdatech.pmapi.sys.service.IRoleMenuService; | |||
import com.ningdatech.pmapi.sys.service.IMenuService; | |||
import com.ningdatech.pmapi.sys.service.IRoleMenuService; | |||
import com.ningdatech.pmapi.sys.service.IRoleService; | |||
import lombok.RequiredArgsConstructor; | |||
import org.apache.commons.lang3.StringUtils; | |||
@@ -35,9 +35,8 @@ import java.util.stream.Collectors; | |||
@RequiredArgsConstructor | |||
public class RoleManage { | |||
private final IRoleService IRoleService; | |||
private final IMenuService IMenuService; | |||
private final IRoleService roleService; | |||
private final IMenuService menuService; | |||
private final IRoleMenuService roleMenuService; | |||
public PageVo<RoleVO> queryList(RolePageReq rolePageReq) { | |||
@@ -48,16 +47,17 @@ public class RoleManage { | |||
return PageVo.of(data, page.getTotal()); | |||
} | |||
private void searchList(Page<Role> page, LambdaQueryWrapper<Role> wrapper, RolePageReq rolePageReq) { | |||
wrapper.like(StringUtils.isNotBlank(rolePageReq.getName()), Role::getName, rolePageReq.getName()) | |||
private void searchList(Page<Role> page, LambdaQueryWrapper<Role> wrapper, RolePageReq req) { | |||
wrapper.like(StringUtils.isNotBlank(req.getName()), Role::getName, req.getName()) | |||
.eq(req.getFixed() != null, Role::getFixed, req.getFixed()) | |||
.orderByDesc(Role::getUpdateOn); | |||
IRoleService.page(page, wrapper); | |||
roleService.page(page, wrapper); | |||
} | |||
public void buildMenu(RoleVO query) { | |||
//改成直接返回有权限的 | |||
List<Menu> list = IMenuService.list(Wrappers.lambdaQuery(Menu.class).orderByAsc(Menu::getSort)); | |||
List<Menu> list = menuService.list(Wrappers.lambdaQuery(Menu.class).orderByAsc(Menu::getSort)); | |||
List<RoleMenu> roleMenus = roleMenuService.list(Wrappers.lambdaQuery(RoleMenu.class) | |||
.eq(RoleMenu::getRoleId, query.getId())); | |||
List<MenuRoleVO> menus = list.stream().map(menu -> { | |||
@@ -1,5 +1,7 @@ | |||
package com.ningdatech.pmapi.sys.model.entity; | |||
import com.baomidou.mybatisplus.annotation.FieldFill; | |||
import com.baomidou.mybatisplus.annotation.TableField; | |||
import io.swagger.annotations.ApiModelProperty; | |||
import lombok.Getter; | |||
import lombok.Setter; | |||
@@ -20,15 +22,19 @@ import java.time.LocalDateTime; | |||
public class BaseEntity implements Serializable { | |||
@ApiModelProperty("创建人userId") | |||
@TableField(fill = FieldFill.INSERT) | |||
protected Long createBy; | |||
@ApiModelProperty("最后修改人userId") | |||
@TableField(fill = FieldFill.INSERT_UPDATE) | |||
protected Long updateBy; | |||
@ApiModelProperty("创建时间") | |||
@TableField(fill = FieldFill.INSERT) | |||
protected LocalDateTime createOn; | |||
@ApiModelProperty("修改时间") | |||
@TableField(fill = FieldFill.INSERT_UPDATE) | |||
protected LocalDateTime updateOn; | |||
} |
@@ -1,8 +1,10 @@ | |||
package com.ningdatech.pmapi.sys.model.entity; | |||
import com.baomidou.mybatisplus.annotation.FieldFill; | |||
import com.baomidou.mybatisplus.annotation.TableField; | |||
import com.baomidou.mybatisplus.annotation.TableName; | |||
import com.ningdatech.pmapi.common.model.entity.MenuTreeEntity; | |||
import com.ningdatech.pmapi.sys.model.enumeration.DataScopeEnum; | |||
import io.swagger.annotations.ApiModel; | |||
import io.swagger.annotations.ApiModelProperty; | |||
import lombok.*; | |||
@@ -59,15 +61,22 @@ public class Menu extends MenuTreeEntity<Menu, Long> { | |||
@ApiModelProperty("是否有数据权限") | |||
private Boolean hasDataScope; | |||
/** | |||
* @see DataScopeEnum | |||
*/ | |||
@ApiModelProperty("数据权限选项") | |||
private String dataScopeOption; | |||
@TableField(fill = FieldFill.INSERT_UPDATE) | |||
private Long updateBy; | |||
@TableField(fill = FieldFill.INSERT) | |||
private Long createBy; | |||
@TableField(fill = FieldFill.INSERT_UPDATE) | |||
private LocalDateTime updateOn; | |||
@TableField(fill = FieldFill.INSERT) | |||
private LocalDateTime createOn; | |||
} |
@@ -29,7 +29,7 @@ public class Role extends BaseEntity { | |||
private static final long serialVersionUID = 1L; | |||
@TableId(value = "id",type = IdType.AUTO) | |||
@TableId(value = "id", type = IdType.AUTO) | |||
private Long id; | |||
@ApiModelProperty(value = "名称") | |||
@@ -38,6 +38,8 @@ public class Role extends BaseEntity { | |||
@ApiModelProperty("角色编码") | |||
private String code; | |||
@ApiModelProperty("是否是内置角色") | |||
private Boolean fixed; | |||
@ApiModelProperty(value = "描述") | |||
private String describe; | |||
@@ -34,4 +34,7 @@ public class RolePageReq extends PagePo implements Serializable { | |||
@ApiModelProperty(value = "名称") | |||
private String name; | |||
@ApiModelProperty("是否是内置角色") | |||
private Boolean fixed; | |||
} |
@@ -43,6 +43,9 @@ public class RoleVO extends BaseEntity { | |||
@ApiModelProperty(value = "描述") | |||
private String describe; | |||
@ApiModelProperty("是否是内置角色:true 是、false 否") | |||
private Boolean fixed; | |||
/** | |||
* 数据范围 | |||
*/ | |||
@@ -1,10 +1,9 @@ | |||
package com.ningdatech.pmapi.todocenter.bean.entity; | |||
import com.ningdatech.pmapi.todocenter.bean.vo.ProgressNodeAuditInfoVo; | |||
import com.wflow.workflow.bean.process.OrgUser; | |||
import com.wflow.workflow.bean.dto.ProcessInstanceUserDto; | |||
import com.wflow.workflow.bean.process.enums.ApprovalModeEnum; | |||
import com.wflow.workflow.bean.process.enums.NodeTypeEnum; | |||
import com.wflow.workflow.bean.vo.UserInfoVO; | |||
import com.wflow.workflow.enums.ProcessHandlerEnum; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Builder; | |||
@@ -12,7 +11,6 @@ import lombok.Data; | |||
import lombok.NoArgsConstructor; | |||
import java.util.Date; | |||
import java.util.List; | |||
/** | |||
* 流程节点实体 | |||
@@ -48,7 +46,7 @@ public class ProgressNode { | |||
/** | |||
* 节点相关人员 | |||
*/ | |||
private UserInfoVO user; | |||
private ProcessInstanceUserDto user; | |||
/** | |||
* 该节点动作操作类型 | |||
*/ | |||
@@ -2,8 +2,8 @@ package com.ningdatech.pmapi.todocenter.bean.vo; | |||
import com.ningdatech.pmapi.todocenter.bean.entity.ProgressNode; | |||
import com.wflow.workflow.bean.dto.ProcessInstanceUserDto; | |||
import com.wflow.workflow.bean.process.form.Form; | |||
import com.wflow.workflow.bean.vo.UserInfoVO; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Builder; | |||
import lombok.Data; | |||
@@ -60,7 +60,7 @@ public class ProcessProgressDetailVo { | |||
/** | |||
* 发起人 | |||
*/ | |||
private UserInfoVO startUser; | |||
private ProcessInstanceUserDto startUser; | |||
/** | |||
* 发起时间 | |||
*/ | |||
@@ -1,7 +1,7 @@ | |||
package com.ningdatech.pmapi.todocenter.bean.vo; | |||
import com.ningdatech.pmapi.todocenter.bean.entity.ProcessComment; | |||
import com.wflow.workflow.bean.vo.UserInfoVO; | |||
import com.wflow.workflow.bean.dto.ProcessInstanceUserDto; | |||
import io.swagger.annotations.ApiModelProperty; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Builder; | |||
@@ -34,7 +34,7 @@ public class ProgressNodeAuditInfoVo extends ProcessComment { | |||
private String commentType; | |||
@ApiModelProperty("评论用户信息") | |||
private UserInfoVO user; | |||
private ProcessInstanceUserDto user; | |||
@ApiModelProperty("评论时间") | |||
private Date createTime; | |||
@@ -61,14 +61,12 @@ public class TodoCenterController { | |||
/** | |||
* 查询流程表单数据及审批的进度步骤 | |||
* @param instanceId 流程实例ID | |||
* @param nodeId 当前获取流程人员关联的流程节点ID | |||
* @return 流程进度及表单详情 | |||
*/ | |||
@GetMapping("/progress/{instanceId}/{nodeId}") | |||
@GetMapping("/progress/{instanceId}/{projectId}") | |||
public ApiResponse<ProcessProgressDetailVo> getProcessDetail(@PathVariable String instanceId, | |||
@PathVariable(required = false) String nodeId, | |||
@PathVariable Long projectId) { | |||
return ApiResponse.ofSuccess(todoCenterManage.getProcessDetail(nodeId, instanceId, projectId)); | |||
return ApiResponse.ofSuccess(todoCenterManage.getProcessDetail(instanceId, projectId)); | |||
} | |||
/** | |||
@@ -48,7 +48,7 @@ import com.wflow.mapper.WflowCcTasksMapper; | |||
import com.wflow.mapper.WflowModelHistorysMapper; | |||
import com.wflow.service.OrgRepositoryService; | |||
import com.wflow.utils.CodeUtil; | |||
import com.wflow.workflow.bean.process.OrgUser; | |||
import com.wflow.workflow.bean.dto.ProcessInstanceUserDto; | |||
import com.wflow.workflow.bean.process.ProcessNode; | |||
import com.wflow.workflow.bean.process.enums.ApprovalModeEnum; | |||
import com.wflow.workflow.bean.process.enums.NodeTypeEnum; | |||
@@ -56,7 +56,6 @@ import com.wflow.workflow.bean.process.form.Form; | |||
import com.wflow.workflow.bean.process.props.ApprovalProps; | |||
import com.wflow.workflow.bean.vo.ProcessInstanceVo; | |||
import com.wflow.workflow.bean.vo.ProcessTaskVo; | |||
import com.wflow.workflow.bean.vo.UserInfoVO; | |||
import com.wflow.workflow.config.WflowGlobalVarDef; | |||
import com.wflow.workflow.enums.ProcessHandlerEnum; | |||
import com.wflow.workflow.service.FormService; | |||
@@ -177,15 +176,9 @@ public class TodoCenterManage { | |||
// 从待办任务列表中取出当前登录用户及项目实例对应的任务 | |||
Task task = taskMap.get(d.getInstCode()); | |||
HistoricProcessInstance instance = instanceMap.get(task.getProcessInstanceId()); | |||
//HashSet<String> userSet = new HashSet<>(); | |||
//userSet.add(instance.getStartUserId()) ; | |||
//Map<Long, UserInfoVO> userMap = userInfoService.getUserMapByIds(userSet); | |||
//res.setOwner(userMap.get(Long.valueOf(instance.getStartUserId()))); | |||
UserInfoVO owner = new UserInfoVO(); | |||
owner.setUserId(Long.valueOf(instance.getStartUserId())); | |||
res.setOwner(owner); | |||
// 从缓存取 | |||
staterUsers.add(String.valueOf(userId)); | |||
staterUsers.add(String.valueOf(instance.getStartUserId())); | |||
ProcessTaskVo processTaskVo = ProcessTaskVo.builder() | |||
.taskId(task.getId()) | |||
.taskName(task.getName()) | |||
@@ -210,7 +203,7 @@ public class TodoCenterManage { | |||
// 取用户信息,减少数据库查询,一次构建 | |||
List<ResToBeProcessedDTO> result = null; | |||
if (isNotEmpty(staterUsers)) { | |||
//Map<Long, UserInfoVO> userMap = userInfoService.getUserMapByIds(staterUsers); | |||
//Map<String, ProcessInstanceUserDto> userMap = userInfoService.getUserMapByIds(staterUsers); | |||
//result = resVos.stream() | |||
// .peek(v -> v.getProcessTaskInfo().setStartUser(userMap.get(Long.valueOf(v.getProcessTaskInfo().getOwnerId())))) | |||
// .collect(Collectors.toList()); | |||
@@ -318,9 +311,7 @@ public class TodoCenterManage { | |||
* @since 2023/02/01 | |||
*/ | |||
public void handler(ReqProcessHandlerDTO param) { | |||
// 获取登录用户ID | |||
// Long userId = LoginUserUtil.getUserId(); | |||
// Long userId = 381496L; | |||
@@ -329,8 +320,12 @@ public class TodoCenterManage { | |||
// 若进行的是撤回操作(流程发起人和当前流程审核人的前一个审核人操作) | |||
if (param.getAction().equals(ProcessHandlerEnum.WITHDRAW)) { | |||
HistoricTaskInstance handledTaskInstance = historyService.createHistoricTaskInstanceQuery().taskId(param.getTaskId()).singleResult(); | |||
doWithDrawProcess(handledTaskInstance, userId); | |||
HistoricTaskInstance handledTaskInstance = historyService.createHistoricTaskInstanceQuery() | |||
.taskId(param.getTaskId()) | |||
.singleResult(); | |||
// 获取要处理的项目ID | |||
Long projectId = param.getProjectId(); | |||
doWithDrawProcess(handledTaskInstance, userId, projectId); | |||
return; | |||
} | |||
Task task = taskService.createTaskQuery().taskId(param.getTaskId()).active().singleResult(); | |||
@@ -373,8 +368,9 @@ public class TodoCenterManage { | |||
*/ | |||
private void doReject(Task task, Long userId, ReqProcessHandlerDTO param) { | |||
// 获取当前申报项目 | |||
Project declaredProject = projectService | |||
.getOne(Wrappers.lambdaQuery(Project.class).eq(Project::getInstCode, task.getProcessInstanceId())); | |||
Project declaredProject = projectService.getOne(Wrappers.lambdaQuery(Project.class) | |||
.eq(Project::getInstCode, task.getProcessInstanceId()) | |||
.eq(Project::getId,param.getProjectId())); | |||
String projectName = declaredProject.getProjectName(); | |||
Map<String, Object> var = new HashMap<>(16); | |||
@@ -487,8 +483,9 @@ public class TodoCenterManage { | |||
private void doPass(Task task, Long userId, ReqProcessHandlerDTO param) { | |||
String processInstanceId = task.getProcessInstanceId(); | |||
// 获取当前申报项目 | |||
Project declaredProject = projectService | |||
.getOne(Wrappers.lambdaQuery(Project.class).eq(Project::getInstCode, task.getProcessInstanceId())); | |||
Project declaredProject = projectService.getOne(Wrappers.lambdaQuery(Project.class) | |||
.eq(Project::getInstCode, task.getProcessInstanceId()) | |||
.eq(Project::getId,param.getProjectId())); | |||
String projectName = declaredProject.getProjectName(); | |||
Map<String, Object> var = new HashMap<>(16); | |||
@@ -721,8 +718,9 @@ public class TodoCenterManage { | |||
* | |||
* @param handledTaskInstance 已处理的历史任务实例 | |||
* @param userId 当前登录用户ID | |||
* @param projectId | |||
*/ | |||
private void doWithDrawProcess(HistoricTaskInstance handledTaskInstance, Long userId) { | |||
private void doWithDrawProcess(HistoricTaskInstance handledTaskInstance, Long userId, Long projectId) { | |||
String processInstanceId = handledTaskInstance.getProcessInstanceId(); | |||
// 获取当前流程实例待审核任务信息 | |||
Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult(); | |||
@@ -735,7 +733,8 @@ public class TodoCenterManage { | |||
String startUserId = historicProcessInstance.getStartUserId(); | |||
// 获取当前申报项目 | |||
Project declaredProject = projectService.getOne(Wrappers.lambdaQuery(Project.class) | |||
.eq(Project::getInstCode, processInstanceId)); | |||
.eq(Project::getInstCode, processInstanceId) | |||
.eq(Project::getId,projectId)); | |||
String projectName = declaredProject.getProjectName(); | |||
// 获取bpm对象 | |||
@@ -869,8 +868,9 @@ public class TodoCenterManage { | |||
*/ | |||
private void doBackTask(Task task, Long userId, ReqProcessHandlerDTO param) { | |||
// 获取当前申报项目 | |||
Project declaredProject = projectService | |||
.getOne(Wrappers.lambdaQuery(Project.class).eq(Project::getInstCode, task.getProcessInstanceId())); | |||
Project declaredProject = projectService.getOne(Wrappers.lambdaQuery(Project.class) | |||
.eq(Project::getInstCode, task.getProcessInstanceId()) | |||
.eq(Project::getId,param.getProjectId())); | |||
String projectName = declaredProject.getProjectName(); | |||
// 保存审核意见 | |||
if (hasComment(param.getAuditInfo())) { | |||
@@ -910,11 +910,10 @@ public class TodoCenterManage { | |||
/** | |||
* 查询流程表单数据及审批的进度步骤 | |||
* @param nodeId 当前获取流程人员关联的流程节点ID | |||
* @param instanceId 流程实例ID | |||
* @return 流程进度及表单详情 | |||
*/ | |||
public ProcessProgressDetailVo getProcessDetail(String nodeId, String instanceId, Long projectId) { | |||
public ProcessProgressDetailVo getProcessDetail(String instanceId, Long projectId) { | |||
HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery() | |||
.processInstanceId(instanceId) | |||
.singleResult(); | |||
@@ -924,39 +923,59 @@ public class TodoCenterManage { | |||
.variableName(WflowGlobalVarDef.WFLOW_FORMS) | |||
.singleResult(); | |||
List<HistoricVariableInstance> formDatas = null; | |||
if (nodeId.equals("undefined")) { | |||
List<HistoricTaskInstance> historicTaskInstances = historyService.createHistoricTaskInstanceQuery().processInstanceId(instanceId).list(); | |||
formDatas = historyService | |||
.createHistoricVariableInstanceQuery() | |||
.executionIds(historicTaskInstances.stream() | |||
.map(HistoricTaskInstance::getExecutionId) | |||
.collect(Collectors.toSet())) | |||
.processInstanceId(instanceId) | |||
.list(); | |||
} else { | |||
formDatas = historyService | |||
.createHistoricVariableInstanceQuery() | |||
.processInstanceId(instanceId) | |||
.list(); | |||
} | |||
//if (nodeId.equals("undefined")) { | |||
// List<HistoricTaskInstance> historicTaskInstances = historyService.createHistoricTaskInstanceQuery().processInstanceId(instanceId).list(); | |||
// formDatas = historyService | |||
// .createHistoricVariableInstanceQuery() | |||
// .executionIds(historicTaskInstances.stream() | |||
// .map(HistoricTaskInstance::getExecutionId) | |||
// .collect(Collectors.toSet())) | |||
// .processInstanceId(instanceId) | |||
// .list(); | |||
//} else { | |||
// formDatas = historyService | |||
// .createHistoricVariableInstanceQuery() | |||
// .processInstanceId(instanceId) | |||
// .list(); | |||
//} | |||
List<HistoricTaskInstance> historicTaskInstances = historyService.createHistoricTaskInstanceQuery() | |||
.processInstanceId(instanceId).list(); | |||
formDatas = historyService.createHistoricVariableInstanceQuery() | |||
.executionIds(historicTaskInstances.stream() | |||
.map(HistoricTaskInstance::getExecutionId) | |||
.collect(Collectors.toSet())) | |||
.processInstanceId(instanceId).list(); | |||
// 取节点设置 | |||
HistoricVariableInstance nodeProps = historyService.createHistoricVariableInstanceQuery() | |||
.processInstanceId(instanceId).variableName(WflowGlobalVarDef.WFLOW_NODE_PROPS).singleResult(); | |||
Map<String, Object> nodePropsValue = (Map<String, Object>) nodeProps.getValue(); | |||
ProcessNode<?> currentNode = null; | |||
if (StrUtil.isNotBlank(nodeId)) { | |||
// 搜索当前版本流程的配置 | |||
WflowModelHistorys modelHistory = modelHistorysMapper.selectOne(new QueryWrapper<>(WflowModelHistorys.builder() | |||
//if (StrUtil.isNotBlank(nodeId)) { | |||
// // 搜索当前版本流程的配置 | |||
// WflowModelHistorys modelHistory = modelHistorysMapper.selectOne(new QueryWrapper<>(WflowModelHistorys.builder() | |||
// .processDefId(instance.getProcessDefinitionId()) | |||
// .version(instance.getProcessDefinitionVersion()) | |||
// .build())); | |||
// currentNode = nodeCatchService.reloadProcessByStr(modelHistory.getProcess()).get(nodeId); | |||
//} | |||
// 搜索当前版本流程的配置 | |||
WflowModelHistorys modelHistory = modelHistorysMapper.selectOne(new QueryWrapper<>(WflowModelHistorys.builder() | |||
.processDefId(instance.getProcessDefinitionId()) | |||
.version(instance.getProcessDefinitionVersion()) | |||
.build())); | |||
currentNode = nodeCatchService.reloadProcessByStr(modelHistory.getProcess()).get(nodeId); | |||
} | |||
currentNode = nodeCatchService.reloadProcessByStr(modelHistory.getProcess()).get("undefined"); | |||
//UserInfo userInfo = userInfoService.getById(Long.valueOf(instance.getStartUserId())); | |||
//HashSet<String> userSet = new HashSet<>(); | |||
//userSet.add(String.valueOf(userInfo.getId())); | |||
//Map<Long, UserInfoVO> userMap = userInfoService.getUserMapByIds(userSet); | |||
//UserInfoVO userInfoVO = userMap.get(userInfo.getId()); | |||
//Map<String, ProcessInstanceUserDto> userMap = userInfoService.getUserMapByIds(userSet); | |||
//ProcessInstanceUserDto startUser = userMap.get(String.valueOf(userInfo.getId())); | |||
List<ProgressNode> taskRecords = getHisTaskRecords(instanceId, nodePropsValue); | |||
// 获取添加抄送任务 | |||
taskRecords.addAll(getCcTaskRecords(instanceId)); | |||
@@ -989,7 +1008,7 @@ public class TodoCenterManage { | |||
.startTime(instance.getStartTime()) | |||
.progress(taskRecords) | |||
.build(); | |||
//res.setStartUser(userInfoVO); | |||
//res.setStartUser(startUser); | |||
Project project = projectService.getById(projectId); | |||
res.setStatus(ProcessStatusEnum.getDescByCode(project.getProcessStatus())); | |||
return res; | |||
@@ -1010,13 +1029,13 @@ public class TodoCenterManage { | |||
ccUsers.add(task.getUserId()); | |||
return ProgressNode.builder().nodeId(task.getNodeId()).nodeType(NodeTypeEnum.CC) | |||
.name(task.getNodeName()) | |||
.user(UserInfoVO.builder().userId(Long.valueOf(task.getUserId())).build()) | |||
.user(ProcessInstanceUserDto.builder().userId(task.getUserId()).build()) | |||
.startTime(task.getCreateTime()) | |||
.finishTime(task.getCreateTime()) | |||
.build(); | |||
}).collect(Collectors.toList()); | |||
if (isNotEmpty(ccUsers)) { | |||
Map<Long, UserInfoVO> userMap = userInfoService.getUserMapByIds(ccUsers); | |||
Map<String, ProcessInstanceUserDto> userMap = userInfoService.getUserMapByIds(ccUsers); | |||
ccList.stream().peek(v -> v.setUser(userMap.get(v.getUser().getUserId()))).collect(Collectors.toList()); | |||
} | |||
return ccList; | |||
@@ -1051,7 +1070,7 @@ public class TodoCenterManage { | |||
.commentType(comment.getType()) | |||
.type("COMMENT") | |||
.createTime(comment.getTime()) | |||
.user(UserInfoVO.builder().userId(Long.valueOf(comment.getUserId())).build()) | |||
.user(ProcessInstanceUserDto.builder().userId(comment.getUserId()).build()) | |||
.build(); | |||
ProcessComment processComment = JSONObject.parseObject(comment.getFullMessage(), ProcessComment.class); | |||
progressNodeAuditInfoVo.setText(processComment.getText()); | |||
@@ -1079,7 +1098,7 @@ public class TodoCenterManage { | |||
.nodeId(his.getActivityId()) | |||
.name(his.getActivityName()) | |||
.nodeType(NodeTypeEnum.APPROVAL) | |||
.user(UserInfoVO.builder().userId(Long.valueOf(his.getAssignee())).build()) | |||
.user(ProcessInstanceUserDto.builder().userId(his.getAssignee()).build()) | |||
.startTime(his.getStartTime()) | |||
.finishTime(his.getEndTime()) | |||
.taskId(his.getTaskId()) | |||
@@ -1088,7 +1107,7 @@ public class TodoCenterManage { | |||
.result(varMap.get("approve_" + his.getTaskId())).build(); | |||
}).collect(Collectors.toList()); | |||
if (isNotEmpty(userSet)) { | |||
Map<Long, UserInfoVO> userMap = userInfoService.getUserMapByIds(userSet); | |||
Map<String, ProcessInstanceUserDto> userMap = userInfoService.getUserMapByIds(userSet); | |||
progressNodes.stream().peek(v -> v.setUser(userMap.get(v.getUser().getUserId()))).collect(Collectors.toList()); | |||
} | |||
return progressNodes; | |||
@@ -1158,34 +1177,35 @@ public class TodoCenterManage { | |||
res.setProcessStatusName(ProcessStatusEnum.getDescByCode(d.getProcessStatus())); | |||
LocalDateTime processLaunchTime = d.getCreateOn(); | |||
String launchTimeFormat = NdDateUtils.format(processLaunchTime, "yyyy-MM-dd HH:mm"); | |||
LocalDateTime launchTime = | |||
LocalDateTime.parse(launchTimeFormat, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); | |||
LocalDateTime launchTime = LocalDateTime.parse(launchTimeFormat, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); | |||
res.setProcessLaunchTime(launchTime); | |||
LocalDateTime processHandleTime = d.getUpdateOn(); | |||
String handleTimeFormat = NdDateUtils.format(processHandleTime, "yyyy-MM-dd HH:mm"); | |||
LocalDateTime handleTime = | |||
LocalDateTime.parse(handleTimeFormat, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); | |||
LocalDateTime handleTime = LocalDateTime.parse(handleTimeFormat, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); | |||
res.setProcessHandleTime(handleTime); | |||
// 从已处理任务实例列表中取出当前登录用户及项目实例对应的任务实例 | |||
HistoricTaskInstance task = taskInstanceMap.get(d.getInstCode()); | |||
HistoricProcessInstance instance = instanceMap.get(task.getProcessInstanceId()); | |||
//HashSet<String> userSet = new HashSet<>(); | |||
//userSet.add(instance.getStartUserId()) ; | |||
//Map<Long, UserInfoVO> userMap = userInfoService.getUserMapByIds(userSet); | |||
//res.setOwner(userMap.get(Long.valueOf(instance.getStartUserId()))); | |||
UserInfoVO owner = new UserInfoVO(); | |||
owner.setUserId(Long.valueOf(instance.getStartUserId())); | |||
res.setOwner(owner); | |||
// 从缓存取 | |||
staterUsers.add(instance.getStartUserId()); | |||
ProcessTaskVo processTaskVo = ProcessTaskVo.builder().taskId(task.getId()).taskName(task.getName()) | |||
.taskDefKey(task.getTaskDefinitionKey()).processDefId(task.getProcessDefinitionId()) | |||
.executionId(task.getExecutionId()).nodeId(task.getTaskDefinitionKey()) | |||
.deployId(instance.getDeploymentId()).processDefName(instance.getProcessDefinitionName()) | |||
.version(instance.getProcessDefinitionVersion()).instanceId(task.getProcessInstanceId()) | |||
.ownerId(instance.getStartUserId()).createTime(instance.getStartTime()) | |||
.taskCreateTime(task.getCreateTime()).build(); | |||
ProcessTaskVo processTaskVo = ProcessTaskVo | |||
.builder() | |||
.taskId(task.getId()) | |||
.taskName(task.getName()) | |||
.taskDefKey(task.getTaskDefinitionKey()) | |||
.processDefId(task.getProcessDefinitionId()) | |||
.executionId(task.getExecutionId()) | |||
.nodeId(task.getTaskDefinitionKey()) | |||
.deployId(instance.getDeploymentId()) | |||
.processDefName(instance.getProcessDefinitionName()) | |||
.version(instance.getProcessDefinitionVersion()) | |||
.instanceId(task.getProcessInstanceId()) | |||
.ownerId(instance.getStartUserId()) | |||
.createTime(instance.getStartTime()) | |||
.taskCreateTime(task.getCreateTime()) | |||
.build(); | |||
res.setProcessTaskInfo(processTaskVo); | |||
return res; | |||
}).collect(Collectors.toList()); | |||
@@ -1195,9 +1215,9 @@ public class TodoCenterManage { | |||
// 取用户信息,减少数据库查询,一次构建 | |||
List<ResToBeProcessedDTO> result = null; | |||
if (isNotEmpty(staterUsers)) { | |||
//Map<Long, UserInfoVO> userMap = userInfoService.getUserMapByIds(staterUsers); | |||
//Map<String, ProcessInstanceUserDto> userMap = userInfoService.getUserMapByIds(staterUsers); | |||
//result = resVos.stream().peek(v -> v.getProcessTaskInfo() | |||
// .setStartUser(userMap.get(Long.valueOf(v.getProcessTaskInfo().getOwnerId())))) | |||
// .setStartUser(userMap.get(v.getProcessTaskInfo().getOwnerId()))) | |||
// .collect(Collectors.toList()); | |||
} | |||
// return PageVo.of(result, total); | |||
@@ -1315,10 +1335,10 @@ public class TodoCenterManage { | |||
Long startUserId = 381496L; | |||
HistoricProcessInstanceQuery instanceQuery = historyService.createHistoricProcessInstanceQuery(); | |||
Executor.builder().ifNotBlankNext(String.valueOf(startUserId), instanceQuery::startedBy); | |||
List<HistoricProcessInstance> historicProcessInstances = | |||
instanceQuery.orderByProcessInstanceStartTime().desc().orderByProcessInstanceEndTime().desc().list(); | |||
Set<String> historicProcessInstanceIds = | |||
historicProcessInstances.stream().map(HistoricProcessInstance::getId).collect(Collectors.toSet()); | |||
List<HistoricProcessInstance> historicProcessInstances = instanceQuery.orderByProcessInstanceStartTime() | |||
.desc().orderByProcessInstanceEndTime().desc().list(); | |||
Set<String> historicProcessInstanceIds = historicProcessInstances.stream() | |||
.map(HistoricProcessInstance::getId).collect(Collectors.toSet()); | |||
List<Project> results = getMySubmittedProjects(param, historicProcessInstanceIds); | |||
@@ -1335,8 +1355,11 @@ public class TodoCenterManage { | |||
// 把已办任务流程实例一次性取出来,减少查询次数 | |||
Map<String, HistoricProcessInstance> instanceMap = isNotEmpty(historicProcessInstances) ? historyService | |||
.createHistoricProcessInstanceQuery() | |||
.processInstanceIds(historicProcessInstances.stream().map(HistoricProcessInstance::getId).collect(Collectors.toSet())) | |||
.list().stream().collect(Collectors.toMap(HistoricProcessInstance::getId, v -> v)) : new HashMap<>(); | |||
.processInstanceIds(historicProcessInstances.stream() | |||
.map(HistoricProcessInstance::getId) | |||
.collect(Collectors.toSet())) | |||
.list().stream() | |||
.collect(Collectors.toMap(HistoricProcessInstance::getId, v -> v)) : new HashMap<>(); | |||
Set<String> staterUsers = new HashSet<>(); | |||
@@ -1348,18 +1371,11 @@ public class TodoCenterManage { | |||
res.setProcessStatusName(ProcessStatusEnum.getDescByCode(d.getProcessStatus())); | |||
LocalDateTime processLaunchTime = d.getCreateOn(); | |||
String launchTimeFormat = NdDateUtils.format(processLaunchTime, "yyyy-MM-dd HH:mm"); | |||
LocalDateTime launchTime = | |||
LocalDateTime.parse(launchTimeFormat, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); | |||
LocalDateTime launchTime = LocalDateTime.parse(launchTimeFormat, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); | |||
res.setProcessLaunchTime(launchTime); | |||
HistoricProcessInstance ist = instanceMap.get(d.getInstCode()); | |||
staterUsers.add(ist.getStartUserId()); | |||
//HashSet<String> userSet = new HashSet<>(); | |||
//userSet.add(instance.getStartUserId()) ; | |||
//Map<Long, UserInfoVO> userMap = userInfoService.getUserMapByIds(userSet); | |||
//res.setOwner(userMap.get(Long.valueOf(ist.getStartUserId()))); | |||
UserInfoVO owner = new UserInfoVO(); | |||
owner.setUserId(Long.valueOf(ist.getStartUserId())); | |||
res.setOwner(owner); | |||
ProcessInstanceVo processInstanceVo = getProcessInstanceVos(ist); | |||
res.setProcessInstanceInfo(processInstanceVo); | |||
return res; | |||
@@ -1371,9 +1387,9 @@ public class TodoCenterManage { | |||
// 取用户信息,减少数据库查询,一次构建 | |||
List<ResToBeProcessedDTO> result = null; | |||
if (isNotEmpty(staterUsers)) { | |||
//Map<Long, UserInfoVO> userMap = userInfoService.getUserMapByIds(staterUsers); | |||
//Map<String, ProcessInstanceUserDto> userMap = userInfoService.getUserMapByIds(staterUsers); | |||
//result = resVos.stream() | |||
// .peek(v -> v.getProcessTaskInfo().setStartUser(userMap.get(Long.valueOf(v.getProcessTaskInfo().getOwnerId())))) | |||
// .peek(v -> v.getProcessInstanceInfo().setStartUser(userMap.get(v.getProcessTaskInfo().getOwnerId()))) | |||
// .collect(Collectors.toList()); | |||
} | |||
// return PageVo.of(result, total); | |||
@@ -1416,16 +1432,25 @@ public class TodoCenterManage { | |||
*/ | |||
private ProcessInstanceVo getProcessInstanceVos(HistoricProcessInstance ist) { | |||
Map<String, String> instanceNodeMap = new HashMap<>(); | |||
ProcessInstanceVo instanceVo = ProcessInstanceVo.builder().processDefId(ist.getProcessDefinitionId()) | |||
.instanceId(ist.getId()).nodeId(instanceNodeMap.get(ist.getId())).formId(ist.getProcessDefinitionKey()) | |||
.staterUserId(ist.getStartUserId()).startTime(ist.getStartTime()).finishTime(ist.getEndTime()) | |||
.processDefName(ist.getProcessDefinitionName()).result(ist.getEndActivityId()) | |||
.version(ist.getProcessDefinitionVersion()).build(); | |||
ProcessInstanceVo instanceVo = ProcessInstanceVo | |||
.builder() | |||
.processDefId(ist.getProcessDefinitionId()) | |||
.instanceId(ist.getId()) | |||
.nodeId(instanceNodeMap.get(ist.getId())) | |||
.formId(ist.getProcessDefinitionKey()) | |||
.staterUserId(ist.getStartUserId()) | |||
.startTime(ist.getStartTime()) | |||
.finishTime(ist.getEndTime()) | |||
.processDefName(ist.getProcessDefinitionName()) | |||
.result(ist.getEndActivityId()) | |||
.version(ist.getProcessDefinitionVersion()) | |||
.build(); | |||
if (Objects.isNull(ist.getEndActivityId())) { | |||
instanceVo.setStatus(ProcessStatusEnum.UNDER_REVIEW.name()); | |||
} else if (HisProInsEndActId.BACK.equals(ist.getEndActivityId())) { | |||
// TODO 被退回的审核节点状态这里只是暂时这么判断,具体怎么保存这个退回状态,后面讨论 | |||
// 被流程发起人退回的审核节点状态,此时项目回到上一个状态,当前流程结束 | |||
// (由前一个审核人撤回的流程,未结束,仍在审核中) | |||
instanceVo.setStatus(ProcessStatusEnum.BE_BACKED.name()); | |||
} else if (HisProInsEndActId.REJECT.equals(ist.getEndActivityId())) { | |||
instanceVo.setStatus(ProcessStatusEnum.BE_REJECTED.name()); | |||
@@ -1435,8 +1460,7 @@ public class TodoCenterManage { | |||
if (ObjectUtil.isNull(ist.getEndActivityId())) { | |||
// 没有结束,还在走流程,获取任务 | |||
List<Task> list = | |||
taskService.createTaskQuery().processInstanceId(ist.getId()).includeIdentityLinks().active().list(); | |||
List<Task> list = taskService.createTaskQuery().processInstanceId(ist.getId()).includeIdentityLinks().active().list(); | |||
instanceVo.setNodeId(Optional.ofNullable(instanceVo.getNodeId()).orElseGet(() -> { | |||
if (isNotEmpty(list)) { | |||
return list.get(0).getTaskDefinitionKey(); | |||
@@ -1453,7 +1477,6 @@ public class TodoCenterManage { | |||
instanceVo.setTaskName(ProcessStatusEnum.APPROVED.getDesc()); | |||
} | |||
} | |||
return instanceVo; | |||
} | |||
@@ -1571,18 +1594,11 @@ public class TodoCenterManage { | |||
res.setProcessStatusName(ProcessStatusEnum.getDescByCode(d.getProcessStatus())); | |||
LocalDateTime processLaunchTime = d.getCreateOn(); | |||
String launchTimeFormat = NdDateUtils.format(processLaunchTime, "yyyy-MM-dd HH:mm"); | |||
LocalDateTime launchTime = | |||
LocalDateTime.parse(launchTimeFormat, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); | |||
LocalDateTime launchTime = LocalDateTime.parse(launchTimeFormat, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); | |||
res.setProcessLaunchTime(launchTime); | |||
HistoricProcessInstance ist = instanceMap.get(d.getInstCode()); | |||
staterUsers.add(ist.getStartUserId()); | |||
//HashSet<String> userSet = new HashSet<>(); | |||
//userSet.add(instance.getStartUserId()) ; | |||
//Map<Long, UserInfoVO> userMap = userInfoService.getUserMapByIds(userSet); | |||
//res.setOwner(userMap.get(Long.valueOf(ist.getStartUserId()))); | |||
UserInfoVO owner = new UserInfoVO(); | |||
owner.setUserId(Long.valueOf(ist.getStartUserId())); | |||
res.setOwner(owner); | |||
ProcessInstanceVo processInstanceVo = getProcessInstanceVos(ist); | |||
res.setProcessInstanceInfo(processInstanceVo); | |||
return res; | |||
@@ -1593,9 +1609,9 @@ public class TodoCenterManage { | |||
// 取用户信息,减少数据库查询,一次构建 | |||
List<ResToBeProcessedDTO> result = null; | |||
if (isNotEmpty(staterUsers)) { | |||
//Map<Long, UserInfoVO> userMap = userInfoService.getUserMapByIds(staterUsers); | |||
//Map<String, ProcessInstanceUserDto> userMap = userInfoService.getUserMapByIds(staterUsers); | |||
//result = resVos.stream() | |||
// .peek(v -> v.getProcessTaskInfo().setStartUser(userMap.get(Long.valueOf(v.getProcessTaskInfo().getOwnerId())))) | |||
// .peek(v -> v.getProcessInstanceInfo().setStartUser(userMap.get(v.getProcessTaskInfo().getOwnerId()))) | |||
// .collect(Collectors.toList()); | |||
} | |||
// return PageVo.of(result, total); | |||
@@ -2,10 +2,13 @@ package com.ningdatech.pmapi.todocenter.model.dto.req; | |||
import com.ningdatech.pmapi.todocenter.bean.entity.ProcessComment; | |||
import com.wflow.workflow.enums.ProcessHandlerEnum; | |||
import io.swagger.annotations.ApiModelProperty; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Data; | |||
import lombok.NoArgsConstructor; | |||
import javax.validation.constraints.NotNull; | |||
/** | |||
* 流程处理操作参数实体 | |||
* | |||
@@ -18,12 +21,20 @@ import lombok.NoArgsConstructor; | |||
public class ReqProcessHandlerDTO { | |||
/** | |||
* 要处理的项目ID | |||
*/ | |||
@NotNull(message = "要处理的项目ID不能为空!") | |||
private Long projectId; | |||
/** | |||
* 实例ID | |||
*/ | |||
@NotNull(message = "流程实例ID不能为空!") | |||
private String instanceId; | |||
/** | |||
* 任务ID | |||
*/ | |||
@NotNull(message = "任务ID不能为空!") | |||
private String taskId; | |||
/** | |||
* 签名图片地址 | |||
@@ -32,6 +43,7 @@ public class ReqProcessHandlerDTO { | |||
/** | |||
* 操作类型 | |||
*/ | |||
@NotNull(message = "操作类型不能为空!") | |||
private ProcessHandlerEnum action; | |||
/** | |||
* 目标用户 | |||
@@ -2,9 +2,9 @@ package com.ningdatech.pmapi.todocenter.model.dto.res; | |||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; | |||
import com.fasterxml.jackson.annotation.JsonFormat; | |||
import com.wflow.workflow.bean.dto.ProcessInstanceUserDto; | |||
import com.wflow.workflow.bean.vo.ProcessInstanceVo; | |||
import com.wflow.workflow.bean.vo.ProcessTaskVo; | |||
import com.wflow.workflow.bean.vo.UserInfoVO; | |||
import io.swagger.annotations.ApiModelProperty; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Data; | |||
@@ -67,7 +67,4 @@ public class ResToBeProcessedDTO implements Serializable { | |||
@ApiModelProperty("流程实例信息") | |||
private ProcessInstanceVo processInstanceInfo; | |||
@ApiModelProperty("流程发起人信息") | |||
private UserInfoVO owner; | |||
} |
@@ -2,7 +2,7 @@ package com.ningdatech.pmapi.user.service; | |||
import com.ningdatech.pmapi.user.entity.UserInfo; | |||
import com.baomidou.mybatisplus.extension.service.IService; | |||
import com.wflow.workflow.bean.vo.UserInfoVO; | |||
import com.wflow.workflow.bean.dto.ProcessInstanceUserDto; | |||
import java.util.Map; | |||
import java.util.Set; | |||
@@ -16,5 +16,5 @@ import java.util.Set; | |||
* @since 2023-02-01 | |||
*/ | |||
public interface IUserInfoService extends IService<UserInfo> { | |||
Map<Long, UserInfoVO> getUserMapByIds(Set<String> staterUsers); | |||
Map<String, ProcessInstanceUserDto> getUserMapByIds(Set<String> staterUsers); | |||
} |
@@ -10,7 +10,7 @@ import com.ningdatech.pmapi.user.entity.UserInfo; | |||
import com.ningdatech.pmapi.user.mapper.NdUserInfoMapper; | |||
import com.ningdatech.pmapi.user.service.IUserInfoService; | |||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | |||
import com.wflow.workflow.bean.vo.UserInfoVO; | |||
import com.wflow.workflow.bean.dto.ProcessInstanceUserDto; | |||
import lombok.RequiredArgsConstructor; | |||
import org.springframework.stereotype.Service; | |||
import java.util.*; | |||
@@ -33,13 +33,12 @@ public class UserInfoServiceImpl extends ServiceImpl<NdUserInfoMapper, UserInfo> | |||
private final IDingOrganizationService dingOrganizationService; | |||
@Override | |||
public Map<Long, UserInfoVO> getUserMapByIds(Set<String> staterUsers) { | |||
public Map<String, ProcessInstanceUserDto> getUserMapByIds(Set<String> staterUsers) { | |||
List<UserInfo> userInfos = userInfoMapper.selectBatchIds(staterUsers); | |||
List<UserInfoVO> resVos = userInfos.stream().map(u -> { | |||
UserInfoVO userInfoVO = new UserInfoVO(); | |||
userInfoVO.setUserId(u.getId()); | |||
userInfoVO.setRealName(u.getRealName()); | |||
userInfoVO.setAccountId(u.getAccountId()); | |||
List<ProcessInstanceUserDto> resVos = userInfos.stream().map(u -> { | |||
ProcessInstanceUserDto userInfo = new ProcessInstanceUserDto(); | |||
userInfo.setUserId(String.valueOf(u.getId())); | |||
userInfo.setUserName(u.getRealName()); | |||
Long accountId = u.getAccountId(); | |||
if (Objects.isNull(accountId)){ | |||
throw new BizException("该用户没有录入浙政钉用户信息!"); | |||
@@ -52,10 +51,10 @@ public class UserInfoServiceImpl extends ServiceImpl<NdUserInfoMapper, UserInfo> | |||
DingOrganization dingOrganization = dingOrganizationService.getOne(Wrappers.lambdaQuery(DingOrganization.class) | |||
.eq(DingOrganization::getOrganizationCode, organizationCode)); | |||
String organizationName = dingOrganization.getOrganizationName(); | |||
userInfoVO.setOrganizationCode(organizationCode); | |||
userInfoVO.setOrganizationName(organizationName); | |||
return userInfoVO; | |||
userInfo.setOrgCode(organizationCode); | |||
userInfo.setOrgName(organizationName); | |||
return userInfo; | |||
}).collect(Collectors.toList()); | |||
return resVos.stream().collect(Collectors.toMap(UserInfoVO::getUserId, v -> v)); | |||
return resVos.stream().collect(Collectors.toMap((ProcessInstanceUserDto::getUserId), v -> v)); | |||
} | |||
} |
@@ -8,12 +8,17 @@ import cn.hutool.json.JSONObject; | |||
import cn.hutool.json.JSONUtil; | |||
import com.ningdatech.pmapi.AppTests; | |||
import com.ningdatech.pmapi.sys.model.entity.Menu; | |||
import com.ningdatech.pmapi.sys.model.entity.Role; | |||
import com.ningdatech.pmapi.sys.model.entity.RoleMenu; | |||
import com.ningdatech.pmapi.user.entity.enumeration.RoleEnum; | |||
import org.junit.jupiter.api.Test; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import java.sql.SQLException; | |||
import java.time.LocalDateTime; | |||
import java.util.Arrays; | |||
import java.util.List; | |||
import java.util.concurrent.atomic.AtomicLong; | |||
/** | |||
* <p> | |||
@@ -27,6 +32,27 @@ class IMenuServiceTest extends AppTests { | |||
@Autowired | |||
private IMenuService menuService; | |||
@Autowired | |||
private IRoleService roleService; | |||
/** | |||
* 初始化固定角色 | |||
*/ | |||
@Test | |||
public void roleService() { | |||
roleService.remove(null); | |||
AtomicLong integer = new AtomicLong(0); | |||
Arrays.stream(RoleEnum.values()).map(w -> { | |||
Role role = new Role(); | |||
role.setId(integer.incrementAndGet()); | |||
role.setCode(w.name()); | |||
role.setName(w.getDesc()); | |||
role.setDescribe(w.getDesc()); | |||
role.setFixed(true); | |||
role.setCreateOn(LocalDateTime.now()); | |||
return role; | |||
}).forEach(roleService::save); | |||
} | |||
@Test | |||
public void test() throws SQLException { | |||
@@ -27,10 +27,9 @@ public class TodoCenterTest extends AppTests { | |||
private ZwddClient zwddClient; | |||
@Test | |||
public void sendWorkNoticeTest(){ | |||
// String msg = String.format(PASS_MSG_TEMPLATE, "发改委", "0216-4-测试项目"); | |||
String msg = "发改委的0216-6-测试项目需要您审核"; | |||
String msg = String.format(PASS_MSG_TEMPLATE, "发改委", "0216-7-测试项目"); | |||
log.info("开始发送工作通知"); | |||
zwddClient.sendWorkNotice("846085","0216-7",msg); | |||
zwddClient.sendWorkNotice("846085","0216-8",msg); | |||
// zwddClient.sendWorkNotice("829728","0216-5",msg); | |||
log.info("发送工作通知结束"); | |||
} | |||