新增 CLAUDE.md 文件以提供项目指导,添加 .claudeignore 文件以排除不必要的文件,更新 pom.xml 版本至 3.9.2,修复多个路径遍历和 SQL 注入漏洞,优化字典翻译切面逻辑,增强文件上传和下载的安全性,新增音频文件类型支持,改进动态数据源的安全校验。
This commit is contained in:
@@ -1,74 +0,0 @@
|
||||
package org.jeecg.modules.airag;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.common.exception.JeecgBootException;
|
||||
import org.jeecg.common.util.DateUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.airag.flow.component.enhance.IAiRagEnhanceJava;
|
||||
import org.jeecg.modules.airag.wordtpl.entity.EoaWordTemplate;
|
||||
import org.jeecg.modules.airag.wordtpl.service.IEoaWordTemplateService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @Description: JavaAIFlow增强节点:生成在线word文档
|
||||
* @Author: chenrui
|
||||
* @Date: 2025-08-06 16:39
|
||||
*/
|
||||
@Slf4j
|
||||
@Component("jeecgDemoAiWordGen")
|
||||
public class TestAiGenWordEnhance implements IAiRagEnhanceJava {
|
||||
|
||||
@Autowired
|
||||
IEoaWordTemplateService eoaWordTemplateService;
|
||||
|
||||
@Override
|
||||
public Map<String, Object> process(Map<String, Object> inputParams) {
|
||||
Object resp = inputParams.get("resp");
|
||||
String respStr = String.valueOf(resp);
|
||||
log.info("AI生成word响应内容:{}", respStr);
|
||||
if(oConvertUtils.isEmpty(respStr)){
|
||||
throw new JeecgBootException("AI生成内容失败。请稍后再试或查看后台日志。");
|
||||
}
|
||||
String mainStr = null;
|
||||
Matcher matcher = Pattern.compile("\\[.*]", Pattern.DOTALL).matcher(respStr);
|
||||
if (matcher.find()) {
|
||||
mainStr = matcher.group();
|
||||
// 替换中文双引号为英文双引号
|
||||
mainStr = mainStr.replaceAll("[“”]", "\"");
|
||||
// 替换 NBSP 为普通空格
|
||||
mainStr = mainStr.replaceAll("\\u00A0", " ");
|
||||
|
||||
log.info("生成word json:{}", mainStr);
|
||||
// 校验是否为合法 JSON 字符串
|
||||
try {
|
||||
JSON.parse(mainStr);
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
throw new JeecgBootException("AI生成的内容不是合法的 JSON 字符串,请稍后再试或优化提示词。");
|
||||
}
|
||||
}else{
|
||||
throw new JeecgBootException("AI生成的内容不是合法的 JSON 字符串,请稍后再试或优化提示词。");
|
||||
}
|
||||
|
||||
EoaWordTemplate template = new EoaWordTemplate();
|
||||
String dateFormat = DateUtils.formatDate();
|
||||
template.setName("AI生成的简历_"+dateFormat);
|
||||
template.setCode("AI_GEN_"+System.currentTimeMillis());
|
||||
template.setHeader("[]");
|
||||
template.setFooter("[]");
|
||||
template.setMain(mainStr);
|
||||
template.setWidth(794);
|
||||
template.setHeight(1123);
|
||||
template.setMargins("[100,120,100,120]");
|
||||
template.setPaperDirection("vertical");
|
||||
eoaWordTemplateService.save(template);
|
||||
return Collections.singletonMap("result","success");
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -196,6 +197,17 @@ public class SystemApiController {
|
||||
return sysBaseApi.getDepartParentIdsByDepIds(depIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 userIds 查询部门ID列表
|
||||
*
|
||||
* @param userIds
|
||||
* @return key = userId; value = 用户拥有的部门ID列表
|
||||
*/
|
||||
@GetMapping("/getDepartIdsByUserIds")
|
||||
Map<String, List<String>> getDepartIdsByUserIds(@RequestParam("userIds") Collection<String> userIds) {
|
||||
return sysBaseApi.getDepartIdsByUserIds(userIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过用户账号查询部门 name
|
||||
* @param username
|
||||
@@ -1124,4 +1136,20 @@ public class SystemApiController {
|
||||
public void uniPushMsgToUser(@RequestBody PushMessageDTO pushMessageDTO){
|
||||
sysBaseApi.uniPushMsgToUser(pushMessageDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户名查询用户主部门信息。
|
||||
* <p>
|
||||
* 逻辑:取用户的主岗位(mainDepPostId),再查询该岗位节点在 sys_depart 中的父节点,
|
||||
* 父节点即为用户的主部门,返回其信息。
|
||||
* <p>
|
||||
*
|
||||
* @param username 用户账号
|
||||
* @return 主部门信息,若用户未配置主岗位则返回 {@code null}
|
||||
*/
|
||||
@GetMapping("/queryMainDepartByUsername")
|
||||
SysDepartModel queryMainDepartByUsername(@RequestParam("username") String username) {
|
||||
return sysBaseApi.queryMainDepartByUsername(username);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -70,10 +70,16 @@ public final class XmlUtils {
|
||||
*/
|
||||
public static XMLReader getXmlReader() {
|
||||
try {
|
||||
final XMLReader reader = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
|
||||
//update-begin---author:wangshuai---date:2026-03-30---for:【issues/9422】XmlUtils.extractCustomAttributes可能存在疑似的外部实体依赖漏洞---
|
||||
final SAXParserFactory spf = SAXParserFactory.newInstance();
|
||||
spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||
spf.setFeature("http://xml.org/sax/features/external-general-entities", false);
|
||||
spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
|
||||
spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
|
||||
final XMLReader reader = spf.newSAXParser().getXMLReader();
|
||||
//update-end---author:wangshuai---date:2026-03-30---for:【issues/9422】XmlUtils.extractCustomAttributes可能存在疑似的外部实体依赖漏洞---
|
||||
reader.setFeature("http://xml.org/sax/features/namespaces", true);
|
||||
reader.setFeature("http://xml.org/sax/features/namespace-prefixes", false);
|
||||
reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
|
||||
return reader;
|
||||
} catch (final Exception e) {
|
||||
throw new RuntimeException("Unable to create XMLReader", e);
|
||||
@@ -196,6 +202,12 @@ public final class XmlUtils {
|
||||
spf.setNamespaceAware(true);
|
||||
spf.setValidating(false);
|
||||
try {
|
||||
//update-begin---author:wangshuai---date:2026-03-30---for:【issues/9422】XmlUtils.extractCustomAttributes可能存在疑似的外部实体依赖漏洞---
|
||||
spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||
spf.setFeature("http://xml.org/sax/features/external-general-entities", false);
|
||||
spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
|
||||
spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
|
||||
//update-end---author:wangshuai---date:2026-03-30---for:【issues/9422】XmlUtils.extractCustomAttributes可能存在疑似的外部实体依赖漏洞---
|
||||
final SAXParser saxParser = spf.newSAXParser();
|
||||
final XMLReader xmlReader = saxParser.getXMLReader();
|
||||
final CustomAttributeHandler handler = new CustomAttributeHandler();
|
||||
|
||||
@@ -9,11 +9,13 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.exception.JeecgBootBizTipException;
|
||||
import org.jeecg.common.system.base.controller.JeecgController;
|
||||
import org.jeecg.common.system.query.QueryGenerator;
|
||||
import org.jeecg.common.system.util.JwtUtil;
|
||||
import org.jeecg.common.util.CommonUtils;
|
||||
import org.jeecg.common.util.RedisUtil;
|
||||
import org.jeecg.common.util.RestUtil;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.openapi.entity.OpenApi;
|
||||
import org.jeecg.modules.openapi.entity.OpenApiAuth;
|
||||
import org.jeecg.modules.openapi.entity.OpenApiHeader;
|
||||
@@ -24,6 +26,7 @@ import org.jeecg.modules.openapi.service.OpenApiService;
|
||||
import org.jeecg.modules.openapi.swagger.*;
|
||||
import org.jeecg.modules.system.entity.SysUser;
|
||||
import org.jeecg.modules.system.service.ISysUserService;
|
||||
import org.apache.shiro.authz.annotation.RequiresRoles;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
@@ -78,8 +81,13 @@ public class OpenApiController extends JeecgController<OpenApi, OpenApiService>
|
||||
* @param openApi
|
||||
* @return
|
||||
*/
|
||||
@RequiresRoles({"admin"})
|
||||
@PostMapping(value = "/add")
|
||||
public Result<?> add(@RequestBody OpenApi openApi) {
|
||||
if (openApi == null) {
|
||||
return Result.error("请求参数不能为空");
|
||||
}
|
||||
validOriginUrl(openApi.getOriginUrl());
|
||||
service.save(openApi);
|
||||
return Result.ok("添加成功!");
|
||||
}
|
||||
@@ -90,8 +98,13 @@ public class OpenApiController extends JeecgController<OpenApi, OpenApiService>
|
||||
* @param openApi
|
||||
* @return
|
||||
*/
|
||||
@RequiresRoles({"admin"})
|
||||
@PutMapping(value = "/edit")
|
||||
public Result<?> edit(@RequestBody OpenApi openApi) {
|
||||
if (openApi == null) {
|
||||
return Result.error("请求参数不能为空");
|
||||
}
|
||||
validOriginUrl(openApi.getOriginUrl());
|
||||
service.updateById(openApi);
|
||||
return Result.ok("修改成功!");
|
||||
|
||||
@@ -103,6 +116,7 @@ public class OpenApiController extends JeecgController<OpenApi, OpenApiService>
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
@RequiresRoles({"admin"})
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<?> delete(@RequestParam(name = "id", required = true) String id) {
|
||||
service.removeById(id);
|
||||
@@ -115,6 +129,7 @@ public class OpenApiController extends JeecgController<OpenApi, OpenApiService>
|
||||
* @param ids
|
||||
* @return
|
||||
*/
|
||||
@RequiresRoles({"admin"})
|
||||
@DeleteMapping(value = "/deleteBatch")
|
||||
public Result<?> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
|
||||
|
||||
@@ -159,6 +174,8 @@ public class OpenApiController extends JeecgController<OpenApi, OpenApiService>
|
||||
}
|
||||
|
||||
String url = openApi.getOriginUrl();
|
||||
// 校验原始接口路径是否合法
|
||||
validOriginUrl(url);
|
||||
String method = openApi.getRequestMethod();
|
||||
String appkey = request.getHeader("appkey");
|
||||
OpenApiAuth openApiAuth = openApiAuthService.getByAppkey(appkey);
|
||||
@@ -167,7 +184,17 @@ public class OpenApiController extends JeecgController<OpenApi, OpenApiService>
|
||||
httpHeaders.put("X-Access-Token", Lists.newArrayList(token));
|
||||
httpHeaders.put("Content-Type",Lists.newArrayList("application/json"));
|
||||
HttpEntity<String> httpEntity = new HttpEntity<>(json, httpHeaders);
|
||||
url = RestUtil.getBaseUrl() + url;
|
||||
//update-begin---author:scott ---date:20260429 for:【issues/9590】微服务nginx部署openApi接口访问不到-----------
|
||||
// originUrl 支持两种形式:
|
||||
// 1) 相对路径(如 /house/houseTest/list):拼接当前请求的 baseUrl;
|
||||
// 使用 CommonUtils.getBaseUrl(request)(而非 RestUtil.getBaseUrl()),
|
||||
// 可读取 X-Gateway-Base-Path 请求头,兼容微服务网关下的真实 base path
|
||||
// 2) 完整URL(http(s)://host:port/path):直接使用,适用于微服务模式下接口部署在其他微服务模块(如 erp 7003)的场景
|
||||
String lowerUrl = url.toLowerCase();
|
||||
if (!lowerUrl.startsWith("http://") && !lowerUrl.startsWith("https://")) {
|
||||
url = CommonUtils.getBaseUrl(request) + url;
|
||||
}
|
||||
//update-end---author:scott ---date:20260429 for:【issues/9590】微服务nginx部署openApi接口访问不到-----------
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
|
||||
if (HttpMethod.GET.matches(method)
|
||||
|| HttpMethod.DELETE.matches(method)
|
||||
@@ -213,6 +240,51 @@ public class OpenApiController extends JeecgController<OpenApi, OpenApiService>
|
||||
return token;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验原始接口路径是否合法:
|
||||
* - 相对路径:必须以 / 开头,不允许 // 和 .. 防止路径穿越
|
||||
* - 完整URL:仅允许 http/https 协议,禁止 file/ftp/gopher/jar/netdoc 等其它协议(用于微服务模式跨模块调用)
|
||||
*/
|
||||
private void validOriginUrl(String originUrl) {
|
||||
if (oConvertUtils.isEmpty(originUrl)) {
|
||||
throw new JeecgBootBizTipException("原始接口路径不能为空");
|
||||
}
|
||||
String decoded;
|
||||
try {
|
||||
decoded = java.net.URLDecoder.decode(originUrl, "UTF-8");
|
||||
// 二次解码,防止 %252f 这类双重编码绕过
|
||||
decoded = java.net.URLDecoder.decode(decoded, "UTF-8");
|
||||
} catch (Exception e) {
|
||||
throw new JeecgBootBizTipException("原始接口路径包含非法字符");
|
||||
}
|
||||
//update-begin---author:scott ---date:20260429 for:【issues/9590】微服务nginx部署openApi接口访问不到-----------
|
||||
// 微服务部署时,OpenAPI 配置的接口可能位于其他微服务模块(如 erp 7003),允许 originUrl 直接配置完整 http(s) URL
|
||||
String lower = decoded.toLowerCase();
|
||||
boolean isFullHttpUrl = lower.startsWith("http://") || lower.startsWith("https://");
|
||||
if (!isFullHttpUrl) {
|
||||
if (!decoded.startsWith("/")) {
|
||||
throw new JeecgBootBizTipException("原始接口路径必须以 / 开头,或填写完整的 http(s) URL");
|
||||
}
|
||||
if (decoded.startsWith("//") || decoded.startsWith("/\\")) {
|
||||
throw new JeecgBootBizTipException("原始接口路径不能以 // 或 /\\ 开头");
|
||||
}
|
||||
if (lower.contains("://") || lower.startsWith("file:") || lower.startsWith("ftp:") || lower.startsWith("gopher:")
|
||||
|| lower.startsWith("jar:") || lower.startsWith("netdoc:")) {
|
||||
throw new JeecgBootBizTipException("原始接口路径仅支持相对路径或 http(s) 完整URL");
|
||||
}
|
||||
} else {
|
||||
// 即便是完整URL,也禁止其它危险协议(防止 http://x@file:/... 之类的绕过场景)
|
||||
String afterScheme = lower.substring(lower.indexOf("://") + 3);
|
||||
if (afterScheme.contains("file:") || afterScheme.contains("ftp:") || afterScheme.contains("gopher:")
|
||||
|| afterScheme.contains("jar:") || afterScheme.contains("netdoc:")) {
|
||||
throw new JeecgBootBizTipException("原始接口路径不允许嵌套 file/ftp/gopher/jar/netdoc 等协议");
|
||||
}
|
||||
}
|
||||
if (decoded.contains("..")) {
|
||||
throw new JeecgBootBizTipException("原始接口路径不能包含 ..");
|
||||
}
|
||||
//update-end---author:scott ---date:20260429 for:【issues/9590】微服务nginx部署openApi接口访问不到-----------
|
||||
}
|
||||
|
||||
@GetMapping("/json")
|
||||
public SwaggerModel swaggerModel() {
|
||||
@@ -382,7 +454,7 @@ public class OpenApiController extends JeecgController<OpenApi, OpenApiService>
|
||||
SwaggerInfo info = new SwaggerInfo();
|
||||
|
||||
info.setDescription("OpenAPI 接口列表");
|
||||
info.setVersion("3.9.1");
|
||||
info.setVersion("3.9.2");
|
||||
info.setTitle("OpenAPI 接口列表");
|
||||
info.setTermsOfService("https://jeecg.com");
|
||||
|
||||
|
||||
@@ -43,9 +43,16 @@ public class OpenApi implements Serializable {
|
||||
private String requestUrl;
|
||||
|
||||
/**
|
||||
* IP 黑名单
|
||||
* IP 白名单
|
||||
*/
|
||||
private String blackList;
|
||||
private String whiteList;
|
||||
|
||||
//update-begin---author:scott ---date:20260417 for:【PR/9083】OpenAPI新增白名单备注字段-----------
|
||||
/**
|
||||
* 白名单备注说明
|
||||
*/
|
||||
private String comment;
|
||||
//update-end---author:scott ---date:20260417 for:【PR/9083】OpenAPI新增白名单备注字段-----------
|
||||
/**
|
||||
* 请求头json
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,7 @@ import jakarta.servlet.*;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.common.exception.JeecgBootException;
|
||||
import org.jeecg.common.util.IpUtils;
|
||||
import org.jeecg.modules.openapi.entity.OpenApi;
|
||||
import org.jeecg.modules.openapi.entity.OpenApiAuth;
|
||||
import org.jeecg.modules.openapi.entity.OpenApiLog;
|
||||
@@ -20,6 +21,7 @@ import java.security.MessageDigest;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @date 2024/12/19 16:55
|
||||
@@ -38,7 +40,7 @@ public class ApiAuthFilter implements Filter {
|
||||
Date callTime = new Date();
|
||||
|
||||
HttpServletRequest request = (HttpServletRequest)servletRequest;
|
||||
String ip = request.getRemoteAddr();
|
||||
String ip = IpUtils.getIpAddr(request);
|
||||
|
||||
String appkey = request.getHeader("appkey");
|
||||
String signature = request.getHeader("signature");
|
||||
@@ -46,8 +48,8 @@ public class ApiAuthFilter implements Filter {
|
||||
|
||||
OpenApi openApi = findOpenApi(request);
|
||||
|
||||
// IP 黑名单核验
|
||||
checkBlackList(openApi, ip);
|
||||
// IP 白名单核验
|
||||
checkWhiteList(openApi, ip);
|
||||
|
||||
// 签名核验
|
||||
checkSignValid(appkey, signature, timestamp);
|
||||
@@ -80,22 +82,108 @@ public class ApiAuthFilter implements Filter {
|
||||
this.openApiPermissionService = applicationContext.getBean(OpenApiPermissionService.class);
|
||||
}
|
||||
|
||||
//update-begin---author:scott ---date:20260416 for:【PR/9083】OpenAPI白名单增强,支持CIDR网段和通配符匹配-----------
|
||||
/**
|
||||
* IP 黑名单核验
|
||||
* IP 白名单核验,支持精确IP、CIDR网段(如192.168.1.0/24)、通配符(如10.2.3.*)
|
||||
* @param openApi
|
||||
* @param ip
|
||||
*/
|
||||
protected void checkBlackList(OpenApi openApi, String ip) {
|
||||
if (!StringUtils.hasText(openApi.getBlackList())) {
|
||||
protected void checkWhiteList(OpenApi openApi, String ip) {
|
||||
if (!StringUtils.hasText(openApi.getWhiteList())) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> blackList = Arrays.asList(openApi.getBlackList().split(","));
|
||||
if (blackList.contains(ip)) {
|
||||
throw new JeecgBootException("目标接口限制IP[" + ip + "]进行访问,IP已记录,请停止访问");
|
||||
List<String> whiteList = Arrays.stream(openApi.getWhiteList().split("[,\\n]"))
|
||||
.map(String::trim)
|
||||
.filter(StringUtils::hasText)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (String item : whiteList) {
|
||||
if (isIpMatch(ip, item)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new JeecgBootException("IP[" + ip + "]不在白名单中,禁止访问");
|
||||
}
|
||||
|
||||
/**
|
||||
* IP匹配:支持精确匹配、CIDR网段匹配、通配符匹配
|
||||
* @param ip 客户端IP
|
||||
* @param pattern 白名单条目(IP/CIDR/通配符)
|
||||
* @return 是否匹配
|
||||
*/
|
||||
private boolean isIpMatch(String ip, String pattern) {
|
||||
if (!ip.contains(".") || !pattern.contains(".")) {
|
||||
return ip.equals(pattern);
|
||||
}
|
||||
if (pattern.contains("/")) {
|
||||
return isCidrMatch(ip, pattern);
|
||||
}
|
||||
if (pattern.contains("*")) {
|
||||
return isWildcardMatch(ip, pattern);
|
||||
}
|
||||
return ip.equals(pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* CIDR网段匹配(仅IPv4),如 192.168.1.0/24
|
||||
*/
|
||||
private boolean isCidrMatch(String ip, String cidr) {
|
||||
String[] parts = cidr.split("/");
|
||||
if (parts.length != 2) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
long ipLong = ipToLong(ip);
|
||||
long cidrLong = ipToLong(parts[0]);
|
||||
int prefixLength = Integer.parseInt(parts[1]);
|
||||
if (prefixLength < 0 || prefixLength > 32) {
|
||||
return false;
|
||||
}
|
||||
long mask = prefixLength == 0 ? 0 : (-1L << (32 - prefixLength));
|
||||
return (ipLong & mask) == (cidrLong & mask);
|
||||
} catch (Exception e) {
|
||||
log.warn("CIDR匹配解析失败: cidr={}, ip={}", cidr, ip);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通配符匹配,如 10.2.3.*
|
||||
*/
|
||||
private boolean isWildcardMatch(String ip, String pattern) {
|
||||
String[] ipParts = ip.split("\\.");
|
||||
String[] patternParts = pattern.split("\\.");
|
||||
if (ipParts.length != 4 || patternParts.length != 4) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if ("*".equals(patternParts[i])) {
|
||||
continue;
|
||||
}
|
||||
if (!ipParts[i].equals(patternParts[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* IPv4地址转long
|
||||
*/
|
||||
private long ipToLong(String ip) {
|
||||
String[] parts = ip.split("\\.");
|
||||
if (parts.length != 4) {
|
||||
throw new IllegalArgumentException("非法IPv4地址: " + ip);
|
||||
}
|
||||
long result = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
result = (result << 8) | (Integer.parseInt(parts[i]) & 0xFF);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
//update-end---author:scott ---date:20260416 for:【PR/9083】OpenAPI白名单增强,支持CIDR网段和通配符匹配-----------
|
||||
|
||||
/**
|
||||
* 签名验证
|
||||
* @param appkey
|
||||
|
||||
@@ -32,6 +32,7 @@ public class OssFileController {
|
||||
private IOssFileService ossFileService;
|
||||
|
||||
@ResponseBody
|
||||
@RequiresPermissions("system:ossFile:list")
|
||||
@GetMapping("/list")
|
||||
public Result<IPage<OssFile>> queryPageList(OssFile file,
|
||||
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@@ -63,6 +64,7 @@ public class OssFileController {
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@RequiresPermissions("system:ossFile:delete")
|
||||
@DeleteMapping("/delete")
|
||||
public Result delete(@RequestParam(name = "id") String id) {
|
||||
Result result = new Result();
|
||||
|
||||
@@ -1,344 +0,0 @@
|
||||
package org.jeecg.modules.print.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.print.PrintService;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.print.PageFormat;
|
||||
import java.awt.print.Printable;
|
||||
import java.awt.print.PrinterException;
|
||||
import java.awt.print.PrinterJob;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.aspect.annotation.AutoLog;
|
||||
import org.jeecg.common.system.base.controller.JeecgController;
|
||||
import org.jeecg.common.system.query.QueryGenerator;
|
||||
import org.jeecg.modules.print.entity.PrintTemplate;
|
||||
import org.jeecg.modules.print.service.IPrintTemplateService;
|
||||
import org.jeecg.modules.print.support.PrintServerEnvironmentService;
|
||||
import org.jeecg.modules.print.support.PrintServerPdfJobService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.jeecg.modules.print.ai.INativePrintTemplateImageAnalyzeService;
|
||||
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
|
||||
/**
|
||||
* 打印模板维护(Hiprint)
|
||||
*/
|
||||
@Slf4j
|
||||
@Tag(name = "打印模板")
|
||||
@RestController
|
||||
@RequestMapping("/print/template")
|
||||
public class PrintTemplateController extends JeecgController<PrintTemplate, IPrintTemplateService> {
|
||||
@Autowired private PrintServerEnvironmentService printServerEnvironmentService;
|
||||
@Autowired private PrintServerPdfJobService printServerPdfJobService;
|
||||
|
||||
@Autowired
|
||||
private INativePrintTemplateImageAnalyzeService nativePrintTemplateImageAnalyzeService;
|
||||
|
||||
/**
|
||||
* STOMP 实时通知:广播打印模板变更到 /topic/sync/print-templates。
|
||||
* 直接用 SimpMessagingTemplate 内联推送,避免 jeecg-system-biz(核心模块)
|
||||
* 反向依赖 jeecg-module-xslmes(业务模块)造成的循环依赖。
|
||||
* 消息体格式与 MesXslStompNotifyService.publishPrintTemplateChanged 完全一致,
|
||||
* 桌面端订阅方无需任何改动。
|
||||
*/
|
||||
@Autowired
|
||||
private SimpMessagingTemplate messagingTemplate;
|
||||
|
||||
@Operation(summary = "打印模板-分页列表")
|
||||
@GetMapping(value = "/list")
|
||||
@RequiresPermissions("print:template:list")
|
||||
public Result<IPage<PrintTemplate>> list(
|
||||
PrintTemplate query,
|
||||
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
|
||||
HttpServletRequest req) {
|
||||
QueryWrapper<PrintTemplate> qw = QueryGenerator.initQueryWrapper(query, req.getParameterMap());
|
||||
qw.orderByDesc("create_time");
|
||||
Page<PrintTemplate> page = new Page<>(pageNo, pageSize);
|
||||
return Result.OK(service.page(page, qw));
|
||||
}
|
||||
|
||||
@AutoLog(value = "打印模板-添加")
|
||||
@Operation(summary = "打印模板-添加")
|
||||
@PostMapping(value = "/add")
|
||||
@RequiresPermissions("print:template:add")
|
||||
public Result<String> add(@RequestBody PrintTemplate entity) {
|
||||
if (StringUtils.isBlank(entity.getTemplateCode())) {
|
||||
return Result.error("模板编码不能为空");
|
||||
}
|
||||
if (service.getByCode(entity.getTemplateCode()) != null) {
|
||||
return Result.error("模板编码已存在");
|
||||
}
|
||||
if (StringUtils.isBlank(entity.getTemplateJson())) {
|
||||
entity.setTemplateJson("{}");
|
||||
}
|
||||
service.save(entity);
|
||||
publishPrintTemplateChanged("add", entity.getId());
|
||||
return Result.OK("添加成功");
|
||||
}
|
||||
|
||||
@AutoLog(value = "打印模板-编辑")
|
||||
@Operation(summary = "打印模板-编辑")
|
||||
@RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
|
||||
@RequiresPermissions("print:template:edit")
|
||||
public Result<String> edit(@RequestBody PrintTemplate entity) {
|
||||
PrintTemplate db = service.getById(entity.getId());
|
||||
if (db == null) {
|
||||
return Result.error("记录不存在");
|
||||
}
|
||||
if (StringUtils.isNotBlank(entity.getTemplateCode())
|
||||
&& !entity.getTemplateCode().equals(db.getTemplateCode())) {
|
||||
if (service.getByCode(entity.getTemplateCode()) != null) {
|
||||
return Result.error("模板编码已存在");
|
||||
}
|
||||
}
|
||||
service.updateById(entity);
|
||||
publishPrintTemplateChanged("edit", entity.getId());
|
||||
return Result.OK("修改成功");
|
||||
}
|
||||
|
||||
@AutoLog(value = "打印模板-保存JSON")
|
||||
@Operation(summary = "打印模板-保存模板JSON")
|
||||
@PostMapping(value = "/saveJson")
|
||||
@RequiresPermissions("print:template:edit")
|
||||
public Result<String> saveJson(@RequestBody Map<String, String> body) {
|
||||
String id = body.get("id");
|
||||
String templateJson = body.get("templateJson");
|
||||
if (StringUtils.isBlank(id)) {
|
||||
return Result.error("id 不能为空");
|
||||
}
|
||||
if (templateJson == null) {
|
||||
return Result.error("templateJson 不能为空");
|
||||
}
|
||||
PrintTemplate db = service.getById(id);
|
||||
if (db == null) {
|
||||
return Result.error("记录不存在");
|
||||
}
|
||||
db.setTemplateJson(templateJson);
|
||||
service.updateById(db);
|
||||
return Result.OK("保存成功");
|
||||
}
|
||||
|
||||
@AutoLog(value = "打印模板-删除")
|
||||
@Operation(summary = "打印模板-删除")
|
||||
@DeleteMapping(value = "/delete")
|
||||
@RequiresPermissions("print:template:delete")
|
||||
public Result<String> delete(@RequestParam(name = "id") String id) {
|
||||
service.removeById(id);
|
||||
publishPrintTemplateChanged("delete", id);
|
||||
return Result.OK("删除成功");
|
||||
}
|
||||
|
||||
@AutoLog(value = "打印模板-批量删除")
|
||||
@Operation(summary = "打印模板-批量删除")
|
||||
@DeleteMapping(value = "/deleteBatch")
|
||||
@RequiresPermissions("print:template:delete")
|
||||
public Result<String> deleteBatch(@RequestParam(name = "ids") String ids) {
|
||||
if (StringUtils.isBlank(ids)) {
|
||||
return Result.error("参数 ids 不能为空");
|
||||
}
|
||||
List<String> idList = java.util.Arrays.asList(ids.split(","));
|
||||
service.removeByIds(idList);
|
||||
idList.forEach(id -> publishPrintTemplateChanged("delete", id.trim()));
|
||||
return Result.OK("批量删除成功");
|
||||
}
|
||||
|
||||
@Operation(summary = "打印模板-通过id查询")
|
||||
@GetMapping(value = "/queryById")
|
||||
@RequiresPermissions("print:template:list")
|
||||
public Result<PrintTemplate> queryById(@RequestParam(name = "id") String id) {
|
||||
return Result.OK(service.getById(id));
|
||||
}
|
||||
|
||||
@AutoLog(value = "打印模板-图片分析生成原生JSON")
|
||||
@Operation(summary = "打印模板-上传图片分析为原生模板JSON(前端传 imageBase64;可接 OpenAI 兼容视觉模型)")
|
||||
@PostMapping(value = "/analyzeImageForNative")
|
||||
@RequiresPermissions("print:template:edit")
|
||||
public Result<Map<String, Object>> analyzeImageForNative(@RequestBody Map<String, String> body) {
|
||||
try {
|
||||
String imageBase64 = body == null ? null : body.get("imageBase64");
|
||||
if (StringUtils.isBlank(imageBase64)) {
|
||||
return Result.error("imageBase64 不能为空");
|
||||
}
|
||||
String filename = body.get("filename");
|
||||
String mime = body.get("mime");
|
||||
byte[] bytes = decodeImageBase64(imageBase64);
|
||||
return Result.OK(nativePrintTemplateImageAnalyzeService.analyzeBytes(bytes, mime, filename));
|
||||
} catch (Exception e) {
|
||||
log.error("图片分析失败", e);
|
||||
return Result.error("图片分析失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] decodeImageBase64(String imageBase64) {
|
||||
String s = StringUtils.trimToEmpty(imageBase64);
|
||||
int comma = s.indexOf(',');
|
||||
if (s.startsWith("data:") && comma > 0) {
|
||||
s = s.substring(comma + 1);
|
||||
}
|
||||
return Base64.getDecoder().decode(s.replaceAll("\\s", ""));
|
||||
}
|
||||
|
||||
@Operation(summary = "打印模板-通过编码查询")
|
||||
@GetMapping(value = "/queryByCode")
|
||||
@RequiresPermissions("print:template:list")
|
||||
public Result<PrintTemplate> queryByCode(@RequestParam(name = "code") String code) {
|
||||
PrintTemplate t = service.getByCode(code);
|
||||
if (t == null) {
|
||||
return Result.error("未找到模板: " + code);
|
||||
}
|
||||
return Result.OK(t);
|
||||
}
|
||||
|
||||
@Operation(summary = "打印模板-查询可用打印机")
|
||||
@GetMapping(value = "/queryPrinters")
|
||||
@RequiresPermissions("print:template:list")
|
||||
public Result<Map<String, Object>> queryPrinters() {
|
||||
return Result.OK(printServerEnvironmentService.buildPrinterQueryResult());
|
||||
}
|
||||
|
||||
@AutoLog(value = "打印模板-服务端直打")
|
||||
@Operation(summary = "打印模板-服务端直打")
|
||||
@PostMapping(value = "/directPrint")
|
||||
@RequiresPermissions("print:template:list")
|
||||
public Result<String> directPrint(@RequestBody Map<String, Object> body) {
|
||||
String templateCode = String.valueOf(body.getOrDefault("templateCode", "")).trim();
|
||||
String printerName = String.valueOf(body.getOrDefault("printerName", "")).trim();
|
||||
Object dataJsonObj = body.get("dataJson");
|
||||
String dataJsonText = dataJsonObj == null ? "" : String.valueOf(dataJsonObj);
|
||||
if (StringUtils.isBlank(templateCode)) {
|
||||
return Result.error("templateCode 不能为空");
|
||||
}
|
||||
if (StringUtils.isBlank(dataJsonText)) {
|
||||
return Result.error("dataJson 不能为空");
|
||||
}
|
||||
PrintTemplate tpl = service.getByCode(templateCode);
|
||||
if (tpl == null) {
|
||||
return Result.error("模板不存在: " + templateCode);
|
||||
}
|
||||
try {
|
||||
PrintService target = printServerPdfJobService.resolvePrintService(printerName);
|
||||
if (StringUtils.isNotBlank(printerName)
|
||||
&& !"__system_default__".equals(printerName)
|
||||
&& target != null
|
||||
&& !printerName.equalsIgnoreCase(String.valueOf(target.getName()).trim())) {
|
||||
return Result.error("未找到指定打印机: " + printerName);
|
||||
}
|
||||
if (target == null) {
|
||||
return Result.error("未找到可用打印机,请检查服务器打印机配置");
|
||||
}
|
||||
// 说明:当前接口实现的是服务端直打(纯文本)。若需按 hiprint 模板渲染版式,建议接入独立渲染服务。
|
||||
String content =
|
||||
"QH-MES 快速打印\n模板编号: "
|
||||
+ templateCode
|
||||
+ "\n模板名称: "
|
||||
+ String.valueOf(tpl.getTemplateName())
|
||||
+ "\n\n数据JSON:\n"
|
||||
+ dataJsonText
|
||||
+ "\n";
|
||||
final String[] lines = content.replace("\r\n", "\n").split("\n", -1);
|
||||
PrinterJob job = PrinterJob.getPrinterJob();
|
||||
job.setPrintService(target);
|
||||
job.setJobName("QH-MES-" + templateCode);
|
||||
job.setPrintable(
|
||||
new Printable() {
|
||||
@Override
|
||||
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
|
||||
Graphics2D g2 = (Graphics2D) graphics;
|
||||
g2.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
|
||||
g2.setFont(new Font("Microsoft YaHei", Font.PLAIN, 10));
|
||||
int lineHeight = g2.getFontMetrics().getHeight() + 2;
|
||||
int maxLinesPerPage = Math.max(1, (int) (pageFormat.getImageableHeight() / lineHeight));
|
||||
int start = pageIndex * maxLinesPerPage;
|
||||
if (start >= lines.length) {
|
||||
return Printable.NO_SUCH_PAGE;
|
||||
}
|
||||
int end = Math.min(lines.length, start + maxLinesPerPage);
|
||||
int y = g2.getFontMetrics().getAscent();
|
||||
for (int i = start; i < end; i += 1) {
|
||||
g2.drawString(lines[i], 0, y);
|
||||
y += lineHeight;
|
||||
}
|
||||
return Printable.PAGE_EXISTS;
|
||||
}
|
||||
});
|
||||
job.print();
|
||||
return Result.OK("已提交到服务器打印机: " + target.getName());
|
||||
} catch (Exception e) {
|
||||
log.error("服务端直打失败", e);
|
||||
return Result.error("服务端直打失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@AutoLog(value = "打印模板-PDF后端打印")
|
||||
@Operation(summary = "打印模板-PDF后端打印")
|
||||
@PostMapping(value = "/directPrintPdf")
|
||||
@RequiresPermissions("print:template:list")
|
||||
public Result<String> directPrintPdf(@RequestBody Map<String, Object> body) {
|
||||
String templateCode = String.valueOf(body.getOrDefault("templateCode", "")).trim();
|
||||
String printerName = String.valueOf(body.getOrDefault("printerName", "")).trim();
|
||||
String pdfBase64 = String.valueOf(body.getOrDefault("pdfBase64", "")).trim();
|
||||
String fileName = String.valueOf(body.getOrDefault("fileName", "")).trim();
|
||||
if (StringUtils.isBlank(templateCode)) {
|
||||
return Result.error("templateCode 不能为空");
|
||||
}
|
||||
return printServerPdfJobService.submitPdfBase64(printerName, pdfBase64, fileName, templateCode);
|
||||
}
|
||||
|
||||
// ═══════════════════════════ 桌面端免密接口 ═══════════════════════════
|
||||
|
||||
@Operation(summary = "打印模板-免密通过编码查询(桌面端)")
|
||||
@GetMapping(value = "/anon/queryByCode")
|
||||
public Result<PrintTemplate> anonQueryByCode(@RequestParam(name = "code") String code) {
|
||||
PrintTemplate t = service.getByCode(code);
|
||||
if (t == null) {
|
||||
return Result.error("未找到模板: " + code);
|
||||
}
|
||||
return Result.OK(t);
|
||||
}
|
||||
|
||||
@Operation(summary = "打印模板-免密分页列表(桌面端)")
|
||||
@GetMapping(value = "/anon/list")
|
||||
public Result<IPage<PrintTemplate>> anonList(PrintTemplate query,
|
||||
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(name = "pageSize", defaultValue = "100") Integer pageSize,
|
||||
HttpServletRequest req) {
|
||||
QueryWrapper<PrintTemplate> qw = QueryGenerator.initQueryWrapper(query, req.getParameterMap());
|
||||
qw.orderByAsc("template_code");
|
||||
Page<PrintTemplate> page = new Page<>(pageNo, pageSize);
|
||||
return Result.OK(service.page(page, qw));
|
||||
}
|
||||
|
||||
/**
|
||||
* 广播打印模板变更事件到 /topic/sync/print-templates,桌面端订阅同步刷新本地缓存。
|
||||
* 消息体格式 = MesXslStompNotifyService.publishPrintTemplateChanged 的输出,
|
||||
* 内联实现避免反向依赖业务模块。
|
||||
*/
|
||||
private void publishPrintTemplateChanged(String action, String templateId) {
|
||||
try {
|
||||
Map<String, Object> event = new HashMap<>();
|
||||
event.put("cmd", "PRINT_TEMPLATE_CHANGED");
|
||||
event.put("action", action);
|
||||
event.put("templateId", templateId);
|
||||
event.put("timestamp", System.currentTimeMillis());
|
||||
messagingTemplate.convertAndSend("/topic/sync/print-templates", JSON.toJSONString(event));
|
||||
} catch (Exception e) {
|
||||
log.debug("广播 STOMP 事件失败 [PRINT_TEMPLATE_CHANGED]: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -174,9 +174,22 @@ public class QuartzJobServiceImpl extends ServiceImpl<QuartzJobMapper, QuartzJob
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全加载Job类:仅允许 org.jeecg. 包下的类,且必须实现 org.quartz.Job 接口
|
||||
*/
|
||||
private static Job getClass(String classname) throws Exception {
|
||||
Class<?> class1 = Class.forName(classname);
|
||||
return (Job) class1.newInstance();
|
||||
// 包名白名单校验,防止任意类实例化导致RCE
|
||||
if (classname == null || !classname.startsWith("org.jeecg.")) {
|
||||
throw new IllegalArgumentException("非法的任务类名:" + classname + ",仅允许 org.jeecg 包下的Job类");
|
||||
}
|
||||
//update-begin---author:scott ---date:20260416 for:【PR#9538】Class.forName使用上下文类加载器,增强部署兼容性-----------
|
||||
Class<?> clazz = Class.forName(classname, true, Thread.currentThread().getContextClassLoader());
|
||||
//update-end---author:scott ---date:20260416 for:【PR#9538】Class.forName使用上下文类加载器,增强部署兼容性-----------
|
||||
// 校验是否实现了 org.quartz.Job 接口
|
||||
if (!Job.class.isAssignableFrom(clazz)) {
|
||||
throw new IllegalArgumentException("非法的任务类:" + classname + ",必须实现 org.quartz.Job 接口");
|
||||
}
|
||||
return (Job) clazz.getDeclaredConstructor().newInstance();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"id": "E0CC280",
|
||||
"appTitle": null,
|
||||
"appLogo": null,
|
||||
"carouselImgJson": null,
|
||||
"routeImgJson": null,
|
||||
"appVersion": "1.0.0",
|
||||
"versionNum": 100,
|
||||
"downloadUrl": "https://upload.jeecg.com/jeecg/qiaoqiaoyunsite/app/JeecgUniapp3_0617.apk",
|
||||
"wgtUrl": "",
|
||||
"webDownloadUrl": "https://upload.jeecg.com/jeecg/qiaoqiaoyunsite/app/jeecgboot-setup-3.8.3.exe",
|
||||
"updateNote": "1. 优化用户体验\n2. 修复已知bug\n"
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import com.jeecg.dingtalk.api.core.response.Response;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.jeecg.common.api.dto.PushMessageDTO;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
@@ -110,6 +111,7 @@ public class SysAnnouncementController {
|
||||
* @param req
|
||||
* @return
|
||||
*/
|
||||
@RequiresPermissions("system:sysAnnouncement:list")
|
||||
@RequestMapping(value = "/list", method = RequestMethod.GET)
|
||||
public Result<IPage<SysAnnouncement>> queryPageList(SysAnnouncement sysAnnouncement,
|
||||
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
|
||||
@@ -136,6 +138,7 @@ public class SysAnnouncementController {
|
||||
* @param sysAnnouncement
|
||||
* @return
|
||||
*/
|
||||
@RequiresPermissions("system:sysAnnouncement:add")
|
||||
@RequestMapping(value = "/add", method = RequestMethod.POST)
|
||||
public Result<SysAnnouncement> add(@RequestBody SysAnnouncement sysAnnouncement) {
|
||||
Result<SysAnnouncement> result = new Result<SysAnnouncement>();
|
||||
@@ -143,6 +146,10 @@ public class SysAnnouncementController {
|
||||
// 代码逻辑说明: 标题处理xss攻击的问题
|
||||
String title = XssUtils.scriptXss(sysAnnouncement.getTitile());
|
||||
sysAnnouncement.setTitile(title);
|
||||
//update-begin---author:liusq ---date:2025-04-13 for:【issues/9521】富文本msgContent字段未做XSS过滤,存在存储型XSS漏洞-----------
|
||||
String msgContent = XssUtils.richTextXss(sysAnnouncement.getMsgContent());
|
||||
sysAnnouncement.setMsgContent(msgContent);
|
||||
//update-end---author:liusq ---date:2025-04-13 for:【issues/9521】富文本msgContent字段未做XSS过滤,存在存储型XSS漏洞-----------
|
||||
// 【安全校验】校验附件文件名,防止路径遍历攻击
|
||||
SsrfFileTypeFilter.checkPathTraversalBatch(sysAnnouncement.getFiles());
|
||||
sysAnnouncement.setDelFlag(CommonConstant.DEL_FLAG_0.toString());
|
||||
@@ -165,6 +172,7 @@ public class SysAnnouncementController {
|
||||
* @param sysAnnouncement
|
||||
* @return
|
||||
*/
|
||||
@RequiresPermissions("system:sysAnnouncement:edit")
|
||||
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
|
||||
public Result<SysAnnouncement> eidt(@RequestBody SysAnnouncement sysAnnouncement) {
|
||||
Result<SysAnnouncement> result = new Result<SysAnnouncement>();
|
||||
@@ -176,6 +184,10 @@ public class SysAnnouncementController {
|
||||
// 代码逻辑说明: 标题处理xss攻击的问题
|
||||
String title = XssUtils.scriptXss(sysAnnouncement.getTitile());
|
||||
sysAnnouncement.setTitile(title);
|
||||
//update-begin---author:liusq ---date:2025-04-13 for:【issues/9521】富文本msgContent字段未做XSS过滤,存在存储型XSS漏洞-----------
|
||||
String msgContent = XssUtils.richTextXss(sysAnnouncement.getMsgContent());
|
||||
sysAnnouncement.setMsgContent(msgContent);
|
||||
//update-end---author:liusq ---date:2025-04-13 for:【issues/9521】富文本msgContent字段未做XSS过滤,存在存储型XSS漏洞-----------
|
||||
// 【安全校验】校验附件文件名,防止路径遍历攻击
|
||||
SsrfFileTypeFilter.checkPathTraversalBatch(sysAnnouncement.getFiles());
|
||||
sysAnnouncement.setNoticeType(NoticeTypeEnum.NOTICE_TYPE_SYSTEM.getValue());
|
||||
@@ -196,6 +208,7 @@ public class SysAnnouncementController {
|
||||
* @param sysAnnouncement
|
||||
* @return
|
||||
*/
|
||||
//@RequiresPermissions("system:sysAnnouncement:editIzTop")
|
||||
@RequestMapping(value = "/editIzTop", method = {RequestMethod.PUT,RequestMethod.POST})
|
||||
public Result<SysAnnouncement> editIzTop(@RequestBody SysAnnouncement sysAnnouncement) {
|
||||
Result<SysAnnouncement> result = new Result<SysAnnouncement>();
|
||||
@@ -216,6 +229,7 @@ public class SysAnnouncementController {
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
@RequiresPermissions("system:sysAnnouncement:delete")
|
||||
@RequestMapping(value = "/delete", method = RequestMethod.DELETE)
|
||||
public Result<SysAnnouncement> delete(@RequestParam(name="id",required=true) String id) {
|
||||
Result<SysAnnouncement> result = new Result<SysAnnouncement>();
|
||||
@@ -238,6 +252,7 @@ public class SysAnnouncementController {
|
||||
* @param ids
|
||||
* @return
|
||||
*/
|
||||
@RequiresPermissions("system:sysAnnouncement:deleteBatch")
|
||||
@RequestMapping(value = "/deleteBatch", method = RequestMethod.DELETE)
|
||||
public Result<SysAnnouncement> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
|
||||
Result<SysAnnouncement> result = new Result<SysAnnouncement>();
|
||||
@@ -278,6 +293,7 @@ public class SysAnnouncementController {
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
@RequiresPermissions("system:sysAnnouncement:doReleaseData")
|
||||
@RequestMapping(value = "/doReleaseData", method = RequestMethod.GET)
|
||||
public Result<SysAnnouncement> doReleaseData(@RequestParam(name="id",required=true) String id, HttpServletRequest request) {
|
||||
Result<SysAnnouncement> result = new Result<SysAnnouncement>();
|
||||
@@ -358,6 +374,7 @@ public class SysAnnouncementController {
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
@RequiresPermissions("system:sysAnnouncement:doReovkeData")
|
||||
@RequestMapping(value = "/doReovkeData", method = RequestMethod.GET)
|
||||
public Result<SysAnnouncement> doReovkeData(@RequestParam(name="id",required=true) String id, HttpServletRequest request) {
|
||||
Result<SysAnnouncement> result = new Result<SysAnnouncement>();
|
||||
@@ -467,6 +484,7 @@ public class SysAnnouncementController {
|
||||
*
|
||||
* @param request
|
||||
*/
|
||||
@RequiresPermissions("system:sysAnnouncement:exportXls")
|
||||
@RequestMapping(value = "/exportXls")
|
||||
public ModelAndView exportXls(SysAnnouncement sysAnnouncement,HttpServletRequest request) {
|
||||
// Step.1 组装查询条件
|
||||
@@ -491,6 +509,7 @@ public class SysAnnouncementController {
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
@RequiresPermissions("system:sysAnnouncement:importExcel")
|
||||
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
|
||||
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
|
||||
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
|
||||
@@ -532,6 +551,7 @@ public class SysAnnouncementController {
|
||||
* @param anntId
|
||||
* @return
|
||||
*/
|
||||
//@RequiresPermissions("system:sysAnnouncement:syncNotic")
|
||||
@RequestMapping(value = "/syncNotic", method = RequestMethod.GET)
|
||||
public Result<SysAnnouncement> syncNotic(@RequestParam(name="anntId",required=false) String anntId, HttpServletRequest request) {
|
||||
Result<SysAnnouncement> result = new Result<SysAnnouncement>();
|
||||
@@ -681,7 +701,7 @@ public class SysAnnouncementController {
|
||||
Result<Page<SysAnnouncementSend>> result = new Result<>();
|
||||
//----------------------------------------------------------------------------------------
|
||||
// step.1 此接口过慢,可以采用缓存一小时方案
|
||||
String keyString = String.format(CommonConstant.CACHE_KEY_USER_LAST_ANNOUNT_TIME_1HOUR + "_" + noticeType, userId);
|
||||
String keyString = String.format(CommonConstant.CACHE_KEY_USER_LAST_ANNOUNT_TIME_1HOUR, userId) + "_" + noticeType;
|
||||
if (redisTemplate.hasKey(keyString)) {
|
||||
log.debug("[SysAnnouncementSend Redis] 通过Redis缓存查询用户最后一次收到系统通知时间,userId={}", userId);
|
||||
Page<SysAnnouncementSend> pageList = (Page<SysAnnouncementSend>) redisTemplate.opsForValue().get(keyString);
|
||||
|
||||
@@ -285,14 +285,14 @@ public class SysAnnouncementSendController {
|
||||
@RequestParam(name="busId",required=true) String busId,
|
||||
@RequestParam(name="busType",required=false) String busType) {
|
||||
//更新阅读状态
|
||||
sysAnnouncementSendService.updateReadFlagByBusId(busId,busType);
|
||||
|
||||
//刷新未读数量
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put(WebsocketConst.MSG_CMD, WebsocketConst.CMD_USER);
|
||||
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
|
||||
webSocket.sendMessage(sysUser.getId(), obj.toJSONString());
|
||||
|
||||
boolean updateFlag = sysAnnouncementSendService.updateReadFlagByBusId(busId,busType);
|
||||
if(updateFlag){
|
||||
//刷新未读数量
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put(WebsocketConst.MSG_CMD, WebsocketConst.CMD_USER);
|
||||
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
|
||||
webSocket.sendMessage(sysUser.getId(), obj.toJSONString());
|
||||
}
|
||||
return Result.ok();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.jeecg.modules.system.controller;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.shiro.authz.annotation.RequiresRoles;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.util.RedisUtil;
|
||||
@@ -10,6 +11,9 @@ import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.system.entity.SysAppVersion;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* @Description: app系统配置
|
||||
@@ -25,6 +29,10 @@ public class SysAppVersionController{
|
||||
|
||||
@Autowired
|
||||
private RedisUtil redisUtil;
|
||||
/**
|
||||
* app3版本json文件路径
|
||||
*/
|
||||
private final String JSON_PATH = "classpath:org/jeecg/modules/system/config/json/app3-version.json";
|
||||
|
||||
/**
|
||||
* APP缓存前缀
|
||||
@@ -41,16 +49,29 @@ public class SysAppVersionController{
|
||||
if (oConvertUtils.isNotEmpty(appConfig)) {
|
||||
try {
|
||||
SysAppVersion sysAppVersion = (SysAppVersion)appConfig;
|
||||
if(oConvertUtils.isEmpty(sysAppVersion.getDownloadUrl())){
|
||||
String jsonContent = readJson(JSON_PATH);
|
||||
sysAppVersion = JSONObject.parseObject(jsonContent, SysAppVersion.class);
|
||||
return Result.OK(sysAppVersion);
|
||||
}
|
||||
return Result.OK(sysAppVersion);
|
||||
} catch (Exception e) {
|
||||
log.error(e.toString(),e);
|
||||
return Result.error("app版本信息获取失败:" + e.getMessage());
|
||||
}
|
||||
}else{
|
||||
// 缓存中没有,从配置的json文件中获取
|
||||
try {
|
||||
String jsonContent = readJson(JSON_PATH);
|
||||
SysAppVersion sysAppVersion = JSONObject.parseObject(jsonContent, SysAppVersion.class);
|
||||
return Result.OK(sysAppVersion);
|
||||
} catch (Exception e) {
|
||||
log.error("从JSON文件读取app版本信息失败:{}", e);
|
||||
}
|
||||
}
|
||||
return Result.OK();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 保存APP3
|
||||
*
|
||||
@@ -65,4 +86,21 @@ public class SysAppVersionController{
|
||||
redisUtil.set(APP3_VERSION + id,sysAppVersion);
|
||||
return Result.OK();
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取json格式文件
|
||||
* @param jsonSrc
|
||||
* @return
|
||||
*/
|
||||
private String readJson(String jsonSrc) {
|
||||
String json = "";
|
||||
try {
|
||||
//换个写法,解决springboot读取jar包中文件的问题
|
||||
InputStream stream = getClass().getClassLoader().getResourceAsStream(jsonSrc.replace("classpath:", ""));
|
||||
json = IOUtils.toString(stream,"UTF-8");
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(),e);
|
||||
}
|
||||
return json;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,11 +121,13 @@ public class SysDataSourceController extends JeecgController<SysDataSource, ISys
|
||||
*/
|
||||
@AutoLog(value = "多数据源管理-添加")
|
||||
@Operation(summary = "多数据源管理-添加")
|
||||
@RequiresPermissions("system:datasource:add")
|
||||
@PostMapping(value = "/add")
|
||||
public Result<?> add(@RequestBody SysDataSource sysDataSource) {
|
||||
// 代码逻辑说明: jdbc连接地址漏洞问题
|
||||
try {
|
||||
JdbcSecurityUtil.validate(sysDataSource.getDbUrl());
|
||||
JdbcSecurityUtil.validateDriver(sysDataSource.getDbDriver());
|
||||
}catch (JeecgBootException e){
|
||||
log.error(e.toString());
|
||||
return Result.error("操作失败:" + e.getMessage());
|
||||
@@ -141,11 +143,13 @@ public class SysDataSourceController extends JeecgController<SysDataSource, ISys
|
||||
*/
|
||||
@AutoLog(value = "多数据源管理-编辑")
|
||||
@Operation(summary = "多数据源管理-编辑")
|
||||
@RequiresPermissions("system:datasource:edit")
|
||||
@RequestMapping(value = "/edit", method ={RequestMethod.PUT, RequestMethod.POST})
|
||||
public Result<?> edit(@RequestBody SysDataSource sysDataSource) {
|
||||
// 代码逻辑说明: jdbc连接地址漏洞问题
|
||||
try {
|
||||
JdbcSecurityUtil.validate(sysDataSource.getDbUrl());
|
||||
JdbcSecurityUtil.validateDriver(sysDataSource.getDbDriver());
|
||||
} catch (JeecgBootException e) {
|
||||
log.error(e.toString());
|
||||
return Result.error("操作失败:" + e.getMessage());
|
||||
@@ -161,6 +165,7 @@ public class SysDataSourceController extends JeecgController<SysDataSource, ISys
|
||||
*/
|
||||
@AutoLog(value = "多数据源管理-通过id删除")
|
||||
@Operation(summary = "多数据源管理-通过id删除")
|
||||
@RequiresPermissions("system:datasource:delete")
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<?> delete(@RequestParam(name = "id") String id) {
|
||||
return sysDataSourceService.deleteDataSource(id);
|
||||
@@ -174,6 +179,7 @@ public class SysDataSourceController extends JeecgController<SysDataSource, ISys
|
||||
*/
|
||||
@AutoLog(value = "多数据源管理-批量删除")
|
||||
@Operation(summary = "多数据源管理-批量删除")
|
||||
@RequiresPermissions("system:datasource:delete")
|
||||
@DeleteMapping(value = "/deleteBatch")
|
||||
public Result<?> deleteBatch(@RequestParam(name = "ids") String ids) {
|
||||
List<String> idList = Arrays.asList(ids.split(","));
|
||||
@@ -193,6 +199,7 @@ public class SysDataSourceController extends JeecgController<SysDataSource, ISys
|
||||
*/
|
||||
@AutoLog(value = "多数据源管理-通过id查询")
|
||||
@Operation(summary = "多数据源管理-通过id查询")
|
||||
@RequiresPermissions("system:datasource:list")
|
||||
@GetMapping(value = "/queryById")
|
||||
public Result<?> queryById(@RequestParam(name = "id") String id) throws InterruptedException {
|
||||
SysDataSource sysDataSource = sysDataSourceService.getById(id);
|
||||
@@ -211,6 +218,7 @@ public class SysDataSourceController extends JeecgController<SysDataSource, ISys
|
||||
* @param request
|
||||
* @param sysDataSource
|
||||
*/
|
||||
@RequiresPermissions("system:datasource:export")
|
||||
@RequestMapping(value = "/exportXls")
|
||||
public ModelAndView exportXls(HttpServletRequest request, SysDataSource sysDataSource) {
|
||||
//------------------------------------------------------------------------------------------------
|
||||
@@ -229,6 +237,7 @@ public class SysDataSourceController extends JeecgController<SysDataSource, ISys
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
@RequiresPermissions("system:datasource:import")
|
||||
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
|
||||
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
|
||||
return super.importExcel(request, response, SysDataSource.class);
|
||||
|
||||
@@ -137,10 +137,10 @@ public class SysDepartController {
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(value = "/queryDepartTreeSync", method = RequestMethod.GET)
|
||||
public Result<List<SysDepartTreeModel>> queryDepartTreeSync(@RequestParam(name = "pid", required = false) String parentId,@RequestParam(name = "ids", required = false) String ids, @RequestParam(name = "primaryKey", required = false) String primaryKey) {
|
||||
public Result<List<SysDepartTreeModel>> queryDepartTreeSync(@RequestParam(name = "pid", required = false) String parentId,@RequestParam(name = "ids", required = false) String ids, @RequestParam(name = "primaryKey", required = false) String primaryKey, @RequestParam(name = "orgCategory", required = false) String orgCategory) {
|
||||
Result<List<SysDepartTreeModel>> result = new Result<>();
|
||||
try {
|
||||
List<SysDepartTreeModel> list = sysDepartService.queryTreeListByPid(parentId,ids, primaryKey);
|
||||
List<SysDepartTreeModel> list = sysDepartService.queryTreeListByPid(parentId,ids, primaryKey, orgCategory);
|
||||
result.setResult(list);
|
||||
result.setSuccess(true);
|
||||
} catch (Exception e) {
|
||||
@@ -737,6 +737,16 @@ public class SysDepartController {
|
||||
List<SysPositionSelectTreeVo> list = sysDepartService.getRankRelation(departId);
|
||||
return Result.ok(list);
|
||||
}
|
||||
/**
|
||||
* 获取ALL职级关系
|
||||
* @param departId
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/getALLRankRelation")
|
||||
public Result<List<SysPositionSelectTreeVo>> getALLRankRelation(@RequestParam(name = "departId",required = false) String departId){
|
||||
List<SysPositionSelectTreeVo> list = sysDepartService.getALLRankRelation(departId);
|
||||
return Result.ok(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据部门code获取当前和上级部门名称
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.shiro.authz.annotation.RequiresRoles;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.aspect.annotation.AutoLog;
|
||||
import org.jeecg.common.system.base.controller.JeecgController;
|
||||
@@ -65,6 +66,7 @@ public class SysFillRuleController extends JeecgController<SysFillRule, ISysFill
|
||||
* @param ruleCode
|
||||
* @return
|
||||
*/
|
||||
@RequiresRoles({"admin"})
|
||||
@GetMapping(value = "/testFillRule")
|
||||
public Result testFillRule(@RequestParam("ruleCode") String ruleCode) {
|
||||
Object result = FillRuleUtil.executeRule(ruleCode, new JSONObject());
|
||||
@@ -79,6 +81,7 @@ public class SysFillRuleController extends JeecgController<SysFillRule, ISysFill
|
||||
*/
|
||||
@AutoLog(value = "填值规则-添加")
|
||||
@Operation(summary = "填值规则-添加")
|
||||
@RequiresRoles({"admin"})
|
||||
@PostMapping(value = "/add")
|
||||
public Result<?> add(@RequestBody SysFillRule sysFillRule) {
|
||||
sysFillRuleService.save(sysFillRule);
|
||||
@@ -93,6 +96,7 @@ public class SysFillRuleController extends JeecgController<SysFillRule, ISysFill
|
||||
*/
|
||||
@AutoLog(value = "填值规则-编辑")
|
||||
@Operation(summary = "填值规则-编辑")
|
||||
@RequiresRoles({"admin"})
|
||||
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
|
||||
public Result<?> edit(@RequestBody SysFillRule sysFillRule) {
|
||||
sysFillRuleService.updateById(sysFillRule);
|
||||
@@ -107,6 +111,7 @@ public class SysFillRuleController extends JeecgController<SysFillRule, ISysFill
|
||||
*/
|
||||
@AutoLog(value = "填值规则-通过id删除")
|
||||
@Operation(summary = "填值规则-通过id删除")
|
||||
@RequiresRoles({"admin"})
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<?> delete(@RequestParam(name = "id", required = true) String id) {
|
||||
sysFillRuleService.removeById(id);
|
||||
@@ -121,6 +126,7 @@ public class SysFillRuleController extends JeecgController<SysFillRule, ISysFill
|
||||
*/
|
||||
@AutoLog(value = "填值规则-批量删除")
|
||||
@Operation(summary = "填值规则-批量删除")
|
||||
@RequiresRoles({"admin"})
|
||||
@DeleteMapping(value = "/deleteBatch")
|
||||
public Result<?> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
|
||||
this.sysFillRuleService.removeByIds(Arrays.asList(ids.split(",")));
|
||||
|
||||
@@ -34,6 +34,7 @@ public class SysGatewayRouteController extends JeecgController<SysGatewayRoute,
|
||||
@Autowired
|
||||
private ISysGatewayRouteService sysGatewayRouteService;
|
||||
|
||||
@RequiresPermissions("system:gateway:updateAll")
|
||||
@PostMapping(value = "/updateAll")
|
||||
public Result<?> updateAll(@RequestBody JSONObject json) {
|
||||
sysGatewayRouteService.updateAll(json);
|
||||
|
||||
@@ -91,9 +91,13 @@ public class SysPermissionController {
|
||||
query.eq(SysPermission::getDelFlag, CommonConstant.DEL_FLAG_0);
|
||||
query.orderByAsc(SysPermission::getSortNo);
|
||||
|
||||
//支持通过菜单名字,模糊查询
|
||||
//支持通过菜单名字或url,模糊查询
|
||||
if(oConvertUtils.isNotEmpty(sysPermission.getName())){
|
||||
query.like(SysPermission::getName, sysPermission.getName());
|
||||
query.and(wrapper -> wrapper
|
||||
.like(SysPermission::getName, sysPermission.getName())
|
||||
.or()
|
||||
.like(SysPermission::getUrl, sysPermission.getName())
|
||||
);
|
||||
}
|
||||
List<SysPermission> list = sysPermissionService.list(query);
|
||||
List<SysPermissionTree> treeList = new ArrayList<>();
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
package org.jeecg.modules.system.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.aspect.annotation.AutoLog;
|
||||
import org.jeecg.common.system.base.controller.JeecgController;
|
||||
import org.jeecg.common.system.query.QueryGenerator;
|
||||
import org.jeecg.modules.system.entity.SysUgroup;
|
||||
import org.jeecg.modules.system.service.ISysUgroupService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
/**
|
||||
* @Description: 用户组表
|
||||
* @Author: jeecg-boot
|
||||
* @Date: 2026-02-27
|
||||
* @Version: V1.0
|
||||
*/
|
||||
@Tag(name="用户组表")
|
||||
@RestController
|
||||
@RequestMapping("/sys/ugroup")
|
||||
@Slf4j
|
||||
public class SysUgroupController extends JeecgController<SysUgroup, ISysUgroupService> {
|
||||
@Autowired
|
||||
private ISysUgroupService sysUgroupService;
|
||||
|
||||
|
||||
/**
|
||||
* 分页列表查询
|
||||
*
|
||||
* @param sysUgroup
|
||||
* @param pageNo
|
||||
* @param pageSize
|
||||
* @param req
|
||||
* @return
|
||||
*/
|
||||
//@AutoLog(value = "用户组表-分页列表查询")
|
||||
@Operation(summary="用户组表-分页列表查询")
|
||||
@GetMapping(value = "/list")
|
||||
public Result<IPage<SysUgroup>> queryPageList(SysUgroup sysUgroup,
|
||||
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
|
||||
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
|
||||
HttpServletRequest req) {
|
||||
|
||||
|
||||
QueryWrapper<SysUgroup> queryWrapper = QueryGenerator.initQueryWrapper(sysUgroup, req.getParameterMap());
|
||||
Page<SysUgroup> page = new Page<SysUgroup>(pageNo, pageSize);
|
||||
IPage<SysUgroup> pageList = sysUgroupService.page(page, queryWrapper);
|
||||
return Result.OK(pageList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加
|
||||
*
|
||||
* @param sysUgroup
|
||||
* @return
|
||||
*/
|
||||
@AutoLog(value = "用户组表-添加")
|
||||
@Operation(summary="用户组表-添加")
|
||||
@RequiresPermissions("system:sys_ugroup:add")
|
||||
@PostMapping(value = "/add")
|
||||
public Result<SysUgroup> add(@RequestBody SysUgroup sysUgroup) {
|
||||
Result<SysUgroup> result = new Result<SysUgroup>();
|
||||
try {
|
||||
sysUgroup.setCreateTime(new Date());
|
||||
sysUgroupService.save(sysUgroup);
|
||||
result.success("添加成功!");
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
result.error500("操作失败");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
*
|
||||
* @param sysUgroup
|
||||
* @return
|
||||
*/
|
||||
@AutoLog(value = "用户组表-编辑")
|
||||
@Operation(summary="用户组表-编辑")
|
||||
@RequiresPermissions("system:sys_ugroup:edit")
|
||||
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
|
||||
public Result<String> edit(@RequestBody SysUgroup sysUgroup) {
|
||||
sysUgroupService.updateById(sysUgroup);
|
||||
return Result.OK("编辑成功!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过id删除
|
||||
*
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
@AutoLog(value = "用户组表-通过id删除")
|
||||
@Operation(summary="用户组表-通过id删除")
|
||||
@RequiresPermissions("system:sys_ugroup:delete")
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
|
||||
sysUgroupService.deleteById(id);
|
||||
return Result.OK("删除成功!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除
|
||||
*
|
||||
* @param ids
|
||||
* @return
|
||||
*/
|
||||
@AutoLog(value = "用户组表-批量删除")
|
||||
@Operation(summary="用户组表-批量删除")
|
||||
@RequiresPermissions("system:sys_ugroup:deleteBatch")
|
||||
@DeleteMapping(value = "/deleteBatch")
|
||||
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
|
||||
this.sysUgroupService.deleteByIds(Arrays.asList(ids.split(",")));
|
||||
return Result.OK("批量删除成功!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过id查询
|
||||
*
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
//@AutoLog(value = "用户组表-通过id查询")
|
||||
@Operation(summary="用户组表-通过id查询")
|
||||
@GetMapping(value = "/queryById")
|
||||
public Result<SysUgroup> queryById(@RequestParam(name="id",required=true) String id) {
|
||||
SysUgroup sysUgroup = sysUgroupService.getById(id);
|
||||
if(sysUgroup==null) {
|
||||
return Result.error("未找到对应数据");
|
||||
}
|
||||
return Result.OK(sysUgroup);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出excel
|
||||
*
|
||||
* @param request
|
||||
* @param sysUgroup
|
||||
*/
|
||||
@RequiresPermissions("system:sys_ugroup:exportXls")
|
||||
@RequestMapping(value = "/exportXls")
|
||||
public ModelAndView exportXls(HttpServletRequest request, SysUgroup sysUgroup) {
|
||||
return super.exportXls(request, sysUgroup, SysUgroup.class, "用户组表");
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过excel导入数据
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
@RequiresPermissions("system:sys_ugroup:importExcel")
|
||||
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
|
||||
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
|
||||
return super.importExcel(request, response, SysUgroup.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package org.jeecg.modules.system.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.aspect.annotation.AutoLog;
|
||||
import org.jeecg.common.system.base.controller.JeecgController;
|
||||
import org.jeecg.common.system.query.QueryGenerator;
|
||||
import org.jeecg.modules.system.entity.SysUgroupUser;
|
||||
import org.jeecg.modules.system.service.ISysUgroupUserService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import java.util.Arrays;
|
||||
/**
|
||||
* @Description: 用户组关系表
|
||||
* @Author: jeecg-boot
|
||||
* @Date: 2026-02-27
|
||||
* @Version: V1.0
|
||||
*/
|
||||
@Tag(name="用户组关系表")
|
||||
@RestController
|
||||
@RequestMapping("/system/sysUgroupUser")
|
||||
@Slf4j
|
||||
public class SysUgroupUserController extends JeecgController<SysUgroupUser, ISysUgroupUserService> {
|
||||
@Autowired
|
||||
private ISysUgroupUserService sysUgroupUserService;
|
||||
|
||||
/**
|
||||
* 分页列表查询
|
||||
*
|
||||
* @param sysUgroupUser
|
||||
* @param pageNo
|
||||
* @param pageSize
|
||||
* @param req
|
||||
* @return
|
||||
*/
|
||||
//@AutoLog(value = "用户组关系表-分页列表查询")
|
||||
@Operation(summary="用户组关系表-分页列表查询")
|
||||
@GetMapping(value = "/list")
|
||||
public Result<IPage<SysUgroupUser>> queryPageList(SysUgroupUser sysUgroupUser,
|
||||
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
|
||||
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
|
||||
HttpServletRequest req) {
|
||||
|
||||
|
||||
QueryWrapper<SysUgroupUser> queryWrapper = QueryGenerator.initQueryWrapper(sysUgroupUser, req.getParameterMap());
|
||||
Page<SysUgroupUser> page = new Page<SysUgroupUser>(pageNo, pageSize);
|
||||
IPage<SysUgroupUser> pageList = sysUgroupUserService.page(page, queryWrapper);
|
||||
return Result.OK(pageList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加
|
||||
*
|
||||
* @param sysUgroupUser
|
||||
* @return
|
||||
*/
|
||||
@AutoLog(value = "用户组关系表-添加")
|
||||
@Operation(summary="用户组关系表-添加")
|
||||
@RequiresPermissions("system:sys_ugroup_user:add")
|
||||
@PostMapping(value = "/add")
|
||||
public Result<String> add(@RequestBody SysUgroupUser sysUgroupUser) {
|
||||
sysUgroupUserService.save(sysUgroupUser);
|
||||
|
||||
return Result.OK("添加成功!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
*
|
||||
* @param sysUgroupUser
|
||||
* @return
|
||||
*/
|
||||
@AutoLog(value = "用户组关系表-编辑")
|
||||
@Operation(summary="用户组关系表-编辑")
|
||||
@RequiresPermissions("system:sys_ugroup_user:edit")
|
||||
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
|
||||
public Result<String> edit(@RequestBody SysUgroupUser sysUgroupUser) {
|
||||
sysUgroupUserService.updateById(sysUgroupUser);
|
||||
return Result.OK("编辑成功!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过id删除
|
||||
*
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
@AutoLog(value = "用户组关系表-通过id删除")
|
||||
@Operation(summary="用户组关系表-通过id删除")
|
||||
@RequiresPermissions("system:sys_ugroup_user:delete")
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
|
||||
sysUgroupUserService.removeById(id);
|
||||
return Result.OK("删除成功!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除
|
||||
*
|
||||
* @param ids
|
||||
* @return
|
||||
*/
|
||||
@AutoLog(value = "用户组关系表-批量删除")
|
||||
@Operation(summary="用户组关系表-批量删除")
|
||||
@RequiresPermissions("system:sys_ugroup_user:deleteBatch")
|
||||
@DeleteMapping(value = "/deleteBatch")
|
||||
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
|
||||
this.sysUgroupUserService.removeByIds(Arrays.asList(ids.split(",")));
|
||||
return Result.OK("批量删除成功!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过id查询
|
||||
*
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
//@AutoLog(value = "用户组关系表-通过id查询")
|
||||
@Operation(summary="用户组关系表-通过id查询")
|
||||
@GetMapping(value = "/queryById")
|
||||
public Result<SysUgroupUser> queryById(@RequestParam(name="id",required=true) String id) {
|
||||
SysUgroupUser sysUgroupUser = sysUgroupUserService.getById(id);
|
||||
if(sysUgroupUser==null) {
|
||||
return Result.error("未找到对应数据");
|
||||
}
|
||||
return Result.OK(sysUgroupUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出excel
|
||||
*
|
||||
* @param request
|
||||
* @param sysUgroupUser
|
||||
*/
|
||||
@RequiresPermissions("system:sys_ugroup_user:exportXls")
|
||||
@RequestMapping(value = "/exportXls")
|
||||
public ModelAndView exportXls(HttpServletRequest request, SysUgroupUser sysUgroupUser) {
|
||||
return super.exportXls(request, sysUgroupUser, SysUgroupUser.class, "用户组关系表");
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过excel导入数据
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
@RequiresPermissions("system:sys_ugroup_user:importExcel")
|
||||
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
|
||||
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
|
||||
return super.importExcel(request, response, SysUgroupUser.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package org.jeecg.modules.system.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.jeecgframework.poi.excel.annotation.Excel;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @Description: 用户组表
|
||||
* @Author: jeecg-boot
|
||||
* @Date: 2026-02-27
|
||||
* @Version: V1.0
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_ugroup")
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@Schema(description="用户组表")
|
||||
public class SysUgroup implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**主键id*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
@Schema(description = "主键id")
|
||||
private java.lang.String id;
|
||||
/**角色名称*/
|
||||
@Excel(name = "用户组名称", width = 15)
|
||||
@Schema(description = "用户组名称")
|
||||
private java.lang.String groupName;
|
||||
/**描述*/
|
||||
@Excel(name = "描述", width = 15)
|
||||
@Schema(description = "描述")
|
||||
private java.lang.String description;
|
||||
/**创建人*/
|
||||
@Schema(description = "创建人")
|
||||
private java.lang.String createBy;
|
||||
/**创建时间*/
|
||||
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
|
||||
@Schema(description = "创建时间")
|
||||
private java.util.Date createTime;
|
||||
/**更新人*/
|
||||
@Schema(description = "更新人")
|
||||
private java.lang.String updateBy;
|
||||
/**更新时间*/
|
||||
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
|
||||
@Schema(description = "更新时间")
|
||||
private java.util.Date updateTime;
|
||||
/**租户ID*/
|
||||
@Excel(name = "租户ID", width = 15)
|
||||
@Schema(description = "租户ID")
|
||||
private java.lang.Integer tenantId;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package org.jeecg.modules.system.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.jeecgframework.poi.excel.annotation.Excel;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @Description: 用户组关系表
|
||||
* @Author: jeecg-boot
|
||||
* @Date: 2026-02-27
|
||||
* @Version: V1.0
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_ugroup_user")
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@Schema(description="用户组关系表")
|
||||
public class SysUgroupUser implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**主键id*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
@Schema(description = "主键id")
|
||||
private java.lang.String id;
|
||||
/**用户id*/
|
||||
@Excel(name = "用户id", width = 15)
|
||||
@Schema(description = "用户id")
|
||||
private java.lang.String userId;
|
||||
/**用户组id*/
|
||||
@Excel(name = "用户组id", width = 15)
|
||||
@Schema(description = "用户组id")
|
||||
private java.lang.String groupId;
|
||||
/**租户ID*/
|
||||
@Excel(name = "租户ID", width = 15)
|
||||
@Schema(description = "租户ID")
|
||||
private java.lang.Integer tenantId;
|
||||
|
||||
public SysUgroupUser() {
|
||||
}
|
||||
|
||||
public SysUgroupUser(String userId, String groupId) {
|
||||
this.userId = userId;
|
||||
this.groupId = groupId;
|
||||
}
|
||||
}
|
||||
@@ -62,4 +62,13 @@ public interface SysAnnouncementSendMapper extends BaseMapper<SysAnnouncementSen
|
||||
* @return
|
||||
*/
|
||||
List<String> getReadAnnSendByUserId(@Param("ids") List<String> ids, @Param("userId") String userId);
|
||||
|
||||
/**
|
||||
* 根据业务id、业务类型和用户id获取未读消息
|
||||
* @param busId
|
||||
* @param busType
|
||||
* @param userId
|
||||
* @return
|
||||
*/
|
||||
List<String> getUnReadAnnByBusAndUserId(@Param("busId")String busId, @Param("busType")String busType, @Param("userId")String userId);
|
||||
}
|
||||
|
||||
@@ -12,9 +12,11 @@ import org.jeecg.modules.system.vo.SysDepartExportVo;
|
||||
import org.jeecg.modules.system.vo.SysDepartPositionVo;
|
||||
import org.jeecg.modules.system.vo.SysUserDepVo;
|
||||
import org.jeecg.modules.system.vo.lowapp.ExportDepartVo;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -42,13 +44,21 @@ public interface SysDepartMapper extends BaseMapper<SysDepart> {
|
||||
public List<SysDepart> queryDepartsByUsername(@Param("username") String username);
|
||||
|
||||
/**
|
||||
* 根据用户名查询部门
|
||||
* 根据 userId 查询部门
|
||||
*
|
||||
* @param userId
|
||||
* @return
|
||||
*/
|
||||
public List<String> queryDepartsByUserId(@Param("userId") String userId);
|
||||
|
||||
/**
|
||||
* 根据 userIds 查询部门ID
|
||||
*
|
||||
* @param userIds 用户ID列表
|
||||
* @return
|
||||
*/
|
||||
List<Map<String, String>> queryDepartIdsByUserIds(@Param("userIds") Collection<String> userIds);
|
||||
|
||||
/**
|
||||
* 通过部门编码获取部门id
|
||||
* @param orgCode 部门编码
|
||||
@@ -311,4 +321,11 @@ public interface SysDepartMapper extends BaseMapper<SysDepart> {
|
||||
* @return
|
||||
*/
|
||||
List<SysUser> getDepartmentHead(@Param("page") Page<SysUser> page, @Param("departId") String departId);
|
||||
|
||||
/**
|
||||
*获取所有部门
|
||||
* @param departId
|
||||
* @return
|
||||
*/
|
||||
List<SysDepartPositionVo> getAllDepartPost(@Param("departId")String departId);
|
||||
}
|
||||
|
||||
@@ -33,7 +33,17 @@ public interface SysPermissionMapper extends BaseMapper<SysPermission> {
|
||||
* @return List<SysPermission>
|
||||
*/
|
||||
public List<SysPermission> queryByUser(@Param("userId") String userId);
|
||||
|
||||
|
||||
//update-begin---author:scott ---date:2026-04-16 for:【pull/9445】开启多租户模式时,获取用户权限时加入tenant_id判断-----------
|
||||
/**
|
||||
* 根据用户id和租户id查询用户权限
|
||||
* @param userId 用户ID
|
||||
* @param tenantId 租户ID
|
||||
* @return List<SysPermission>
|
||||
*/
|
||||
public List<SysPermission> queryByUserWithTenantId(@Param("userId") String userId, @Param("tenantId") Integer tenantId);
|
||||
//update-end---author:scott ---date:2026-04-16 for:【pull/9445】开启多租户模式时,获取用户权限时加入tenant_id判断-----------
|
||||
|
||||
/**
|
||||
* 修改菜单状态字段: 是否子节点
|
||||
* @param id 菜单id
|
||||
|
||||
@@ -4,7 +4,8 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.jeecg.modules.system.entity.SysPosition;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.jeecg.modules.system.vo.SysPositionVO;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -37,4 +38,13 @@ public interface SysPositionMapper extends BaseMapper<SysPosition> {
|
||||
*/
|
||||
@Select("SELECT id FROM sys_position WHERE name = #{name} AND tenant_id = #{tenantId} ORDER BY create_time DESC")
|
||||
List<String> getPositionIdByName(@Param("name") String name, @Param("tenantId") Integer tenantId, @Param("page") Page<SysPosition> page);
|
||||
|
||||
/**
|
||||
* 批量通过用户id列表查询职位(含userId字段,用于批量同步场景)
|
||||
*
|
||||
* @param userIds 用户id列表
|
||||
* @return 职位VO列表(每条记录含userId字段,供调用方分组)
|
||||
*/
|
||||
List<SysPositionVO> getPositionListByUserIds(@Param("userIds") List<String> userIds);
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package org.jeecg.modules.system.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.jeecg.modules.system.entity.SysThirdAppConfig;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.jeecg.modules.system.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.jeecg.modules.system.entity.SysUgroup;
|
||||
|
||||
/**
|
||||
* @Description: 用户组表
|
||||
* @Author: jeecg-boot
|
||||
* @Date: 2026-02-27
|
||||
* @Version: V1.0
|
||||
*/
|
||||
public interface SysUgroupMapper extends BaseMapper<SysUgroup> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.jeecg.modules.system.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.jeecg.modules.system.entity.SysUgroupUser;
|
||||
|
||||
/**
|
||||
* @Description: 用户组关系表
|
||||
* @Author: jeecg-boot
|
||||
* @Date: 2026-02-27
|
||||
* @Version: V1.0
|
||||
*/
|
||||
public interface SysUgroupUserMapper extends BaseMapper<SysUgroupUser> {
|
||||
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.core.toolkit.Constants;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.jeecg.modules.system.entity.SysDepart;
|
||||
import org.jeecg.modules.system.entity.SysUser;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.jeecg.modules.system.model.SysUserSysDepPostModel;
|
||||
@@ -271,4 +272,23 @@ public interface SysUserMapper extends BaseMapper<SysUser> {
|
||||
* @return
|
||||
*/
|
||||
List<SysUserSysDepPostModel> queryDepartUserByOrgCode(@Param("page") IPage page, @Param("orgCode") String orgCode, @Param("userParams") SysUser userParams);
|
||||
|
||||
/**
|
||||
* 根据用户名查询用户的主部门信息
|
||||
*
|
||||
* @param username
|
||||
* @return
|
||||
*/
|
||||
SysDepart getMainDepartByUsername(@Param("username") String username);
|
||||
|
||||
|
||||
/**
|
||||
* 根据用户组id获取用户分页列表
|
||||
* @param page
|
||||
* @param groupId
|
||||
* @param username
|
||||
* @param realname
|
||||
* @return
|
||||
*/
|
||||
IPage<SysUser> getUserByUgroupId(Page page, @Param("groupId") String groupId, @Param("username") String username, @Param("realname") String realname);
|
||||
}
|
||||
|
||||
@@ -134,4 +134,25 @@
|
||||
#{id}
|
||||
</foreach>
|
||||
</select>
|
||||
|
||||
<!-- 根据用户id、业务id和业务类型获取未读消息 -->
|
||||
<select id="getUnReadAnnByBusAndUserId" resultType="java.lang.String">
|
||||
SELECT
|
||||
sas.annt_id
|
||||
FROM
|
||||
sys_announcement_send sas
|
||||
LEFT JOIN sys_announcement sa ON sas.annt_id = sa.id
|
||||
WHERE sa.send_status = '1'
|
||||
AND sa.del_flag = '0'
|
||||
AND sas.read_flag = 0
|
||||
<if test="userId!=null and userId != ''">
|
||||
AND sas.user_id = #{userId}
|
||||
</if>
|
||||
<if test="busId !=null and busId != ''">
|
||||
AND sa.bus_id = #{busId}
|
||||
</if>
|
||||
<if test="busType !=null and busType != ''">
|
||||
AND sa.bus_type = #{busType}
|
||||
</if>
|
||||
</select>
|
||||
</mapper>
|
||||
@@ -20,8 +20,8 @@
|
||||
)
|
||||
)
|
||||
</select>
|
||||
|
||||
<!-- 根据username查询所拥有的部门 -->
|
||||
|
||||
<!-- 根据 userId 查询所拥有的部门 -->
|
||||
<select id="queryDepartsByUserId" parameterType="String" resultType="java.lang.String">
|
||||
SELECT id
|
||||
FROM sys_depart
|
||||
@@ -32,6 +32,18 @@
|
||||
)
|
||||
</select>
|
||||
|
||||
<!-- 根据 userIds 查询所拥有的部门 -->
|
||||
<select id="queryDepartIdsByUserIds" parameterType="String" resultType="java.util.Map">
|
||||
SELECT sd.id depart_id,
|
||||
sud.user_id user_id
|
||||
FROM sys_depart sd
|
||||
LEFT JOIN sys_user_depart sud ON sd.id = sud.dep_id
|
||||
WHERE
|
||||
<foreach item="idItem" index="index" collection="userIds" open=" sud.user_id IN (" separator="," close=")">
|
||||
#{idItem}
|
||||
</foreach>
|
||||
</select>
|
||||
|
||||
<!-- 根据username和分类查询所拥有的部门/岗位/公司 -->
|
||||
<select id="queryDeptByUserAndCategory" parameterType="String" resultType="org.jeecg.modules.system.entity.SysDepart">
|
||||
SELECT *
|
||||
@@ -310,8 +322,14 @@
|
||||
<select id="getDepartmentHead" resultType="org.jeecg.modules.system.entity.SysUser">
|
||||
select id, realname, avatar, sex, telephone, phone, main_dep_post_id, iz_hide_contact, sort, create_time from sys_user
|
||||
where status = 1 and del_flag = 0
|
||||
<bind name="bindDepartId" value="departId+'%'"/>
|
||||
<bind name="bindDepartId" value="'%'+departId+'%'"/>
|
||||
and depart_ids like #{bindDepartId}
|
||||
order by sort,create_time desc
|
||||
</select>
|
||||
|
||||
<!--获取所有顶级公司部门信息-->
|
||||
<select id="getAllDepartPost" resultType="org.jeecg.modules.system.vo.SysDepartPositionVo">
|
||||
select depart_name as positionName,id,iz_leaf,parent_id,org_category,org_code, dep_post_parent_id from sys_depart
|
||||
where (parent_id IS NULL OR parent_id = '')
|
||||
</select>
|
||||
</mapper>
|
||||
@@ -224,4 +224,108 @@
|
||||
and p.del_flag = 0
|
||||
</select>
|
||||
|
||||
<!-- update-begin author:scott date:2026-04-16 for:【pull/9445】开启多租户模式时,获取用户权限时加入tenant_id判断 -->
|
||||
<!-- 获取登录用户拥有的权限(多租户模式,加入tenant_id过滤) -->
|
||||
<select id="queryByUserWithTenantId" parameterType="Object" resultMap="SysPermission">
|
||||
SELECT * FROM (
|
||||
SELECT p.id,
|
||||
p.parent_id,
|
||||
p.name,
|
||||
p.url,
|
||||
p.component,
|
||||
p.is_route,
|
||||
p.component_name,
|
||||
p.redirect,
|
||||
p.menu_type,
|
||||
p.perms,
|
||||
p.perms_type,
|
||||
p.sort_no,
|
||||
p.always_show,
|
||||
p.icon,
|
||||
p.is_leaf,
|
||||
p.keep_alive,
|
||||
p.hidden,
|
||||
p.hide_tab,
|
||||
p.rule_flag,
|
||||
p.status,
|
||||
p.internal_or_external
|
||||
FROM sys_permission p
|
||||
WHERE p.del_flag = 0
|
||||
AND ( p.id in (
|
||||
SELECT DISTINCT a.permission_id
|
||||
FROM sys_role_permission a
|
||||
JOIN sys_role b ON a.role_id = b.id
|
||||
JOIN sys_user_role c ON c.role_id = b.id AND c.user_id = #{userId,jdbcType=VARCHAR}
|
||||
)
|
||||
or (p.url like '%:code' and p.url like '/online%' and p.hidden = 1)
|
||||
or (p.url like '%:id' and p.url like '/online%' and p.hidden = 1)
|
||||
or p.url = '/online'
|
||||
)
|
||||
<!--加入部门权限-->
|
||||
UNION
|
||||
SELECT p.id,
|
||||
p.parent_id,
|
||||
p.name,
|
||||
p.url,
|
||||
p.component,
|
||||
p.is_route,
|
||||
p.component_name,
|
||||
p.redirect,
|
||||
p.menu_type,
|
||||
p.perms,
|
||||
p.perms_type,
|
||||
p.sort_no,
|
||||
p.always_show,
|
||||
p.icon,
|
||||
p.is_leaf,
|
||||
p.keep_alive,
|
||||
p.hidden,
|
||||
p.hide_tab,
|
||||
p.rule_flag,
|
||||
p.status,
|
||||
p.internal_or_external
|
||||
FROM sys_permission p
|
||||
WHERE p.id in(
|
||||
SELECT DISTINCT a.permission_id
|
||||
FROM sys_depart_role_permission a
|
||||
INNER JOIN sys_depart_role b ON a.role_id = b.id
|
||||
INNER JOIN sys_depart_role_user c ON c.drole_id = b.id AND c.user_id = #{userId,jdbcType=VARCHAR}
|
||||
)
|
||||
and p.del_flag = 0
|
||||
<!-- 租户套餐权限,加入tenant_id过滤 -->
|
||||
UNION
|
||||
SELECT p.id,
|
||||
p.parent_id,
|
||||
p.name,
|
||||
p.url,
|
||||
p.component,
|
||||
p.is_route,
|
||||
p.component_name,
|
||||
p.redirect,
|
||||
p.menu_type,
|
||||
p.perms,
|
||||
p.perms_type,
|
||||
p.sort_no,
|
||||
p.always_show,
|
||||
p.icon,
|
||||
p.is_leaf,
|
||||
p.keep_alive,
|
||||
p.hidden,
|
||||
p.hide_tab,
|
||||
p.rule_flag,
|
||||
p.status,
|
||||
p.internal_or_external
|
||||
FROM sys_permission p
|
||||
WHERE p.id in (
|
||||
SELECT distinct a.permission_id
|
||||
FROM sys_tenant_pack_perms a
|
||||
INNER JOIN sys_tenant_pack b ON a.pack_id = b.id AND b.STATUS = '1'
|
||||
INNER JOIN sys_tenant st ON st.id = b.tenant_id and st.del_flag = 0 and st.id = #{tenantId,jdbcType=BIGINT}
|
||||
INNER JOIN sys_tenant_pack_user c ON c.pack_id = b.id AND c.STATUS = '1' AND c.user_id = #{userId,jdbcType=VARCHAR}
|
||||
)
|
||||
and p.del_flag = 0
|
||||
) h order by h.sort_no ASC
|
||||
</select>
|
||||
<!-- update-end author:scott date:2026-04-16 for:【pull/9445】开启多租户模式时,获取用户权限时加入tenant_id判断 -->
|
||||
|
||||
</mapper>
|
||||
@@ -19,4 +19,15 @@
|
||||
#{positionId}
|
||||
</foreach>
|
||||
</select>
|
||||
|
||||
<!--批量通过用户id列表查询职位,结果含userId字段,供批量同步场景分组使用-->
|
||||
<select id="getPositionListByUserIds" resultType="org.jeecg.modules.system.vo.SysPositionVO">
|
||||
SELECT sp.name, sp.id, sup.user_id AS userId
|
||||
FROM sys_position sp
|
||||
INNER JOIN sys_user_position sup ON sp.id = sup.position_id
|
||||
WHERE sup.user_id IN
|
||||
<foreach collection="userIds" item="uid" open="(" separator="," close=")">
|
||||
#{uid}
|
||||
</foreach>
|
||||
</select>
|
||||
</mapper>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?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="org.jeecg.modules.system.mapper.SysUgroupMapper">
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?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="org.jeecg.modules.system.mapper.SysUgroupUserMapper">
|
||||
|
||||
</mapper>
|
||||
@@ -19,6 +19,8 @@
|
||||
<bind name="bindRealname" value="'%'+realname+'%'"/>
|
||||
and a.realname like #{bindRealname}
|
||||
</if>
|
||||
ORDER BY
|
||||
a.sort,a.create_time desc
|
||||
</select>
|
||||
|
||||
<!-- 根据部门查询部门用户 分页 -->
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
<!-- 根据用户ids批量查询用户账号 -->
|
||||
<select id="getUsernameByIds" resultType="String">
|
||||
select username from sys_user where del_flag = 0 and a.status = 1 and id in
|
||||
select username from sys_user where del_flag = 0 and status = 1 and id in
|
||||
<foreach collection="userIds" index="index" item="id" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
@@ -69,12 +69,23 @@
|
||||
and realname LIKE concat(concat('%',#{realname}),'%')
|
||||
</if>
|
||||
</select>
|
||||
|
||||
|
||||
<!-- 根据用户组Id查询 -->
|
||||
<select id="getUserByUgroupId" resultType="org.jeecg.modules.system.entity.SysUser">
|
||||
select * from sys_user where del_flag = 0 and id in (select user_id from sys_ugroup_user where group_id=#{groupId})
|
||||
<if test="username!=null and username!=''">
|
||||
and username LIKE concat(concat('%',#{username}),'%')
|
||||
</if>
|
||||
<if test="realname!=null and realname!=''">
|
||||
and realname LIKE concat(concat('%',#{realname}),'%')
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<!-- 修改用户部门code -->
|
||||
<update id="updateUserDepart">
|
||||
UPDATE sys_user SET
|
||||
<if test="orgCode!=null and loginTenantId!=null">
|
||||
org_code = #{orgCode, jdbcType=VARCHAR}
|
||||
org_code = #{orgCode, jdbcType=VARCHAR}
|
||||
,login_tenant_id = #{loginTenantId, jdbcType=VARCHAR}
|
||||
</if>
|
||||
<if test="orgCode==null and loginTenantId!=null">
|
||||
@@ -403,11 +414,11 @@
|
||||
<if test="userParams != null">
|
||||
<if test="userParams.realname != null and userParams.realname != ''">
|
||||
<bind name="bindRealname" value="'%'+ userParams.realname +'%'"/>
|
||||
AND su.realname LIKE bindRealname
|
||||
AND su.realname LIKE #{bindRealname}
|
||||
</if>
|
||||
<if test="userParams.workNo != null and userParams.workNo != ''">
|
||||
<bind name="bindWorkNo" value="'%'+ userParams.workNo +'%'"/>
|
||||
AND su.work_no LIKE bindWorkNo
|
||||
AND su.work_no LIKE #{bindWorkNo}
|
||||
</if>
|
||||
</if>
|
||||
</sql>
|
||||
@@ -462,4 +473,16 @@
|
||||
</if>
|
||||
order by su.sort,su.create_time desc
|
||||
</select>
|
||||
|
||||
<!-- 根据用户名查询用户的主部门信息 -->
|
||||
<select id="getMainDepartByUsername" resultType="org.jeecg.modules.system.entity.SysDepart" parameterType="java.lang.String">
|
||||
SELECT sdp.* FROM sys_depart sdp
|
||||
WHERE EXISTS (
|
||||
SELECT 1 FROM sys_depart sd
|
||||
INNER JOIN sys_user su ON su.main_dep_post_id = sd.id
|
||||
WHERE su.username = #{username}
|
||||
AND sd.parent_id = sdp.id
|
||||
);
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -51,5 +51,5 @@ public interface ISysAnnouncementSendService extends IService<SysAnnouncementSen
|
||||
* @param busId
|
||||
* @param busType
|
||||
*/
|
||||
void updateReadFlagByBusId(String busId, String busType);
|
||||
boolean updateReadFlagByBusId(String busId, String busType);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,9 @@ import org.jeecg.modules.system.vo.SysDepartExportVo;
|
||||
import org.jeecg.modules.system.vo.SysPositionSelectTreeVo;
|
||||
import org.jeecg.modules.system.vo.lowapp.ExportDepartVo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -116,6 +118,14 @@ public interface ISysDepartService extends IService<SysDepart>{
|
||||
*/
|
||||
List<String> queryDepartsByUserId(String userId);
|
||||
|
||||
/**
|
||||
* 根据 用户ID 查询部门ID列表
|
||||
*
|
||||
* @param userIds
|
||||
* @return key = 用户ID, value = 部门ID列表
|
||||
*/
|
||||
Map<String, List<String>> queryDepartIdsByUserIds(Collection<String> userIds);
|
||||
|
||||
/**
|
||||
* 根据部门id批量删除并删除其可能存在的子级部门
|
||||
* @param ids 多个部门id
|
||||
@@ -147,9 +157,10 @@ public interface ISysDepartService extends IService<SysDepart>{
|
||||
* @param parentId 父id
|
||||
* @param ids 多个部门id
|
||||
* @param primaryKey 主键字段(id或者orgCode)
|
||||
* @param orgCategory 逗号分隔的 orgCategory 值,如 "1,2";为空时退化为默认行为(排除岗位)
|
||||
* @return
|
||||
*/
|
||||
List<SysDepartTreeModel> queryTreeListByPid(String parentId,String ids, String primaryKey);
|
||||
List<SysDepartTreeModel> queryTreeListByPid(String parentId,String ids, String primaryKey, String orgCategory);
|
||||
|
||||
/**
|
||||
* 获取某个部门的所有父级部门的ID
|
||||
@@ -305,4 +316,11 @@ public interface ISysDepartService extends IService<SysDepart>{
|
||||
* @return
|
||||
*/
|
||||
IPage<SysUser> getDepartmentHead(String departId, Page<SysUser> page);
|
||||
|
||||
/**
|
||||
* 获取所有职级关系
|
||||
* @param departId
|
||||
* @return
|
||||
*/
|
||||
List<SysPositionSelectTreeVo> getALLRankRelation(String departId);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.jeecg.modules.system.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.jeecg.modules.system.entity.SysPosition;
|
||||
import org.jeecg.modules.system.vo.SysPositionVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -33,4 +34,13 @@ public interface ISysPositionService extends IService<SysPosition> {
|
||||
* @return
|
||||
*/
|
||||
String getPositionName(List<String> postList);
|
||||
|
||||
/**
|
||||
* 批量通过用户id列表查询职位VO(含userId字段,用于批量同步场景消除N+1查询)
|
||||
*
|
||||
* @param userIds 用户id列表
|
||||
* @return 职位VO列表(每条记录含userId字段,供调用方分组)
|
||||
*/
|
||||
List<SysPositionVO> getPositionListByUserIds(List<String> userIds);
|
||||
|
||||
}
|
||||
|
||||
@@ -79,4 +79,14 @@ public interface ISysThirdAccountService extends IService<SysThirdAccount> {
|
||||
* @return
|
||||
*/
|
||||
SysThirdAccount getOneByUuidAndThirdType(String unionid, String thirdType,Integer tenantId,String thirdUserId);
|
||||
|
||||
/**
|
||||
* 批量通过本地用户id列表查询第三方账号(用于全量同步批量预加载)
|
||||
*
|
||||
* @param sysUserIds 本地用户id列表
|
||||
* @param thirdType 第三方类型
|
||||
* @return 第三方账号列表
|
||||
*/
|
||||
List<SysThirdAccount> listBySysUserIds(List<String> sysUserIds, String thirdType);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.jeecg.modules.system.service;
|
||||
|
||||
import org.jeecg.modules.system.entity.SysUgroup;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Description: 用户组表
|
||||
* @Author: jeecg-boot
|
||||
* @Date: 2026-02-27
|
||||
* @Version: V1.0
|
||||
*/
|
||||
public interface ISysUgroupService extends IService<SysUgroup> {
|
||||
|
||||
void deleteById(String id);
|
||||
|
||||
void deleteByIds(List<String> list);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.jeecg.modules.system.service;
|
||||
|
||||
import org.jeecg.modules.system.entity.SysUgroupUser;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* @Description: 用户组关系表
|
||||
* @Author: jeecg-boot
|
||||
* @Date: 2026-02-27
|
||||
* @Version: V1.0
|
||||
*/
|
||||
public interface ISysUgroupUserService extends IService<SysUgroupUser> {
|
||||
|
||||
}
|
||||
@@ -538,4 +538,14 @@ public interface ISysUserService extends IService<SysUser> {
|
||||
* @param userId
|
||||
*/
|
||||
void updateClientId(String clientId,String userId);
|
||||
|
||||
/**
|
||||
* 根据用户组查询用户列表
|
||||
* @param page
|
||||
* @param groupId
|
||||
* @param username
|
||||
* @param realname
|
||||
* @return
|
||||
*/
|
||||
IPage<SysUser> getUserByUgroupId(Page<SysUser> page, String groupId, String username, String realname);
|
||||
}
|
||||
|
||||
@@ -86,18 +86,15 @@ public class SysAnnouncementSendServiceImpl extends ServiceImpl<SysAnnouncementS
|
||||
* @param busType
|
||||
*/
|
||||
@Override
|
||||
public void updateReadFlagByBusId(String busId, String busType) {
|
||||
SysAnnouncement announcement = sysAnnouncementMapper.selectOne(new QueryWrapper<SysAnnouncement>().eq("bus_type",busType).eq("bus_id",busId));
|
||||
if(oConvertUtils.isNotEmpty(announcement)){
|
||||
LoginUser sysUser = (LoginUser)SecurityUtils.getSubject().getPrincipal();
|
||||
String userId = sysUser.getId();
|
||||
LambdaUpdateWrapper<SysAnnouncementSend> updateWrapper = new UpdateWrapper().lambda();
|
||||
updateWrapper.set(SysAnnouncementSend::getReadFlag, CommonConstant.HAS_READ_FLAG);
|
||||
updateWrapper.set(SysAnnouncementSend::getReadTime, new Date());
|
||||
updateWrapper.eq(SysAnnouncementSend::getAnntId,announcement.getId());
|
||||
updateWrapper.eq(SysAnnouncementSend::getUserId,userId);
|
||||
SysAnnouncementSend announcementSend = new SysAnnouncementSend();
|
||||
sysAnnouncementSendMapper.update(announcementSend, updateWrapper);
|
||||
public boolean updateReadFlagByBusId(String busId, String busType) {
|
||||
boolean updateFlag = false;
|
||||
LoginUser sysUser = (LoginUser)SecurityUtils.getSubject().getPrincipal();
|
||||
String userId = sysUser.getId();
|
||||
List<String> unReadAnnouncementsIds = sysAnnouncementSendMapper.getUnReadAnnByBusAndUserId(busId,busType,userId);
|
||||
if(CollectionUtil.isNotEmpty(unReadAnnouncementsIds)){
|
||||
sysAnnouncementSendMapper.updateReaded(userId, unReadAnnouncementsIds);
|
||||
updateFlag = true;
|
||||
}
|
||||
return updateFlag;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,6 +370,11 @@ public class SysBaseApiImpl implements ISysBaseAPI {
|
||||
return sysDepartService.queryDepartsByUserId(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<String>> getDepartIdsByUserIds(Collection<String> userIds) {
|
||||
return sysDepartService.queryDepartIdsByUserIds(userIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getDepartParentIdsByUsername(String username) {
|
||||
List<SysDepart> list = sysDepartService.queryDepartsByUsername(username);
|
||||
@@ -404,6 +409,29 @@ public class SysBaseApiImpl implements ISysBaseAPI {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(cacheNames = CacheConstant.SYS_USERS_CACHE, key = "#username + '::main_depart_info'", unless = "#result == null")
|
||||
public SysDepartModel queryMainDepartByUsername(String username) {
|
||||
if (oConvertUtils.isEmpty(username)) {
|
||||
return null;
|
||||
}
|
||||
// 根据用户名查询主部门信息
|
||||
SysDepart mainDepart = userMapper.getMainDepartByUsername(username);
|
||||
if (mainDepart == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 复制部门信息到模型对象
|
||||
SysDepartModel model = new SysDepartModel();
|
||||
BeanUtils.copyProperties(mainDepart, model);
|
||||
|
||||
// 设置部门路径名称
|
||||
String departPathName = sysDepartService.getDepartPathNameByOrgCode(model.getOrgCode(), null);
|
||||
model.setDepartPathName(departPathName);
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DictModel getParentDepartId(String departId) {
|
||||
SysDepart depart = departMapper.getParentDepartId(departId);
|
||||
@@ -2179,7 +2207,7 @@ public class SysBaseApiImpl implements ISysBaseAPI {
|
||||
log.error("{} UniPush消息推送失败 返回response:{}", pushType, response.getBody());
|
||||
}
|
||||
} catch (RestClientException e) {
|
||||
log.warn("UniAPP 消息推送异常:"+ e.getMessage(), e);
|
||||
log.warn("UniAPP 消息推送异常:"+ e.getMessage());
|
||||
}
|
||||
}
|
||||
/**
|
||||
|
||||
@@ -353,7 +353,14 @@ public class SysCommentServiceImpl extends ServiceImpl<SysCommentMapper, SysComm
|
||||
try {
|
||||
String ctxPath = uploadpath;
|
||||
String fileName = null;
|
||||
File file = new File(ctxPath + File.separator + bizPath + File.separator);
|
||||
//update-begin---author:liusq ---date:2026-03-30 for:【issues/9427】修复uploadLocal bizPath路径遍历漏洞(CWE-22)-----------
|
||||
// 路径遍历校验:规范化后确保目标目录在uploadpath内
|
||||
File uploadDir = new File(ctxPath).getCanonicalFile();
|
||||
File file = new File(ctxPath + File.separator + bizPath + File.separator).getCanonicalFile();
|
||||
if (!file.toPath().startsWith(uploadDir.toPath())) {
|
||||
throw new JeecgBootException("非法业务路径,禁止访问上传目录之外的路径: " + bizPath);
|
||||
}
|
||||
//update-end---author:liusq ---date:2026-03-30 for:【issues/9427】修复uploadLocal bizPath路径遍历漏洞(CWE-22)-----------
|
||||
if (!file.exists()) {
|
||||
file.mkdirs();// 创建文件根目录
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ import org.jeecg.modules.system.vo.lowapp.ExportDepartVo;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
@@ -563,6 +563,21 @@ public class SysDepartServiceImpl extends ServiceImpl<SysDepartMapper, SysDepart
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<String>> queryDepartIdsByUserIds(Collection<String> userIds) {
|
||||
List<Map<String, String>> mapList = baseMapper.queryDepartIdsByUserIds(userIds);
|
||||
if (CollectionUtils.isEmpty(mapList)) {
|
||||
return Map.of();
|
||||
}
|
||||
Map<String, List<String>> res = new HashMap<>();
|
||||
for (Map<String, String> map : mapList) {
|
||||
String userId = map.get("user_id");
|
||||
String departId = map.get("depart_id");
|
||||
res.computeIfAbsent(userId, k -> new ArrayList<>()).add(departId);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户所负责部门ids获取父级部门编码
|
||||
* @param departIds
|
||||
@@ -665,7 +680,7 @@ public class SysDepartServiceImpl extends ServiceImpl<SysDepartMapper, SysDepart
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public List<SysDepartTreeModel> queryTreeListByPid(String parentId,String ids, String primaryKey) {
|
||||
public List<SysDepartTreeModel> queryTreeListByPid(String parentId,String ids, String primaryKey, String orgCategory) {
|
||||
Consumer<LambdaQueryWrapper<SysDepart>> square = i -> {
|
||||
if (oConvertUtils.isNotEmpty(ids)) {
|
||||
if (CommonConstant.DEPART_KEY_ORG_CODE.equals(primaryKey)) {
|
||||
@@ -689,8 +704,13 @@ public class SysDepartServiceImpl extends ServiceImpl<SysDepartMapper, SysDepart
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------
|
||||
lqw.eq(true,SysDepart::getDelFlag,CommonConstant.DEL_FLAG_0.toString());
|
||||
// 代码逻辑说明: 【QQYUN-13427】部门选择组件修改:需要过滤掉岗位 只保留 公司 子公司 部门---
|
||||
lqw.ne(SysDepart::getOrgCategory,DepartCategoryEnum.DEPART_CATEGORY_POST.getValue());
|
||||
// 按 orgCategory 过滤:传入则精确匹配,否则默认排除岗位
|
||||
if (oConvertUtils.isNotEmpty(orgCategory)) {
|
||||
lqw.in(SysDepart::getOrgCategory, (Object[]) orgCategory.split(SymbolConstant.COMMA));
|
||||
} else {
|
||||
// 代码逻辑说明: 【QQYUN-13427】部门选择组件修改:需要过滤掉岗位 只保留 公司 子公司 部门---
|
||||
lqw.ne(SysDepart::getOrgCategory, DepartCategoryEnum.DEPART_CATEGORY_POST.getValue());
|
||||
}
|
||||
lqw.func(square);
|
||||
// 代码逻辑说明: [VUEN-1143]排序不对,vue3和2应该都有问题,应该按照升序排------------
|
||||
lqw.orderByAsc(SysDepart::getDepartOrder);
|
||||
@@ -1754,12 +1774,34 @@ public class SysDepartServiceImpl extends ServiceImpl<SysDepartMapper, SysDepart
|
||||
//step2 查看是否有子级部门,存在递归查询职位
|
||||
if (!CommonConstant.IS_LEAF.equals(sysDepartPosition.getIzLeaf())) {
|
||||
//获取子级职位根据部门编码
|
||||
this.getChildrenDepartPositionByOrgCode(selectTreeVos, departNameMap, sysDepartPosition);
|
||||
this.getChildrenDepartPositionByOrgCode(selectTreeVos, departNameMap, sysDepartPosition,departId);
|
||||
return buildTree(selectTreeVos);
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有部门职务
|
||||
* @param departId
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public List<SysPositionSelectTreeVo> getALLRankRelation(String departId) {
|
||||
//记录当前部门 key为部门id,value为部门名称
|
||||
Map<String, String> departNameMap = new HashMap<>(5);
|
||||
//step1 根据id查询部门信息
|
||||
List<SysDepartPositionVo> departPositionList = baseMapper.getAllDepartPost(departId);
|
||||
List<SysPositionSelectTreeVo> selectTreeVos = new ArrayList<>();
|
||||
departPositionList.forEach(position -> {
|
||||
//step2 查看是否有子级部门,存在递归查询职位
|
||||
if (!CommonConstant.IS_LEAF.equals(position.getIzLeaf())) {
|
||||
//获取子级职位根据部门编码
|
||||
this.getChildrenDepartPositionByOrgCode(selectTreeVos, departNameMap, position,departId);
|
||||
}
|
||||
});
|
||||
return buildTree(selectTreeVos);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取子级职位根据部门编码
|
||||
*
|
||||
@@ -1767,7 +1809,7 @@ public class SysDepartServiceImpl extends ServiceImpl<SysDepartMapper, SysDepart
|
||||
* @param departNameMap
|
||||
* @param sysDepartPosition
|
||||
*/
|
||||
private void getChildrenDepartPositionByOrgCode(List<SysPositionSelectTreeVo> selectTreeVos, Map<String, String> departNameMap, SysDepartPositionVo sysDepartPosition) {
|
||||
private void getChildrenDepartPositionByOrgCode(List<SysPositionSelectTreeVo> selectTreeVos, Map<String, String> departNameMap, SysDepartPositionVo sysDepartPosition,String departId) {
|
||||
String orgCode = sysDepartPosition.getOrgCode();
|
||||
//step1 根据父级id获取子级部门信息
|
||||
List<SysDepartPositionVo> positionList = baseMapper.getDepartPostByOrgCode(orgCode + "%");
|
||||
@@ -1778,9 +1820,11 @@ public class SysDepartServiceImpl extends ServiceImpl<SysDepartMapper, SysDepart
|
||||
departNameMap = new HashMap<>(5);
|
||||
}
|
||||
SysDepart depart = baseMapper.getDepartById(position.getParentId());
|
||||
if(null != depart){
|
||||
position.setDepartName(depart.getDepartName());
|
||||
}
|
||||
if(null != depart && oConvertUtils.isNotEmpty(departId)) {
|
||||
position.setDepartName(depart.getDepartName());
|
||||
}else{
|
||||
position.setDepartName(this.getDepartPathNameByOrgCode(depart.getOrgCode(),null));
|
||||
}
|
||||
if(oConvertUtils.isNotEmpty(position.getDepPostParentId())){
|
||||
LambdaQueryWrapper<SysDepart> query = new LambdaQueryWrapper<>();
|
||||
query.eq(SysDepart::getId,position.getDepPostParentId());
|
||||
@@ -1793,6 +1837,9 @@ public class SysDepartServiceImpl extends ServiceImpl<SysDepartMapper, SysDepart
|
||||
departNameMap.put(position.getParentId(), position.getPositionName());
|
||||
//查看是否为部门岗位,不是则不需要处理
|
||||
SysPositionSelectTreeVo treeVo = new SysPositionSelectTreeVo(position);
|
||||
if(oConvertUtils.isEmpty(departId)){
|
||||
treeVo.setOrgCode(position.getOrgCode());
|
||||
}
|
||||
selectTreeVos.add(treeVo);
|
||||
}
|
||||
}
|
||||
@@ -2252,4 +2299,5 @@ public class SysDepartServiceImpl extends ServiceImpl<SysDepartMapper, SysDepart
|
||||
}
|
||||
return page.setRecords(departmentHead);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -559,11 +559,15 @@ public class SysDictServiceImpl extends ServiceImpl<SysDictMapper, SysDict> impl
|
||||
String orderInfo = keyword.substring(keyword.indexOf(orderKey) + orderKey.length() + 1, keyword.length() - 1);
|
||||
keyword = keyword.substring(0, keyword.indexOf(orderKey));
|
||||
String[] orderInfoArray = orderInfo.split(SymbolConstant.COMMA);
|
||||
orderField = orderInfoArray[0];
|
||||
orderType = orderInfoArray[1];
|
||||
// 【issue/9570】排序字段和排序方向使用白名单校验,防止 boolean-blind SQL 注入(CASE WHEN/LIKE/database() 等绕过黑名单)
|
||||
orderField = SqlInjectionUtil.getSqlInjectField(orderInfoArray[0]);
|
||||
orderType = SqlInjectionUtil.getSqlInjectOrderType(orderInfoArray[1]);
|
||||
}
|
||||
|
||||
if (oConvertUtils.isNotEmpty(keyword)) {
|
||||
// 【安全】对keyword进行SQL注入检测和单引号转义,防止通过keyword参数进行SQL注入
|
||||
keyword = keyword.replace("'", "''");
|
||||
|
||||
// 判断是否是多选
|
||||
if (keyword.contains(SymbolConstant.COMMA)) {
|
||||
// 代码逻辑说明: JTC-529【表单设计器】 编辑页面报错,in参数采用双引号导致 ----
|
||||
@@ -661,6 +665,13 @@ public class SysDictServiceImpl extends ServiceImpl<SysDictMapper, SysDict> impl
|
||||
if (query != null) {
|
||||
for (Map.Entry<String, String> searchItem : query.entrySet()) {
|
||||
String fieldName = searchItem.getKey();
|
||||
// update-begin---author:sjlei---date:20260413 for:【#9524】修复 SQL _tableFilterSql 注入漏洞
|
||||
// _tableFilterSql 是服务端内部专用 key,对应 Mapper 中的 ${value} 裸拼接,
|
||||
// 禁止从外部 condition 参数传入,防止 SQL 注入(#9520)
|
||||
if ("_tableFilterSql".equals(fieldName)) {
|
||||
continue;
|
||||
}
|
||||
// update-end-----author:sjlei---date:20260413 for:【#9520】修复 SQL _tableFilterSql 注入漏洞
|
||||
queryParams.put(SqlInjectionUtil.getSqlInjectField(fieldName), searchItem.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.jeecg.modules.system.service.impl;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
import org.jeecg.common.constant.CacheConstant;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.exception.JeecgBootException;
|
||||
@@ -235,7 +236,19 @@ public class SysPermissionServiceImpl extends ServiceImpl<SysPermissionMapper, S
|
||||
|
||||
@Override
|
||||
public List<SysPermission> queryByUser(String userId) {
|
||||
List<SysPermission> permissionList = this.sysPermissionMapper.queryByUser(userId);
|
||||
//update-begin---author:scott ---date:2026-04-16 for:【pull/9445】开启多租户模式时,获取用户权限时加入tenant_id判断-----------
|
||||
List<SysPermission> permissionList;
|
||||
if (MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL) {
|
||||
int tenantId = oConvertUtils.getInt(TenantContext.getTenant(), -1);
|
||||
if (tenantId != -1) {
|
||||
permissionList = this.sysPermissionMapper.queryByUserWithTenantId(userId, tenantId);
|
||||
} else {
|
||||
permissionList = this.sysPermissionMapper.queryByUser(userId);
|
||||
}
|
||||
} else {
|
||||
permissionList = this.sysPermissionMapper.queryByUser(userId);
|
||||
}
|
||||
//update-end---author:scott ---date:2026-04-16 for:【pull/9445】开启多租户模式时,获取用户权限时加入tenant_id判断-----------
|
||||
//================= begin 开启租户的时候 如果没有test角色,默认加入test角色================
|
||||
if (MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL) {
|
||||
if (permissionList == null) {
|
||||
|
||||
@@ -6,8 +6,10 @@ import org.jeecg.common.constant.SymbolConstant;
|
||||
import org.jeecg.modules.system.entity.SysPosition;
|
||||
import org.jeecg.modules.system.mapper.SysPositionMapper;
|
||||
import org.jeecg.modules.system.service.ISysPositionService;
|
||||
import org.jeecg.modules.system.vo.SysPositionVO;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -40,4 +42,14 @@ public class SysPositionServiceImpl extends ServiceImpl<SysPositionMapper, SysPo
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
//update-begin---author:sjlei ---date:2026-04-17 for:【#9496】全量同步N+1查询性能优化-----------
|
||||
@Override
|
||||
public List<SysPositionVO> getPositionListByUserIds(List<String> userIds) {
|
||||
if (userIds == null || userIds.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return this.baseMapper.getPositionListByUserIds(userIds);
|
||||
}
|
||||
//update-end---author:sjlei ---date:2026-04-17 for:【#9496】全量同步N+1查询性能优化-----------
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public class SysRoleIndexServiceImpl extends ServiceImpl<SysRoleIndexMapper, Sys
|
||||
private RedisUtil redisUtil;
|
||||
|
||||
@Override
|
||||
@Cacheable(cacheNames = DefIndexConst.CACHE_KEY, key = "'" + DefIndexConst.DEF_INDEX_ALL + "'")
|
||||
@Cacheable(cacheNames = DefIndexConst.CACHE_KEY + "#3600", key = "'" + DefIndexConst.DEF_INDEX_ALL + "'")
|
||||
public SysRoleIndex queryDefaultIndex() {
|
||||
LambdaQueryWrapper<SysRoleIndex> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(SysRoleIndex::getRoleCode, DefIndexConst.DEF_INDEX_ALL);
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@@ -151,6 +152,19 @@ public class SysThirdAccountServiceImpl extends ServiceImpl<SysThirdAccountMappe
|
||||
return super.getOne(queryWrapper);
|
||||
}
|
||||
|
||||
//update-begin---author:sjlei ---date:2026-04-17 for:【#9496】全量同步N+1查询性能优化-----------
|
||||
@Override
|
||||
public List<SysThirdAccount> listBySysUserIds(List<String> sysUserIds, String thirdType) {
|
||||
if (sysUserIds == null || sysUserIds.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
LambdaQueryWrapper<SysThirdAccount> qw = new LambdaQueryWrapper<>();
|
||||
qw.in(SysThirdAccount::getSysUserId, sysUserIds);
|
||||
qw.eq(SysThirdAccount::getThirdType, thirdType);
|
||||
return list(qw);
|
||||
}
|
||||
//update-end---author:sjlei ---date:2026-04-17 for:【#9496】全量同步N+1查询性能优化-----------
|
||||
|
||||
@Override
|
||||
public List<SysThirdAccount> listThirdUserIdByUsername(String[] sysUsernameArr, String thirdType, Integer tenantId) {
|
||||
return sysThirdAccountMapper.selectThirdIdsByUsername(sysUsernameArr, thirdType,tenantId);
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package org.jeecg.modules.system.service.impl;
|
||||
|
||||
import org.jeecg.modules.system.entity.SysUgroup;
|
||||
import org.jeecg.modules.system.entity.SysUgroupUser;
|
||||
import org.jeecg.modules.system.mapper.SysUgroupMapper;
|
||||
import org.jeecg.modules.system.service.ISysUgroupService;
|
||||
import org.jeecg.modules.system.service.ISysUgroupUserService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Description: 用户组表
|
||||
* @Author: jeecg-boot
|
||||
* @Date: 2026-02-27
|
||||
* @Version: V1.0
|
||||
*/
|
||||
@Service("sysUgroupServiceImpl")
|
||||
public class SysUgroupServiceImpl extends ServiceImpl<SysUgroupMapper, SysUgroup> implements ISysUgroupService {
|
||||
|
||||
@Autowired
|
||||
private ISysUgroupUserService sysUgroupUserService;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteById(String id) {
|
||||
this.baseMapper.deleteById(id);
|
||||
sysUgroupUserService.remove(new QueryWrapper<SysUgroupUser>().eq("group_id", id));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteByIds(List<String> list) {
|
||||
this.baseMapper.deleteBatchIds(list);
|
||||
sysUgroupUserService.remove(new QueryWrapper<SysUgroupUser>().in("group_id", list));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.jeecg.modules.system.service.impl;
|
||||
|
||||
import org.jeecg.modules.system.entity.SysUgroupUser;
|
||||
import org.jeecg.modules.system.mapper.SysUgroupUserMapper;
|
||||
import org.jeecg.modules.system.service.ISysUgroupUserService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
|
||||
/**
|
||||
* @Description: 用户组关系表
|
||||
* @Author: jeecg-boot
|
||||
* @Date: 2026-02-27
|
||||
* @Version: V1.0
|
||||
*/
|
||||
@Service("sysUgroupUserServiceImpl")
|
||||
public class SysUgroupUserServiceImpl extends ServiceImpl<SysUgroupUserMapper, SysUgroupUser> implements ISysUgroupUserService {
|
||||
|
||||
}
|
||||
@@ -135,7 +135,7 @@ public class SysUserDepartServiceImpl extends ServiceImpl<SysUserDepartMapper, S
|
||||
realname = realname.trim();
|
||||
}
|
||||
List<SysUser> userList = this.baseMapper.queryDepartUserList(depCode, realname);
|
||||
Map<String, SysUser> map = new HashMap(5);
|
||||
Map<String, SysUser> map = new LinkedHashMap(5);
|
||||
for (SysUser sysUser : userList) {
|
||||
// 返回的用户数据去掉密码信息
|
||||
sysUser.setSalt("");
|
||||
|
||||
@@ -3036,6 +3036,31 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
this.baseMapper.updateById(sysUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户组查询用户列表
|
||||
* @param page
|
||||
* @param groupId
|
||||
* @param username
|
||||
* @param realname
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public IPage<SysUser> getUserByUgroupId(Page<SysUser> page, String groupId, String username, String realname) {
|
||||
IPage<SysUser> userGroupList = userMapper.getUserByUgroupId(page, groupId, username,realname);
|
||||
List<SysUser> records = userGroupList.getRecords();
|
||||
if (null != records && records.size() > 0) {
|
||||
List<String> userIds = records.stream().map(SysUser::getId).collect(Collectors.toList());
|
||||
Map<String, String> useDepNames = this.getDepNamesByUserIds(userIds);
|
||||
for (SysUser sysUser : userGroupList.getRecords()) {
|
||||
//设置部门
|
||||
sysUser.setOrgCodeTxt(useDepNames.get(sysUser.getId()));
|
||||
//设置用户职位id
|
||||
this.userPositionId(sysUser);
|
||||
}
|
||||
}
|
||||
return userGroupList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否有交集
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,7 @@ import cn.hutool.core.util.ObjectUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.jeecg.dingtalk.api.base.JdtBaseAPI;
|
||||
import com.jeecg.dingtalk.api.core.response.Response;
|
||||
@@ -40,6 +41,7 @@ import org.jeecg.modules.system.mapper.*;
|
||||
import org.jeecg.modules.system.model.SysDepartTreeModel;
|
||||
import org.jeecg.modules.system.model.ThirdLoginModel;
|
||||
import org.jeecg.modules.system.service.*;
|
||||
import org.jeecg.modules.system.vo.SysPositionVO;
|
||||
import org.jeecg.modules.system.vo.thirdapp.JdtDepartmentTreeVo;
|
||||
import org.jeecg.modules.system.vo.thirdapp.SyncInfoVo;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
@@ -346,6 +348,41 @@ public class ThirdAppDingtalkServiceImpl implements IThirdAppService {
|
||||
// 获取本地所有用户
|
||||
sysUsers = userMapper.selectList(Wrappers.emptyWrapper());
|
||||
}
|
||||
if (CollectionUtils.isEmpty(sysUsers)) {
|
||||
return syncInfo;
|
||||
}
|
||||
//update-begin---author:sjlei ---date:2026-04-17 for:【#9496】全量同步N+1查询性能优化-----------
|
||||
List<String> userIds = sysUsers.stream().map(SysUser::getId).collect(Collectors.toList());
|
||||
// ① 批量预加载 sys_third_account → Map<sysUserId, SysThirdAccount>
|
||||
Map<String, SysThirdAccount> thirdAccountMap = sysThirdAccountService
|
||||
.listBySysUserIds(userIds, THIRD_TYPE)
|
||||
.stream()
|
||||
.collect(Collectors.toMap(SysThirdAccount::getSysUserId, a -> a, (a, b) -> a));
|
||||
// ② 批量预加载用户-部门关系 → Map<userId, List<departId>>
|
||||
LambdaQueryWrapper<SysUserDepart> udQw = new LambdaQueryWrapper<>();
|
||||
udQw.in(SysUserDepart::getUserId, userIds);
|
||||
Map<String, List<String>> userDepartIdsMap = sysUserDepartService.list(udQw)
|
||||
.stream()
|
||||
.collect(Collectors.groupingBy(
|
||||
SysUserDepart::getUserId,
|
||||
Collectors.mapping(SysUserDepart::getDepId, Collectors.toList())
|
||||
));
|
||||
// ③ 批量预加载所有涉及的部门 → Map<departId, SysDepart>
|
||||
Set<String> allDepartIds = userDepartIdsMap.values().stream()
|
||||
.flatMap(Collection::stream).collect(Collectors.toSet());
|
||||
Map<String, SysDepart> departMap = Collections.emptyMap();
|
||||
if (!allDepartIds.isEmpty()) {
|
||||
departMap = sysDepartService.listByIds(allDepartIds)
|
||||
.stream()
|
||||
.collect(Collectors.toMap(SysDepart::getId, d -> d, (a, b) -> a));
|
||||
}
|
||||
// ④ 批量预加载职位 → Map<userId, List<SysPositionVO>>
|
||||
Map<String, List<SysPositionVO>> positionMap = sysPositionService
|
||||
.getPositionListByUserIds(userIds)
|
||||
.stream()
|
||||
.collect(Collectors.groupingBy(SysPositionVO::getUserId));
|
||||
//update-end---author:sjlei ---date:2026-04-17 for:【#9496】全量同步N+1查询性能优化-----------
|
||||
|
||||
// 查询钉钉所有的部门,用于同步用户和部门的关系
|
||||
List<Department> allDepartment = JdtDepartmentAPI.listAll(accessToken);
|
||||
|
||||
@@ -361,7 +398,9 @@ public class ThirdAppDingtalkServiceImpl implements IThirdAppService {
|
||||
* 1. 查询 sys_third_account(第三方账号表)是否有数据,如果有代表已同步
|
||||
* 2. 本地表里没有,就先用手机号判断,不通过再用username(用户账号)判断。
|
||||
*/
|
||||
SysThirdAccount sysThirdAccount = sysThirdAccountService.getOneBySysUserId(sysUser.getId(), THIRD_TYPE);
|
||||
//update-begin---author:sjlei ---date:2026-04-17 for:【#9496】全量同步N+1查询性能优化-----------
|
||||
SysThirdAccount sysThirdAccount = thirdAccountMap.get(sysUser.getId());
|
||||
//update-end---author:sjlei ---date:2026-04-17 for:【#9496】全量同步N+1查询性能优化-----------
|
||||
if (sysThirdAccount != null && oConvertUtils.isNotEmpty(sysThirdAccount.getThirdUserId())) {
|
||||
// sys_third_account 表匹配成功,通过第三方userId查询出第三方userInfo
|
||||
dtUserInfo = JdtUserAPI.getUserById(sysThirdAccount.getThirdUserId(), accessToken);
|
||||
@@ -384,12 +423,16 @@ public class ThirdAppDingtalkServiceImpl implements IThirdAppService {
|
||||
if (dtUserInfo != null && dtUserInfo.isSuccess() && dtUserInfo.getResult() != null) {
|
||||
User dtUser = dtUserInfo.getResult();
|
||||
dtUserId = dtUser.getUserid();
|
||||
User updateQwUser = this.sysUserToDtUser(sysUser, dtUser, allDepartment);
|
||||
//update-begin---author:sjlei ---date:2026-04-17 for:【#9496】全量同步N+1查询性能优化-----------
|
||||
User updateQwUser = this.sysUserToDtUser(sysUser, dtUser, allDepartment, userDepartIdsMap, departMap, positionMap);
|
||||
//update-end---author:sjlei ---date:2026-04-17 for:【#9496】全量同步N+1查询性能优化-----------
|
||||
Response<JSONObject> updateRes = JdtUserAPI.update(updateQwUser, accessToken);
|
||||
// 收集成功/失败信息
|
||||
apiSuccess = this.syncUserCollectErrInfo(updateRes, sysUser, syncInfo);
|
||||
} else {
|
||||
User newQwUser = this.sysUserToDtUser(sysUser, allDepartment);
|
||||
//update-begin---author:sjlei ---date:2026-04-17 for:【#9496】全量同步N+1查询性能优化-----------
|
||||
User newQwUser = this.sysUserToDtUser(sysUser, allDepartment, userDepartIdsMap, departMap, positionMap);
|
||||
//update-end---author:sjlei ---date:2026-04-17 for:【#9496】全量同步N+1查询性能优化-----------
|
||||
Response<String> createRes = JdtUserAPI.create(newQwUser, accessToken);
|
||||
dtUserId = createRes.getResult();
|
||||
// 收集成功/失败信息
|
||||
@@ -611,6 +654,56 @@ public class ThirdAppDingtalkServiceImpl implements IThirdAppService {
|
||||
return user;
|
||||
}
|
||||
|
||||
//update-begin---author:sjlei ---date:2026-04-17 for:【#9496】全量同步N+1查询性能优化-----------
|
||||
/**
|
||||
* 【同步用户】将SysUser转为【钉钉】的User对象(创建新用户,使用批量预加载Map,消除N+1查询)
|
||||
*/
|
||||
private User sysUserToDtUser(SysUser sysUser, List<Department> allDepartment,
|
||||
Map<String, List<String>> userDepartIdsMap, Map<String, SysDepart> departMap,
|
||||
Map<String, List<SysPositionVO>> positionMap) {
|
||||
User user = new User();
|
||||
user.setUserid(sysUser.getUsername());
|
||||
return this.sysUserToDtUser(sysUser, user, allDepartment, userDepartIdsMap, departMap, positionMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 【同步用户】将SysUser转为【钉钉】的User对象(更新旧用户,使用批量预加载Map,消除N+1查询)
|
||||
*/
|
||||
private User sysUserToDtUser(SysUser sysUser, User user, List<Department> allDepartment,
|
||||
Map<String, List<String>> userDepartIdsMap, Map<String, SysDepart> departMap,
|
||||
Map<String, List<SysPositionVO>> positionMap) {
|
||||
user.setName(sysUser.getRealname());
|
||||
user.setMobile(sysUser.getPhone());
|
||||
user.setTelephone(sysUser.getTelephone());
|
||||
user.setJob_number(sysUser.getWorkNo());
|
||||
// 职务翻译(使用预加载Map替代单次查询)
|
||||
List<SysPositionVO> positionList = positionMap.getOrDefault(sysUser.getId(), Collections.emptyList());
|
||||
if (!positionList.isEmpty()) {
|
||||
String positionName = positionList.stream().map(SysPositionVO::getName).collect(Collectors.joining(SymbolConstant.COMMA));
|
||||
user.setTitle(positionName);
|
||||
}
|
||||
user.setEmail(sysUser.getEmail());
|
||||
// 查询并同步用户部门关系(使用预加载Map替代单次查询)
|
||||
List<SysDepart> departList = this.getUserDepart(sysUser, userDepartIdsMap, departMap);
|
||||
if (departList != null) {
|
||||
List<Integer> departmentIdList = new ArrayList<>();
|
||||
for (SysDepart sysDepart : departList) {
|
||||
Department department = this.getDepartmentByDepartId(sysDepart.getId(), allDepartment);
|
||||
if (department != null) {
|
||||
departmentIdList.add(department.getDept_id());
|
||||
}
|
||||
}
|
||||
user.setDept_id_list(departmentIdList.toArray(new Integer[]{}));
|
||||
user.setDept_order_list(null);
|
||||
}
|
||||
if (oConvertUtils.isEmpty(user.getDept_id_list())) {
|
||||
user.setDept_id_list(1);
|
||||
user.setDept_order_list(null);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
//update-end---author:sjlei ---date:2026-04-17 for:【#9496】全量同步N+1查询性能优化-----------
|
||||
|
||||
|
||||
/**
|
||||
* 【同步用户】将【钉钉】的User对象转为SysUser(创建新用户)
|
||||
@@ -694,6 +787,24 @@ public class ThirdAppDingtalkServiceImpl implements IThirdAppService {
|
||||
return departList.size() == 0 ? null : departList;
|
||||
}
|
||||
|
||||
//update-begin---author:sjlei ---date:2026-04-17 for:【#9496】全量同步N+1查询性能优化-----------
|
||||
/**
|
||||
* 查询用户和部门的关系(使用批量预加载Map,消除N+1查询)
|
||||
*/
|
||||
private List<SysDepart> getUserDepart(SysUser sysUser, Map<String, List<String>> userDepartIdsMap,
|
||||
Map<String, SysDepart> departMap) {
|
||||
List<String> departIds = userDepartIdsMap.get(sysUser.getId());
|
||||
if (departIds == null || departIds.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
List<SysDepart> departList = departIds.stream()
|
||||
.map(departMap::get)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
return departList.isEmpty() ? null : departList;
|
||||
}
|
||||
//update-end---author:sjlei ---date:2026-04-17 for:【#9496】全量同步N+1查询性能优化-----------
|
||||
|
||||
/**
|
||||
* 根据sysDepartId查询钉钉的部门
|
||||
*/
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.jeecg.qywx.api.base.JwAccessTokenAPI;
|
||||
import com.jeecg.qywx.api.core.common.AccessToken;
|
||||
@@ -43,6 +44,7 @@ import org.jeecg.modules.system.entity.*;
|
||||
import org.jeecg.modules.system.mapper.*;
|
||||
import org.jeecg.modules.system.model.SysDepartTreeModel;
|
||||
import org.jeecg.modules.system.service.*;
|
||||
import org.jeecg.modules.system.vo.SysPositionVO;
|
||||
import org.jeecg.modules.system.vo.thirdapp.JwDepartmentTreeVo;
|
||||
import org.jeecg.modules.system.vo.thirdapp.JwSysUserDepartVo;
|
||||
import org.jeecg.modules.system.vo.thirdapp.JwUserDepartVo;
|
||||
@@ -353,6 +355,40 @@ public class ThirdAppWechatEnterpriseServiceImpl implements IThirdAppService {
|
||||
// 获取本地所有用户
|
||||
sysUsers = userMapper.selectList(Wrappers.emptyWrapper());
|
||||
}
|
||||
if (CollectionUtils.isEmpty(sysUsers)) {
|
||||
return syncInfo;
|
||||
}
|
||||
//update-begin---author:sjlei ---date:2026-04-17 for:【#9496】全量同步N+1查询性能优化-----------
|
||||
List<String> userIds = sysUsers.stream().map(SysUser::getId).collect(Collectors.toList());
|
||||
// ① 批量预加载 sys_third_account → Map<sysUserId, SysThirdAccount>
|
||||
Map<String, SysThirdAccount> thirdAccountMap = sysThirdAccountService
|
||||
.listBySysUserIds(userIds, THIRD_TYPE)
|
||||
.stream()
|
||||
.collect(Collectors.toMap(SysThirdAccount::getSysUserId, a -> a, (a, b) -> a));
|
||||
// ② 批量预加载用户-部门关系 → Map<userId, List<departId>>
|
||||
LambdaQueryWrapper<SysUserDepart> udQw = new LambdaQueryWrapper<>();
|
||||
udQw.in(SysUserDepart::getUserId, userIds);
|
||||
Map<String, List<String>> userDepartIdsMap = sysUserDepartService.list(udQw)
|
||||
.stream()
|
||||
.collect(Collectors.groupingBy(
|
||||
SysUserDepart::getUserId,
|
||||
Collectors.mapping(SysUserDepart::getDepId, Collectors.toList())
|
||||
));
|
||||
// ③ 批量预加载所有涉及的部门 → Map<departId, SysDepart>
|
||||
Set<String> allDepartIds = userDepartIdsMap.values().stream()
|
||||
.flatMap(Collection::stream).collect(Collectors.toSet());
|
||||
Map<String, SysDepart> departMap = Collections.emptyMap();
|
||||
if (!allDepartIds.isEmpty()) {
|
||||
departMap = sysDepartService.listByIds(allDepartIds)
|
||||
.stream()
|
||||
.collect(Collectors.toMap(SysDepart::getId, d -> d, (a, b) -> a));
|
||||
}
|
||||
// ④ 批量预加载职位 → Map<userId, List<SysPositionVO>>
|
||||
Map<String, List<SysPositionVO>> positionMap = sysPositionService
|
||||
.getPositionListByUserIds(userIds)
|
||||
.stream()
|
||||
.collect(Collectors.groupingBy(SysPositionVO::getUserId));
|
||||
//update-end---author:sjlei ---date:2026-04-17 for:【#9496】全量同步N+1查询性能优化-----------
|
||||
|
||||
// 循环判断新用户和需要更新的用户
|
||||
for1:
|
||||
@@ -367,7 +403,9 @@ public class ThirdAppWechatEnterpriseServiceImpl implements IThirdAppService {
|
||||
* 2. 本地表里没有,就先用手机号判断,不通过再用username判断。
|
||||
*/
|
||||
User qwUser;
|
||||
SysThirdAccount sysThirdAccount = sysThirdAccountService.getOneBySysUserId(sysUser.getId(), THIRD_TYPE);
|
||||
//update-begin---author:sjlei ---date:2026-04-17 for:【#9496】全量同步N+1查询性能优化-----------
|
||||
SysThirdAccount sysThirdAccount = thirdAccountMap.get(sysUser.getId());
|
||||
//update-end---author:sjlei ---date:2026-04-17 for:【#9496】全量同步N+1查询性能优化-----------
|
||||
for (User qwUserTemp : qwUsers) {
|
||||
if (sysThirdAccount == null || oConvertUtils.isEmpty(sysThirdAccount.getThirdUserId()) || !sysThirdAccount.getThirdUserId().equals(qwUserTemp.getUserid())) {
|
||||
// sys_third_account 表匹配失败,尝试用手机号匹配
|
||||
@@ -383,7 +421,9 @@ public class ThirdAppWechatEnterpriseServiceImpl implements IThirdAppService {
|
||||
// }
|
||||
}
|
||||
// 循环到此说明用户匹配成功,进行更新操作
|
||||
qwUser = this.sysUserToQwUser(sysUser, qwUserTemp);
|
||||
//update-begin---author:sjlei ---date:2026-04-17 for:【#9496】全量同步N+1查询性能优化-----------
|
||||
qwUser = this.sysUserToQwUser(sysUser, qwUserTemp, userDepartIdsMap, departMap, positionMap);
|
||||
//update-end---author:sjlei ---date:2026-04-17 for:【#9496】全量同步N+1查询性能优化-----------
|
||||
int errCode = JwUserAPI.updateUser(qwUser, accessToken);
|
||||
// 收集错误信息
|
||||
this.syncUserCollectErrInfo(errCode, sysUser, syncInfo);
|
||||
@@ -392,7 +432,9 @@ public class ThirdAppWechatEnterpriseServiceImpl implements IThirdAppService {
|
||||
continue for1;
|
||||
}
|
||||
// 循环到此说明是新用户,直接调接口创建
|
||||
qwUser = this.sysUserToQwUser(sysUser);
|
||||
//update-begin---author:sjlei ---date:2026-04-17 for:【#9496】全量同步N+1查询性能优化-----------
|
||||
qwUser = this.sysUserToQwUser(sysUser, userDepartIdsMap, departMap, positionMap);
|
||||
//update-end---author:sjlei ---date:2026-04-17 for:【#9496】全量同步N+1查询性能优化-----------
|
||||
int errCode = JwUserAPI.createUser(qwUser, accessToken);
|
||||
// 收集错误信息
|
||||
boolean apiSuccess = this.syncUserCollectErrInfo(errCode, sysUser, syncInfo);
|
||||
@@ -554,6 +596,79 @@ public class ThirdAppWechatEnterpriseServiceImpl implements IThirdAppService {
|
||||
return this.sysUserToQwUser(sysUser, user);
|
||||
}
|
||||
|
||||
//update-begin---author:sjlei ---date:2026-04-17 for:【#9496】全量同步N+1查询性能优化-----------
|
||||
/**
|
||||
* 【同步用户】将SysUser转为企业微信的User对象(创建新用户,使用批量预加载Map)
|
||||
*/
|
||||
private User sysUserToQwUser(SysUser sysUser, Map<String, List<String>> userDepartIdsMap,
|
||||
Map<String, SysDepart> departMap, Map<String, List<SysPositionVO>> positionMap) {
|
||||
User user = new User();
|
||||
user.setUserid(sysUser.getUsername());
|
||||
return this.sysUserToQwUser(sysUser, user, userDepartIdsMap, departMap, positionMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 【同步用户】将SysUser转为企业微信的User对象(更新旧用户,使用批量预加载Map)
|
||||
*/
|
||||
private User sysUserToQwUser(SysUser sysUser, User user, Map<String, List<String>> userDepartIdsMap,
|
||||
Map<String, SysDepart> departMap, Map<String, List<SysPositionVO>> positionMap) {
|
||||
user.setName(sysUser.getRealname());
|
||||
user.setMobile(sysUser.getPhone());
|
||||
// 查询并同步用户部门关系(使用预加载Map替代单次查询)
|
||||
List<SysDepart> departList = this.getUserDepart(sysUser, userDepartIdsMap, departMap);
|
||||
if (departList != null) {
|
||||
List<Integer> departmentIdList = new ArrayList<>();
|
||||
List<Integer> isLeaderInDept = new ArrayList<>();
|
||||
List<String> manageDepartIdList = new ArrayList<>();
|
||||
if (oConvertUtils.isNotEmpty(sysUser.getDepartIds())) {
|
||||
manageDepartIdList = Arrays.asList(sysUser.getDepartIds().split(","));
|
||||
}
|
||||
for (SysDepart sysDepart : departList) {
|
||||
if (oConvertUtils.isNotEmpty(sysDepart.getQywxIdentifier())) {
|
||||
try {
|
||||
departmentIdList.add(Integer.parseInt(sysDepart.getQywxIdentifier()));
|
||||
} catch (NumberFormatException ignored) {
|
||||
continue;
|
||||
}
|
||||
if (CommonConstant.USER_IDENTITY_2.equals(sysUser.getUserIdentity())) {
|
||||
isLeaderInDept.add(manageDepartIdList.contains(sysDepart.getId()) ? 1 : 0);
|
||||
} else {
|
||||
isLeaderInDept.add(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
user.setDepartment(departmentIdList.toArray(new Integer[]{}));
|
||||
user.setIs_leader_in_dept(isLeaderInDept.toArray(new Integer[]{}));
|
||||
}
|
||||
if (user.getDepartment() == null || user.getDepartment().length == 0) {
|
||||
user.setDepartment(new Integer[]{1});
|
||||
user.setIs_leader_in_dept(new Integer[]{0});
|
||||
}
|
||||
// 职务翻译(使用预加载Map替代单次查询)
|
||||
List<SysPositionVO> positionList = positionMap.getOrDefault(sysUser.getId(), Collections.emptyList());
|
||||
if (!positionList.isEmpty()) {
|
||||
String positionName = positionList.stream().map(SysPositionVO::getName).collect(Collectors.joining(SymbolConstant.COMMA));
|
||||
user.setPosition(positionName);
|
||||
}
|
||||
if (sysUser.getSex() != null) {
|
||||
user.setGender(sysUser.getSex().toString());
|
||||
}
|
||||
user.setEmail(sysUser.getEmail());
|
||||
if (sysUser.getStatus() != null) {
|
||||
if (CommonConstant.USER_UNFREEZE.equals(sysUser.getStatus()) || CommonConstant.USER_FREEZE.equals(sysUser.getStatus())) {
|
||||
user.setEnable(sysUser.getStatus() == 1 ? 1 : 0);
|
||||
} else {
|
||||
user.setEnable(1);
|
||||
}
|
||||
}
|
||||
user.setTelephone(sysUser.getTelephone());
|
||||
if (CommonConstant.DEL_FLAG_1.equals(sysUser.getDelFlag())) {
|
||||
user.setEnable(0);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
//update-end---author:sjlei ---date:2026-04-17 for:【#9496】全量同步N+1查询性能优化-----------
|
||||
|
||||
/**
|
||||
* 【同步用户】将SysUser转为企业微信的User对象(更新旧用户)
|
||||
*/
|
||||
@@ -648,6 +763,24 @@ public class ThirdAppWechatEnterpriseServiceImpl implements IThirdAppService {
|
||||
return departList.size() == 0 ? null : departList;
|
||||
}
|
||||
|
||||
//update-begin---author:sjlei ---date:2026-04-17 for:【#9496】全量同步N+1查询性能优化-----------
|
||||
/**
|
||||
* 查询用户和部门的关系(使用批量预加载Map,消除N+1查询)
|
||||
*/
|
||||
private List<SysDepart> getUserDepart(SysUser sysUser, Map<String, List<String>> userDepartIdsMap,
|
||||
Map<String, SysDepart> departMap) {
|
||||
List<String> departIds = userDepartIdsMap.get(sysUser.getId());
|
||||
if (departIds == null || departIds.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
List<SysDepart> departList = departIds.stream()
|
||||
.map(departMap::get)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
return departList.isEmpty() ? null : departList;
|
||||
}
|
||||
//update-end---author:sjlei ---date:2026-04-17 for:【#9496】全量同步N+1查询性能优化-----------
|
||||
|
||||
/**
|
||||
* 【同步用户】将企业微信的User对象转为SysUser(创建新用户)
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,7 @@ import org.apache.commons.fileupload.FileItem;
|
||||
import org.apache.commons.fileupload.FileItemFactory;
|
||||
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
|
||||
import org.jeecg.common.util.MyCommonsMultipartFile;
|
||||
import org.jeecg.common.util.filter.SsrfFileTypeFilter;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.*;
|
||||
@@ -26,6 +27,7 @@ public class HttpFileToMultipartFileUtil {
|
||||
* @throws Exception
|
||||
*/
|
||||
public static MultipartFile httpFileToMultipartFile(String fileUrl, String filename) throws Exception {
|
||||
SsrfFileTypeFilter.checkSsrfHttpUrl(fileUrl);
|
||||
byte[] bytes = downloadImageData(fileUrl);
|
||||
return convertByteToMultipartFile(bytes, filename);
|
||||
}
|
||||
|
||||
@@ -32,6 +32,43 @@ public class XssUtils {
|
||||
Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
|
||||
};
|
||||
|
||||
//update-begin---author:liusq ---date:2025-04-13 for:【issues/9521】富文本msgContent字段存储型XSS过滤-----------
|
||||
/**
|
||||
* 针对富文本HTML内容的XSS过滤:移除危险脚本和事件处理器,保留合法HTML标签和样式。
|
||||
* 与 scriptXss() 的区别:本方法不对HTML实体进行全局转义,适用于富文本内容(如公告正文)。
|
||||
*/
|
||||
private static final Pattern[] RICH_TEXT_PATTERNS = new Pattern[]{
|
||||
// <script> 标签及其内容
|
||||
Pattern.compile("<script[^>]*>[\\s\\S]*?</script>", Pattern.CASE_INSENSITIVE),
|
||||
// 自闭合 <script/>
|
||||
Pattern.compile("<script[^>]*/>", Pattern.CASE_INSENSITIVE),
|
||||
// 所有内联事件处理器属性,如 onerror= onclick= onmouseover= 等
|
||||
Pattern.compile("\\s+on\\w+\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s>]*)", Pattern.CASE_INSENSITIVE),
|
||||
// javascript: 协议
|
||||
Pattern.compile("javascript\\s*:", Pattern.CASE_INSENSITIVE),
|
||||
// vbscript: 协议
|
||||
Pattern.compile("vbscript\\s*:", Pattern.CASE_INSENSITIVE),
|
||||
// CSS expression()(IE 特有)
|
||||
Pattern.compile("expression\\s*\\(", Pattern.CASE_INSENSITIVE),
|
||||
// <iframe> 标签(防止内嵌恶意页面)
|
||||
Pattern.compile("<iframe[^>]*>[\\s\\S]*?</iframe>", Pattern.CASE_INSENSITIVE),
|
||||
Pattern.compile("<iframe[^>]*/>", Pattern.CASE_INSENSITIVE),
|
||||
// <object> / <embed> 标签
|
||||
Pattern.compile("<object[^>]*>[\\s\\S]*?</object>", Pattern.CASE_INSENSITIVE),
|
||||
Pattern.compile("<embed[^>]*>", Pattern.CASE_INSENSITIVE),
|
||||
};
|
||||
|
||||
public static String richTextXss(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
for (Pattern pattern : RICH_TEXT_PATTERNS) {
|
||||
value = pattern.matcher(value).replaceAll("");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
//update-end---author:liusq ---date:2025-04-13 for:【issues/9521】富文本msgContent字段存储型XSS过滤-----------
|
||||
|
||||
public static String scriptXss(String value) {
|
||||
if (value != null) {
|
||||
value = value.replaceAll(" ", "");
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.jeecg.modules.system.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import org.jeecg.modules.system.entity.SysDictItem;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Description: 批量字典VO
|
||||
* @author: zzl
|
||||
*/
|
||||
@Data
|
||||
public class SysDictBatchVo {
|
||||
|
||||
/**
|
||||
* 字典列表
|
||||
*/
|
||||
private List<SysDictPage> dictList;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.jeecg.modules.system.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.jeecg.modules.system.entity.SysPosition;
|
||||
|
||||
/**
|
||||
* 职务VO,扩展了userId字段,用于批量查询职位时携带用户ID(供全量同步批量预加载场景)
|
||||
*
|
||||
* @author sjlei
|
||||
* @version V1.0
|
||||
* @date 2026-04-17
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class SysPositionVO extends SysPosition {
|
||||
|
||||
/**
|
||||
* 批量查询时携带的用户ID(非数据库字段,仅用于查询结果分组)
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.jeecg.modules.system.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Description: 用户组vo
|
||||
* @author: jeecg-boot
|
||||
*/
|
||||
@Data
|
||||
public class SysUserGroupVO implements Serializable{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**用户组id*/
|
||||
private String groupId;
|
||||
/**对应的用户id集合*/
|
||||
private List<String> userIdList;
|
||||
|
||||
public SysUserGroupVO() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SysUserGroupVO(String groupId, List<String> userIdList) {
|
||||
super();
|
||||
this.groupId = groupId;
|
||||
this.userIdList = userIdList;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user