commit:同步2.2版本
@@ -14,6 +14,22 @@
|
|||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.anji-plus</groupId>
|
||||||
|
<artifactId>spring-boot-starter-captcha</artifactId>
|
||||||
|
<version>${ajcaptcha.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- aj-captcha 依赖data-redis作为缓存 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>spring-boot-starter-logging</artifactId>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
<!-- 业务组件依赖 -->
|
<!-- 业务组件依赖 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.orangeforms</groupId>
|
<groupId>com.orangeforms</groupId>
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package com.orangeforms.webadmin.app.util;
|
||||||
|
|
||||||
|
import com.anji.captcha.service.CaptchaCacheService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对于分布式部署的应用,我们建议应用自己实现CaptchaCacheService,比如用Redis,参考service/spring-boot代码示例。
|
||||||
|
* 如果应用是单点的,也没有使用redis,那默认使用内存。
|
||||||
|
* 内存缓存只适合单节点部署的应用,否则验证码生产与验证在节点之间信息不同步,导致失败。
|
||||||
|
*
|
||||||
|
* ☆☆☆ SPI: 在resources目录新建META-INF.services文件夹(两层),参考当前服务resources。
|
||||||
|
* @author lide1202@hotmail.com
|
||||||
|
* @date 2020-05-12
|
||||||
|
*/
|
||||||
|
public class CaptchaCacheServiceRedisImpl implements CaptchaCacheService {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String type() {
|
||||||
|
return "redis";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private StringRedisTemplate stringRedisTemplate;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(String key, String value, long expiresInSeconds) {
|
||||||
|
stringRedisTemplate.opsForValue().set(key, value, expiresInSeconds, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean exists(String key) {
|
||||||
|
return stringRedisTemplate.hasKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete(String key) {
|
||||||
|
stringRedisTemplate.delete(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String get(String key) {
|
||||||
|
return stringRedisTemplate.opsForValue().get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long increment(String key, long val) {
|
||||||
|
return stringRedisTemplate.opsForValue().increment(key,val);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -126,6 +126,22 @@ public class FlowIdentityExtHelper implements BaseFlowIdentityExtHelper {
|
|||||||
return usernameSet;
|
return usernameSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean supprtDataPerm() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> mapUserShowNameByLoginName(Set<String> loginNameSet) {
|
||||||
|
if (CollUtil.isEmpty(loginNameSet)) {
|
||||||
|
return new HashMap<>(1);
|
||||||
|
}
|
||||||
|
Map<String, String> resultMap = new HashMap<>(loginNameSet.size());
|
||||||
|
List<SysUser> userList = sysUserService.getInList("loginName", loginNameSet);
|
||||||
|
userList.forEach(user -> resultMap.put(user.getLoginName(), user.getShowName()));
|
||||||
|
return resultMap;
|
||||||
|
}
|
||||||
|
|
||||||
private void extractAndAppendUsernameList(Set<String> resultUsernameList, List<SysUser> userList) {
|
private void extractAndAppendUsernameList(Set<String> resultUsernameList, List<SysUser> userList) {
|
||||||
List<String> usernameList = userList.stream().map(SysUser::getLoginName).collect(Collectors.toList());
|
List<String> usernameList = userList.stream().map(SysUser::getLoginName).collect(Collectors.toList());
|
||||||
if (CollUtil.isNotEmpty(usernameList)) {
|
if (CollUtil.isNotEmpty(usernameList)) {
|
||||||
|
|||||||
@@ -62,15 +62,19 @@ public class AuthenticationInterceptor implements HandlerInterceptor {
|
|||||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
String url = request.getRequestURI();
|
String url = request.getRequestURI();
|
||||||
|
String token = request.getHeader(appConfig.getTokenHeaderKey());
|
||||||
|
boolean noLoginUrl = false;
|
||||||
// 如果接口方法标记NoAuthInterface注解,可以直接跳过Token鉴权验证,这里主要为了测试接口方便
|
// 如果接口方法标记NoAuthInterface注解,可以直接跳过Token鉴权验证,这里主要为了测试接口方便
|
||||||
if (handler instanceof HandlerMethod) {
|
if (handler instanceof HandlerMethod) {
|
||||||
HandlerMethod hm = (HandlerMethod) handler;
|
HandlerMethod hm = (HandlerMethod) handler;
|
||||||
if (hm.getBeanType().getAnnotation(NoAuthInterface.class) != null
|
if (hm.getBeanType().getAnnotation(NoAuthInterface.class) != null
|
||||||
|| hm.getMethodAnnotation(NoAuthInterface.class) != null) {
|
|| hm.getMethodAnnotation(NoAuthInterface.class) != null) {
|
||||||
|
noLoginUrl = true;
|
||||||
|
if (StringUtils.isBlank(token)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
String token = request.getHeader(appConfig.getTokenHeaderKey());
|
}
|
||||||
if (StringUtils.isBlank(token)) {
|
if (StringUtils.isBlank(token)) {
|
||||||
token = request.getParameter(appConfig.getTokenHeaderKey());
|
token = request.getParameter(appConfig.getTokenHeaderKey());
|
||||||
}
|
}
|
||||||
@@ -95,8 +99,8 @@ public class AuthenticationInterceptor implements HandlerInterceptor {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
TokenData.addToRequest(tokenData);
|
TokenData.addToRequest(tokenData);
|
||||||
// 如果url在权限资源白名单中,则不需要进行鉴权操作
|
// 如果url是免登陆、白名单中,则不需要进行鉴权操作
|
||||||
if (Boolean.FALSE.equals(tokenData.getIsAdmin()) && !whitelistPermSet.contains(url)) {
|
if (!noLoginUrl && Boolean.FALSE.equals(tokenData.getIsAdmin()) && !whitelistPermSet.contains(url)) {
|
||||||
RSet<String> permSet = redissonClient.getSet(RedisKeyUtil.makeSessionPermIdKey(sessionId));
|
RSet<String> permSet = redissonClient.getSet(RedisKeyUtil.makeSessionPermIdKey(sessionId));
|
||||||
if (!permSet.contains(url)) {
|
if (!permSet.contains(url)) {
|
||||||
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
|
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
package com.orangeforms.webadmin.upms.controller;
|
package com.orangeforms.webadmin.upms.controller;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.alibaba.fastjson.JSONArray;
|
||||||
import com.alibaba.fastjson.serializer.SerializerFeature;
|
import com.alibaba.fastjson.serializer.SerializerFeature;
|
||||||
|
import com.anji.captcha.model.common.ResponseModel;
|
||||||
|
import com.anji.captcha.model.vo.CaptchaVO;
|
||||||
|
import com.anji.captcha.service.CaptchaService;
|
||||||
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
|
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
import io.swagger.annotations.ApiImplicitParam;
|
import io.swagger.annotations.ApiImplicitParam;
|
||||||
@@ -25,6 +31,7 @@ import com.orangeforms.common.core.constant.ApplicationConstant;
|
|||||||
import com.orangeforms.common.core.constant.ErrorCodeEnum;
|
import com.orangeforms.common.core.constant.ErrorCodeEnum;
|
||||||
import com.orangeforms.common.core.object.*;
|
import com.orangeforms.common.core.object.*;
|
||||||
import com.orangeforms.common.core.util.*;
|
import com.orangeforms.common.core.util.*;
|
||||||
|
import com.orangeforms.common.core.upload.*;
|
||||||
import com.orangeforms.common.redis.cache.SessionCacheHelper;
|
import com.orangeforms.common.redis.cache.SessionCacheHelper;
|
||||||
import org.apache.commons.collections4.CollectionUtils;
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@@ -33,7 +40,9 @@ import org.redisson.api.RedissonClient;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -79,27 +88,51 @@ public class LoginController {
|
|||||||
private SessionCacheHelper cacheHelper;
|
private SessionCacheHelper cacheHelper;
|
||||||
@Autowired
|
@Autowired
|
||||||
private PasswordEncoder passwordEncoder;
|
private PasswordEncoder passwordEncoder;
|
||||||
|
@Autowired
|
||||||
|
private CaptchaService captchaService;
|
||||||
|
@Autowired
|
||||||
|
private UpDownloaderFactory upDownloaderFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登录接口。
|
* 登录接口。
|
||||||
*
|
*
|
||||||
* @param loginName 登录名。
|
* @param loginName 登录名。
|
||||||
* @param password 密码。
|
* @param password 密码。
|
||||||
|
* @param captchaVerification 验证码。
|
||||||
* @return 应答结果对象,其中包括JWT的Token数据,以及菜单列表。
|
* @return 应答结果对象,其中包括JWT的Token数据,以及菜单列表。
|
||||||
*/
|
*/
|
||||||
@ApiImplicitParams({
|
@ApiImplicitParams({
|
||||||
// 这里包含密码密文,仅用于方便开发期间的接口测试,集成测试和发布阶段,需要将当前注解去掉。
|
// 这里包含密码密文,仅用于方便开发期间的接口测试,集成测试和发布阶段,需要将当前注解去掉。
|
||||||
// 如果您重新生成了公钥和私钥,请替换password的缺省值。
|
// 如果您重新生成了公钥和私钥,请替换password的缺省值。
|
||||||
@ApiImplicitParam(name = "loginName", defaultValue = "admin"),
|
@ApiImplicitParam(name = "loginName", defaultValue = "admin"),
|
||||||
@ApiImplicitParam(name = "password", defaultValue = "IP3ccke3GhH45iGHB5qP9p7iZw6xUyj28Ju10rnBiPKOI35sc%2BjI7%2FdsjOkHWMfUwGYGfz8ik31HC2Ruk%2Fhkd9f6RPULTHj7VpFdNdde2P9M4mQQnFBAiPM7VT9iW3RyCtPlJexQ3nAiA09OqG%2F0sIf1kcyveSrulxembARDbDo%3D")
|
@ApiImplicitParam(name = "password", defaultValue = "IP3ccke3GhH45iGHB5qP9p7iZw6xUyj28Ju10rnBiPKOI35sc%2BjI7%2FdsjOkHWMfUwGYGfz8ik31HC2Ruk%2Fhkd9f6RPULTHj7VpFdNdde2P9M4mQQnFBAiPM7VT9iW3RyCtPlJexQ3nAiA09OqG%2F0sIf1kcyveSrulxembARDbDo%3D"),
|
||||||
|
@ApiImplicitParam(name = "captchaVerification", defaultValue = "为了方便测试,这里可以修改一下代码,hardcode一个每次都ok的验证码")
|
||||||
})
|
})
|
||||||
@NoAuthInterface
|
@NoAuthInterface
|
||||||
@PostMapping("/doLogin")
|
@PostMapping("/doLogin")
|
||||||
public ResponseResult<JSONObject> doLogin(
|
public ResponseResult<JSONObject> doLogin(
|
||||||
@MyRequestBody String loginName, @MyRequestBody String password) throws Exception {
|
@MyRequestBody String loginName,
|
||||||
if (MyCommonUtil.existBlankArgument(loginName, password)) {
|
@MyRequestBody String password,
|
||||||
|
@MyRequestBody String captchaVerification) throws Exception {
|
||||||
|
if (MyCommonUtil.existBlankArgument(loginName, password, captchaVerification)) {
|
||||||
return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
|
return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
|
||||||
}
|
}
|
||||||
|
String errorMessage;
|
||||||
|
CaptchaVO captchaVO = new CaptchaVO();
|
||||||
|
captchaVO.setCaptchaVerification(captchaVerification);
|
||||||
|
ResponseModel response = captchaService.verification(captchaVO);
|
||||||
|
if (!response.isSuccess()) {
|
||||||
|
//验证码校验失败,返回信息告诉前端
|
||||||
|
//repCode 0000 无异常,代表成功
|
||||||
|
//repCode 9999 服务器内部异常
|
||||||
|
//repCode 0011 参数不能为空
|
||||||
|
//repCode 6110 验证码已失效,请重新获取
|
||||||
|
//repCode 6111 验证失败
|
||||||
|
//repCode 6112 获取验证码失败,请联系管理员
|
||||||
|
errorMessage = String.format("数据验证失败,验证码错误,错误码 [%s] 错误信息 [%s]",
|
||||||
|
response.getRepCode(), response.getRepMsg());
|
||||||
|
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
|
||||||
|
}
|
||||||
SysUser user = sysUserService.getSysUserByLoginName(loginName);
|
SysUser user = sysUserService.getSysUserByLoginName(loginName);
|
||||||
password = URLDecoder.decode(password, StandardCharsets.UTF_8.name());
|
password = URLDecoder.decode(password, StandardCharsets.UTF_8.name());
|
||||||
// NOTE: 第一次使用时,请务必阅读ApplicationConstant.PRIVATE_KEY的代码注释。
|
// NOTE: 第一次使用时,请务必阅读ApplicationConstant.PRIVATE_KEY的代码注释。
|
||||||
@@ -108,13 +141,10 @@ public class LoginController {
|
|||||||
if (user == null || !passwordEncoder.matches(password, user.getPassword())) {
|
if (user == null || !passwordEncoder.matches(password, user.getPassword())) {
|
||||||
return ResponseResult.error(ErrorCodeEnum.INVALID_USERNAME_PASSWORD);
|
return ResponseResult.error(ErrorCodeEnum.INVALID_USERNAME_PASSWORD);
|
||||||
}
|
}
|
||||||
String errorMessage;
|
|
||||||
if (user.getUserStatus() == SysUserStatus.STATUS_LOCKED) {
|
if (user.getUserStatus() == SysUserStatus.STATUS_LOCKED) {
|
||||||
errorMessage = "登录失败,用户账号被锁定!";
|
errorMessage = "登录失败,用户账号被锁定!";
|
||||||
return ResponseResult.error(ErrorCodeEnum.INVALID_USER_STATUS, errorMessage);
|
return ResponseResult.error(ErrorCodeEnum.INVALID_USER_STATUS, errorMessage);
|
||||||
}
|
}
|
||||||
String patternKey = RedisKeyUtil.getSessionIdPrefix(user.getLoginName(), MyCommonUtil.getDeviceType()) + "*";
|
|
||||||
redissonClient.getKeys().deleteByPatternAsync(patternKey);
|
|
||||||
JSONObject jsonData = this.buildLoginData(user);
|
JSONObject jsonData = this.buildLoginData(user);
|
||||||
return ResponseResult.success(jsonData);
|
return ResponseResult.success(jsonData);
|
||||||
}
|
}
|
||||||
@@ -151,6 +181,9 @@ public class LoginController {
|
|||||||
JSONObject jsonData = new JSONObject();
|
JSONObject jsonData = new JSONObject();
|
||||||
jsonData.put("showName", tokenData.getShowName());
|
jsonData.put("showName", tokenData.getShowName());
|
||||||
jsonData.put("isAdmin", tokenData.getIsAdmin());
|
jsonData.put("isAdmin", tokenData.getIsAdmin());
|
||||||
|
if (StrUtil.isNotBlank(tokenData.getHeadImageUrl())) {
|
||||||
|
jsonData.put("headImageUrl", tokenData.getHeadImageUrl());
|
||||||
|
}
|
||||||
Collection<SysMenu> menuList;
|
Collection<SysMenu> menuList;
|
||||||
Collection<String> permCodeList;
|
Collection<String> permCodeList;
|
||||||
if (tokenData.getIsAdmin()) {
|
if (tokenData.getIsAdmin()) {
|
||||||
@@ -195,9 +228,71 @@ public class LoginController {
|
|||||||
return ResponseResult.success();
|
return ResponseResult.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传并修改用户头像。
|
||||||
|
*
|
||||||
|
* @param uploadFile 上传的头像文件。
|
||||||
|
*/
|
||||||
|
@PostMapping("/changeHeadImage")
|
||||||
|
public void changeHeadImage(
|
||||||
|
@RequestParam("uploadFile") MultipartFile uploadFile) throws Exception {
|
||||||
|
String fieldName = "headImageUrl";
|
||||||
|
UploadStoreInfo storeInfo = MyModelUtil.getUploadStoreInfo(SysUser.class, fieldName);
|
||||||
|
BaseUpDownloader upDownloader = upDownloaderFactory.get(storeInfo.getStoreType());
|
||||||
|
UploadResponseInfo responseInfo = upDownloader.doUpload(null,
|
||||||
|
appConfig.getUploadFileBaseDir(), SysUser.class.getSimpleName(), fieldName, true, uploadFile);
|
||||||
|
if (responseInfo.getUploadFailed()) {
|
||||||
|
ResponseResult.output(HttpServletResponse.SC_FORBIDDEN,
|
||||||
|
ResponseResult.error(ErrorCodeEnum.UPLOAD_FAILED, responseInfo.getErrorMessage()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
responseInfo.setDownloadUri("/admin/upms/login/downloadHeadImage");
|
||||||
|
String newHeadImage = JSONArray.toJSONString(CollUtil.newArrayList(responseInfo));
|
||||||
|
if (!sysUserService.changeHeadImage(TokenData.takeFromRequest().getUserId(), newHeadImage)) {
|
||||||
|
ResponseResult.output(HttpServletResponse.SC_FORBIDDEN,
|
||||||
|
ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ResponseResult.output(ResponseResult.success(responseInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载用户头像。
|
||||||
|
*
|
||||||
|
* @param filename 文件名。如果没有提供该参数,就从当前记录的指定字段中读取。
|
||||||
|
* @param response Http 应答对象。
|
||||||
|
*/
|
||||||
|
@GetMapping("/downloadHeadImage")
|
||||||
|
public void downloadHeadImage(String filename, HttpServletResponse response) {
|
||||||
|
try {
|
||||||
|
SysUser user = sysUserService.getById(TokenData.takeFromRequest().getUserId());
|
||||||
|
if (user == null) {
|
||||||
|
ResponseResult.output(HttpServletResponse.SC_NOT_FOUND);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (StrUtil.isBlank(user.getHeadImageUrl())) {
|
||||||
|
ResponseResult.output(HttpServletResponse.SC_BAD_REQUEST);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!BaseUpDownloader.containFile(user.getHeadImageUrl(), filename)) {
|
||||||
|
ResponseResult.output(HttpServletResponse.SC_FORBIDDEN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String fieldName = "headImageUrl";
|
||||||
|
UploadStoreInfo storeInfo = MyModelUtil.getUploadStoreInfo(SysUser.class, fieldName);
|
||||||
|
BaseUpDownloader upDownloader = upDownloaderFactory.get(storeInfo.getStoreType());
|
||||||
|
upDownloader.doDownload(appConfig.getUploadFileBaseDir(),
|
||||||
|
SysUser.class.getSimpleName(), fieldName, filename, true, response);
|
||||||
|
} catch (Exception e) {
|
||||||
|
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private JSONObject buildLoginData(SysUser user) {
|
private JSONObject buildLoginData(SysUser user) {
|
||||||
int deviceType = MyCommonUtil.getDeviceType();
|
int deviceType = MyCommonUtil.getDeviceType();
|
||||||
boolean isAdmin = user.getUserType() == SysUserType.TYPE_ADMIN;
|
boolean isAdmin = user.getUserType() == SysUserType.TYPE_ADMIN;
|
||||||
|
String headImageUrl = user.getHeadImageUrl();
|
||||||
Map<String, Object> claims = new HashMap<>(3);
|
Map<String, Object> claims = new HashMap<>(3);
|
||||||
String sessionId = user.getLoginName() + "_" + deviceType + "_" + MyCommonUtil.generateUuid();
|
String sessionId = user.getLoginName() + "_" + deviceType + "_" + MyCommonUtil.generateUuid();
|
||||||
claims.put("sessionId", sessionId);
|
claims.put("sessionId", sessionId);
|
||||||
@@ -206,6 +301,9 @@ public class LoginController {
|
|||||||
jsonData.put(TokenData.REQUEST_ATTRIBUTE_NAME, token);
|
jsonData.put(TokenData.REQUEST_ATTRIBUTE_NAME, token);
|
||||||
jsonData.put("showName", user.getShowName());
|
jsonData.put("showName", user.getShowName());
|
||||||
jsonData.put("isAdmin", isAdmin);
|
jsonData.put("isAdmin", isAdmin);
|
||||||
|
if (StrUtil.isNotBlank(headImageUrl)) {
|
||||||
|
jsonData.put("headImageUrl", headImageUrl);
|
||||||
|
}
|
||||||
TokenData tokenData = new TokenData();
|
TokenData tokenData = new TokenData();
|
||||||
tokenData.setSessionId(sessionId);
|
tokenData.setSessionId(sessionId);
|
||||||
tokenData.setUserId(user.getUserId());
|
tokenData.setUserId(user.getUserId());
|
||||||
@@ -216,6 +314,9 @@ public class LoginController {
|
|||||||
tokenData.setLoginIp(IpUtil.getRemoteIpAddress(ContextUtil.getHttpRequest()));
|
tokenData.setLoginIp(IpUtil.getRemoteIpAddress(ContextUtil.getHttpRequest()));
|
||||||
tokenData.setLoginTime(new Date());
|
tokenData.setLoginTime(new Date());
|
||||||
tokenData.setDeviceType(deviceType);
|
tokenData.setDeviceType(deviceType);
|
||||||
|
if (StrUtil.isNotBlank(headImageUrl)) {
|
||||||
|
tokenData.setHeadImageUrl(headImageUrl);
|
||||||
|
}
|
||||||
List<SysUserPost> userPostList = sysPostService.getSysUserPostListByUserId(user.getUserId());
|
List<SysUserPost> userPostList = sysPostService.getSysUserPostListByUserId(user.getUserId());
|
||||||
if (CollectionUtils.isNotEmpty(userPostList)) {
|
if (CollectionUtils.isNotEmpty(userPostList)) {
|
||||||
Set<Long> deptPostIdSet = userPostList.stream().map(SysUserPost::getDeptPostId).collect(Collectors.toSet());
|
Set<Long> deptPostIdSet = userPostList.stream().map(SysUserPost::getDeptPostId).collect(Collectors.toSet());
|
||||||
@@ -303,6 +404,7 @@ public class LoginController {
|
|||||||
// 所以就不能把这个字典列表接口放到数据库的白名单表中。
|
// 所以就不能把这个字典列表接口放到数据库的白名单表中。
|
||||||
permData.permUrlSet.add(onlineProperties.getUrlPrefix() + "/onlineOperation/listDict");
|
permData.permUrlSet.add(onlineProperties.getUrlPrefix() + "/onlineOperation/listDict");
|
||||||
permData.permUrlSet.add(onlineProperties.getUrlPrefix() + "/onlineForm/render");
|
permData.permUrlSet.add(onlineProperties.getUrlPrefix() + "/onlineForm/render");
|
||||||
|
permData.permUrlSet.add(onlineProperties.getUrlPrefix() + "/onlineForm/view");
|
||||||
return permData;
|
return permData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -111,27 +111,7 @@ public class SysDeptController {
|
|||||||
if (MyCommonUtil.existBlankArgument(deptId)) {
|
if (MyCommonUtil.existBlankArgument(deptId)) {
|
||||||
return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
|
return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
|
||||||
}
|
}
|
||||||
// 验证关联Id的数据合法性
|
return this.doDelete(deptId);
|
||||||
SysDept originalSysDept = sysDeptService.getById(deptId);
|
|
||||||
if (originalSysDept == null) {
|
|
||||||
// NOTE: 修改下面方括号中的话述
|
|
||||||
errorMessage = "数据验证失败,当前 [对象] 并不存在,请刷新后重试!";
|
|
||||||
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage);
|
|
||||||
}
|
|
||||||
if (sysDeptService.hasChildren(deptId)) {
|
|
||||||
// NOTE: 修改下面方括号中的话述
|
|
||||||
errorMessage = "数据验证失败,当前 [对象存在子对象] ,请刷新后重试!";
|
|
||||||
return ResponseResult.error(ErrorCodeEnum.HAS_CHILDREN_DATA, errorMessage);
|
|
||||||
}
|
|
||||||
if (sysDeptService.hasChildrenUser(deptId)) {
|
|
||||||
errorMessage = "数据验证失败,请先移除部门用户数据后,再删除当前部门!";
|
|
||||||
return ResponseResult.error(ErrorCodeEnum.HAS_CHILDREN_DATA, errorMessage);
|
|
||||||
}
|
|
||||||
if (!sysDeptService.remove(deptId)) {
|
|
||||||
errorMessage = "数据操作失败,删除的对象不存在,请刷新后重试!";
|
|
||||||
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage);
|
|
||||||
}
|
|
||||||
return ResponseResult.success();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -372,4 +352,29 @@ public class SysDeptController {
|
|||||||
return ResponseResult.success(BeanQuery.select(
|
return ResponseResult.success(BeanQuery.select(
|
||||||
"parentId as parentId", "deptId as id", "deptName as name").executeFrom(resultList));
|
"parentId as parentId", "deptId as id", "deptName as name").executeFrom(resultList));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ResponseResult<Void> doDelete(Long deptId) {
|
||||||
|
String errorMessage;
|
||||||
|
// 验证关联Id的数据合法性
|
||||||
|
SysDept originalSysDept = sysDeptService.getById(deptId);
|
||||||
|
if (originalSysDept == null) {
|
||||||
|
// NOTE: 修改下面方括号中的话述
|
||||||
|
errorMessage = "数据验证失败,当前 [对象] 并不存在,请刷新后重试!";
|
||||||
|
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage);
|
||||||
|
}
|
||||||
|
if (sysDeptService.hasChildren(deptId)) {
|
||||||
|
// NOTE: 修改下面方括号中的话述
|
||||||
|
errorMessage = "数据验证失败,当前 [对象存在子对象] ,请刷新后重试!";
|
||||||
|
return ResponseResult.error(ErrorCodeEnum.HAS_CHILDREN_DATA, errorMessage);
|
||||||
|
}
|
||||||
|
if (sysDeptService.hasChildrenUser(deptId)) {
|
||||||
|
errorMessage = "数据验证失败,请先移除部门用户数据后,再删除当前部门!";
|
||||||
|
return ResponseResult.error(ErrorCodeEnum.HAS_CHILDREN_DATA, errorMessage);
|
||||||
|
}
|
||||||
|
if (!sysDeptService.remove(deptId)) {
|
||||||
|
errorMessage = "数据操作失败,删除的对象不存在,请刷新后重试!";
|
||||||
|
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage);
|
||||||
|
}
|
||||||
|
return ResponseResult.success();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
package com.orangeforms.webadmin.upms.controller;
|
package com.orangeforms.webadmin.upms.controller;
|
||||||
|
|
||||||
import com.alibaba.fastjson.TypeReference;
|
import com.alibaba.fastjson.TypeReference;
|
||||||
|
import cn.hutool.core.util.ReflectUtil;
|
||||||
|
import com.orangeforms.common.core.upload.BaseUpDownloader;
|
||||||
|
import com.orangeforms.common.core.upload.UpDownloaderFactory;
|
||||||
|
import com.orangeforms.common.core.upload.UploadResponseInfo;
|
||||||
|
import com.orangeforms.common.core.upload.UploadStoreInfo;
|
||||||
import com.github.pagehelper.page.PageMethod;
|
import com.github.pagehelper.page.PageMethod;
|
||||||
import com.orangeforms.webadmin.upms.vo.*;
|
import com.orangeforms.webadmin.upms.vo.*;
|
||||||
import com.orangeforms.webadmin.upms.dto.*;
|
import com.orangeforms.webadmin.upms.dto.*;
|
||||||
@@ -10,6 +15,7 @@ import com.orangeforms.common.core.object.*;
|
|||||||
import com.orangeforms.common.core.util.*;
|
import com.orangeforms.common.core.util.*;
|
||||||
import com.orangeforms.common.core.constant.*;
|
import com.orangeforms.common.core.constant.*;
|
||||||
import com.orangeforms.common.core.annotation.MyRequestBody;
|
import com.orangeforms.common.core.annotation.MyRequestBody;
|
||||||
|
import com.orangeforms.common.redis.cache.SessionCacheHelper;
|
||||||
import com.orangeforms.webadmin.config.ApplicationConfig;
|
import com.orangeforms.webadmin.config.ApplicationConfig;
|
||||||
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
@@ -17,7 +23,9 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,6 +46,10 @@ public class SysUserController {
|
|||||||
private PasswordEncoder passwordEncoder;
|
private PasswordEncoder passwordEncoder;
|
||||||
@Autowired
|
@Autowired
|
||||||
private ApplicationConfig appConfig;
|
private ApplicationConfig appConfig;
|
||||||
|
@Autowired
|
||||||
|
private SessionCacheHelper cacheHelper;
|
||||||
|
@Autowired
|
||||||
|
private UpDownloaderFactory upDownloaderFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增用户操作。
|
* 新增用户操作。
|
||||||
@@ -145,18 +157,7 @@ public class SysUserController {
|
|||||||
if (MyCommonUtil.existBlankArgument(userId)) {
|
if (MyCommonUtil.existBlankArgument(userId)) {
|
||||||
return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
|
return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
|
||||||
}
|
}
|
||||||
// 验证关联Id的数据合法性
|
return this.doDelete(userId);
|
||||||
SysUser originalSysUser = sysUserService.getById(userId);
|
|
||||||
if (originalSysUser == null) {
|
|
||||||
// NOTE: 修改下面方括号中的话述
|
|
||||||
errorMessage = "数据验证失败,当前 [对象] 并不存在,请刷新后重试!";
|
|
||||||
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage);
|
|
||||||
}
|
|
||||||
if (!sysUserService.remove(userId)) {
|
|
||||||
errorMessage = "数据操作失败,删除的对象不存在,请刷新后重试!";
|
|
||||||
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage);
|
|
||||||
}
|
|
||||||
return ResponseResult.success();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -201,6 +202,99 @@ public class SysUserController {
|
|||||||
return ResponseResult.success(sysUserVo);
|
return ResponseResult.success(sysUserVo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 附件文件下载。
|
||||||
|
* 这里将图片和其他类型的附件文件放到不同的父目录下,主要为了便于今后图片文件的迁移。
|
||||||
|
*
|
||||||
|
* @param userId 附件所在记录的主键Id。
|
||||||
|
* @param fieldName 附件所属的字段名。
|
||||||
|
* @param filename 文件名。如果没有提供该参数,就从当前记录的指定字段中读取。
|
||||||
|
* @param asImage 下载文件是否为图片。
|
||||||
|
* @param response Http 应答对象。
|
||||||
|
*/
|
||||||
|
@GetMapping("/download")
|
||||||
|
public void download(
|
||||||
|
@RequestParam(required = false) Long userId,
|
||||||
|
@RequestParam String fieldName,
|
||||||
|
@RequestParam String filename,
|
||||||
|
@RequestParam Boolean asImage,
|
||||||
|
HttpServletResponse response) {
|
||||||
|
if (MyCommonUtil.existBlankArgument(fieldName, filename, asImage)) {
|
||||||
|
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 使用try来捕获异常,是为了保证一旦出现异常可以返回500的错误状态,便于调试。
|
||||||
|
// 否则有可能给前端返回的是200的错误码。
|
||||||
|
try {
|
||||||
|
// 如果请求参数中没有包含主键Id,就判断该文件是否为当前session上传的。
|
||||||
|
if (userId == null) {
|
||||||
|
if (!cacheHelper.existSessionUploadFile(filename)) {
|
||||||
|
ResponseResult.output(HttpServletResponse.SC_FORBIDDEN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SysUser sysUser = sysUserService.getById(userId);
|
||||||
|
if (sysUser == null) {
|
||||||
|
ResponseResult.output(HttpServletResponse.SC_NOT_FOUND);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String fieldJsonData = (String) ReflectUtil.getFieldValue(sysUser, fieldName);
|
||||||
|
if (fieldJsonData == null) {
|
||||||
|
ResponseResult.output(HttpServletResponse.SC_BAD_REQUEST);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!BaseUpDownloader.containFile(fieldJsonData, filename)) {
|
||||||
|
ResponseResult.output(HttpServletResponse.SC_FORBIDDEN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UploadStoreInfo storeInfo = MyModelUtil.getUploadStoreInfo(SysUser.class, fieldName);
|
||||||
|
if (!storeInfo.isSupportUpload()) {
|
||||||
|
ResponseResult.output(HttpServletResponse.SC_NOT_IMPLEMENTED,
|
||||||
|
ResponseResult.error(ErrorCodeEnum.INVALID_UPLOAD_FIELD));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BaseUpDownloader upDownloader = upDownloaderFactory.get(storeInfo.getStoreType());
|
||||||
|
upDownloader.doDownload(appConfig.getUploadFileBaseDir(),
|
||||||
|
SysUser.class.getSimpleName(), fieldName, filename, asImage, response);
|
||||||
|
} catch (Exception e) {
|
||||||
|
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传操作。
|
||||||
|
*
|
||||||
|
* @param fieldName 上传文件名。
|
||||||
|
* @param asImage 是否作为图片上传。如果是图片,今后下载的时候无需权限验证。否则就是附件上传,下载时需要权限验证。
|
||||||
|
* @param uploadFile 上传文件对象。
|
||||||
|
*/
|
||||||
|
@PostMapping("/upload")
|
||||||
|
public void upload(
|
||||||
|
@RequestParam String fieldName,
|
||||||
|
@RequestParam Boolean asImage,
|
||||||
|
@RequestParam("uploadFile") MultipartFile uploadFile) throws Exception {
|
||||||
|
UploadStoreInfo storeInfo = MyModelUtil.getUploadStoreInfo(SysUser.class, fieldName);
|
||||||
|
// 这里就会判断参数中指定的字段,是否支持上传操作。
|
||||||
|
if (!storeInfo.isSupportUpload()) {
|
||||||
|
ResponseResult.output(HttpServletResponse.SC_FORBIDDEN,
|
||||||
|
ResponseResult.error(ErrorCodeEnum.INVALID_UPLOAD_FIELD));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 根据字段注解中的存储类型,通过工厂方法获取匹配的上传下载实现类,从而解耦。
|
||||||
|
BaseUpDownloader upDownloader = upDownloaderFactory.get(storeInfo.getStoreType());
|
||||||
|
UploadResponseInfo responseInfo = upDownloader.doUpload(null,
|
||||||
|
appConfig.getUploadFileBaseDir(), SysUser.class.getSimpleName(), fieldName, asImage, uploadFile);
|
||||||
|
if (responseInfo.getUploadFailed()) {
|
||||||
|
ResponseResult.output(HttpServletResponse.SC_FORBIDDEN,
|
||||||
|
ResponseResult.error(ErrorCodeEnum.UPLOAD_FAILED, responseInfo.getErrorMessage()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cacheHelper.putSessionUploadFile(responseInfo.getFilename());
|
||||||
|
ResponseResult.output(ResponseResult.success(responseInfo));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询用户的权限资源地址列表。同时返回详细的分配路径。
|
* 查询用户的权限资源地址列表。同时返回详细的分配路径。
|
||||||
*
|
*
|
||||||
@@ -245,4 +339,20 @@ public class SysUserController {
|
|||||||
}
|
}
|
||||||
return ResponseResult.success(sysUserService.getSysMenuListWithDetail(userId, menuName));
|
return ResponseResult.success(sysUserService.getSysMenuListWithDetail(userId, menuName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ResponseResult<Void> doDelete(Long userId) {
|
||||||
|
String errorMessage;
|
||||||
|
// 验证关联Id的数据合法性
|
||||||
|
SysUser originalSysUser = sysUserService.getById(userId);
|
||||||
|
if (originalSysUser == null) {
|
||||||
|
// NOTE: 修改下面方括号中的话述
|
||||||
|
errorMessage = "数据验证失败,当前 [对象] 并不存在,请刷新后重试!";
|
||||||
|
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage);
|
||||||
|
}
|
||||||
|
if (!sysUserService.remove(userId)) {
|
||||||
|
errorMessage = "数据操作失败,删除的对象不存在,请刷新后重试!";
|
||||||
|
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage);
|
||||||
|
}
|
||||||
|
return ResponseResult.success();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.orangeforms.webadmin.upms.model;
|
|||||||
import com.baomidou.mybatisplus.annotation.*;
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
import com.orangeforms.webadmin.upms.model.constant.SysUserType;
|
import com.orangeforms.webadmin.upms.model.constant.SysUserType;
|
||||||
import com.orangeforms.webadmin.upms.model.constant.SysUserStatus;
|
import com.orangeforms.webadmin.upms.model.constant.SysUserStatus;
|
||||||
|
import com.orangeforms.common.core.upload.UploadStoreTypeEnum;
|
||||||
import com.orangeforms.common.core.annotation.*;
|
import com.orangeforms.common.core.annotation.*;
|
||||||
import com.orangeforms.common.core.base.mapper.BaseModelMapper;
|
import com.orangeforms.common.core.base.mapper.BaseModelMapper;
|
||||||
import com.orangeforms.webadmin.upms.vo.SysUserVo;
|
import com.orangeforms.webadmin.upms.vo.SysUserVo;
|
||||||
@@ -62,6 +63,7 @@ public class SysUser {
|
|||||||
/**
|
/**
|
||||||
* 用户头像的Url。
|
* 用户头像的Url。
|
||||||
*/
|
*/
|
||||||
|
@UploadFlagColumn(storeType = UploadStoreTypeEnum.LOCAL_SYSTEM)
|
||||||
@TableField(value = "head_image_url")
|
@TableField(value = "head_image_url")
|
||||||
private String headImageUrl;
|
private String headImageUrl;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.orangeforms.webadmin.upms.service;
|
package com.orangeforms.webadmin.upms.service;
|
||||||
|
|
||||||
import com.orangeforms.common.core.base.service.IBaseService;
|
import com.orangeforms.common.core.base.service.IBaseService;
|
||||||
import com.orangeforms.common.core.object.CallResult;
|
|
||||||
import com.orangeforms.webadmin.upms.model.SysPerm;
|
import com.orangeforms.webadmin.upms.model.SysPerm;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|||||||
@@ -53,6 +53,15 @@ public interface SysUserService extends IBaseService<SysUser, Long> {
|
|||||||
*/
|
*/
|
||||||
boolean changePassword(Long userId, String newPass);
|
boolean changePassword(Long userId, String newPass);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改用户头像。
|
||||||
|
*
|
||||||
|
* @param userId 用户主键Id。
|
||||||
|
* @param newHeadImage 新的头像信息。
|
||||||
|
* @return 成功返回true,否则false。
|
||||||
|
*/
|
||||||
|
boolean changeHeadImage(Long userId, String newHeadImage);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除指定数据。
|
* 删除指定数据。
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.orangeforms.webadmin.upms.service.impl;
|
package com.orangeforms.webadmin.upms.service.impl;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.*;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||||
import com.orangeforms.webadmin.upms.service.*;
|
import com.orangeforms.webadmin.upms.service.*;
|
||||||
import com.orangeforms.webadmin.upms.dao.*;
|
import com.orangeforms.webadmin.upms.dao.*;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package com.orangeforms.webadmin.upms.service.impl;
|
package com.orangeforms.webadmin.upms.service.impl;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.*;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||||
import com.orangeforms.webadmin.upms.service.*;
|
import com.orangeforms.webadmin.upms.service.*;
|
||||||
import com.orangeforms.webadmin.upms.dao.*;
|
import com.orangeforms.webadmin.upms.dao.*;
|
||||||
import com.orangeforms.webadmin.upms.model.*;
|
import com.orangeforms.webadmin.upms.model.*;
|
||||||
@@ -203,6 +203,15 @@ public class SysUserServiceImpl extends BaseService<SysUser, Long> implements Sy
|
|||||||
return sysUserMapper.updateById(updatedUser) == 1;
|
return sysUserMapper.updateById(updatedUser) == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
@Override
|
||||||
|
public boolean changeHeadImage(Long userId, String newHeadImage) {
|
||||||
|
SysUser updatedUser = new SysUser();
|
||||||
|
updatedUser.setUserId(userId);
|
||||||
|
updatedUser.setHeadImageUrl(newHeadImage);
|
||||||
|
return sysUserMapper.updateById(updatedUser) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除指定数据。
|
* 删除指定数据。
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
com.orangeforms.webadmin.app.util.CaptchaCacheServiceRedisImpl
|
||||||
@@ -94,6 +94,8 @@ redis:
|
|||||||
minIdle: 5
|
minIdle: 5
|
||||||
|
|
||||||
common-online:
|
common-online:
|
||||||
|
# 可选值为 mysql / postgresql
|
||||||
|
datasourceType: mysql
|
||||||
# 注意不要以反斜杠(/)结尾。
|
# 注意不要以反斜杠(/)结尾。
|
||||||
operationUrlPrefix: /admin/online
|
operationUrlPrefix: /admin/online
|
||||||
# 在线表单业务数据上传资源路径
|
# 在线表单业务数据上传资源路径
|
||||||
@@ -169,10 +171,53 @@ management:
|
|||||||
servlet:
|
servlet:
|
||||||
context-path: "/"
|
context-path: "/"
|
||||||
|
|
||||||
|
aj:
|
||||||
|
captcha:
|
||||||
|
# 缓存local/redis..
|
||||||
|
cache-type: redis
|
||||||
|
# 验证码类型default两种都实例化。
|
||||||
|
type: default
|
||||||
|
# 右下角水印文字(我的水印)
|
||||||
|
water-mark: 我的水印
|
||||||
|
#点选字体样式 默认Font.BOLD
|
||||||
|
font-style: 1
|
||||||
|
#点选字体字体大小
|
||||||
|
font-size: 25
|
||||||
|
# 支持项目路径,以classpath:开头,取resource目录下路径,例:classpath:images/jigsaw
|
||||||
|
jigsaw: classpath:images/jigsaw
|
||||||
|
# 支持项目路径,以classpath:开头,取resource目录下路径,例:classpath:images/pic-click
|
||||||
|
pic-click: classpath:images/pic-click
|
||||||
|
history-data-clear-enable: false
|
||||||
|
# 接口请求次数一分钟限制是否开启 true|false
|
||||||
|
req-frequency-limit-enable: false
|
||||||
|
# 验证失败5次,get接口锁定
|
||||||
|
req-get-lock-limit: 5
|
||||||
|
# 验证失败后,锁定时间间隔,s
|
||||||
|
req-get-lock-seconds: 360
|
||||||
|
# get接口一分钟内请求数限制
|
||||||
|
req-get-minute-limit: 30
|
||||||
|
# check接口一分钟内请求数限制
|
||||||
|
req-check-minute-limit: 30
|
||||||
|
# verify接口一分钟内请求数限制
|
||||||
|
req-verify-minute-limit: 60
|
||||||
|
# 校验滑动拼图允许误差偏移量(默认5像素)
|
||||||
|
slip-offset: 5
|
||||||
|
# aes加密坐标开启或者禁用(true|false)
|
||||||
|
aes-status: true
|
||||||
|
# 滑动干扰项(0/1/2)
|
||||||
|
interference-options: 2
|
||||||
|
|
||||||
# 开发数据库相关配置
|
# 开发数据库相关配置
|
||||||
---
|
---
|
||||||
spring:
|
spring:
|
||||||
profiles: dev
|
profiles: dev
|
||||||
|
# aj-captcha 对redis缓存的依赖。
|
||||||
|
redis:
|
||||||
|
database: 2
|
||||||
|
host: localhost
|
||||||
|
port: 6379
|
||||||
|
password: ''
|
||||||
|
timeout: 60000
|
||||||
datasource:
|
datasource:
|
||||||
type: com.alibaba.druid.pool.DruidDataSource
|
type: com.alibaba.druid.pool.DruidDataSource
|
||||||
druid:
|
druid:
|
||||||
@@ -233,6 +278,13 @@ sequence:
|
|||||||
---
|
---
|
||||||
spring:
|
spring:
|
||||||
profiles: product
|
profiles: product
|
||||||
|
# aj-captcha 对redis缓存的依赖。
|
||||||
|
redis:
|
||||||
|
database: 2
|
||||||
|
host: localhost
|
||||||
|
port: 6379
|
||||||
|
password: ''
|
||||||
|
timeout: 60000
|
||||||
datasource:
|
datasource:
|
||||||
type: com.alibaba.druid.pool.DruidDataSource
|
type: com.alibaba.druid.pool.DruidDataSource
|
||||||
druid:
|
druid:
|
||||||
|
|||||||
|
After Width: | Height: | Size: 103 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 39 KiB |
@@ -1,329 +0,0 @@
|
|||||||
<html>
|
|
||||||
<head>
|
|
||||||
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
|
||||||
<style type="text/css">
|
|
||||||
.b1{white-space-collapsing:preserve;}
|
|
||||||
.t1{border-collapse:collapse;border-spacing:0;}
|
|
||||||
.r1{height:23.25pt;}
|
|
||||||
.r2{height:18.75pt;}
|
|
||||||
.r3{height:17.25pt;}
|
|
||||||
.r4{height:15.0pt;}
|
|
||||||
.r5{height:14.25pt;}
|
|
||||||
.r6{height:21.0pt;}
|
|
||||||
.r7{height:18.0pt;}
|
|
||||||
.r8{height:15.75pt;}
|
|
||||||
.r9{height:19.5pt;}
|
|
||||||
.r10{height:17.0pt;}
|
|
||||||
.r11{height:20.25pt;}
|
|
||||||
.r12{height:28.5pt;}
|
|
||||||
.c1{white-space:pre-wrap;text-align:center;border-bottom:thin solid black;font-weight:bold;color: #dd0806; font-size:18pt;}
|
|
||||||
.c2{white-space:pre-wrap;text-align:center;background-color:#ffff00;border-top:thin solid black;border-left:thin solid black;color: #dd0806; font-size:12pt;}
|
|
||||||
.c3{white-space:pre-wrap;text-align:center;border-top:thin solid black;border-right:thin solid black;border-left:thin solid black;color: #dd0806; font-size:12pt;}
|
|
||||||
.c4{white-space:pre-wrap;text-align:center;border-top:thin solid black;border-right:thin solid black;border-bottom:thin solid black;border-left:thin solid black;color: #dd0806; font-size:12pt;}
|
|
||||||
.c5{white-space:pre-wrap;text-align:center;color: #dd0806; font-size:10pt;}
|
|
||||||
.c6{white-space:pre-wrap;text-align:center;border-top:thin solid black;border-right:thin solid black;border-bottom:thin solid black;color: #dd0806; font-size:10pt;}
|
|
||||||
.c7{white-space:pre-wrap;text-align:center;border-top:thin solid black;border-right:thin solid black;border-bottom:thin solid black;border-left:thin solid black;color: #dd0806; font-size:10pt;}
|
|
||||||
.c8{white-space:pre-wrap;text-align:center;border-top:thin solid black;border-right:thin solid black;border-bottom:thin solid black;border-left:thin solid black;font-size:9pt;}
|
|
||||||
.c9{white-space:pre-wrap;text-align:center;background-color:#ffff00;border-top:thin solid black;border-right:thin solid black;border-bottom:thin solid black;border-left:thin solid black;color: #dd0806; font-size:11pt;}
|
|
||||||
.c10{white-space:pre-wrap;text-align:center;border-right:thin solid black;border-left:thin solid black;font-size:10pt;}
|
|
||||||
.c11{white-space:pre-wrap;text-align:center;border-right:thin solid black;border-bottom:thin solid black;border-left:thin solid black;font-size:10pt;}
|
|
||||||
.c12{white-space:pre-wrap;text-align:center;border-top:thin solid black;border-right:thin solid black;border-left:thin solid black;font-size:10pt;}
|
|
||||||
.c13{white-space:pre-wrap;text-align:center;border-top:thin solid black;border-right:thin solid black;border-bottom:thin solid black;border-left:thin solid black;font-size:8pt;}
|
|
||||||
.c14{white-space:pre-wrap;text-align:center;border-top:thin solid black;border-right:thin solid black;border-left:thin solid black;font-size:8pt;}
|
|
||||||
.c15{white-space:pre-wrap;text-align:center;border-top:thin solid black;border-right:thin solid black;border-bottom:thin solid black;border-left:thin solid black;font-size:10pt;}
|
|
||||||
.c16{white-space:pre-wrap;text-align:center;background-color:#ffff00;border-top:thin solid black;border-right:thin solid black;border-bottom:thin solid black;border-left:thin solid black;font-size:11pt;}
|
|
||||||
.c17{white-space:pre-wrap;text-align:center;border-top:thin solid black;border-right:thin solid black;border-bottom:thin solid black;border-left:thin solid black;font-size:11pt;}
|
|
||||||
.c18{white-space:pre-wrap;text-align:center;border-top:thin solid black;border-right:thin solid black;border-bottom:thin solid black;border-left:thin solid black;font-size:6pt;}
|
|
||||||
.c19{white-space:pre-wrap;text-align:center;border-right:thin solid black;border-bottom:thin solid black;border-left:thin solid black;font-size:6pt;}
|
|
||||||
.c20{white-space:pre-wrap;text-align:center;border-top:thin solid black;border-right:thin solid black;border-left:thin solid black;font-weight:bold;font-size:6pt;}
|
|
||||||
.c21{white-space:pre-wrap;text-align:center;border-top:thin solid black;border-right:thin solid black;border-left:thin solid black;font-weight:bold;font-size:8pt;}
|
|
||||||
.c22{white-space:pre-wrap;text-align:left;border-top:thin solid black;font-weight:bold;font-size:9pt;}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body class="b1">
|
|
||||||
<table class="t1">
|
|
||||||
<colgroup>
|
|
||||||
<col width="40">
|
|
||||||
<col width="40">
|
|
||||||
<col width="57">
|
|
||||||
<col width="57">
|
|
||||||
<col width="57">
|
|
||||||
<col width="57">
|
|
||||||
<col width="57">
|
|
||||||
<col width="56">
|
|
||||||
<col width="59">
|
|
||||||
<col width="57">
|
|
||||||
<col width="57">
|
|
||||||
<col width="57">
|
|
||||||
<col width="57">
|
|
||||||
<col width="57">
|
|
||||||
<col width="57">
|
|
||||||
<col width="57">
|
|
||||||
</colgroup>
|
|
||||||
<tbody>
|
|
||||||
<tr class="r1">
|
|
||||||
<td class="c1" colspan="16">天津公安警官职业学院2017—2018学年度第一学期课程表</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="r1">
|
|
||||||
<td class="c2" colspan="2" rowspan="2">班 级</td>
|
|
||||||
<td class="c3" colspan="3">星期一</td>
|
|
||||||
<td class="c4" colspan="3">星期二</td>
|
|
||||||
<td class="c4" colspan="3">星期三</td>
|
|
||||||
<td class="c4" colspan="3">星期四</td>
|
|
||||||
<td class="c4" colspan="2">星期五</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="r2">
|
|
||||||
<td class="c5">第1节</td>
|
|
||||||
<td class="c5">第2节</td>
|
|
||||||
<td class="c5">第3节</td>
|
|
||||||
<td class="c6">第1节</td>
|
|
||||||
<td class="c7">第2节</td>
|
|
||||||
<td class="c7">第3节</td>
|
|
||||||
<td class="c7">第1节</td>
|
|
||||||
<td class="c7">第2节</td>
|
|
||||||
<td class="c7">第3节</td>
|
|
||||||
<td class="c7">第1节</td>
|
|
||||||
<td class="c7">第2节</td>
|
|
||||||
<td class="c7">第3节</td>
|
|
||||||
<td class="c7">第1节</td>
|
|
||||||
<td class="c7">第2节</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="r2">
|
|
||||||
<td class="c8" rowspan="6">16 级 刑 事 技 术 班 </td>
|
|
||||||
<td class="c9" rowspan="2">课程</td>
|
|
||||||
<td class="c10" rowspan="2">刑法</td>
|
|
||||||
<td class="c10" rowspan="6">自习</td>
|
|
||||||
<td class="c11">刑事图像</td>
|
|
||||||
<td class="c12" rowspan="2">民 法(选修)</td>
|
|
||||||
<td class="c13">派出所工作</td>
|
|
||||||
<td class="c12" rowspan="2">刑事图像</td>
|
|
||||||
<td class="c12" rowspan="2">法医学</td>
|
|
||||||
<td class="c14" rowspan="2">派出所工作</td>
|
|
||||||
<td class="c15">法医学</td>
|
|
||||||
<td class="c12" rowspan="2">国内安全保卫</td>
|
|
||||||
<td class="c12" rowspan="2">体能</td>
|
|
||||||
<td class="c12" rowspan="6">自习</td>
|
|
||||||
<td class="c12" rowspan="2">刑事技术总论</td>
|
|
||||||
<td class="c12" rowspan="2">刑法</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="r3">
|
|
||||||
<td class="c11">自习</td>
|
|
||||||
<td class="c11">自习</td>
|
|
||||||
<td class="c11">自习</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="r3">
|
|
||||||
<td class="c16" rowspan="2">教师</td>
|
|
||||||
<td class="c12" rowspan="2">曾岚</td>
|
|
||||||
<td class="c15">陈磊</td>
|
|
||||||
<td class="c12" rowspan="2">邵刚</td>
|
|
||||||
<td class="c15">杨丽伟</td>
|
|
||||||
<td class="c12" rowspan="2">陈磊</td>
|
|
||||||
<td class="c12" rowspan="2">于辉</td>
|
|
||||||
<td class="c12" rowspan="2">杨丽伟</td>
|
|
||||||
<td class="c15">于辉</td>
|
|
||||||
<td class="c12" rowspan="2">朱学强</td>
|
|
||||||
<td class="c12" rowspan="2">张付海</td>
|
|
||||||
<td class="c12" rowspan="2">王 伟(刑技)</td>
|
|
||||||
<td class="c12" rowspan="2">曾岚</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="r3">
|
|
||||||
<td class="c11"> </td>
|
|
||||||
<td class="c11"> </td>
|
|
||||||
<td class="c11"> </td>
|
|
||||||
</tr>
|
|
||||||
<tr class="r3">
|
|
||||||
<td class="c16" rowspan="2">教室</td>
|
|
||||||
<td class="c15" rowspan="2">206</td>
|
|
||||||
<td class="c15">206</td>
|
|
||||||
<td class="c15" rowspan="2">206</td>
|
|
||||||
<td class="c15">206</td>
|
|
||||||
<td class="c15" rowspan="2">206</td>
|
|
||||||
<td class="c15" rowspan="2">206</td>
|
|
||||||
<td class="c15" rowspan="2">206</td>
|
|
||||||
<td class="c15">206</td>
|
|
||||||
<td class="c15" rowspan="2">206</td>
|
|
||||||
<td class="c15" rowspan="2">操场</td>
|
|
||||||
<td class="c15" rowspan="2">206</td>
|
|
||||||
<td class="c15" rowspan="2">206</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="r4">
|
|
||||||
<td class="c15"> </td>
|
|
||||||
<td class="c11"> </td>
|
|
||||||
<td class="c11"> </td>
|
|
||||||
</tr>
|
|
||||||
<tr class="r2">
|
|
||||||
<td class="c8" rowspan="6">16 级 刑 事 侦 查 </td>
|
|
||||||
<td class="c17" rowspan="2">课程</td>
|
|
||||||
<td class="c12" rowspan="6">自习</td>
|
|
||||||
<td class="c12" rowspan="2">侦查措施</td>
|
|
||||||
<td class="c12" rowspan="2">经济案件侦查</td>
|
|
||||||
<td class="c14" rowspan="2">公安信息化</td>
|
|
||||||
<td class="c14" rowspan="2">公安信息化</td>
|
|
||||||
<td class="c12" rowspan="2">刑法</td>
|
|
||||||
<td class="c12" rowspan="2">体能</td>
|
|
||||||
<td class="c12" rowspan="6">自习</td>
|
|
||||||
<td class="c12" rowspan="2">痕迹检验</td>
|
|
||||||
<td class="c12" rowspan="2">刑法</td>
|
|
||||||
<td class="c12" rowspan="2">国内安全保卫</td>
|
|
||||||
<td class="c18">经济案件侦查</td>
|
|
||||||
<td class="c12" rowspan="2">痕迹检验</td>
|
|
||||||
<td class="c12" rowspan="2">民 法(选修)</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="r3">
|
|
||||||
<td class="c11">侦查措施</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="r3">
|
|
||||||
<td class="c17" rowspan="2">教师</td>
|
|
||||||
<td class="c12" rowspan="2">徐宏涛</td>
|
|
||||||
<td class="c12" rowspan="2">张静</td>
|
|
||||||
<td class="c12" rowspan="2">赵晓松</td>
|
|
||||||
<td class="c12" rowspan="2">赵晓松</td>
|
|
||||||
<td class="c12" rowspan="2">王骏强</td>
|
|
||||||
<td class="c12" rowspan="2">张付海</td>
|
|
||||||
<td class="c12" rowspan="2">郭海川</td>
|
|
||||||
<td class="c12" rowspan="2">王骏强</td>
|
|
||||||
<td class="c12" rowspan="2">朱学强</td>
|
|
||||||
<td class="c15">张静</td>
|
|
||||||
<td class="c12" rowspan="2">郭海川</td>
|
|
||||||
<td class="c12" rowspan="2">邵刚</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="r3">
|
|
||||||
<td class="c11">徐宏涛</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="r3">
|
|
||||||
<td class="c17" rowspan="2">教室</td>
|
|
||||||
<td class="c15" rowspan="2">202</td>
|
|
||||||
<td class="c15" rowspan="2">202</td>
|
|
||||||
<td class="c15" rowspan="2">3号机房</td>
|
|
||||||
<td class="c15" rowspan="2">3号机房</td>
|
|
||||||
<td class="c15" rowspan="2">202</td>
|
|
||||||
<td class="c15" rowspan="2">操场</td>
|
|
||||||
<td class="c15" rowspan="2">202</td>
|
|
||||||
<td class="c15" rowspan="2">202</td>
|
|
||||||
<td class="c15" rowspan="2">202</td>
|
|
||||||
<td class="c15" rowspan="2">202</td>
|
|
||||||
<td class="c15" rowspan="2">202</td>
|
|
||||||
<td class="c15" rowspan="2">202</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="r5">
|
|
||||||
</tr>
|
|
||||||
<tr class="r2">
|
|
||||||
<td class="c8" rowspan="6">16 级 治 安 管 理 班 </td>
|
|
||||||
<td class="c17" rowspan="2">课程</td>
|
|
||||||
<td class="c12" rowspan="2">刑事技术</td>
|
|
||||||
<td class="c12" rowspan="2">体能</td>
|
|
||||||
<td class="c12" rowspan="2">刑事技术</td>
|
|
||||||
<td class="c12" rowspan="2">治安秩序管理</td>
|
|
||||||
<td class="c12" rowspan="2">刑事侦查概论</td>
|
|
||||||
<td class="c12" rowspan="2">刑法</td>
|
|
||||||
<td class="c18">群众工作与纠纷调解</td>
|
|
||||||
<td class="c12" rowspan="2">群众工作与纠纷调解</td>
|
|
||||||
<td class="c12" rowspan="2">公共关系(选修)</td>
|
|
||||||
<td class="c12" rowspan="2">刑事侦查概论</td>
|
|
||||||
<td class="c12" rowspan="2">刑法</td>
|
|
||||||
<td class="c12" rowspan="6">自习</td>
|
|
||||||
<td class="c12" rowspan="6">自习</td>
|
|
||||||
<td class="c12" rowspan="6">自习</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="r6">
|
|
||||||
<td class="c19">q</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="r7">
|
|
||||||
<td class="c17" rowspan="2">教师</td>
|
|
||||||
<td class="c12" rowspan="2">郭海川 韩易浦</td>
|
|
||||||
<td class="c12" rowspan="2">张付海</td>
|
|
||||||
<td class="c12" rowspan="2">郭海川 韩易浦</td>
|
|
||||||
<td class="c12" rowspan="2">翟政亮</td>
|
|
||||||
<td class="c12" rowspan="2">邵妍</td>
|
|
||||||
<td class="c12" rowspan="2">薛强</td>
|
|
||||||
<td class="c15">刘晓鹏</td>
|
|
||||||
<td class="c12" rowspan="2">刘晓鹏</td>
|
|
||||||
<td class="c12" rowspan="2">尚欣</td>
|
|
||||||
<td class="c12" rowspan="2">邵妍</td>
|
|
||||||
<td class="c12" rowspan="2">薛强</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="r7">
|
|
||||||
<td class="c11">翟政亮</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="r3">
|
|
||||||
<td class="c17" rowspan="2">教室</td>
|
|
||||||
<td class="c15" rowspan="2">218</td>
|
|
||||||
<td class="c15" rowspan="2">操场</td>
|
|
||||||
<td class="c15" rowspan="2">218</td>
|
|
||||||
<td class="c15" rowspan="2">218</td>
|
|
||||||
<td class="c15" rowspan="2">218</td>
|
|
||||||
<td class="c15" rowspan="2">218</td>
|
|
||||||
<td class="c15" rowspan="2">218</td>
|
|
||||||
<td class="c15" rowspan="2">218</td>
|
|
||||||
<td class="c15" rowspan="2">218</td>
|
|
||||||
<td class="c15" rowspan="2">218</td>
|
|
||||||
<td class="c15" rowspan="2">218</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="r8">
|
|
||||||
</tr>
|
|
||||||
<tr class="r2">
|
|
||||||
<td class="c13" rowspan="6">16 网 络 安 全 监 察 1 班</td>
|
|
||||||
<td class="c17" rowspan="2">课程</td>
|
|
||||||
<td class="c12" rowspan="2">应用写作</td>
|
|
||||||
<td class="c14" rowspan="2">数据库系统应用</td>
|
|
||||||
<td class="c20" rowspan="2">周二中午:计算机安全管理及实用技术</td>
|
|
||||||
<td class="c13">刑事诉讼法</td>
|
|
||||||
<td class="c21" rowspan="2">周一中午:数据库系统应用</td>
|
|
||||||
<td class="c12" rowspan="2">民法</td>
|
|
||||||
<td class="c12" rowspan="2">体育</td>
|
|
||||||
<td class="c12" rowspan="2">VB语言程序设计</td>
|
|
||||||
<td class="c12" rowspan="6">选修</td>
|
|
||||||
<td class="c12" rowspan="2">VB语言程序设计</td>
|
|
||||||
<td class="c12" rowspan="2">刑事诉讼法</td>
|
|
||||||
<td class="c12" rowspan="6">选修</td>
|
|
||||||
<td class="c15">应用写作</td>
|
|
||||||
<td class="c12" rowspan="2">犯罪心理</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="r2">
|
|
||||||
<td class="c11">民法</td>
|
|
||||||
<td class="c11">犯罪心理</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="r9">
|
|
||||||
<td class="c17" rowspan="2">教师</td>
|
|
||||||
<td class="c12" rowspan="2">关利</td>
|
|
||||||
<td class="c12" rowspan="2">杨斌</td>
|
|
||||||
<td class="c12" rowspan="2">赵晓松</td>
|
|
||||||
<td class="c15">王伟</td>
|
|
||||||
<td class="c12" rowspan="2">杨斌</td>
|
|
||||||
<td class="c12" rowspan="2">李静</td>
|
|
||||||
<td class="c12" rowspan="2">程军</td>
|
|
||||||
<td class="c12" rowspan="2">赵伟</td>
|
|
||||||
<td class="c12" rowspan="2">赵伟</td>
|
|
||||||
<td class="c12" rowspan="2">王伟</td>
|
|
||||||
<td class="c15">关利</td>
|
|
||||||
<td class="c12" rowspan="2">张学林</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="r10">
|
|
||||||
<td class="c11">李静</td>
|
|
||||||
<td class="c11">张学林</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="r3">
|
|
||||||
<td class="c17" rowspan="2">教室</td>
|
|
||||||
<td class="c15" rowspan="2">东阶梯</td>
|
|
||||||
<td class="c15" rowspan="2">2号机房</td>
|
|
||||||
<td class="c15" rowspan="2">主楼201</td>
|
|
||||||
<td class="c15" rowspan="2">101</td>
|
|
||||||
<td class="c15" rowspan="2">2号机房</td>
|
|
||||||
<td class="c15" rowspan="2">101</td>
|
|
||||||
<td class="c15" rowspan="2">操场</td>
|
|
||||||
<td class="c15" rowspan="2">3号机房</td>
|
|
||||||
<td class="c15" rowspan="2">3号机房</td>
|
|
||||||
<td class="c15" rowspan="2">101</td>
|
|
||||||
<td class="c15">东阶梯</td>
|
|
||||||
<td class="c15" rowspan="2">主楼201</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="r11">
|
|
||||||
<td class="c11">主楼201</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="r12">
|
|
||||||
<td class="c22" colspan="16">注:1、课程一栏中有两科次的,上面的课程单周上课,下面的课程双周上课。2、每天上课时间:上午第1节8:30至9:55;第2节10:15至11:40;中午上课时间12:30至13:55;下午第3节14:00至15:25。</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -18,22 +18,16 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.guava</groupId>
|
<groupId>com.google.guava</groupId>
|
||||||
<artifactId>guava</artifactId>
|
<artifactId>guava</artifactId>
|
||||||
|
<version>${guava.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>commons-lang3</artifactId>
|
<artifactId>commons-lang3</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>commons-codec</groupId>
|
|
||||||
<artifactId>commons-codec</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>commons-io</groupId>
|
<groupId>commons-io</groupId>
|
||||||
<artifactId>commons-io</artifactId>
|
<artifactId>commons-io</artifactId>
|
||||||
</dependency>
|
<version>${commons-io.version}</version>
|
||||||
<dependency>
|
|
||||||
<groupId>commons-fileupload</groupId>
|
|
||||||
<artifactId>commons-fileupload</artifactId>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.httpcomponents</groupId>
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
@@ -42,6 +36,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>joda-time</groupId>
|
<groupId>joda-time</groupId>
|
||||||
<artifactId>joda-time</artifactId>
|
<artifactId>joda-time</artifactId>
|
||||||
|
<version>${joda-time.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.orangeforms.common.core.advice;
|
package com.orangeforms.common.core.advice;
|
||||||
|
|
||||||
|
import com.orangeforms.common.core.util.MyDateUtil;
|
||||||
import org.springframework.beans.propertyeditors.CustomDateEditor;
|
import org.springframework.beans.propertyeditors.CustomDateEditor;
|
||||||
import org.springframework.web.bind.WebDataBinder;
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||||
@@ -25,6 +26,6 @@ public class MyControllerAdvice {
|
|||||||
@InitBinder
|
@InitBinder
|
||||||
public void initBinder(WebDataBinder binder) {
|
public void initBinder(WebDataBinder binder) {
|
||||||
binder.registerCustomEditor(Date.class,
|
binder.registerCustomEditor(Date.class,
|
||||||
new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"), false));
|
new CustomDateEditor(new SimpleDateFormat(MyDateUtil.COMMON_SHORT_DATETIME_FORMAT), false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,4 +24,12 @@ public @interface EnableDataPerm {
|
|||||||
* @return 被排序的方法名称数据。
|
* @return 被排序的方法名称数据。
|
||||||
*/
|
*/
|
||||||
String[] excluseMethodName() default {};
|
String[] excluseMethodName() default {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 必须包含能看用户自己数据的数据过滤条件,如果当前用户的数据过滤中,没有DataPermRuleType.TYPE_USER_ONLY,
|
||||||
|
* 在进行数据权限过滤时,会自动包含该权限。
|
||||||
|
*
|
||||||
|
* @return 是否必须包含DataPermRuleType.TYPE_USER_ONLY类型的数据权限。
|
||||||
|
*/
|
||||||
|
boolean mustIncludeUserRule() default false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.orangeforms.common.core.base.service;
|
package com.orangeforms.common.core.base.service;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import com.baomidou.mybatisplus.annotation.*;
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||||
@@ -45,6 +46,7 @@ import static java.util.stream.Collectors.*;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public abstract class BaseService<M, K extends Serializable> extends ServiceImpl<BaseDaoMapper<M>, M> implements IBaseService<M, K> {
|
public abstract class BaseService<M, K extends Serializable> extends ServiceImpl<BaseDaoMapper<M>, M> implements IBaseService<M, K> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 当前Service关联的主Model实体对象的Class。
|
* 当前Service关联的主Model实体对象的Class。
|
||||||
*/
|
*/
|
||||||
@@ -435,6 +437,53 @@ public abstract class BaseService<M, K extends Serializable> extends ServiceImpl
|
|||||||
return mapper().selectCount(new QueryWrapper<M>().in(column, inFilterValues)) == inFilterValues.size();
|
return mapper().selectCount(new QueryWrapper<M>().in(column, inFilterValues)) == inFilterValues.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R> List<R> notExist(String filterField, Set<R> filterSet, boolean findFirst) {
|
||||||
|
List<R> notExistIdList = new LinkedList<>();
|
||||||
|
String columnName = this.safeMapToColumnName(filterField);
|
||||||
|
int start = 0;
|
||||||
|
int count = 1000;
|
||||||
|
if (filterSet.size() > count) {
|
||||||
|
outloop:
|
||||||
|
do {
|
||||||
|
int end = Math.min(filterSet.size(), start + count);
|
||||||
|
List<R> subFilterList = CollUtil.sub(filterSet, start, end);
|
||||||
|
QueryWrapper<M> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.in(columnName, subFilterList);
|
||||||
|
queryWrapper.select(columnName);
|
||||||
|
Set<Object> existIdSet = mapper().selectList(queryWrapper).stream()
|
||||||
|
.map(c -> ReflectUtil.getFieldValue(c, filterField)).collect(toSet());
|
||||||
|
for (R filterData : subFilterList) {
|
||||||
|
if (!existIdSet.contains(filterData)) {
|
||||||
|
notExistIdList.add(filterData);
|
||||||
|
if (findFirst) {
|
||||||
|
break outloop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (end == filterSet.size()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
start += count;
|
||||||
|
} while (true);
|
||||||
|
} else {
|
||||||
|
QueryWrapper<M> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.in(columnName, filterSet);
|
||||||
|
queryWrapper.select(columnName);
|
||||||
|
Set<Object> existIdSet = mapper().selectList(queryWrapper).stream()
|
||||||
|
.map(c -> ReflectUtil.getFieldValue(c, filterField)).collect(toSet());
|
||||||
|
for (R filterData : filterSet) {
|
||||||
|
if (!existIdSet.contains(filterData)) {
|
||||||
|
notExistIdList.add(filterData);
|
||||||
|
if (findFirst) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return notExistIdList;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 返回符合主键 in (idValues) 条件的所有数据。
|
* 返回符合主键 in (idValues) 条件的所有数据。
|
||||||
*
|
*
|
||||||
@@ -637,6 +686,23 @@ public abstract class BaseService<M, K extends Serializable> extends ServiceImpl
|
|||||||
return CallResult.ok();
|
return CallResult.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public CallResult verifyRelatedData(M data) {
|
||||||
|
if (data == null) {
|
||||||
|
return CallResult.ok();
|
||||||
|
}
|
||||||
|
Object id = ReflectUtil.getFieldValue(data, idFieldName);
|
||||||
|
if (id == null) {
|
||||||
|
return this.verifyRelatedData(data, null);
|
||||||
|
}
|
||||||
|
M originalData = this.getById((K) id);
|
||||||
|
if (originalData == null) {
|
||||||
|
return CallResult.error("数据验证失败,源数据不存在!");
|
||||||
|
}
|
||||||
|
return this.verifyRelatedData(data, originalData);
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public CallResult verifyRelatedData(List<M> dataList) {
|
public CallResult verifyRelatedData(List<M> dataList) {
|
||||||
@@ -673,6 +739,130 @@ public abstract class BaseService<M, K extends Serializable> extends ServiceImpl
|
|||||||
return CallResult.ok();
|
return CallResult.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R> CallResult verifyImportForConstDict(List<M> dataList, String fieldName, Function<M, R> idGetter) {
|
||||||
|
if (CollUtil.isEmpty(dataList)) {
|
||||||
|
return CallResult.ok();
|
||||||
|
}
|
||||||
|
// 这里均为内部调用方法,因此出现任何错误均为代码BUG,所以我们会及时抛出异常。
|
||||||
|
Field field = ReflectUtil.getField(modelClass, fieldName);
|
||||||
|
if (field == null) {
|
||||||
|
throw new MyRuntimeException("FieldName [" + fieldName + "] doesn't exist.");
|
||||||
|
}
|
||||||
|
RelationConstDict relationConstDict = field.getAnnotation(RelationConstDict.class);
|
||||||
|
if (relationConstDict == null) {
|
||||||
|
throw new MyRuntimeException("FieldName [" + fieldName + "] doesn't have RelationConstDict.");
|
||||||
|
}
|
||||||
|
Method m = ReflectUtil.getMethodByName(relationConstDict.constantDictClass(), "isValid");
|
||||||
|
for (M data : dataList) {
|
||||||
|
R id = idGetter.apply(data);
|
||||||
|
if (id != null) {
|
||||||
|
boolean ok = ReflectUtil.invokeStatic(m, id);
|
||||||
|
if (!ok) {
|
||||||
|
String errorMessage = String.format("数据验证失败,字段 [%s] 存在无效的常量字典值 [%s]!",
|
||||||
|
relationConstDict.masterIdField(), id);
|
||||||
|
return CallResult.error(errorMessage, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CallResult.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R> CallResult verifyImportForDict(List<M> dataList, String fieldName, Function<M, R> idGetter) {
|
||||||
|
if (CollUtil.isEmpty(dataList)) {
|
||||||
|
return CallResult.ok();
|
||||||
|
}
|
||||||
|
// 这里均为内部调用方法,因此出现任何错误均为代码BUG,所以我们会及时抛出异常。
|
||||||
|
Field field = ReflectUtil.getField(modelClass, fieldName);
|
||||||
|
if (field == null) {
|
||||||
|
throw new MyRuntimeException("FieldName [" + fieldName + "] doesn't exist.");
|
||||||
|
}
|
||||||
|
RelationDict relationDict = field.getAnnotation(RelationDict.class);
|
||||||
|
if (relationDict == null) {
|
||||||
|
throw new MyRuntimeException("FieldName [" + fieldName + "] doesn't have RelationDict.");
|
||||||
|
}
|
||||||
|
BaseService<Object, Serializable> service = ApplicationContextHolder.getBean(
|
||||||
|
StringUtils.uncapitalize(relationDict.slaveServiceName()));
|
||||||
|
Set<Object> dictIdSet = service.getAllList().stream()
|
||||||
|
.map(c -> ReflectUtil.getFieldValue(c, relationDict.slaveIdField())).collect(toSet());
|
||||||
|
for (M data : dataList) {
|
||||||
|
R id = idGetter.apply(data);
|
||||||
|
if (id != null && !dictIdSet.contains(id)) {
|
||||||
|
String errorMessage = String.format("数据验证失败,字段 [%s] 存在无效的字典表字典值 [%s]!",
|
||||||
|
relationDict.masterIdField(), id);
|
||||||
|
return CallResult.error(errorMessage, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CallResult.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R> CallResult verifyImportForDatasourceDict(List<M> dataList, String fieldName, Function<M, R> idGetter) {
|
||||||
|
if (CollUtil.isEmpty(dataList)) {
|
||||||
|
return CallResult.ok();
|
||||||
|
}
|
||||||
|
// 这里均为内部调用方法,因此出现任何错误均为代码BUG,所以我们会及时抛出异常。
|
||||||
|
Field field = ReflectUtil.getField(modelClass, fieldName);
|
||||||
|
if (field == null) {
|
||||||
|
throw new MyRuntimeException("FieldName [" + fieldName + "] doesn't exist.");
|
||||||
|
}
|
||||||
|
RelationDict relationDict = field.getAnnotation(RelationDict.class);
|
||||||
|
if (relationDict == null) {
|
||||||
|
throw new MyRuntimeException("FieldName [" + fieldName + "] doesn't have RelationDict.");
|
||||||
|
}
|
||||||
|
// 验证数据源字典Id,由于被依赖的数据表,可能包含大量业务数据,因此还是分批做存在性比对更为高效。
|
||||||
|
Set<R> idSet = dataList.stream()
|
||||||
|
.filter(c -> idGetter.apply(c) != null).map(idGetter).collect(toSet());
|
||||||
|
if (CollUtil.isNotEmpty(idSet)) {
|
||||||
|
BaseService<Object, Serializable> slaveService = ApplicationContextHolder.getBean(
|
||||||
|
StringUtils.uncapitalize(relationDict.slaveServiceName()));
|
||||||
|
List<R> notExistIdList = slaveService.notExist(relationDict.slaveIdField(), idSet, true);
|
||||||
|
if (CollUtil.isNotEmpty(notExistIdList)) {
|
||||||
|
R notExistId = notExistIdList.get(0);
|
||||||
|
String errorMessage = String.format("数据验证失败,字段 [%s] 存在无效的数据源表字典值 [%s]!",
|
||||||
|
relationDict.masterIdField(), notExistId);
|
||||||
|
M data = dataList.stream()
|
||||||
|
.filter(c -> ObjectUtil.equals(idGetter.apply(c), notExistId)).findFirst().orElse(null);
|
||||||
|
return CallResult.error(errorMessage, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CallResult.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R> CallResult verifyImportForOneToOneRelation(List<M> dataList, String fieldName, Function<M, R> idGetter) {
|
||||||
|
if (CollUtil.isEmpty(dataList)) {
|
||||||
|
return CallResult.ok();
|
||||||
|
}
|
||||||
|
// 这里均为内部调用方法,因此出现任何错误均为代码BUG,所以我们会及时抛出异常。
|
||||||
|
Field field = ReflectUtil.getField(modelClass, fieldName);
|
||||||
|
if (field == null) {
|
||||||
|
throw new MyRuntimeException("FieldName [" + fieldName + "] doesn't exist.");
|
||||||
|
}
|
||||||
|
RelationOneToOne relationOneToOne = field.getAnnotation(RelationOneToOne.class);
|
||||||
|
if (relationOneToOne == null) {
|
||||||
|
throw new MyRuntimeException("FieldName [" + fieldName + "] doesn't have RelationOneToOne.");
|
||||||
|
}
|
||||||
|
// 验证一对一关联Id,由于被依赖的数据表,可能包含大量业务数据,因此还是分批做存在性比对更为高效。
|
||||||
|
Set<R> idSet = dataList.stream()
|
||||||
|
.filter(c -> idGetter.apply(c) != null).map(idGetter).collect(toSet());
|
||||||
|
if (CollUtil.isNotEmpty(idSet)) {
|
||||||
|
BaseService<Object, Serializable> slaveService = ApplicationContextHolder.getBean(
|
||||||
|
StringUtils.uncapitalize(relationOneToOne.slaveServiceName()));
|
||||||
|
List<R> notExistIdList = slaveService.notExist(relationOneToOne.slaveIdField(), idSet, true);
|
||||||
|
if (CollUtil.isNotEmpty(notExistIdList)) {
|
||||||
|
R notExistId = notExistIdList.get(0);
|
||||||
|
String errorMessage = String.format("数据验证失败,字段 [%s] 存在无效的一对一关联值 [%s]!",
|
||||||
|
relationOneToOne.masterIdField(), notExistId);
|
||||||
|
M data = dataList.stream()
|
||||||
|
.filter(c -> ObjectUtil.equals(idGetter.apply(c), notExistId)).findFirst().orElse(null);
|
||||||
|
return CallResult.error(errorMessage, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CallResult.ok();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 集成所有与主表实体对象相关的关联数据列表。包括本地和远程服务的一对一、字典、一对多和多对多聚合运算等。
|
* 集成所有与主表实体对象相关的关联数据列表。包括本地和远程服务的一对一、字典、一对多和多对多聚合运算等。
|
||||||
* 也可以根据实际需求,单独调用该函数所包含的各个数据集成函数。
|
* 也可以根据实际需求,单独调用该函数所包含的各个数据集成函数。
|
||||||
@@ -1585,6 +1775,7 @@ public abstract class BaseService<M, K extends Serializable> extends ServiceImpl
|
|||||||
RelationConstDict relationConstDict = f.getAnnotation(RelationConstDict.class);
|
RelationConstDict relationConstDict = f.getAnnotation(RelationConstDict.class);
|
||||||
if (relationConstDict != null) {
|
if (relationConstDict != null) {
|
||||||
RelationStruct relationStruct = new RelationStruct();
|
RelationStruct relationStruct = new RelationStruct();
|
||||||
|
relationStruct.relationConstDict = relationConstDict;
|
||||||
relationStruct.relationField = f;
|
relationStruct.relationField = f;
|
||||||
relationStruct.masterIdField = ReflectUtil.getField(modelClass, relationConstDict.masterIdField());
|
relationStruct.masterIdField = ReflectUtil.getField(modelClass, relationConstDict.masterIdField());
|
||||||
Field dictMapField = ReflectUtil.getField(relationConstDict.constantDictClass(), "DICT_MAP");
|
Field dictMapField = ReflectUtil.getField(relationConstDict.constantDictClass(), "DICT_MAP");
|
||||||
@@ -1806,6 +1997,7 @@ public abstract class BaseService<M, K extends Serializable> extends ServiceImpl
|
|||||||
private BaseService<Object, Serializable> service;
|
private BaseService<Object, Serializable> service;
|
||||||
private BaseDaoMapper<Object> manyToManyMapper;
|
private BaseDaoMapper<Object> manyToManyMapper;
|
||||||
private Map<Object, String> dictMap;
|
private Map<Object, String> dictMap;
|
||||||
|
private RelationConstDict relationConstDict;
|
||||||
private RelationDict relationDict;
|
private RelationDict relationDict;
|
||||||
private RelationOneToOne relationOneToOne;
|
private RelationOneToOne relationOneToOne;
|
||||||
private RelationOneToMany relationOneToMany;
|
private RelationOneToMany relationOneToMany;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import java.io.Serializable;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 所有Service的接口。
|
* 所有Service的接口。
|
||||||
@@ -137,6 +138,17 @@ public interface IBaseService<M, K extends Serializable> extends IService<M>{
|
|||||||
*/
|
*/
|
||||||
<T> boolean existUniqueKeyList(String inFilterField, Set<T> inFilterValues);
|
<T> boolean existUniqueKeyList(String inFilterField, Set<T> inFilterValues);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据过滤字段和过滤集合,返回不存在的数据。
|
||||||
|
*
|
||||||
|
* @param filterField 过滤的Java字段。
|
||||||
|
* @param filterSet 过滤字段数据集合。
|
||||||
|
* @param findFirst 是否找到第一个就返回。
|
||||||
|
* @param <R> 过滤字段类型。
|
||||||
|
* @return filterSet中,在从表中不存在的数据集合。
|
||||||
|
*/
|
||||||
|
<R> List<R> notExist(String filterField, Set<R> filterSet, boolean findFirst);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 返回符合主键 in (idValues) 条件的所有数据。
|
* 返回符合主键 in (idValues) 条件的所有数据。
|
||||||
*
|
*
|
||||||
@@ -269,15 +281,68 @@ public interface IBaseService<M, K extends Serializable> extends IService<M>{
|
|||||||
*/
|
*/
|
||||||
CallResult verifyRelatedData(M data, M originalData);
|
CallResult verifyRelatedData(M data, M originalData);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据最新对象和原有对象的数据对比,判断关联的字典数据和多对一主表数据是否都是合法数据。
|
||||||
|
* 如果data对象中包含主键值,方法内部会获取原有对象值,并进行更新方式的关联数据比对,否则视为新增数据关联对象比对。
|
||||||
|
*
|
||||||
|
* @param data 数据对象。
|
||||||
|
* @return 应答结果对象。
|
||||||
|
*/
|
||||||
|
CallResult verifyRelatedData(M data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据最新对象列表和原有对象列表的数据对比,判断关联的字典数据和多对一主表数据是否都是合法数据。
|
* 根据最新对象列表和原有对象列表的数据对比,判断关联的字典数据和多对一主表数据是否都是合法数据。
|
||||||
* NOTE: BaseService中会给出返回CallResult.ok()的缺省实现。每个业务服务实现类在需要的时候可以重载该方法。
|
* 如果dataList列表中的对象包含主键值,方法内部会获取原有对象值,并进行更新方式的关联数据比对,否则视为新增数据关联对象比对。
|
||||||
*
|
*
|
||||||
* @param dataList 数据对象列表。
|
* @param dataList 数据对象列表。
|
||||||
* @return 应答结果对象。
|
* @return 应答结果对象。
|
||||||
*/
|
*/
|
||||||
CallResult verifyRelatedData(List<M> dataList);
|
CallResult verifyRelatedData(List<M> dataList);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量导入数据列表,对依赖常量字典的数据进行验证。
|
||||||
|
*
|
||||||
|
* @param dataList 批量导入数据列表。
|
||||||
|
* @param fieldName 业务主表中依赖常量字典的字段名。
|
||||||
|
* @param idGetter 获取业务主表中依赖常量字典字段值的Function对象。
|
||||||
|
* @param <R> 业务主表中依赖常量字典的字段类型。
|
||||||
|
* @return 验证结果,如果失败,在data中包含具体的错误对象。
|
||||||
|
*/
|
||||||
|
<R> CallResult verifyImportForConstDict(List<M> dataList, String fieldName, Function<M, R> idGetter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量导入数据列表,对依赖字典表字典的数据进行验证。
|
||||||
|
*
|
||||||
|
* @param dataList 批量导入数据列表。
|
||||||
|
* @param fieldName 业务主表中依赖字典表字典的字段名。
|
||||||
|
* @param idGetter 获取业务主表中依赖字典表字典字段值的Function对象。
|
||||||
|
* @param <R> 业务主表中依赖字典表字典的字段类型。
|
||||||
|
* @return 验证结果,如果失败,在data中包含具体的错误对象。
|
||||||
|
*/
|
||||||
|
<R> CallResult verifyImportForDict(List<M> dataList, String fieldName, Function<M, R> idGetter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量导入数据列表,对依赖数据源字典的数据进行验证。
|
||||||
|
*
|
||||||
|
* @param dataList 批量导入数据列表。
|
||||||
|
* @param fieldName 业务主表中依赖数据源字典的字段名。
|
||||||
|
* @param idGetter 获取业务主表中依赖数据源字典字段值的Function对象。
|
||||||
|
* @param <R> 业务主表中依赖数据源字典的字段类型。
|
||||||
|
* @return 验证结果,如果失败,在data中包含具体的错误对象。
|
||||||
|
*/
|
||||||
|
<R> CallResult verifyImportForDatasourceDict(List<M> dataList, String fieldName, Function<M, R> idGetter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量导入数据列表,对存在一对一关联的数据进行验证。
|
||||||
|
*
|
||||||
|
* @param dataList 批量导入数据列表。
|
||||||
|
* @param fieldName 业务主表中存在一对一关联的字段名。
|
||||||
|
* @param idGetter 获取业务主表中一对一关联字段值的Function对象。
|
||||||
|
* @param <R> 业务主表中存在一对一关联的字段类型。
|
||||||
|
* @return 验证结果,如果失败,在data中包含具体的错误对象。
|
||||||
|
*/
|
||||||
|
<R> CallResult verifyImportForOneToOneRelation(List<M> dataList, String fieldName, Function<M, R> idGetter);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 集成所有与主表实体对象相关的关联数据列表。包括一对一、字典、一对多和多对多聚合运算等。
|
* 集成所有与主表实体对象相关的关联数据列表。包括一对一、字典、一对多和多对多聚合运算等。
|
||||||
* 也可以根据实际需求,单独调用该函数所包含的各个数据集成函数。
|
* 也可以根据实际需求,单独调用该函数所包含的各个数据集成函数。
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.alibaba.fastjson.serializer.SerializerFeature;
|
|||||||
import com.alibaba.fastjson.support.config.FastJsonConfig;
|
import com.alibaba.fastjson.support.config.FastJsonConfig;
|
||||||
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
|
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
|
||||||
import com.orangeforms.common.core.interceptor.MyRequestArgumentResolver;
|
import com.orangeforms.common.core.interceptor.MyRequestArgumentResolver;
|
||||||
|
import com.orangeforms.common.core.util.MyDateUtil;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
@@ -54,7 +55,7 @@ public class CommonWebMvcConfig implements WebMvcConfigurer {
|
|||||||
SerializerFeature.PrettyFormat,
|
SerializerFeature.PrettyFormat,
|
||||||
SerializerFeature.DisableCircularReferenceDetect,
|
SerializerFeature.DisableCircularReferenceDetect,
|
||||||
SerializerFeature.IgnoreNonFieldGetter);
|
SerializerFeature.IgnoreNonFieldGetter);
|
||||||
fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");
|
fastJsonConfig.setDateFormat(MyDateUtil.COMMON_SHORT_DATETIME_FORMAT);
|
||||||
fastConverter.setFastJsonConfig(fastJsonConfig);
|
fastConverter.setFastJsonConfig(fastJsonConfig);
|
||||||
return fastConverter;
|
return fastConverter;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.orangeforms.common.core.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* common-core的配置属性类。
|
||||||
|
*
|
||||||
|
* @author Jerry
|
||||||
|
* @date 2021-06-06
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "common-core")
|
||||||
|
public class CoreProperties {
|
||||||
|
|
||||||
|
public static final String MYSQL_TYPE = "mysql";
|
||||||
|
public static final String POSTGRESQL_TYPE = "postgresql";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据库类型。
|
||||||
|
*/
|
||||||
|
private String databaseType = MYSQL_TYPE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为MySQL。
|
||||||
|
*
|
||||||
|
* @return 是返回true,否则false。
|
||||||
|
*/
|
||||||
|
public boolean isMySql() {
|
||||||
|
return this.databaseType.equals(MYSQL_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为PostgreSQl。
|
||||||
|
*
|
||||||
|
* @return 是返回true,否则false。
|
||||||
|
*/
|
||||||
|
public boolean isPostgresql() {
|
||||||
|
return this.databaseType.equals(POSTGRESQL_TYPE);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -84,4 +84,21 @@ public class CallResult {
|
|||||||
result.errorMessage = errorMessage;
|
result.errorMessage = errorMessage;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建表示验证失败的对象实例。
|
||||||
|
*
|
||||||
|
* @param errorMessage 错误描述。
|
||||||
|
* @param data 附带的数据对象。
|
||||||
|
* @return 验证失败对象实例。
|
||||||
|
*/
|
||||||
|
public static <T> CallResult error(String errorMessage, T data) {
|
||||||
|
CallResult result = new CallResult();
|
||||||
|
result.success = false;
|
||||||
|
result.errorMessage = errorMessage;
|
||||||
|
JSONObject jsonObject = new JSONObject();
|
||||||
|
jsonObject.put("errorData", data);
|
||||||
|
result.data = jsonObject;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
package com.orangeforms.common.core.object;
|
package com.orangeforms.common.core.object;
|
||||||
|
|
||||||
import cn.hutool.core.util.ReflectUtil;
|
import cn.hutool.core.util.ReflectUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.orangeforms.common.core.config.CoreProperties;
|
||||||
import com.orangeforms.common.core.constant.ApplicationConstant;
|
import com.orangeforms.common.core.constant.ApplicationConstant;
|
||||||
import com.orangeforms.common.core.exception.InvalidClassFieldException;
|
import com.orangeforms.common.core.exception.InvalidClassFieldException;
|
||||||
import com.orangeforms.common.core.exception.InvalidDataFieldException;
|
import com.orangeforms.common.core.exception.InvalidDataFieldException;
|
||||||
import com.orangeforms.common.core.exception.InvalidDataModelException;
|
import com.orangeforms.common.core.exception.InvalidDataModelException;
|
||||||
|
import com.orangeforms.common.core.util.ApplicationContextHolder;
|
||||||
import com.orangeforms.common.core.util.MyModelUtil;
|
import com.orangeforms.common.core.util.MyModelUtil;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -27,6 +29,9 @@ import java.util.List;
|
|||||||
@Data
|
@Data
|
||||||
public class MyGroupParam extends ArrayList<MyGroupParam.GroupInfo> {
|
public class MyGroupParam extends ArrayList<MyGroupParam.GroupInfo> {
|
||||||
|
|
||||||
|
private final CoreProperties coreProperties =
|
||||||
|
ApplicationContextHolder.getBean(CoreProperties.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SQL语句的SELECT LIST中,分组字段的返回字段名称列表。
|
* SQL语句的SELECT LIST中,分组字段的返回字段名称列表。
|
||||||
*/
|
*/
|
||||||
@@ -54,15 +59,15 @@ public class MyGroupParam extends ArrayList<MyGroupParam.GroupInfo> {
|
|||||||
StringBuilder groupSelectBuilder = new StringBuilder(128);
|
StringBuilder groupSelectBuilder = new StringBuilder(128);
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (GroupInfo groupInfo : groupParam) {
|
for (GroupInfo groupInfo : groupParam) {
|
||||||
GroupBaseData groupBaseData = parseGroupBaseData(groupInfo, modelClazz);
|
GroupBaseData groupBaseData = groupParam.parseGroupBaseData(groupInfo, modelClazz);
|
||||||
if (StringUtils.isBlank(groupBaseData.tableName)) {
|
if (StrUtil.isBlank(groupBaseData.tableName)) {
|
||||||
throw new InvalidDataModelException(groupBaseData.modelName);
|
throw new InvalidDataModelException(groupBaseData.modelName);
|
||||||
}
|
}
|
||||||
if (StringUtils.isBlank(groupBaseData.columnName)) {
|
if (StrUtil.isBlank(groupBaseData.columnName)) {
|
||||||
throw new InvalidDataFieldException(groupBaseData.modelName, groupBaseData.fieldName);
|
throw new InvalidDataFieldException(groupBaseData.modelName, groupBaseData.fieldName);
|
||||||
}
|
}
|
||||||
processGroupInfo(groupInfo, groupBaseData, groupByBuilder, groupSelectBuilder);
|
groupParam.processGroupInfo(groupInfo, groupBaseData, groupByBuilder, groupSelectBuilder);
|
||||||
String aliasName = StringUtils.isBlank(groupInfo.aliasName) ? groupInfo.fieldName : groupInfo.aliasName;
|
String aliasName = StrUtil.isBlank(groupInfo.aliasName) ? groupInfo.fieldName : groupInfo.aliasName;
|
||||||
// selectGroupFieldList中的元素,目前只是被export操作使用。会根据集合中的元素名称匹配导出表头。
|
// selectGroupFieldList中的元素,目前只是被export操作使用。会根据集合中的元素名称匹配导出表头。
|
||||||
groupParam.selectGroupFieldList.add(aliasName);
|
groupParam.selectGroupFieldList.add(aliasName);
|
||||||
if (++i < groupParam.size()) {
|
if (++i < groupParam.size()) {
|
||||||
@@ -74,12 +79,12 @@ public class MyGroupParam extends ArrayList<MyGroupParam.GroupInfo> {
|
|||||||
return groupParam;
|
return groupParam;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GroupBaseData parseGroupBaseData(GroupInfo groupInfo, Class<?> modelClazz) {
|
private GroupBaseData parseGroupBaseData(GroupInfo groupInfo, Class<?> modelClazz) {
|
||||||
GroupBaseData baseData = new GroupBaseData();
|
GroupBaseData baseData = new GroupBaseData();
|
||||||
if (StringUtils.isBlank(groupInfo.fieldName)) {
|
if (StrUtil.isBlank(groupInfo.fieldName)) {
|
||||||
throw new IllegalArgumentException("GroupInfo.fieldName can't be EMPTY");
|
throw new IllegalArgumentException("GroupInfo.fieldName can't be EMPTY");
|
||||||
}
|
}
|
||||||
String[] stringArray = StringUtils.split(groupInfo.fieldName,'.');
|
String[] stringArray = StrUtil.splitToArray(groupInfo.fieldName, '.');
|
||||||
if (stringArray.length == 1) {
|
if (stringArray.length == 1) {
|
||||||
baseData.modelName = modelClazz.getSimpleName();
|
baseData.modelName = modelClazz.getSimpleName();
|
||||||
baseData.fieldName = groupInfo.fieldName;
|
baseData.fieldName = groupInfo.fieldName;
|
||||||
@@ -99,14 +104,15 @@ public class MyGroupParam extends ArrayList<MyGroupParam.GroupInfo> {
|
|||||||
return baseData;
|
return baseData;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void processGroupInfo(
|
private void processGroupInfo(
|
||||||
GroupInfo groupInfo,
|
GroupInfo groupInfo,
|
||||||
GroupBaseData baseData,
|
GroupBaseData baseData,
|
||||||
StringBuilder groupByBuilder,
|
StringBuilder groupByBuilder,
|
||||||
StringBuilder groupSelectBuilder) {
|
StringBuilder groupSelectBuilder) {
|
||||||
String tableName = baseData.tableName;
|
String tableName = baseData.tableName;
|
||||||
String columnName = baseData.columnName;
|
String columnName = baseData.columnName;
|
||||||
if (StringUtils.isNotBlank(groupInfo.dateAggregateBy)) {
|
if (StrUtil.isNotBlank(groupInfo.dateAggregateBy)) {
|
||||||
|
if (coreProperties.isMySql()) {
|
||||||
groupByBuilder.append("DATE_FORMAT(").append(tableName).append(".").append(columnName);
|
groupByBuilder.append("DATE_FORMAT(").append(tableName).append(".").append(columnName);
|
||||||
groupSelectBuilder.append("DATE_FORMAT(").append(tableName).append(".").append(columnName);
|
groupSelectBuilder.append("DATE_FORMAT(").append(tableName).append(".").append(columnName);
|
||||||
if (ApplicationConstant.DAY_AGGREGATION.equals(groupInfo.dateAggregateBy)) {
|
if (ApplicationConstant.DAY_AGGREGATION.equals(groupInfo.dateAggregateBy)) {
|
||||||
@@ -121,7 +127,25 @@ public class MyGroupParam extends ArrayList<MyGroupParam.GroupInfo> {
|
|||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("Illegal DATE_FORMAT for GROUP ID list.");
|
throw new IllegalArgumentException("Illegal DATE_FORMAT for GROUP ID list.");
|
||||||
}
|
}
|
||||||
if (StringUtils.isNotBlank(groupInfo.aliasName)) {
|
} else if (coreProperties.isPostgresql()) {
|
||||||
|
groupByBuilder.append("TO_CHAR(").append(tableName).append(".").append(columnName);
|
||||||
|
groupSelectBuilder.append("TO_CHAR(").append(tableName).append(".").append(columnName);
|
||||||
|
if (ApplicationConstant.DAY_AGGREGATION.equals(groupInfo.dateAggregateBy)) {
|
||||||
|
groupByBuilder.append(", ''YYYY-MM-dd'')");
|
||||||
|
groupSelectBuilder.append(", 'YYYY-MM-dd'')");
|
||||||
|
} else if (ApplicationConstant.MONTH_AGGREGATION.equals(groupInfo.dateAggregateBy)) {
|
||||||
|
groupByBuilder.append(", 'YYYY-MM-01')");
|
||||||
|
groupSelectBuilder.append(", 'YYYY-MM-01')");
|
||||||
|
} else if (ApplicationConstant.YEAR_AGGREGATION.equals(groupInfo.dateAggregateBy)) {
|
||||||
|
groupByBuilder.append(", 'YYYY-01-01')");
|
||||||
|
groupSelectBuilder.append(", 'YYYY-01-01')");
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Illegal TO_CHAR for GROUP ID list.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new UnsupportedOperationException("Unsupport Database Type.");
|
||||||
|
}
|
||||||
|
if (StrUtil.isNotBlank(groupInfo.aliasName)) {
|
||||||
groupSelectBuilder.append(" ").append(groupInfo.aliasName);
|
groupSelectBuilder.append(" ").append(groupInfo.aliasName);
|
||||||
} else {
|
} else {
|
||||||
groupSelectBuilder.append(" ").append(columnName);
|
groupSelectBuilder.append(" ").append(columnName);
|
||||||
@@ -129,7 +153,7 @@ public class MyGroupParam extends ArrayList<MyGroupParam.GroupInfo> {
|
|||||||
} else {
|
} else {
|
||||||
groupByBuilder.append(tableName).append(".").append(columnName);
|
groupByBuilder.append(tableName).append(".").append(columnName);
|
||||||
groupSelectBuilder.append(tableName).append(".").append(columnName);
|
groupSelectBuilder.append(tableName).append(".").append(columnName);
|
||||||
if (StringUtils.isNotBlank(groupInfo.aliasName)) {
|
if (StrUtil.isNotBlank(groupInfo.aliasName)) {
|
||||||
groupSelectBuilder.append(" ").append(groupInfo.aliasName);
|
groupSelectBuilder.append(" ").append(groupInfo.aliasName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,6 +84,10 @@ public class TokenData {
|
|||||||
* 登录时间。
|
* 登录时间。
|
||||||
*/
|
*/
|
||||||
private Date loginTime;
|
private Date loginTime;
|
||||||
|
/**
|
||||||
|
* 登录头像地址。
|
||||||
|
*/
|
||||||
|
private String headImageUrl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将令牌对象添加到Http请求对象。
|
* 将令牌对象添加到Http请求对象。
|
||||||
|
|||||||
@@ -0,0 +1,252 @@
|
|||||||
|
package com.orangeforms.common.core.util;
|
||||||
|
|
||||||
|
import cn.hutool.core.convert.Convert;
|
||||||
|
import cn.hutool.core.io.file.FileNameUtil;
|
||||||
|
import cn.hutool.core.lang.Assert;
|
||||||
|
import cn.hutool.core.util.ReflectUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.poi.excel.ExcelUtil;
|
||||||
|
import cn.hutool.poi.excel.sax.handler.RowHandler;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||||
|
import com.orangeforms.common.core.exception.MyRuntimeException;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.joda.time.DateTime;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导入工具类,目前支持xlsx和xls两种类型。
|
||||||
|
*
|
||||||
|
* @author Jerry
|
||||||
|
* @date 2021-06-06
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class ImportUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据实体类的Class类型,生成导入的头信息。
|
||||||
|
*
|
||||||
|
* @param modelClazz 实体对象的Class类型。
|
||||||
|
* @param ignoreFields 忽略的字段名集合,如创建时间、创建人、更新时间、更新人等。
|
||||||
|
* @param <T> 实体对象类型。
|
||||||
|
* @return 创建后的导入头信息列表。
|
||||||
|
*/
|
||||||
|
public static <T> List<ImportHeaderInfo> makeHeaderInfoList(Class<T> modelClazz, Set<String> ignoreFields) {
|
||||||
|
List<ImportHeaderInfo> resultList = new LinkedList<>();
|
||||||
|
Field[] fields = ReflectUtil.getFields(modelClazz);
|
||||||
|
for (Field field : fields) {
|
||||||
|
int modifiers = field.getModifiers();
|
||||||
|
// transient类型的字段不能作为查询条件,静态字段和逻辑删除都不考虑。需要忽略的字段也要跳过。
|
||||||
|
int transientMask = 128;
|
||||||
|
if ((modifiers & transientMask) == 1
|
||||||
|
|| Modifier.isStatic(modifiers)
|
||||||
|
|| field.getAnnotation(TableLogic.class) != null
|
||||||
|
|| ignoreFields.contains(field.getName())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
TableField tableField = field.getAnnotation(TableField.class);
|
||||||
|
if (tableField == null || tableField.exist()) {
|
||||||
|
ImportHeaderInfo headerInfo = new ImportHeaderInfo();
|
||||||
|
headerInfo.fieldName = field.getName();
|
||||||
|
if (field.getType().equals(Integer.class)) {
|
||||||
|
headerInfo.fieldType = INT_TYPE;
|
||||||
|
} else if (field.getType().equals(Long.class)) {
|
||||||
|
headerInfo.fieldType = LONG_TYPE;
|
||||||
|
} else if (field.getType().equals(String.class)) {
|
||||||
|
headerInfo.fieldType = STRING_TYPE;
|
||||||
|
} else if (field.getType().equals(Boolean.class)) {
|
||||||
|
headerInfo.fieldType = BOOLEAN_TYPE;
|
||||||
|
} else if (field.getType().equals(Date.class)) {
|
||||||
|
headerInfo.fieldType = DATE_TYPE;
|
||||||
|
} else if (field.getType().equals(Double.class)) {
|
||||||
|
headerInfo.fieldType = DOUBLE_TYPE;
|
||||||
|
} else if (field.getType().equals(Float.class)) {
|
||||||
|
headerInfo.fieldType = FLOAT_TYPE;
|
||||||
|
} else if (field.getType().equals(BigDecimal.class)) {
|
||||||
|
headerInfo.fieldType = BIG_DECIMAL_TYPE;
|
||||||
|
} else {
|
||||||
|
throw new MyRuntimeException("Unsupport Import FieldType");
|
||||||
|
}
|
||||||
|
resultList.add(headerInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resultList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存导入文件。
|
||||||
|
*
|
||||||
|
* @param baseDir 导入文件本地缓存的根目录。
|
||||||
|
* @param subDir 导入文件本地缓存的子目录。
|
||||||
|
* @param importFile 导入的文件。
|
||||||
|
* @return 保存的本地文件名。
|
||||||
|
*/
|
||||||
|
public static String saveImportFile(
|
||||||
|
String baseDir, String subDir, MultipartFile importFile) throws IOException {
|
||||||
|
StringBuilder sb = new StringBuilder(256);
|
||||||
|
sb.append(baseDir);
|
||||||
|
if (!StrUtil.endWith(baseDir, "/")) {
|
||||||
|
sb.append("/");
|
||||||
|
}
|
||||||
|
sb.append("importedFile/");
|
||||||
|
if (StrUtil.isNotBlank(subDir)) {
|
||||||
|
sb.append(subDir);
|
||||||
|
if (!StrUtil.endWith(subDir, "/")) {
|
||||||
|
sb.append("/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String pathname = sb.toString();
|
||||||
|
sb.append(new DateTime().toString("yyyy-MM-dd-HH-mm-"));
|
||||||
|
sb.append(MyCommonUtil.generateUuid())
|
||||||
|
.append(".").append(FileNameUtil.getSuffix(importFile.getOriginalFilename()));
|
||||||
|
String fullname = sb.toString();
|
||||||
|
try {
|
||||||
|
byte[] bytes = importFile.getBytes();
|
||||||
|
Path path = Paths.get(fullname);
|
||||||
|
// 如果没有files文件夹,则创建
|
||||||
|
if (!Files.isWritable(path)) {
|
||||||
|
Files.createDirectories(Paths.get(pathname));
|
||||||
|
}
|
||||||
|
// 文件写入指定路径
|
||||||
|
Files.write(path, bytes);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("Failed to write imported file [" + importFile.getOriginalFilename() + " ].", e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
return fullname;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导入指定的excel,基于SAX方式解析后返回数据列表。
|
||||||
|
*
|
||||||
|
* @param headers 头信息数组。
|
||||||
|
* @param skipHeader 是否跳过第一行,通常改行为头信息。
|
||||||
|
* @param filename 文件名。
|
||||||
|
* @return 解析后数据列表。
|
||||||
|
*/
|
||||||
|
public static List<Map<String, Object>> doImport(
|
||||||
|
ImportHeaderInfo[] headers, boolean skipHeader, String filename) {
|
||||||
|
Assert.notNull(headers);
|
||||||
|
Assert.isTrue(StrUtil.isNotBlank(filename));
|
||||||
|
List<Map<String, Object>> resultList = new LinkedList<>();
|
||||||
|
ExcelUtil.readBySax(new File(filename), 0, createRowHandler(headers, skipHeader, resultList));
|
||||||
|
return resultList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导入指定的excel,基于SAX方式解析后返回Bean类型的数据列表。
|
||||||
|
*
|
||||||
|
* @param headers 头信息数组。
|
||||||
|
* @param skipHeader 是否跳过第一行,通常改行为头信息。
|
||||||
|
* @param filename 文件名。
|
||||||
|
* @param clazz Bean的Class类型。
|
||||||
|
* @return 解析后数据列表。
|
||||||
|
*/
|
||||||
|
public static <T> List<T> doImport(
|
||||||
|
ImportHeaderInfo[] headers, boolean skipHeader, String filename, Class<T> clazz) {
|
||||||
|
List<Map<String, Object>> resultList = doImport(headers, skipHeader, filename);
|
||||||
|
return MyModelUtil.mapToBeanList(resultList, clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RowHandler createRowHandler(
|
||||||
|
ImportHeaderInfo[] headers, boolean skipHeader, List<Map<String, Object>> resultList) {
|
||||||
|
return new MyRowHandler(headers, skipHeader, resultList);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final static int INT_TYPE = 0;
|
||||||
|
public final static int LONG_TYPE = 1;
|
||||||
|
public final static int STRING_TYPE = 2;
|
||||||
|
public final static int BOOLEAN_TYPE = 3;
|
||||||
|
public final static int DATE_TYPE = 4;
|
||||||
|
public final static int DOUBLE_TYPE = 5;
|
||||||
|
public final static int FLOAT_TYPE = 6;
|
||||||
|
public final static int BIG_DECIMAL_TYPE = 7;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class ImportHeaderInfo {
|
||||||
|
/**
|
||||||
|
* 对应的Java实体对象属性名。
|
||||||
|
*/
|
||||||
|
private String fieldName;
|
||||||
|
/**
|
||||||
|
* 对应的Java实体对象类型。
|
||||||
|
*/
|
||||||
|
private Integer fieldType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MyRowHandler implements RowHandler {
|
||||||
|
|
||||||
|
private ImportHeaderInfo[] headers;
|
||||||
|
private boolean skipHeader;
|
||||||
|
private List<Map<String, Object>> resultList;
|
||||||
|
|
||||||
|
public MyRowHandler(ImportHeaderInfo[] headers, boolean skipHeader, List<Map<String, Object>> resultList) {
|
||||||
|
this.headers = headers;
|
||||||
|
this.skipHeader = skipHeader;
|
||||||
|
this.resultList = resultList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(int sheetIndex, long rowIndex, List<Object> rowList) {
|
||||||
|
if (this.skipHeader && rowIndex == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int i = 0;
|
||||||
|
Map<String, Object> data = new HashMap<>(headers.length);
|
||||||
|
for (Object rowData : rowList) {
|
||||||
|
if (i >= headers.length) {
|
||||||
|
log.warn("Exceeded the size of headers and ignore the left columns");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ImportHeaderInfo headerInfo = this.headers[i++];
|
||||||
|
switch (headerInfo.fieldType) {
|
||||||
|
case INT_TYPE:
|
||||||
|
data.put(headerInfo.fieldName, Convert.toInt(rowData));
|
||||||
|
break;
|
||||||
|
case LONG_TYPE:
|
||||||
|
data.put(headerInfo.fieldName, Convert.toLong(rowData));
|
||||||
|
break;
|
||||||
|
case STRING_TYPE:
|
||||||
|
data.put(headerInfo.fieldName, Convert.toStr(rowData));
|
||||||
|
break;
|
||||||
|
case BOOLEAN_TYPE:
|
||||||
|
data.put(headerInfo.fieldName, Convert.toBool(rowData));
|
||||||
|
break;
|
||||||
|
case DATE_TYPE:
|
||||||
|
data.put(headerInfo.fieldName, Convert.toDate(rowData));
|
||||||
|
break;
|
||||||
|
case DOUBLE_TYPE:
|
||||||
|
data.put(headerInfo.fieldName, Convert.toDouble(rowData));
|
||||||
|
break;
|
||||||
|
case FLOAT_TYPE:
|
||||||
|
data.put(headerInfo.fieldName, Convert.toFloat(rowData));
|
||||||
|
break;
|
||||||
|
case BIG_DECIMAL_TYPE:
|
||||||
|
data.put(headerInfo.fieldName, Convert.toBigDecimal(rowData));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new MyRuntimeException(
|
||||||
|
"Invalid ImportHeaderInfo.fieldType [" + headerInfo.fieldType + "].");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resultList.add(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 私有构造函数,明确标识该常量类的作用。
|
||||||
|
*/
|
||||||
|
private ImportUtil() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,6 +28,10 @@ public class MyDateUtil {
|
|||||||
* 统一的日期时间pattern,今后可以根据自己的需求去修改。
|
* 统一的日期时间pattern,今后可以根据自己的需求去修改。
|
||||||
*/
|
*/
|
||||||
public static final String COMMON_DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS";
|
public static final String COMMON_DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS";
|
||||||
|
/**
|
||||||
|
* 统一的短日期时间pattern,今后可以根据自己的需求去修改。
|
||||||
|
*/
|
||||||
|
public static final String COMMON_SHORT_DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
|
||||||
/**
|
/**
|
||||||
* 缺省日期格式化器,提前获取提升运行时效率。
|
* 缺省日期格式化器,提前获取提升运行时效率。
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -66,20 +66,34 @@ public class MyModelUtil {
|
|||||||
private static final Map<String, Tuple2<String, Integer>> CACHED_COLUMNINFO_MAP = new ConcurrentHashMap<>();
|
private static final Map<String, Tuple2<String, Integer>> CACHED_COLUMNINFO_MAP = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将bean的数据列表转换为Map列表。
|
* 将Bean的数据列表转换为Map列表。
|
||||||
*
|
*
|
||||||
* @param dataList bean数据列表。
|
* @param dataList Bean数据列表。
|
||||||
* @param <T> bean对象类型。
|
* @param <T> Bean对象类型。
|
||||||
* @return 转换后的Map列表。
|
* @return 转换后的Map列表。
|
||||||
*/
|
*/
|
||||||
public static <T> List<Map<String, Object>> beanToMapList(List<T> dataList) {
|
public static <T> List<Map<String, Object>> beanToMapList(List<T> dataList) {
|
||||||
if (CollectionUtils.isEmpty(dataList)) {
|
if (CollectionUtils.isEmpty(dataList)) {
|
||||||
return null;
|
return new LinkedList<>();
|
||||||
}
|
}
|
||||||
List<Map<String, Object>> resultList = new LinkedList<>();
|
List<Map<String, Object>> resultList = new LinkedList<>();
|
||||||
for (T data : dataList) {
|
dataList.forEach(data -> resultList.add(BeanUtil.beanToMap(data)));
|
||||||
resultList.add(BeanUtil.beanToMap(data));
|
return resultList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将Map的数据列表转换为Bean列表。
|
||||||
|
*
|
||||||
|
* @param dataList Map数据列表。
|
||||||
|
* @param <T> Bean对象类型。
|
||||||
|
* @return 转换后的Bean对象列表。
|
||||||
|
*/
|
||||||
|
public static <T> List<T> mapToBeanList(List<Map<String, Object>> dataList, Class<T> clazz) {
|
||||||
|
if (CollectionUtils.isEmpty(dataList)) {
|
||||||
|
return new LinkedList<>();
|
||||||
|
}
|
||||||
|
List<T> resultList = new LinkedList<>();
|
||||||
|
dataList.forEach(data -> resultList.add(BeanUtil.toBeanIgnoreError(data, clazz)));
|
||||||
return resultList;
|
return resultList;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -742,10 +756,10 @@ public class MyModelUtil {
|
|||||||
* @return 当前数据对象中,所有上传文件字段中,文件名属性的集合。
|
* @return 当前数据对象中,所有上传文件字段中,文件名属性的集合。
|
||||||
*/
|
*/
|
||||||
public static <M> Set<String> extractDownloadFileName(List<M> dataList, Class<M> clazz) {
|
public static <M> Set<String> extractDownloadFileName(List<M> dataList, Class<M> clazz) {
|
||||||
if (CollectionUtils.isEmpty(dataList)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Set<String> resultSet = new HashSet<>();
|
Set<String> resultSet = new HashSet<>();
|
||||||
|
if (CollectionUtils.isEmpty(dataList)) {
|
||||||
|
return resultSet;
|
||||||
|
}
|
||||||
dataList.forEach(data -> resultSet.addAll(extractDownloadFileName(data, clazz)));
|
dataList.forEach(data -> resultSet.addAll(extractDownloadFileName(data, clazz)));
|
||||||
return resultSet;
|
return resultSet;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ public class RedisKeyUtil {
|
|||||||
* @return session缓存的键前缀。
|
* @return session缓存的键前缀。
|
||||||
*/
|
*/
|
||||||
public static String getSessionIdPrefix() {
|
public static String getSessionIdPrefix() {
|
||||||
return "SESSIONID__";
|
return "SESSIONID:";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,7 +24,7 @@ public class RedisKeyUtil {
|
|||||||
* @return session缓存的键前缀。
|
* @return session缓存的键前缀。
|
||||||
*/
|
*/
|
||||||
public static String getSessionIdPrefix(String loginName) {
|
public static String getSessionIdPrefix(String loginName) {
|
||||||
return "SESSIONID__" + loginName + "_";
|
return "SESSIONID:" + loginName + "_";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -35,7 +35,7 @@ public class RedisKeyUtil {
|
|||||||
* @return session缓存的键前缀。
|
* @return session缓存的键前缀。
|
||||||
*/
|
*/
|
||||||
public static String getSessionIdPrefix(String loginName, int deviceType) {
|
public static String getSessionIdPrefix(String loginName, int deviceType) {
|
||||||
return "SESSIONID__" + loginName + "_" + deviceType + "_";
|
return "SESSIONID:" + loginName + "_" + deviceType + "_";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -45,7 +45,7 @@ public class RedisKeyUtil {
|
|||||||
* @return 会话存储于Redis中的键值。
|
* @return 会话存储于Redis中的键值。
|
||||||
*/
|
*/
|
||||||
public static String makeSessionIdKey(String sessionId) {
|
public static String makeSessionIdKey(String sessionId) {
|
||||||
return "SESSIONID__" + sessionId;
|
return "SESSIONID:" + sessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -55,7 +55,7 @@ public class RedisKeyUtil {
|
|||||||
* @return 会话关联的权限数据存储于Redis中的键值。
|
* @return 会话关联的权限数据存储于Redis中的键值。
|
||||||
*/
|
*/
|
||||||
public static String makeSessionPermIdKey(String sessionId) {
|
public static String makeSessionPermIdKey(String sessionId) {
|
||||||
return "PERM__" + sessionId;
|
return "PERM:" + sessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -65,7 +65,7 @@ public class RedisKeyUtil {
|
|||||||
* @return 会话关联的数据权限数据存储于Redis中的键值。
|
* @return 会话关联的数据权限数据存储于Redis中的键值。
|
||||||
*/
|
*/
|
||||||
public static String makeSessionDataPermIdKey(String sessionId) {
|
public static String makeSessionDataPermIdKey(String sessionId) {
|
||||||
return "DATA_PERM__" + sessionId;
|
return "DATA_PERM:" + sessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -75,7 +75,7 @@ public class RedisKeyUtil {
|
|||||||
* @return 会话关联的数据权限数据存储于Redis中的键值。
|
* @return 会话关联的数据权限数据存储于Redis中的键值。
|
||||||
*/
|
*/
|
||||||
public static String makeOnlineTableKey(Long tableId) {
|
public static String makeOnlineTableKey(Long tableId) {
|
||||||
return "ONLINE_TABLE_" + tableId;
|
return "ONLINE_TABLE:" + tableId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -166,6 +166,7 @@ public class MybatisDataFilterInterceptor implements Interceptor {
|
|||||||
// 通过注解解析与Mapper关联的Model,并获取与数据权限关联的信息,并将结果缓存。
|
// 通过注解解析与Mapper关联的Model,并获取与数据权限关联的信息,并将结果缓存。
|
||||||
ModelDataPermInfo info = new ModelDataPermInfo();
|
ModelDataPermInfo info = new ModelDataPermInfo();
|
||||||
info.setMainTableName(MyModelUtil.mapToTableName(modelClazz));
|
info.setMainTableName(MyModelUtil.mapToTableName(modelClazz));
|
||||||
|
info.setMustIncludeUserRule(rule.mustIncludeUserRule());
|
||||||
info.setExcludeMethodNameSet(excludeMethodNameSet);
|
info.setExcludeMethodNameSet(excludeMethodNameSet);
|
||||||
if (userFilterField != null) {
|
if (userFilterField != null) {
|
||||||
info.setUserFilterColumn(MyModelUtil.mapToColumnName(userFilterField, modelClazz));
|
info.setUserFilterColumn(MyModelUtil.mapToColumnName(userFilterField, modelClazz));
|
||||||
@@ -291,6 +292,12 @@ public class MybatisDataFilterInterceptor implements Interceptor {
|
|||||||
if (dataPermMap.containsKey(DataPermRuleType.TYPE_ALL)) {
|
if (dataPermMap.containsKey(DataPermRuleType.TYPE_ALL)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// 如果当前过滤注解中mustIncludeUserRule参数为true,同时当前用户的数据权限中,不包含TYPE_USER_ONLY,
|
||||||
|
// 这里就需要自动添加该数据权限。
|
||||||
|
if (info.getMustIncludeUserRule()
|
||||||
|
&& !dataPermMap.containsKey(DataPermRuleType.TYPE_USER_ONLY)) {
|
||||||
|
dataPermMap.put(DataPermRuleType.TYPE_USER_ONLY, null);
|
||||||
|
}
|
||||||
this.processDataPerm(info, dataPermMap, boundSql, commandType, statement);
|
this.processDataPerm(info, dataPermMap, boundSql, commandType, statement);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,6 +465,7 @@ public class MybatisDataFilterInterceptor implements Interceptor {
|
|||||||
private String userFilterColumn;
|
private String userFilterColumn;
|
||||||
private String deptFilterColumn;
|
private String deptFilterColumn;
|
||||||
private String mainTableName;
|
private String mainTableName;
|
||||||
|
private Boolean mustIncludeUserRule;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
|
|||||||
@@ -29,11 +29,13 @@ import com.orangeforms.common.online.service.OnlineOperationService;
|
|||||||
import com.orangeforms.common.online.service.OnlineTableService;
|
import com.orangeforms.common.online.service.OnlineTableService;
|
||||||
import com.orangeforms.common.online.util.OnlineOperationHelper;
|
import com.orangeforms.common.online.util.OnlineOperationHelper;
|
||||||
import com.orangeforms.common.flow.online.service.FlowOnlineOperationService;
|
import com.orangeforms.common.flow.online.service.FlowOnlineOperationService;
|
||||||
|
import com.orangeforms.common.flow.constant.FlowConstant;
|
||||||
import com.orangeforms.common.flow.constant.FlowApprovalType;
|
import com.orangeforms.common.flow.constant.FlowApprovalType;
|
||||||
import com.orangeforms.common.flow.util.FlowOperationHelper;
|
|
||||||
import com.orangeforms.common.flow.constant.FlowTaskStatus;
|
import com.orangeforms.common.flow.constant.FlowTaskStatus;
|
||||||
|
import com.orangeforms.common.flow.util.FlowOperationHelper;
|
||||||
import com.orangeforms.common.flow.dto.FlowTaskCommentDto;
|
import com.orangeforms.common.flow.dto.FlowTaskCommentDto;
|
||||||
import com.orangeforms.common.flow.exception.FlowOperationException;
|
import com.orangeforms.common.flow.exception.FlowOperationException;
|
||||||
|
import com.orangeforms.common.flow.model.constant.FlowMessageType;
|
||||||
import com.orangeforms.common.flow.model.*;
|
import com.orangeforms.common.flow.model.*;
|
||||||
import com.orangeforms.common.flow.service.*;
|
import com.orangeforms.common.flow.service.*;
|
||||||
import com.orangeforms.common.flow.vo.TaskInfoVo;
|
import com.orangeforms.common.flow.vo.TaskInfoVo;
|
||||||
@@ -73,6 +75,8 @@ public class FlowOnlineOperationController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private FlowWorkOrderService flowWorkOrderService;
|
private FlowWorkOrderService flowWorkOrderService;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
private FlowMessageService flowMessageService;
|
||||||
|
@Autowired
|
||||||
private OnlineFormService onlineFormService;
|
private OnlineFormService onlineFormService;
|
||||||
@Autowired
|
@Autowired
|
||||||
private OnlinePageService onlinePageService;
|
private OnlinePageService onlinePageService;
|
||||||
@@ -92,6 +96,7 @@ public class FlowOnlineOperationController {
|
|||||||
* @param taskVariableData 流程任务变量数据。
|
* @param taskVariableData 流程任务变量数据。
|
||||||
* @param masterData 流程审批相关的主表数据。
|
* @param masterData 流程审批相关的主表数据。
|
||||||
* @param slaveData 流程审批相关的多个从表数据。
|
* @param slaveData 流程审批相关的多个从表数据。
|
||||||
|
* @param copyData 传阅数据,格式为type和id,type的值参考FlowConstant中的常量值。
|
||||||
* @return 应答结果对象。
|
* @return 应答结果对象。
|
||||||
*/
|
*/
|
||||||
@DisableDataFilter
|
@DisableDataFilter
|
||||||
@@ -101,7 +106,8 @@ public class FlowOnlineOperationController {
|
|||||||
@MyRequestBody(required = true) FlowTaskCommentDto flowTaskCommentDto,
|
@MyRequestBody(required = true) FlowTaskCommentDto flowTaskCommentDto,
|
||||||
@MyRequestBody JSONObject taskVariableData,
|
@MyRequestBody JSONObject taskVariableData,
|
||||||
@MyRequestBody(required = true) JSONObject masterData,
|
@MyRequestBody(required = true) JSONObject masterData,
|
||||||
@MyRequestBody JSONObject slaveData) {
|
@MyRequestBody JSONObject slaveData,
|
||||||
|
@MyRequestBody JSONObject copyData) {
|
||||||
String errorMessage;
|
String errorMessage;
|
||||||
// 1. 验证流程数据的合法性。
|
// 1. 验证流程数据的合法性。
|
||||||
ResponseResult<FlowEntry> flowEntryResult = flowOperationHelper.verifyAndGetFlowEntry(processDefinitionKey);
|
ResponseResult<FlowEntry> flowEntryResult = flowOperationHelper.verifyAndGetFlowEntry(processDefinitionKey);
|
||||||
@@ -133,6 +139,13 @@ public class FlowOnlineOperationController {
|
|||||||
if (!columnDataListResult.isSuccess()) {
|
if (!columnDataListResult.isSuccess()) {
|
||||||
return ResponseResult.errorFrom(columnDataListResult);
|
return ResponseResult.errorFrom(columnDataListResult);
|
||||||
}
|
}
|
||||||
|
// 这里把传阅数据放到任务变量中,是为了避免给流程数据操作方法增加额外的方法调用参数。
|
||||||
|
if (MapUtil.isNotEmpty(copyData)) {
|
||||||
|
if (taskVariableData == null) {
|
||||||
|
taskVariableData = new JSONObject();
|
||||||
|
}
|
||||||
|
taskVariableData.put(FlowConstant.COPY_DATA_KEY, copyData);
|
||||||
|
}
|
||||||
FlowTaskComment flowTaskComment = BeanUtil.copyProperties(flowTaskCommentDto, FlowTaskComment.class);
|
FlowTaskComment flowTaskComment = BeanUtil.copyProperties(flowTaskCommentDto, FlowTaskComment.class);
|
||||||
// 5. 保存在线表单提交的数据,同时启动流程和自动完成第一个用户任务。
|
// 5. 保存在线表单提交的数据,同时启动流程和自动完成第一个用户任务。
|
||||||
if (slaveData == null) {
|
if (slaveData == null) {
|
||||||
@@ -170,6 +183,7 @@ public class FlowOnlineOperationController {
|
|||||||
* @param taskVariableData 流程任务变量数据。
|
* @param taskVariableData 流程任务变量数据。
|
||||||
* @param masterData 流程审批相关的主表数据。
|
* @param masterData 流程审批相关的主表数据。
|
||||||
* @param slaveData 流程审批相关的多个从表数据。
|
* @param slaveData 流程审批相关的多个从表数据。
|
||||||
|
* @param copyData 传阅数据,格式为type和id,type的值参考FlowConstant中的常量值。
|
||||||
* @return 应答结果对象。
|
* @return 应答结果对象。
|
||||||
*/
|
*/
|
||||||
@DisableDataFilter
|
@DisableDataFilter
|
||||||
@@ -180,7 +194,8 @@ public class FlowOnlineOperationController {
|
|||||||
@MyRequestBody(required = true) FlowTaskCommentDto flowTaskCommentDto,
|
@MyRequestBody(required = true) FlowTaskCommentDto flowTaskCommentDto,
|
||||||
@MyRequestBody JSONObject taskVariableData,
|
@MyRequestBody JSONObject taskVariableData,
|
||||||
@MyRequestBody JSONObject masterData,
|
@MyRequestBody JSONObject masterData,
|
||||||
@MyRequestBody JSONObject slaveData) {
|
@MyRequestBody JSONObject slaveData,
|
||||||
|
@MyRequestBody JSONObject copyData) {
|
||||||
String errorMessage;
|
String errorMessage;
|
||||||
// 验证流程任务的合法性。
|
// 验证流程任务的合法性。
|
||||||
Task task = flowApiService.getProcessInstanceActiveTask(processInstanceId, taskId);
|
Task task = flowApiService.getProcessInstanceActiveTask(processInstanceId, taskId);
|
||||||
@@ -203,6 +218,13 @@ public class FlowOnlineOperationController {
|
|||||||
Long datasourceId = datasource.getDatasourceId();
|
Long datasourceId = datasource.getDatasourceId();
|
||||||
ProcessInstance instance = flowApiService.getProcessInstance(processInstanceId);
|
ProcessInstance instance = flowApiService.getProcessInstance(processInstanceId);
|
||||||
String dataId = instance.getBusinessKey();
|
String dataId = instance.getBusinessKey();
|
||||||
|
// 这里把传阅数据放到任务变量中,是为了避免给流程数据操作方法增加额外的方法调用参数。
|
||||||
|
if (MapUtil.isNotEmpty(copyData)) {
|
||||||
|
if (taskVariableData == null) {
|
||||||
|
taskVariableData = new JSONObject();
|
||||||
|
}
|
||||||
|
taskVariableData.put(FlowConstant.COPY_DATA_KEY, copyData);
|
||||||
|
}
|
||||||
FlowTaskComment flowTaskComment = BeanUtil.copyProperties(flowTaskCommentDto, FlowTaskComment.class);
|
FlowTaskComment flowTaskComment = BeanUtil.copyProperties(flowTaskCommentDto, FlowTaskComment.class);
|
||||||
if (StrUtil.isBlank(dataId)) {
|
if (StrUtil.isBlank(dataId)) {
|
||||||
return this.submitNewTask(processInstanceId, taskId,
|
return this.submitNewTask(processInstanceId, taskId,
|
||||||
@@ -286,9 +308,11 @@ public class FlowOnlineOperationController {
|
|||||||
String loginName = TokenData.takeFromRequest().getLoginName();
|
String loginName = TokenData.takeFromRequest().getLoginName();
|
||||||
if (StrUtil.isBlank(taskId)) {
|
if (StrUtil.isBlank(taskId)) {
|
||||||
if (!StrUtil.equals(loginName, instance.getStartUserId())) {
|
if (!StrUtil.equals(loginName, instance.getStartUserId())) {
|
||||||
errorMessage = "数据验证失败,指定历史流程的发起人与当前用户不匹配!";
|
if (!flowWorkOrderService.hasDataPermOnFlowWorkOrder(processInstanceId)) {
|
||||||
|
errorMessage = "数据验证失败,指定历史流程的发起人与当前用户不匹配,或者没有查看该工单详情的数据权限!";
|
||||||
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
|
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
HistoricTaskInstance taskInstance = flowApiService.getHistoricTaskInstance(processInstanceId, taskId);
|
HistoricTaskInstance taskInstance = flowApiService.getHistoricTaskInstance(processInstanceId, taskId);
|
||||||
if (taskInstance == null) {
|
if (taskInstance == null) {
|
||||||
@@ -296,10 +320,12 @@ public class FlowOnlineOperationController {
|
|||||||
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
|
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
|
||||||
}
|
}
|
||||||
if (!StrUtil.equals(loginName, taskInstance.getAssignee())) {
|
if (!StrUtil.equals(loginName, taskInstance.getAssignee())) {
|
||||||
errorMessage = "数据验证失败,历史任务的指派人与当前用户不匹配!";
|
if (!flowWorkOrderService.hasDataPermOnFlowWorkOrder(processInstanceId)) {
|
||||||
|
errorMessage = "数据验证失败,历史任务的指派人与当前用户不匹配,或者没有查看该工单详情的数据权限!";
|
||||||
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
|
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (StrUtil.isBlank(instance.getBusinessKey())) {
|
if (StrUtil.isBlank(instance.getBusinessKey())) {
|
||||||
// 对于没有提交过任何用户任务的场景,可直接返回空数据。
|
// 对于没有提交过任何用户任务的场景,可直接返回空数据。
|
||||||
return ResponseResult.success(new JSONObject());
|
return ResponseResult.success(new JSONObject());
|
||||||
@@ -323,6 +349,59 @@ public class FlowOnlineOperationController {
|
|||||||
return ResponseResult.success(jsonData);
|
return ResponseResult.success(jsonData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据消息Id,获取流程Id关联的业务数据。
|
||||||
|
* NOTE:白名单接口。
|
||||||
|
*
|
||||||
|
* @param messageId 抄送消息Id。
|
||||||
|
* @return 抄送消息关联的流程实例业务数据。
|
||||||
|
*/
|
||||||
|
@DisableDataFilter
|
||||||
|
@GetMapping("/viewCopyBusinessData")
|
||||||
|
public ResponseResult<JSONObject> viewCopyBusinessData(@RequestParam Long messageId) {
|
||||||
|
String errorMessage;
|
||||||
|
// 验证流程任务的合法性。
|
||||||
|
FlowMessage flowMessage = flowMessageService.getById(messageId);
|
||||||
|
if (flowMessage == null) {
|
||||||
|
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
|
||||||
|
}
|
||||||
|
if (flowMessage.getMessageType() != FlowMessageType.COPY_TYPE) {
|
||||||
|
errorMessage = "数据验证失败,当前消息不是抄送类型消息!";
|
||||||
|
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
|
||||||
|
}
|
||||||
|
if (flowMessage.getOnlineFormData() == null || !flowMessage.getOnlineFormData()) {
|
||||||
|
errorMessage = "数据验证失败,当前消息为静态路由表单数据,不能通过该接口获取!";
|
||||||
|
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
|
||||||
|
}
|
||||||
|
if (!flowMessageService.isCandidateIdentityOnMessage(messageId)) {
|
||||||
|
errorMessage = "数据验证失败,当前用户没有权限访问该消息!";
|
||||||
|
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
|
||||||
|
}
|
||||||
|
ProcessInstance instance = flowApiService.getProcessInstance(flowMessage.getProcessInstanceId());
|
||||||
|
// 如果业务主数据为空,则直接返回。
|
||||||
|
if (StrUtil.isBlank(instance.getBusinessKey())) {
|
||||||
|
errorMessage = "数据验证失败,当前消息为所属流程实例没有包含业务主键Id!";
|
||||||
|
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
|
||||||
|
}
|
||||||
|
Long formId = Long.valueOf(flowMessage.getBusinessDataShot());
|
||||||
|
// 验证在线表单及其关联数据源的合法性。
|
||||||
|
ResponseResult<OnlineDatasource> datasourceResult = this.verifyAndGetOnlineDatasource(formId);
|
||||||
|
if (!datasourceResult.isSuccess()) {
|
||||||
|
return ResponseResult.errorFrom(datasourceResult);
|
||||||
|
}
|
||||||
|
OnlineDatasource datasource = datasourceResult.getData();
|
||||||
|
ResponseResult<List<OnlineDatasourceRelation>> relationListResult =
|
||||||
|
onlineOperationHelper.verifyAndGetRelationList(datasource.getDatasourceId(), null);
|
||||||
|
if (!relationListResult.isSuccess()) {
|
||||||
|
return ResponseResult.errorFrom(relationListResult);
|
||||||
|
}
|
||||||
|
JSONObject jsonData = this.buildUserTaskData(
|
||||||
|
instance.getBusinessKey(), datasource.getMasterTable(), relationListResult.getData());
|
||||||
|
// 将当前消息更新为已读
|
||||||
|
flowMessageService.readCopyTask(messageId);
|
||||||
|
return ResponseResult.success(jsonData);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 工作流工单列表。
|
* 工作流工单列表。
|
||||||
*
|
*
|
||||||
@@ -344,9 +423,13 @@ public class FlowOnlineOperationController {
|
|||||||
MyOrderParam orderParam = new MyOrderParam();
|
MyOrderParam orderParam = new MyOrderParam();
|
||||||
orderParam.add(new MyOrderParam.OrderInfo("workOrderId", false, null));
|
orderParam.add(new MyOrderParam.OrderInfo("workOrderId", false, null));
|
||||||
String orderBy = MyOrderParam.buildOrderBy(orderParam, FlowWorkOrder.class);
|
String orderBy = MyOrderParam.buildOrderBy(orderParam, FlowWorkOrder.class);
|
||||||
List<FlowWorkOrder> flowWorkOrderList = flowWorkOrderService.getFlowWorkOrderList(flowWorkOrderFilter, orderBy);
|
List<FlowWorkOrder> flowWorkOrderList =
|
||||||
|
flowWorkOrderService.getFlowWorkOrderList(flowWorkOrderFilter, orderBy);
|
||||||
MyPageData<FlowWorkOrderVo> resultData =
|
MyPageData<FlowWorkOrderVo> resultData =
|
||||||
MyPageUtil.makeResponseData(flowWorkOrderList, FlowWorkOrder.INSTANCE);
|
MyPageUtil.makeResponseData(flowWorkOrderList, FlowWorkOrder.INSTANCE);
|
||||||
|
// 根据工单的提交用户名获取用户的显示名称,便于前端显示。
|
||||||
|
// 同时这也是一个如何通过插件方法,将loginName映射到showName的示例,
|
||||||
|
flowWorkOrderService.fillUserShowNameByLoginName(resultData.getDataList());
|
||||||
// 工单自身的查询中可以受到数据权限的过滤,但是工单集成业务数据时,则无需再对业务数据进行数据权限过滤了。
|
// 工单自身的查询中可以受到数据权限的过滤,但是工单集成业务数据时,则无需再对业务数据进行数据权限过滤了。
|
||||||
GlobalThreadLocal.setDataFilter(false);
|
GlobalThreadLocal.setDataFilter(false);
|
||||||
ResponseResult<Void> responseResult = this.makeWorkOrderTaskInfo(resultData.getDataList());
|
ResponseResult<Void> responseResult = this.makeWorkOrderTaskInfo(resultData.getDataList());
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
package com.orangeforms.common.flow.base.service;
|
package com.orangeforms.common.flow.base.service;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.orangeforms.common.core.base.service.BaseService;
|
import com.orangeforms.common.core.base.service.BaseService;
|
||||||
|
import com.orangeforms.common.core.object.MyRelationParam;
|
||||||
|
import com.orangeforms.common.core.util.MyDateUtil;
|
||||||
import com.orangeforms.common.flow.constant.FlowApprovalType;
|
import com.orangeforms.common.flow.constant.FlowApprovalType;
|
||||||
import com.orangeforms.common.flow.constant.FlowTaskStatus;
|
import com.orangeforms.common.flow.constant.FlowTaskStatus;
|
||||||
import com.orangeforms.common.flow.model.FlowTaskComment;
|
import com.orangeforms.common.flow.model.FlowTaskComment;
|
||||||
@@ -50,13 +53,44 @@ public abstract class BaseFlowService<M, K extends Serializable> extends BaseSer
|
|||||||
flowApiService.completeTask(task, comment, variables);
|
flowApiService.completeTask(task, comment, variables);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否支持业务数据同步。每个子类需要根据实际情况判断是否需要支持。
|
||||||
|
*
|
||||||
|
* @return true支持,否则false。
|
||||||
|
*/
|
||||||
|
public boolean supportSyncBusinessData() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在流程实例审批结束后,需要进行审批表到发布表数据同步的服务实现子类,需要实现该方法。
|
* 在流程实例审批结束后,需要进行审批表到发布表数据同步的服务实现子类,需要实现该方法。
|
||||||
*
|
*
|
||||||
* @param processInstanceId 流程实例Id。
|
* @param processInstanceId 流程实例Id。
|
||||||
* @param businessKey 业务主键Id。如果与实际主键值类型不同,需要在子类中自行完成类型转换。
|
* @param businessKey 业务主键Id。如果与实际主键值类型不同,需要在子类中自行完成类型转换。
|
||||||
*/
|
*/
|
||||||
public void doSyncBusinessData(String processInstanceId, String businessKey) {
|
public void syncBusinessData(String processInstanceId, String businessKey) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取业务详情数据。
|
||||||
|
*
|
||||||
|
* @param processInstanceId 流程实例Id。
|
||||||
|
* @param businessKey 业务主键Id。如果与实际主键值类型不同,需要在子类中自行完成类型转换。
|
||||||
|
* @return 业务主表数据,以及关联从表数据。
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public String getBusinessData(String processInstanceId, String businessKey) {
|
||||||
|
M data;
|
||||||
|
if (idFieldClass.equals(Long.class)) {
|
||||||
|
Long dataId = Long.valueOf(businessKey);
|
||||||
|
data = this.getByIdWithRelation((K) dataId, MyRelationParam.full());
|
||||||
|
} else if (idFieldClass.equals(Integer.class)) {
|
||||||
|
Integer dataId = Integer.valueOf(businessKey);
|
||||||
|
data = this.getByIdWithRelation((K) dataId, MyRelationParam.full());
|
||||||
|
} else {
|
||||||
|
data = this.getByIdWithRelation((K) businessKey, MyRelationParam.full());
|
||||||
|
}
|
||||||
|
return JSON.toJSONStringWithDateFormat(data, MyDateUtil.COMMON_SHORT_DATETIME_FORMAT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,11 @@ public class FlowConstant {
|
|||||||
*/
|
*/
|
||||||
public final static String PROC_INSTANCE_START_USER_NAME_VAR = "startUserName";
|
public final static String PROC_INSTANCE_START_USER_NAME_VAR = "startUserName";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程任务的指定人变量。
|
||||||
|
*/
|
||||||
|
public final static String TASK_APPOINTED_ASSIGNEE_VAR = "appointedAssignee";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 操作类型变量。
|
* 操作类型变量。
|
||||||
*/
|
*/
|
||||||
@@ -64,47 +69,62 @@ public class FlowConstant {
|
|||||||
public final static String MULTI_ASSIGNEE_LIST_VAR = "assigneeList";
|
public final static String MULTI_ASSIGNEE_LIST_VAR = "assigneeList";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上级部门领导审批变量
|
* 上级部门领导审批变量。
|
||||||
*/
|
*/
|
||||||
public final static String GROUP_TYPE_UP_DEPT_POST_LEADER_VAR = "upDeptPostLeader";
|
public final static String GROUP_TYPE_UP_DEPT_POST_LEADER_VAR = "upDeptPostLeader";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上级部门领导审批变量
|
* 本部门领导审批变量。
|
||||||
*/
|
*/
|
||||||
public final static String GROUP_TYPE_DEPT_POST_LEADER_VAR = "deptPostLeader";
|
public final static String GROUP_TYPE_DEPT_POST_LEADER_VAR = "deptPostLeader";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 所有部门岗位审批变量
|
* 所有部门岗位审批变量。
|
||||||
*/
|
*/
|
||||||
public final static String GROUP_TYPE_ALL_DEPT_POST_VAR = "allDeptPost";
|
public final static String GROUP_TYPE_ALL_DEPT_POST_VAR = "allDeptPost";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 本部门岗位审批变量
|
* 本部门岗位审批变量。
|
||||||
*/
|
*/
|
||||||
public final static String GROUP_TYPE_SELF_DEPT_POST_VAR = "selfDeptPost";
|
public final static String GROUP_TYPE_SELF_DEPT_POST_VAR = "selfDeptPost";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上级部门岗位审批变量
|
* 上级部门岗位审批变量。
|
||||||
*/
|
*/
|
||||||
public final static String GROUP_TYPE_UP_DEPT_POST_VAR = "upDeptPost";
|
public final static String GROUP_TYPE_UP_DEPT_POST_VAR = "upDeptPost";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 任意部门关联的岗位审批变量
|
* 任意部门关联的岗位审批变量。
|
||||||
*/
|
*/
|
||||||
public final static String GROUP_TYPE_DEPT_POST_VAR = "deptPost";
|
public final static String GROUP_TYPE_DEPT_POST_VAR = "deptPost";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 岗位
|
* 指定角色分组变量。
|
||||||
|
*/
|
||||||
|
public final static String GROUP_TYPE_ROLE_VAR = "role";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定部门分组变量。
|
||||||
|
*/
|
||||||
|
public final static String GROUP_TYPE_DEPT_VAR = "dept";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定用户分组变量。
|
||||||
|
*/
|
||||||
|
public final static String GROUP_TYPE_USER_VAR = "user";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 岗位。
|
||||||
*/
|
*/
|
||||||
public final static String GROUP_TYPE_POST = "POST";
|
public final static String GROUP_TYPE_POST = "POST";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上级部门领导审批
|
* 上级部门领导审批。
|
||||||
*/
|
*/
|
||||||
public final static String GROUP_TYPE_UP_DEPT_POST_LEADER = "UP_DEPT_POST_LEADER";
|
public final static String GROUP_TYPE_UP_DEPT_POST_LEADER = "UP_DEPT_POST_LEADER";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 本部门岗位领导审批
|
* 本部门岗位领导审批。
|
||||||
*/
|
*/
|
||||||
public final static String GROUP_TYPE_DEPT_POST_LEADER = "DEPT_POST_LEADER";
|
public final static String GROUP_TYPE_DEPT_POST_LEADER = "DEPT_POST_LEADER";
|
||||||
|
|
||||||
@@ -114,7 +134,17 @@ public class FlowConstant {
|
|||||||
public final static String SELF_DEPT_POST_PREFIX = "SELF_DEPT_";
|
public final static String SELF_DEPT_POST_PREFIX = "SELF_DEPT_";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 本部门岗位前缀。
|
* 上级部门岗位前缀。
|
||||||
*/
|
*/
|
||||||
public final static String UP_DEPT_POST_PREFIX = "UP_DEPT_";
|
public final static String UP_DEPT_POST_PREFIX = "UP_DEPT_";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前流程实例所有任务的抄送数据前缀。
|
||||||
|
*/
|
||||||
|
public final static String COPY_DATA_MAP_PREFIX = "copyDataMap_";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 作为临时变量存入任务变量JSONObject对象时的key。
|
||||||
|
*/
|
||||||
|
public static final String COPY_DATA_KEY = "copyDataKey";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ import com.orangeforms.common.core.util.MyCommonUtil;
|
|||||||
import com.orangeforms.common.core.util.MyModelUtil;
|
import com.orangeforms.common.core.util.MyModelUtil;
|
||||||
import com.orangeforms.common.core.util.MyPageUtil;
|
import com.orangeforms.common.core.util.MyPageUtil;
|
||||||
import com.orangeforms.common.core.validator.UpdateGroup;
|
import com.orangeforms.common.core.validator.UpdateGroup;
|
||||||
|
import com.orangeforms.common.core.exception.MyRuntimeException;
|
||||||
import com.orangeforms.common.flow.object.FlowTaskMultiSignAssign;
|
import com.orangeforms.common.flow.object.FlowTaskMultiSignAssign;
|
||||||
import com.orangeforms.common.flow.constant.FlowTaskType;
|
import com.orangeforms.common.flow.constant.FlowTaskType;
|
||||||
|
import com.orangeforms.common.flow.constant.FlowConstant;
|
||||||
import com.orangeforms.common.flow.dto.*;
|
import com.orangeforms.common.flow.dto.*;
|
||||||
import com.orangeforms.common.flow.model.*;
|
import com.orangeforms.common.flow.model.*;
|
||||||
import com.orangeforms.common.flow.model.constant.FlowEntryStatus;
|
import com.orangeforms.common.flow.model.constant.FlowEntryStatus;
|
||||||
@@ -371,6 +373,10 @@ public class FlowEntryController {
|
|||||||
if (deptPostList != null) {
|
if (deptPostList != null) {
|
||||||
flowTaskExt.setDeptPostListJson(JSON.toJSONString(deptPostList));
|
flowTaskExt.setDeptPostListJson(JSON.toJSONString(deptPostList));
|
||||||
}
|
}
|
||||||
|
List<JSONObject> copyList = this.buildCopyListExtensionElement(extensionMap);
|
||||||
|
if (copyList != null) {
|
||||||
|
flowTaskExt.setCopyListJson(JSON.toJSONString(copyList));
|
||||||
|
}
|
||||||
JSONObject candidateGroupObject = this.buildUserCandidateGroupsExtensionElement(extensionMap);
|
JSONObject candidateGroupObject = this.buildUserCandidateGroupsExtensionElement(extensionMap);
|
||||||
if (candidateGroupObject != null) {
|
if (candidateGroupObject != null) {
|
||||||
String type = candidateGroupObject.getString("type");
|
String type = candidateGroupObject.getString("type");
|
||||||
@@ -572,6 +578,37 @@ public class FlowEntryController {
|
|||||||
return resultList;
|
return resultList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<JSONObject> buildCopyListExtensionElement(Map<String, List<ExtensionElement>> extensionMap) {
|
||||||
|
List<ExtensionElement> copyElements =
|
||||||
|
this.getMyExtensionElementList(extensionMap, "copyItemList", "copyItem");
|
||||||
|
if (CollUtil.isEmpty(copyElements)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<JSONObject> resultList = new LinkedList<>();
|
||||||
|
for (ExtensionElement e : copyElements) {
|
||||||
|
JSONObject copyJsonData = new JSONObject();
|
||||||
|
String type = e.getAttributeValue(null, "type");
|
||||||
|
copyJsonData.put("type", type);
|
||||||
|
if (!StrUtil.equalsAny(type, FlowConstant.GROUP_TYPE_DEPT_POST_LEADER_VAR,
|
||||||
|
FlowConstant.GROUP_TYPE_UP_DEPT_POST_LEADER_VAR,
|
||||||
|
FlowConstant.GROUP_TYPE_USER_VAR,
|
||||||
|
FlowConstant.GROUP_TYPE_ROLE_VAR,
|
||||||
|
FlowConstant.GROUP_TYPE_DEPT_VAR,
|
||||||
|
FlowConstant.GROUP_TYPE_DEPT_POST_VAR,
|
||||||
|
FlowConstant.GROUP_TYPE_ALL_DEPT_POST_VAR,
|
||||||
|
FlowConstant.GROUP_TYPE_SELF_DEPT_POST_VAR,
|
||||||
|
FlowConstant.GROUP_TYPE_UP_DEPT_POST_VAR)) {
|
||||||
|
throw new MyRuntimeException("Invalid TYPE [" + type + " ] for CopyItenList Extension!");
|
||||||
|
}
|
||||||
|
String id = e.getAttributeValue(null, "id");
|
||||||
|
if (StrUtil.isNotBlank(id)) {
|
||||||
|
copyJsonData.put("id", id);
|
||||||
|
}
|
||||||
|
resultList.add(copyJsonData);
|
||||||
|
}
|
||||||
|
return resultList;
|
||||||
|
}
|
||||||
|
|
||||||
private List<ExtensionElement> getMyExtensionElementList(
|
private List<ExtensionElement> getMyExtensionElementList(
|
||||||
Map<String, List<ExtensionElement>> extensionMap, String rootName, String childName) {
|
Map<String, List<ExtensionElement>> extensionMap, String rootName, String childName) {
|
||||||
List<ExtensionElement> elementList = extensionMap.get(rootName);
|
List<ExtensionElement> elementList = extensionMap.get(rootName);
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
package com.orangeforms.common.flow.controller;
|
package com.orangeforms.common.flow.controller;
|
||||||
|
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.orangeforms.common.core.annotation.MyRequestBody;
|
import com.orangeforms.common.core.annotation.MyRequestBody;
|
||||||
import com.orangeforms.common.core.object.*;
|
import com.orangeforms.common.core.object.*;
|
||||||
|
import com.orangeforms.common.core.constant.ErrorCodeEnum;
|
||||||
import com.orangeforms.common.core.util.MyPageUtil;
|
import com.orangeforms.common.core.util.MyPageUtil;
|
||||||
|
import com.orangeforms.common.flow.model.constant.FlowMessageType;
|
||||||
import com.orangeforms.common.flow.model.FlowMessage;
|
import com.orangeforms.common.flow.model.FlowMessage;
|
||||||
import com.orangeforms.common.flow.service.FlowMessageService;
|
import com.orangeforms.common.flow.service.FlowMessageService;
|
||||||
import com.orangeforms.common.flow.vo.FlowMessageVo;
|
import com.orangeforms.common.flow.vo.FlowMessageVo;
|
||||||
@@ -29,9 +32,24 @@ public class FlowMessageController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private FlowMessageService flowMessageService;
|
private FlowMessageService flowMessageService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前用户的未读消息总数。
|
||||||
|
* NOTE:白名单接口。
|
||||||
|
*
|
||||||
|
* @return 应答结果对象,包含当前用户的未读消息总数。
|
||||||
|
*/
|
||||||
|
@GetMapping("/getMessageCount")
|
||||||
|
public ResponseResult<JSONObject> getMessageCount() {
|
||||||
|
JSONObject resultData = new JSONObject();
|
||||||
|
resultData.put("remindingMessageCount", flowMessageService.countRemindingMessageListByUser());
|
||||||
|
resultData.put("copyMessageCount", flowMessageService.countCopyMessageByUser());
|
||||||
|
return ResponseResult.success(resultData);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前用户的催办消息列表。
|
* 获取当前用户的催办消息列表。
|
||||||
* 不仅仅包含,其中包括当前用户所属角色、部门和岗位的候选组催办消息。
|
* 不仅仅包含,其中包括当前用户所属角色、部门和岗位的候选组催办消息。
|
||||||
|
* NOTE:白名单接口。
|
||||||
*
|
*
|
||||||
* @return 应答结果对象,包含查询结果集。
|
* @return 应答结果对象,包含查询结果集。
|
||||||
*/
|
*/
|
||||||
@@ -43,4 +61,48 @@ public class FlowMessageController {
|
|||||||
List<FlowMessage> flowMessageList = flowMessageService.getRemindingMessageListByUser();
|
List<FlowMessage> flowMessageList = flowMessageService.getRemindingMessageListByUser();
|
||||||
return ResponseResult.success(MyPageUtil.makeResponseData(flowMessageList, FlowMessage.INSTANCE));
|
return ResponseResult.success(MyPageUtil.makeResponseData(flowMessageList, FlowMessage.INSTANCE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前用户的抄送消息列表。
|
||||||
|
* 不仅仅包含,其中包括当前用户所属角色、部门和岗位的候选组抄送消息。
|
||||||
|
* NOTE:白名单接口。
|
||||||
|
*
|
||||||
|
* @param read true表示已读,false表示未读。
|
||||||
|
* @return 应答结果对象,包含查询结果集。
|
||||||
|
*/
|
||||||
|
@PostMapping("/listCopyMessage")
|
||||||
|
public ResponseResult<MyPageData<FlowMessageVo>> listCopyMessage(
|
||||||
|
@MyRequestBody MyPageParam pageParam, @MyRequestBody Boolean read) {
|
||||||
|
if (pageParam != null) {
|
||||||
|
PageMethod.startPage(pageParam.getPageNum(), pageParam.getPageSize());
|
||||||
|
}
|
||||||
|
List<FlowMessage> flowMessageList = flowMessageService.getCopyMessageListByUser(read);
|
||||||
|
return ResponseResult.success(MyPageUtil.makeResponseData(flowMessageList, FlowMessage.INSTANCE));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取抄送消息,同时更新当前用户对指定抄送消息的读取状态。
|
||||||
|
*
|
||||||
|
* @param messageId 消息Id。
|
||||||
|
* @return 应答结果对象。
|
||||||
|
*/
|
||||||
|
@PostMapping("/readCopyTask")
|
||||||
|
public ResponseResult<Void> readCopyTask(@MyRequestBody Long messageId) {
|
||||||
|
String errorMessage;
|
||||||
|
// 验证流程任务的合法性。
|
||||||
|
FlowMessage flowMessage = flowMessageService.getById(messageId);
|
||||||
|
if (flowMessage == null) {
|
||||||
|
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
|
||||||
|
}
|
||||||
|
if (flowMessage.getMessageType() != FlowMessageType.COPY_TYPE) {
|
||||||
|
errorMessage = "数据验证失败,当前消息不是抄送类型消息!";
|
||||||
|
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
|
||||||
|
}
|
||||||
|
if (!flowMessageService.isCandidateIdentityOnMessage(messageId)) {
|
||||||
|
errorMessage = "数据验证失败,当前用户没有权限访问该消息!";
|
||||||
|
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
|
||||||
|
}
|
||||||
|
flowMessageService.readCopyTask(messageId);
|
||||||
|
return ResponseResult.success();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package com.orangeforms.common.flow.controller;
|
|||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
import cn.hutool.core.bean.BeanUtil;
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
@@ -15,8 +14,10 @@ import com.orangeforms.common.core.util.MyPageUtil;
|
|||||||
import com.orangeforms.common.flow.constant.FlowApprovalType;
|
import com.orangeforms.common.flow.constant.FlowApprovalType;
|
||||||
import com.orangeforms.common.flow.constant.FlowConstant;
|
import com.orangeforms.common.flow.constant.FlowConstant;
|
||||||
import com.orangeforms.common.flow.constant.FlowTaskStatus;
|
import com.orangeforms.common.flow.constant.FlowTaskStatus;
|
||||||
|
import com.orangeforms.common.flow.model.constant.FlowMessageType;
|
||||||
import com.orangeforms.common.flow.model.*;
|
import com.orangeforms.common.flow.model.*;
|
||||||
import com.orangeforms.common.flow.service.*;
|
import com.orangeforms.common.flow.service.*;
|
||||||
|
import com.orangeforms.common.flow.util.FlowCustomExtFactory;
|
||||||
import com.orangeforms.common.flow.util.FlowOperationHelper;
|
import com.orangeforms.common.flow.util.FlowOperationHelper;
|
||||||
import com.orangeforms.common.flow.vo.FlowTaskCommentVo;
|
import com.orangeforms.common.flow.vo.FlowTaskCommentVo;
|
||||||
import com.orangeforms.common.flow.vo.FlowTaskVo;
|
import com.orangeforms.common.flow.vo.FlowTaskVo;
|
||||||
@@ -29,6 +30,7 @@ import org.flowable.bpmn.model.Process;
|
|||||||
import org.flowable.bpmn.model.SequenceFlow;
|
import org.flowable.bpmn.model.SequenceFlow;
|
||||||
import org.flowable.engine.history.HistoricActivityInstance;
|
import org.flowable.engine.history.HistoricActivityInstance;
|
||||||
import org.flowable.engine.history.HistoricProcessInstance;
|
import org.flowable.engine.history.HistoricProcessInstance;
|
||||||
|
import org.flowable.engine.runtime.ProcessInstance;
|
||||||
import org.flowable.task.api.Task;
|
import org.flowable.task.api.Task;
|
||||||
import org.flowable.task.api.history.HistoricTaskInstance;
|
import org.flowable.task.api.history.HistoricTaskInstance;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -69,6 +71,8 @@ public class FlowOperationController {
|
|||||||
private FlowMessageService flowMessageService;
|
private FlowMessageService flowMessageService;
|
||||||
@Autowired
|
@Autowired
|
||||||
private FlowOperationHelper flowOperationHelper;
|
private FlowOperationHelper flowOperationHelper;
|
||||||
|
@Autowired
|
||||||
|
private FlowCustomExtFactory flowCustomExtFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据指定流程的主版本,发起一个流程实例。
|
* 根据指定流程的主版本,发起一个流程实例。
|
||||||
@@ -199,10 +203,6 @@ public class FlowOperationController {
|
|||||||
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
|
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
|
||||||
}
|
}
|
||||||
FlowTaskComment taskComment = taskCommentList.get(0);
|
FlowTaskComment taskComment = taskCommentList.get(0);
|
||||||
if (ObjectUtil.notEqual(taskComment.getCreateUserId(), TokenData.takeFromRequest().getUserId())) {
|
|
||||||
errorMessage = "数据验证失败,当前流程发起人与当前用户不匹配!";
|
|
||||||
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
|
|
||||||
}
|
|
||||||
HistoricTaskInstance task = flowApiService.getHistoricTaskInstance(processInstanceId, taskComment.getTaskId());
|
HistoricTaskInstance task = flowApiService.getHistoricTaskInstance(processInstanceId, taskComment.getTaskId());
|
||||||
if (StrUtil.isBlank(task.getFormKey())) {
|
if (StrUtil.isBlank(task.getFormKey())) {
|
||||||
errorMessage = "数据验证失败,指定任务的formKey属性不存在,请重新修改流程图!";
|
errorMessage = "数据验证失败,指定任务的formKey属性不存在,请重新修改流程图!";
|
||||||
@@ -213,6 +213,59 @@ public class FlowOperationController {
|
|||||||
return ResponseResult.success(taskInfo);
|
return ResponseResult.success(taskInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据消息Id,获取流程Id关联的业务数据。
|
||||||
|
* NOTE:白名单接口。
|
||||||
|
*
|
||||||
|
* @param messageId 抄送消息Id。
|
||||||
|
* @param snapshot 是否获取抄送或传阅时任务的业务快照数据。如果为true,后续任务导致的业务数据修改,将不会返回给前端。
|
||||||
|
* @return 抄送消息关联的流程实例业务数据。
|
||||||
|
*/
|
||||||
|
@DisableDataFilter
|
||||||
|
@GetMapping("/viewCopyBusinessData")
|
||||||
|
public ResponseResult<JSONObject> viewCopyBusinessData(
|
||||||
|
@RequestParam Long messageId, @RequestParam(required = false) Boolean snapshot) {
|
||||||
|
String errorMessage;
|
||||||
|
// 验证流程任务的合法性。
|
||||||
|
FlowMessage flowMessage = flowMessageService.getById(messageId);
|
||||||
|
if (flowMessage == null) {
|
||||||
|
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
|
||||||
|
}
|
||||||
|
if (flowMessage.getMessageType() != FlowMessageType.COPY_TYPE) {
|
||||||
|
errorMessage = "数据验证失败,当前消息不是抄送类型消息!";
|
||||||
|
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
|
||||||
|
}
|
||||||
|
if (flowMessage.getOnlineFormData() == null || flowMessage.getOnlineFormData()) {
|
||||||
|
errorMessage = "数据验证失败,当前消息为在线表单数据,不能通过该接口获取!";
|
||||||
|
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
|
||||||
|
}
|
||||||
|
if (!flowMessageService.isCandidateIdentityOnMessage(messageId)) {
|
||||||
|
errorMessage = "数据验证失败,当前用户没有权限访问该消息!";
|
||||||
|
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
|
||||||
|
}
|
||||||
|
JSONObject businessObject = null;
|
||||||
|
if (snapshot != null && snapshot) {
|
||||||
|
if (StrUtil.isNotBlank(flowMessage.getBusinessDataShot())) {
|
||||||
|
businessObject = JSON.parseObject(flowMessage.getBusinessDataShot());
|
||||||
|
}
|
||||||
|
return ResponseResult.success(businessObject);
|
||||||
|
}
|
||||||
|
ProcessInstance instance = flowApiService.getProcessInstance(flowMessage.getProcessInstanceId());
|
||||||
|
// 如果业务主数据为空,则直接返回。
|
||||||
|
if (StrUtil.isBlank(instance.getBusinessKey())) {
|
||||||
|
errorMessage = "数据验证失败,当前消息为所属流程实例没有包含业务主键Id!";
|
||||||
|
return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
|
||||||
|
}
|
||||||
|
String businessData = flowCustomExtFactory.getBusinessDataExtHelper().getBusinessData(
|
||||||
|
flowMessage.getProcessDefinitionKey(), flowMessage.getProcessInstanceId(), instance.getBusinessKey());
|
||||||
|
if (StrUtil.isNotBlank(businessData)) {
|
||||||
|
businessObject = JSON.parseObject(businessData);
|
||||||
|
}
|
||||||
|
// 将当前消息更新为已读
|
||||||
|
flowMessageService.readCopyTask(messageId);
|
||||||
|
return ResponseResult.success(businessObject);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 提交多实例加签。
|
* 提交多实例加签。
|
||||||
*
|
*
|
||||||
@@ -292,6 +345,34 @@ public class FlowOperationController {
|
|||||||
return ResponseResult.success(totalCount);
|
return ResponseResult.success(totalCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主动驳回当前的待办任务到开始节点,只用当前待办任务的指派人或者候选者才能完成该操作。
|
||||||
|
*
|
||||||
|
* @param processInstanceId 流程实例Id。
|
||||||
|
* @param taskId 待办任务Id。
|
||||||
|
* @param comment 驳回备注。
|
||||||
|
* @return 操作应答结果。
|
||||||
|
*/
|
||||||
|
@PostMapping("/rejectToStartUserTask")
|
||||||
|
public ResponseResult<Void> rejectToStartUserTask(
|
||||||
|
@MyRequestBody(required = true) String processInstanceId,
|
||||||
|
@MyRequestBody(required = true) String taskId,
|
||||||
|
@MyRequestBody(required = true) String comment) {
|
||||||
|
String errorMessage;
|
||||||
|
ResponseResult<Task> taskResult =
|
||||||
|
flowOperationHelper.verifySubmitAndGetTask(processInstanceId, taskId, null);
|
||||||
|
if (!taskResult.isSuccess()) {
|
||||||
|
return ResponseResult.errorFrom(taskResult);
|
||||||
|
}
|
||||||
|
FlowTaskComment firstTaskComment = flowTaskCommentService.getFirstFlowTaskComment(processInstanceId);
|
||||||
|
CallResult result = flowApiService.backToRuntimeTask(
|
||||||
|
taskResult.getData(), firstTaskComment.getTaskKey(), true, comment);
|
||||||
|
if (!result.isSuccess()) {
|
||||||
|
return ResponseResult.errorFrom(result);
|
||||||
|
}
|
||||||
|
return ResponseResult.success();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主动驳回当前的待办任务,只用当前待办任务的指派人或者候选者才能完成该操作。
|
* 主动驳回当前的待办任务,只用当前待办任务的指派人或者候选者才能完成该操作。
|
||||||
*
|
*
|
||||||
@@ -311,7 +392,7 @@ public class FlowOperationController {
|
|||||||
if (!taskResult.isSuccess()) {
|
if (!taskResult.isSuccess()) {
|
||||||
return ResponseResult.errorFrom(taskResult);
|
return ResponseResult.errorFrom(taskResult);
|
||||||
}
|
}
|
||||||
CallResult result = flowApiService.backToLastRuntimeTask(taskResult.getData(), true, comment);
|
CallResult result = flowApiService.backToRuntimeTask(taskResult.getData(), null, true, comment);
|
||||||
if (!result.isSuccess()) {
|
if (!result.isSuccess()) {
|
||||||
return ResponseResult.errorFrom(result);
|
return ResponseResult.errorFrom(result);
|
||||||
}
|
}
|
||||||
@@ -368,8 +449,8 @@ public class FlowOperationController {
|
|||||||
Task task = activeTaskList.get(0);
|
Task task = activeTaskList.get(0);
|
||||||
task.setAssignee(TokenData.takeFromRequest().getLoginName());
|
task.setAssignee(TokenData.takeFromRequest().getLoginName());
|
||||||
} else {
|
} else {
|
||||||
CallResult result = flowApiService
|
CallResult result =
|
||||||
.backToLastRuntimeTask(activeTaskList.get(0), false, comment);
|
flowApiService.backToRuntimeTask(activeTaskList.get(0), null, false, comment);
|
||||||
if (!result.isSuccess()) {
|
if (!result.isSuccess()) {
|
||||||
return ResponseResult.errorFrom(result);
|
return ResponseResult.errorFrom(result);
|
||||||
}
|
}
|
||||||
@@ -432,14 +513,17 @@ public class FlowOperationController {
|
|||||||
//获取流程实例的历史节点(全部执行过的节点,被拒绝的任务节点将会出现多次)
|
//获取流程实例的历史节点(全部执行过的节点,被拒绝的任务节点将会出现多次)
|
||||||
List<HistoricActivityInstance> activityInstanceList =
|
List<HistoricActivityInstance> activityInstanceList =
|
||||||
flowApiService.getHistoricActivityInstanceList(processInstanceId);
|
flowApiService.getHistoricActivityInstanceList(processInstanceId);
|
||||||
|
List<String> activityInstanceTask = activityInstanceList.stream()
|
||||||
|
.filter(s -> !StrUtil.equals(s.getActivityType(), "sequenceFlow"))
|
||||||
|
.map(HistoricActivityInstance::getActivityId).collect(Collectors.toList());
|
||||||
Set<String> finishedTaskSequenceSet = new LinkedHashSet<>();
|
Set<String> finishedTaskSequenceSet = new LinkedHashSet<>();
|
||||||
for (int i = 0; i < activityInstanceList.size(); i++) {
|
for (int i = 0; i < activityInstanceTask.size(); i++) {
|
||||||
HistoricActivityInstance current = activityInstanceList.get(i);
|
String current = activityInstanceTask.get(i);
|
||||||
if (i != activityInstanceList.size() - 1) {
|
if (i != activityInstanceTask.size() - 1) {
|
||||||
HistoricActivityInstance next = activityInstanceList.get(i + 1);
|
String next = activityInstanceTask.get(i + 1);
|
||||||
finishedTaskSequenceSet.add(current.getActivityId() + next.getActivityId());
|
finishedTaskSequenceSet.add(current + next);
|
||||||
}
|
}
|
||||||
finishedTaskSet.add(current.getActivityId());
|
finishedTaskSet.add(current);
|
||||||
}
|
}
|
||||||
Set<String> finishedSequenceFlowSet = new HashSet<>();
|
Set<String> finishedSequenceFlowSet = new HashSet<>();
|
||||||
finishedTaskSequenceSet.forEach(s -> finishedSequenceFlowSet.add(allSequenceFlowMap.get(s)));
|
finishedTaskSequenceSet.forEach(s -> finishedSequenceFlowSet.add(allSequenceFlowMap.get(s)));
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.orangeforms.common.flow.dao;
|
|||||||
|
|
||||||
import com.orangeforms.common.core.base.dao.BaseDaoMapper;
|
import com.orangeforms.common.core.base.dao.BaseDaoMapper;
|
||||||
import com.orangeforms.common.flow.model.FlowMessageCandidateIdentity;
|
import com.orangeforms.common.flow.model.FlowMessageCandidateIdentity;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 流程任务消息的候选身份数据操作访问接口。
|
* 流程任务消息的候选身份数据操作访问接口。
|
||||||
@@ -10,4 +11,11 @@ import com.orangeforms.common.flow.model.FlowMessageCandidateIdentity;
|
|||||||
* @date 2021-06-06
|
* @date 2021-06-06
|
||||||
*/
|
*/
|
||||||
public interface FlowMessageCandidateIdentityMapper extends BaseDaoMapper<FlowMessageCandidateIdentity> {
|
public interface FlowMessageCandidateIdentityMapper extends BaseDaoMapper<FlowMessageCandidateIdentity> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除指定流程实例的消息关联数据。
|
||||||
|
*
|
||||||
|
* @param processInstanceId 流程实例Id。
|
||||||
|
*/
|
||||||
|
void deleteByProcessInstanceId(@Param("processInstanceId") String processInstanceId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.orangeforms.common.flow.dao;
|
||||||
|
|
||||||
|
import com.orangeforms.common.core.base.dao.BaseDaoMapper;
|
||||||
|
import com.orangeforms.common.flow.model.FlowMessageIdentityOperation;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程任务消息所属用户的操作数据操作访问接口。
|
||||||
|
*
|
||||||
|
* @author Jerry
|
||||||
|
* @date 2021-06-06
|
||||||
|
*/
|
||||||
|
public interface FlowMessageIdentityOperationMapper extends BaseDaoMapper<FlowMessageIdentityOperation> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除指定流程实例的消息关联数据。
|
||||||
|
*
|
||||||
|
* @param processInstanceId 流程实例Id。
|
||||||
|
*/
|
||||||
|
void deleteByProcessInstanceId(@Param("processInstanceId") String processInstanceId);
|
||||||
|
}
|
||||||
@@ -24,4 +24,37 @@ public interface FlowMessageMapper extends BaseDaoMapper<FlowMessage> {
|
|||||||
*/
|
*/
|
||||||
List<FlowMessage> getRemindingMessageListByUser(
|
List<FlowMessage> getRemindingMessageListByUser(
|
||||||
@Param("loginName") String loginName, @Param("groupIdSet") Set<String> groupIdSet);
|
@Param("loginName") String loginName, @Param("groupIdSet") Set<String> groupIdSet);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定用户和身份分组Id集合的抄送消息列表。
|
||||||
|
*
|
||||||
|
* @param loginName 用户登录名。
|
||||||
|
* @param groupIdSet 用户身份分组Id集合。
|
||||||
|
* @param read true表示已读,false表示未读。
|
||||||
|
* @return 查询后的抄送消息列表。
|
||||||
|
*/
|
||||||
|
List<FlowMessage> getCopyMessageListByUser(
|
||||||
|
@Param("loginName") String loginName,
|
||||||
|
@Param("groupIdSet") Set<String> groupIdSet,
|
||||||
|
@Param("read") Boolean read);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算当前用户催办消息的数量。
|
||||||
|
*
|
||||||
|
* @param loginName 用户登录名。
|
||||||
|
* @param groupIdSet 用户身份分组Id集合。
|
||||||
|
* @return 数据数量。
|
||||||
|
*/
|
||||||
|
int countRemindingMessageListByUser(
|
||||||
|
@Param("loginName") String loginName, @Param("groupIdSet") Set<String> groupIdSet);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算当前用户未读抄送消息的数量。
|
||||||
|
*
|
||||||
|
* @param loginName 用户登录名。
|
||||||
|
* @param groupIdSet 用户身份分组Id集合。
|
||||||
|
* @return 数据数量
|
||||||
|
*/
|
||||||
|
int countCopyMessageListByUser(
|
||||||
|
@Param("loginName") String loginName, @Param("groupIdSet") Set<String> groupIdSet);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,14 @@ import java.util.*;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 工作流工单表数据操作访问接口。
|
* 工作流工单表数据操作访问接口。
|
||||||
|
* 如果当前系统支持数据权限过滤,当前用户必须要能看自己的工单数据,所以需要把EnableDataPerm
|
||||||
|
* 的mustIncludeUserRule参数设置为true,即便当前用户的数据权限中并不包含DataPermRuleType.TYPE_USER_ONLY,
|
||||||
|
* 数据过滤拦截组件也会自动补偿该类型的数据权限,以便当前用户可以看到自己发起的工单。
|
||||||
*
|
*
|
||||||
* @author Jerry
|
* @author Jerry
|
||||||
* @date 2021-06-06
|
* @date 2021-06-06
|
||||||
*/
|
*/
|
||||||
@EnableDataPerm
|
@EnableDataPerm(mustIncludeUserRule = true)
|
||||||
public interface FlowWorkOrderMapper extends BaseDaoMapper<FlowWorkOrder> {
|
public interface FlowWorkOrderMapper extends BaseDaoMapper<FlowWorkOrder> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -12,8 +12,29 @@
|
|||||||
<result column="create_user_id" jdbcType="BIGINT" property="createUserId"/>
|
<result column="create_user_id" jdbcType="BIGINT" property="createUserId"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
|
<!-- 如果有逻辑删除字段过滤,请写到这里 -->
|
||||||
|
<sql id="filterRef">
|
||||||
|
<!-- 这里必须加上全包名,否则当filterRef被其他Mapper.xml包含引用的时候,就会调用Mapper.xml中的该SQL片段 -->
|
||||||
|
<include refid="com.orangeforms.common.flow.dao.FlowCategoryMapper.inputFilterRef"/>
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<!-- 这里仅包含调用接口输入的主表过滤条件 -->
|
||||||
|
<sql id="inputFilterRef">
|
||||||
|
<if test="flowCategoryFilter != null">
|
||||||
|
<if test="flowCategoryFilter.name != null and flowCategoryFilter.name != ''">
|
||||||
|
AND zz_flow_category.name = #{flowCategoryFilter.name}
|
||||||
|
</if>
|
||||||
|
<if test="flowCategoryFilter.code != null and flowCategoryFilter.code != ''">
|
||||||
|
AND zz_flow_category.code = #{flowCategoryFilter.code}
|
||||||
|
</if>
|
||||||
|
</if>
|
||||||
|
</sql>
|
||||||
|
|
||||||
<select id="getFlowCategoryList" resultMap="BaseResultMap" parameterType="com.orangeforms.common.flow.model.FlowCategory">
|
<select id="getFlowCategoryList" resultMap="BaseResultMap" parameterType="com.orangeforms.common.flow.model.FlowCategory">
|
||||||
SELECT * FROM zz_flow_category
|
SELECT * FROM zz_flow_category
|
||||||
|
<where>
|
||||||
|
<include refid="filterRef"/>
|
||||||
|
</where>
|
||||||
<if test="orderBy != null and orderBy != ''">
|
<if test="orderBy != null and orderBy != ''">
|
||||||
ORDER BY ${orderBy}
|
ORDER BY ${orderBy}
|
||||||
</if>
|
</if>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<result column="process_definition_key" jdbcType="VARCHAR" property="processDefinitionKey"/>
|
<result column="process_definition_key" jdbcType="VARCHAR" property="processDefinitionKey"/>
|
||||||
<result column="category_id" jdbcType="BIGINT" property="categoryId"/>
|
<result column="category_id" jdbcType="BIGINT" property="categoryId"/>
|
||||||
<result column="main_entry_publish_id" jdbcType="BIGINT" property="mainEntryPublishId"/>
|
<result column="main_entry_publish_id" jdbcType="BIGINT" property="mainEntryPublishId"/>
|
||||||
<result column="lastest_publish_time" jdbcType="TIMESTAMP" property="lastestPublishTime"/>
|
<result column="latest_publish_time" jdbcType="TIMESTAMP" property="latestPublishTime"/>
|
||||||
<result column="status" jdbcType="INTEGER" property="status"/>
|
<result column="status" jdbcType="INTEGER" property="status"/>
|
||||||
<result column="bpmn_xml" jdbcType="LONGVARCHAR" property="bpmnXml"/>
|
<result column="bpmn_xml" jdbcType="LONGVARCHAR" property="bpmnXml"/>
|
||||||
<result column="bind_form_type" jdbcType="INTEGER" property="bindFormType"/>
|
<result column="bind_form_type" jdbcType="INTEGER" property="bindFormType"/>
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
process_definition_key,
|
process_definition_key,
|
||||||
category_id,
|
category_id,
|
||||||
main_entry_publish_id,
|
main_entry_publish_id,
|
||||||
lastest_publish_time,
|
latest_publish_time,
|
||||||
status,
|
status,
|
||||||
bind_form_type,
|
bind_form_type,
|
||||||
page_id,
|
page_id,
|
||||||
|
|||||||
@@ -7,4 +7,10 @@
|
|||||||
<result column="candidate_type" jdbcType="VARCHAR" property="candidateType"/>
|
<result column="candidate_type" jdbcType="VARCHAR" property="candidateType"/>
|
||||||
<result column="candidate_id" jdbcType="VARCHAR" property="candidateId"/>
|
<result column="candidate_id" jdbcType="VARCHAR" property="candidateId"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
|
<delete id="deleteByProcessInstanceId">
|
||||||
|
DELETE FROM zz_flow_message_candicate_identity a
|
||||||
|
WHERE EXISTS (SELECT * FROM zz_flow_message b
|
||||||
|
WHERE a.message_id = b.message_id AND b.process_instance_id = #{processInstanceId})
|
||||||
|
</delete>
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.orangeforms.common.flow.dao.FlowMessageIdentityOperationMapper">
|
||||||
|
<resultMap id="BaseResultMap" type="com.orangeforms.common.flow.model.FlowMessageIdentityOperation">
|
||||||
|
<id column="id" jdbcType="BIGINT" property="id"/>
|
||||||
|
<result column="message_id" jdbcType="BIGINT" property="messageId"/>
|
||||||
|
<result column="login_name" jdbcType="VARCHAR" property="loginName"/>
|
||||||
|
<result column="operation_type" jdbcType="INTEGER" property="operationType"/>
|
||||||
|
<result column="operation_time" jdbcType="TIMESTAMP" property="operationTime"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<delete id="deleteByProcessInstanceId">
|
||||||
|
DELETE FROM zz_flow_message_identity_operation a
|
||||||
|
WHERE EXISTS (SELECT * FROM zz_flow_message b
|
||||||
|
WHERE a.message_id = b.message_id AND b.process_instance_id = #{processInstanceId})
|
||||||
|
</delete>
|
||||||
|
</mapper>
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
<result column="task_assignee" jdbcType="VARCHAR" property="taskAssignee"/>
|
<result column="task_assignee" jdbcType="VARCHAR" property="taskAssignee"/>
|
||||||
<result column="task_finished" jdbcType="BIT" property="taskFinished"/>
|
<result column="task_finished" jdbcType="BIT" property="taskFinished"/>
|
||||||
<result column="business_data_shot" jdbcType="LONGVARCHAR" property="businessDataShot"/>
|
<result column="business_data_shot" jdbcType="LONGVARCHAR" property="businessDataShot"/>
|
||||||
|
<result column="online_form_data" jdbcType="BIT" property="onlineFormData"/>
|
||||||
<result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/>
|
<result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/>
|
||||||
<result column="update_user_id" jdbcType="BIGINT" property="updateUserId"/>
|
<result column="update_user_id" jdbcType="BIGINT" property="updateUserId"/>
|
||||||
<result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
|
<result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
|
||||||
@@ -31,17 +32,60 @@
|
|||||||
<where>
|
<where>
|
||||||
a.task_finished = 0
|
a.task_finished = 0
|
||||||
AND a.message_type = 0
|
AND a.message_type = 0
|
||||||
<if test="groupIdSet == null">
|
|
||||||
AND a.task_assignee = #{loginName}
|
|
||||||
</if>
|
|
||||||
<if test="groupIdSet != null">
|
|
||||||
AND (a.task_assignee = #{loginName} OR EXISTS (SELECT * FROM zz_flow_message_candicate_identity b
|
AND (a.task_assignee = #{loginName} OR EXISTS (SELECT * FROM zz_flow_message_candicate_identity b
|
||||||
WHERE a.message_id = b.message_id AND b.candidate_id in
|
WHERE a.message_id = b.message_id AND b.candidate_id in
|
||||||
<foreach collection="groupIdSet" index="index" item="item" separator="," open="(" close=")">
|
<foreach collection="groupIdSet" index="index" item="item" separator="," open="(" close=")">
|
||||||
#{item}
|
#{item}
|
||||||
</foreach>))
|
</foreach>))
|
||||||
|
</where>
|
||||||
|
ORDER BY a.update_time DESC
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="getCopyMessageListByUser" resultMap="BaseResultMap">
|
||||||
|
SELECT a.* FROM zz_flow_message a
|
||||||
|
<where>
|
||||||
|
a.message_type = 1
|
||||||
|
AND EXISTS (SELECT * FROM zz_flow_message_candicate_identity b
|
||||||
|
WHERE a.message_id = b.message_id AND b.candidate_id in
|
||||||
|
<foreach collection="groupIdSet" index="index" item="item" separator="," open="(" close=")">
|
||||||
|
#{item}
|
||||||
|
</foreach>)
|
||||||
|
<if test="!read">
|
||||||
|
AND NOT EXISTS (SELECT * FROM zz_flow_message_identity_operation c
|
||||||
|
WHERE a.message_id = c.message_id AND c.login_name = #{loginName})
|
||||||
|
</if>
|
||||||
|
<if test="read">
|
||||||
|
AND EXISTS (SELECT * FROM zz_flow_message_identity_operation c
|
||||||
|
WHERE a.message_id = c.message_id AND c.login_name = #{loginName})
|
||||||
</if>
|
</if>
|
||||||
</where>
|
</where>
|
||||||
ORDER BY a.update_time DESC
|
ORDER BY a.update_time DESC
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="countRemindingMessageListByUser" resultType="java.lang.Integer">
|
||||||
|
SELECT COUNT(1) FROM zz_flow_message a
|
||||||
|
<where>
|
||||||
|
a.task_finished = 0
|
||||||
|
AND a.message_type = 0
|
||||||
|
AND (a.task_assignee = #{loginName} OR EXISTS (SELECT * FROM zz_flow_message_candicate_identity b
|
||||||
|
WHERE a.message_id = b.message_id AND b.candidate_id in
|
||||||
|
<foreach collection="groupIdSet" index="index" item="item" separator="," open="(" close=")">
|
||||||
|
#{item}
|
||||||
|
</foreach>))
|
||||||
|
</where>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="countCopyMessageListByUser" resultType="java.lang.Integer">
|
||||||
|
SELECT COUNT(1) FROM zz_flow_message a
|
||||||
|
<where>
|
||||||
|
a.message_type = 1
|
||||||
|
AND EXISTS (SELECT * FROM zz_flow_message_candicate_identity b
|
||||||
|
WHERE a.message_id = b.message_id AND b.candidate_id in
|
||||||
|
<foreach collection="groupIdSet" index="index" item="item" separator="," open="(" close=")">
|
||||||
|
#{item}
|
||||||
|
</foreach>)
|
||||||
|
AND NOT EXISTS (SELECT * FROM zz_flow_message_identity_operation c
|
||||||
|
WHERE a.message_id = c.message_id AND c.login_name = #{loginName})
|
||||||
|
</where>
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -9,8 +9,10 @@
|
|||||||
<result column="task_name" jdbcType="VARCHAR" property="taskName"/>
|
<result column="task_name" jdbcType="VARCHAR" property="taskName"/>
|
||||||
<result column="approval_type" jdbcType="VARCHAR" property="approvalType"/>
|
<result column="approval_type" jdbcType="VARCHAR" property="approvalType"/>
|
||||||
<result column="delegate_assignee" jdbcType="VARCHAR" property="delegateAssginee"/>
|
<result column="delegate_assignee" jdbcType="VARCHAR" property="delegateAssginee"/>
|
||||||
|
<result column="custom_business_data" jdbcType="LONGVARCHAR" property="customBusinessData"/>
|
||||||
<result column="comment" jdbcType="VARCHAR" property="comment"/>
|
<result column="comment" jdbcType="VARCHAR" property="comment"/>
|
||||||
<result column="create_user_id" jdbcType="BIGINT" property="createUserId"/>
|
<result column="create_user_id" jdbcType="BIGINT" property="createUserId"/>
|
||||||
|
<result column="create_login_name" jdbcType="VARCHAR" property="createLoginName"/>
|
||||||
<result column="create_username" jdbcType="VARCHAR" property="createUsername"/>
|
<result column="create_username" jdbcType="VARCHAR" property="createUsername"/>
|
||||||
<result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
|
<result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
<result column="role_ids" jdbcType="VARCHAR" property="roleIds"/>
|
<result column="role_ids" jdbcType="VARCHAR" property="roleIds"/>
|
||||||
<result column="dept_ids" jdbcType="VARCHAR" property="deptIds"/>
|
<result column="dept_ids" jdbcType="VARCHAR" property="deptIds"/>
|
||||||
<result column="candidate_usernames" jdbcType="VARCHAR" property="candidateUsernames"/>
|
<result column="candidate_usernames" jdbcType="VARCHAR" property="candidateUsernames"/>
|
||||||
|
<result column="copy_list_json" jdbcType="VARCHAR" property="copyListJson"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<insert id="insertList">
|
<insert id="insertList">
|
||||||
@@ -26,7 +27,8 @@
|
|||||||
#{item.deptPostListJson},
|
#{item.deptPostListJson},
|
||||||
#{item.roleIds},
|
#{item.roleIds},
|
||||||
#{item.deptIds},
|
#{item.deptIds},
|
||||||
#{item.candidateUsernames})
|
#{item.candidateUsernames},
|
||||||
|
#{item.copyListJson})
|
||||||
</foreach>
|
</foreach>
|
||||||
</insert>
|
</insert>
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ public class FlowFinishedListener implements ExecutionListener {
|
|||||||
flowWorkOrderService.updateFlowStatusByProcessInstanceId(processInstanceId, FlowTaskStatus.FINISHED);
|
flowWorkOrderService.updateFlowStatusByProcessInstanceId(processInstanceId, FlowTaskStatus.FINISHED);
|
||||||
String businessKey = execution.getProcessInstanceBusinessKey();
|
String businessKey = execution.getProcessInstanceBusinessKey();
|
||||||
FlowWorkOrder workOrder = flowWorkOrderService.getFlowWorkOrderByProcessInstanceId(processInstanceId);
|
FlowWorkOrder workOrder = flowWorkOrderService.getFlowWorkOrderByProcessInstanceId(processInstanceId);
|
||||||
flowCustomExtFactory.getDataSyncExtHelper()
|
flowCustomExtFactory.getBusinessDataExtHelper()
|
||||||
.triggerSync(workOrder.getProcessDefinitionKey(), processInstanceId, businessKey);
|
.triggerSync(workOrder.getProcessDefinitionKey(), processInstanceId, businessKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ public class FlowEntry {
|
|||||||
/**
|
/**
|
||||||
* 最新发布时间。
|
* 最新发布时间。
|
||||||
*/
|
*/
|
||||||
@TableField(value = "lastest_publish_time")
|
@TableField(value = "latest_publish_time")
|
||||||
private Date lastestPublishTime;
|
private Date latestPublishTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 流程状态。
|
* 流程状态。
|
||||||
|
|||||||
@@ -123,6 +123,12 @@ public class FlowMessage {
|
|||||||
@TableField(value = "business_data_shot")
|
@TableField(value = "business_data_shot")
|
||||||
private String businessDataShot;
|
private String businessDataShot;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为在线表单消息数据。
|
||||||
|
*/
|
||||||
|
@TableField(value = "online_form_data")
|
||||||
|
private Boolean onlineFormData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新时间。
|
* 更新时间。
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package com.orangeforms.common.flow.model;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程任务消息所属用户的操作表。
|
||||||
|
*
|
||||||
|
* @author Jerry
|
||||||
|
* @date 2021-06-06
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName(value = "zz_flow_message_identity_operation")
|
||||||
|
public class FlowMessageIdentityOperation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键Id。
|
||||||
|
*/
|
||||||
|
@TableId(value = "id")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务消息Id。
|
||||||
|
*/
|
||||||
|
@TableField(value = "message_id")
|
||||||
|
private Long messageId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户登录名。
|
||||||
|
*/
|
||||||
|
@TableField(value = "login_name")
|
||||||
|
private String loginName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作类型。
|
||||||
|
* 常量值参考FlowMessageOperationType对象。
|
||||||
|
*/
|
||||||
|
@TableField(value = "operation_type")
|
||||||
|
private Integer operationType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作时间。
|
||||||
|
*/
|
||||||
|
@TableField(value = "operation_time")
|
||||||
|
private Date operationTime;
|
||||||
|
}
|
||||||
@@ -70,12 +70,24 @@ public class FlowTaskComment {
|
|||||||
@TableField(value = "delegate_assignee")
|
@TableField(value = "delegate_assignee")
|
||||||
private String delegateAssginee;
|
private String delegateAssginee;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义数据。开发者可自行扩展,推荐使用JSON格式数据。
|
||||||
|
*/
|
||||||
|
@TableField(value = "custom_business_data")
|
||||||
|
private String customBusinessData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建者Id。
|
* 创建者Id。
|
||||||
*/
|
*/
|
||||||
@TableField(value = "create_user_id")
|
@TableField(value = "create_user_id")
|
||||||
private Long createUserId;
|
private Long createUserId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建者登录名。
|
||||||
|
*/
|
||||||
|
@TableField(value = "create_login_name")
|
||||||
|
private String createLoginName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建者显示名。
|
* 创建者显示名。
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -72,4 +72,10 @@ public class FlowTaskExt {
|
|||||||
*/
|
*/
|
||||||
@TableField(value = "candidate_usernames")
|
@TableField(value = "candidate_usernames")
|
||||||
private String candidateUsernames;
|
private String candidateUsernames;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抄送相关的数据。
|
||||||
|
*/
|
||||||
|
@TableField(value = "copy_list_json")
|
||||||
|
private String copyListJson;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.orangeforms.common.flow.model.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工作流消息操作类型。
|
||||||
|
*
|
||||||
|
* @author Jerry
|
||||||
|
* @date 2021-06-06
|
||||||
|
*/
|
||||||
|
public final class FlowMessageOperationType {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已读操作。
|
||||||
|
*/
|
||||||
|
public static final int READ_FINISHED = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 私有构造函数,明确标识该常量类的作用。
|
||||||
|
*/
|
||||||
|
private FlowMessageOperationType() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,9 +16,15 @@ public final class FlowMessageType {
|
|||||||
*/
|
*/
|
||||||
public static final int REMIND_TYPE = 0;
|
public static final int REMIND_TYPE = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抄送消息。
|
||||||
|
*/
|
||||||
|
public static final int COPY_TYPE = 1;
|
||||||
|
|
||||||
private static final Map<Object, String> DICT_MAP = new HashMap<>(2);
|
private static final Map<Object, String> DICT_MAP = new HashMap<>(2);
|
||||||
static {
|
static {
|
||||||
DICT_MAP.put(REMIND_TYPE, "催办消息");
|
DICT_MAP.put(REMIND_TYPE, "催办消息");
|
||||||
|
DICT_MAP.put(COPY_TYPE, "抄送消息");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -402,12 +402,13 @@ public interface FlowApiService {
|
|||||||
BpmnModel convertToBpmnModel(String bpmnXml) throws XMLStreamException;
|
BpmnModel convertToBpmnModel(String bpmnXml) throws XMLStreamException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 回退到上一个用户任务节点。
|
* 回退到上一个用户任务节点。如果没有指定,则回退到上一个任务。
|
||||||
*
|
*
|
||||||
* @param task 当前活动任务。
|
* @param task 当前活动任务。
|
||||||
|
* @param targetKey 指定回退到的任务标识。如果为null,则回退到上一个任务。
|
||||||
* @param forReject true表示驳回,false为撤回。
|
* @param forReject true表示驳回,false为撤回。
|
||||||
* @param reason 驳回或者撤销的原因。
|
* @param reason 驳回或者撤销的原因。
|
||||||
* @return 回退结果。
|
* @return 回退结果。
|
||||||
*/
|
*/
|
||||||
CallResult backToLastRuntimeTask(Task task, boolean forReject, String reason);
|
CallResult backToRuntimeTask(Task task, String targetKey, boolean forReject, String reason);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.orangeforms.common.flow.service;
|
package com.orangeforms.common.flow.service;
|
||||||
|
|
||||||
import com.orangeforms.common.core.base.service.IBaseService;
|
import com.orangeforms.common.core.base.service.IBaseService;
|
||||||
import com.orangeforms.common.core.object.CallResult;
|
|
||||||
import com.orangeforms.common.flow.model.*;
|
import com.orangeforms.common.flow.model.*;
|
||||||
|
|
||||||
import javax.xml.stream.XMLStreamException;
|
import javax.xml.stream.XMLStreamException;
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package com.orangeforms.common.flow.service;
|
package com.orangeforms.common.flow.service;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.orangeforms.common.core.base.service.IBaseService;
|
import com.orangeforms.common.core.base.service.IBaseService;
|
||||||
import com.orangeforms.common.flow.model.FlowMessage;
|
import com.orangeforms.common.flow.model.FlowMessage;
|
||||||
import com.orangeforms.common.flow.model.FlowWorkOrder;
|
import com.orangeforms.common.flow.model.FlowWorkOrder;
|
||||||
|
import org.flowable.task.api.Task;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -29,6 +31,14 @@ public interface FlowMessageService extends IBaseService<FlowMessage, Long> {
|
|||||||
*/
|
*/
|
||||||
void saveNewRemindMessage(FlowWorkOrder flowWorkOrder);
|
void saveNewRemindMessage(FlowWorkOrder flowWorkOrder);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存抄送消息对象。
|
||||||
|
*
|
||||||
|
* @param task 待抄送的任务。
|
||||||
|
* @param copyDataJson 抄送人员或者组的Id数据。
|
||||||
|
*/
|
||||||
|
void saveNewCopyMessage(Task task, JSONObject copyDataJson);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新指定运行时任务Id的消费为已完成状态。
|
* 更新指定运行时任务Id的消费为已完成状态。
|
||||||
*
|
*
|
||||||
@@ -49,4 +59,48 @@ public interface FlowMessageService extends IBaseService<FlowMessage, Long> {
|
|||||||
* @return 查询后的催办消息列表。
|
* @return 查询后的催办消息列表。
|
||||||
*/
|
*/
|
||||||
List<FlowMessage> getRemindingMessageListByUser();
|
List<FlowMessage> getRemindingMessageListByUser();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前用户的抄送消息列表。
|
||||||
|
*
|
||||||
|
* @param read true表示已读,false表示未读。
|
||||||
|
* @return 查询后的抄送消息列表。
|
||||||
|
*/
|
||||||
|
List<FlowMessage> getCopyMessageListByUser(Boolean read);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断当前用户是否有权限访问指定消息Id。
|
||||||
|
*
|
||||||
|
* @param messageId 消息Id。
|
||||||
|
* @return true为合法访问者,否则false。
|
||||||
|
*/
|
||||||
|
boolean isCandidateIdentityOnMessage(Long messageId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取抄送消息,同时更新当前用户对指定抄送消息的读取状态。
|
||||||
|
*
|
||||||
|
* @param messageId 消息Id。
|
||||||
|
*/
|
||||||
|
void readCopyTask(Long messageId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算当前用户催办消息的数量。
|
||||||
|
*
|
||||||
|
* @return 当前用户催办消息数量。
|
||||||
|
*/
|
||||||
|
int countRemindingMessageListByUser();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算当前用户未读抄送消息的数量。
|
||||||
|
*
|
||||||
|
* @return 当前用户未读抄送消息数量。
|
||||||
|
*/
|
||||||
|
int countCopyMessageByUser();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除指定流程实例的消息。
|
||||||
|
*
|
||||||
|
* @param processInstanceId 流程实例Id。
|
||||||
|
*/
|
||||||
|
void removeByProcessInstanceId(String processInstanceId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,4 +44,21 @@ public interface FlowTaskCommentService extends IBaseService<FlowTaskComment, Lo
|
|||||||
* @return 查询结果。
|
* @return 查询结果。
|
||||||
*/
|
*/
|
||||||
FlowTaskComment getLatestFlowTaskComment(String processInstanceId);
|
FlowTaskComment getLatestFlowTaskComment(String processInstanceId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定流程实例和任务定义标识的最后一条审批任务。
|
||||||
|
*
|
||||||
|
* @param processInstanceId 流程实例Id。
|
||||||
|
* @param taskDefinitionKey 任务定义标识。
|
||||||
|
* @return 查询结果。
|
||||||
|
*/
|
||||||
|
FlowTaskComment getLatestFlowTaskComment(String processInstanceId, String taskDefinitionKey);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定流程实例的第一条审批任务。
|
||||||
|
*
|
||||||
|
* @param processInstanceId 流程实例Id。
|
||||||
|
* @return 查询结果。
|
||||||
|
*/
|
||||||
|
FlowTaskComment getFirstFlowTaskComment(String processInstanceId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.orangeforms.common.flow.service;
|
|||||||
|
|
||||||
import com.orangeforms.common.core.base.service.IBaseService;
|
import com.orangeforms.common.core.base.service.IBaseService;
|
||||||
import com.orangeforms.common.flow.model.FlowWorkOrder;
|
import com.orangeforms.common.flow.model.FlowWorkOrder;
|
||||||
|
import com.orangeforms.common.flow.vo.FlowWorkOrderVo;
|
||||||
import org.flowable.engine.runtime.ProcessInstance;
|
import org.flowable.engine.runtime.ProcessInstance;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -83,4 +84,19 @@ public interface FlowWorkOrderService extends IBaseService<FlowWorkOrder, Long>
|
|||||||
* @param flowStatus 新的流程状态值。
|
* @param flowStatus 新的流程状态值。
|
||||||
*/
|
*/
|
||||||
void updateFlowStatusByProcessInstanceId(String processInstanceId, int flowStatus);
|
void updateFlowStatusByProcessInstanceId(String processInstanceId, int flowStatus);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否有查看该工单的数据权限。
|
||||||
|
* @param processInstanceId 流程实例Id。
|
||||||
|
* @return 存在返回true,否则false。
|
||||||
|
*/
|
||||||
|
boolean hasDataPermOnFlowWorkOrder(String processInstanceId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据工单列表中的submitUserName,找到映射的userShowName,并会写到Vo中指定字段。
|
||||||
|
* 同时这也是一个如何通过插件方法,将loginName映射到showName的示例,
|
||||||
|
*
|
||||||
|
* @param dataList 工单Vo对象列表。
|
||||||
|
*/
|
||||||
|
void fillUserShowNameByLoginName(List<FlowWorkOrderVo> dataList);
|
||||||
}
|
}
|
||||||
|
|||||||