3.7.0大版本发布

This commit is contained in:
JEECG
2024-06-11 22:58:04 +08:00
parent 857fb53fa1
commit a6b6e7c9d4
205 changed files with 3807 additions and 768 deletions

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>jeecg-system-api</artifactId>
<groupId>org.jeecgframework.boot</groupId>
<version>3.6.3</version>
<version>3.7.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -85,6 +85,14 @@ public interface ISysBaseAPI extends CommonAPI {
*/
@GetMapping("/sys/api/getRolesByUsername")
List<String> getRolesByUsername(@RequestParam("username") String username);
/**
* 7通过用户账号查询角色集合
* @param userId
* @return
*/
@GetMapping("/sys/api/getRolesByUserId")
List<String> getRolesByUserId(@RequestParam("userId") String userId);
/**
* 8通过用户账号查询部门集合
@ -93,6 +101,14 @@ public interface ISysBaseAPI extends CommonAPI {
*/
@GetMapping("/sys/api/getDepartIdsByUsername")
List<String> getDepartIdsByUsername(@RequestParam("username") String username);
/**
* 8通过用户账号查询部门集合
* @param userId
* @return 部门 id
*/
@GetMapping("/sys/api/getDepartIdsByUserId")
List<String> getDepartIdsByUserId(@RequestParam("userId") String userId);
/**
* 8.2 通过用户账号查询部门父ID集合
@ -304,6 +320,14 @@ public interface ISysBaseAPI extends CommonAPI {
*/
@GetMapping("/sys/api/getUserRoleSet")
Set<String> getUserRoleSet(@RequestParam("username")String username);
/**
* 30获取用户的角色集合
* @param userId
* @return
*/
@GetMapping("/sys/api/getUserRoleSetById")
Set<String> getUserRoleSetById(@RequestParam("userId")String userId);
/**
* 31获取用户的权限集合
@ -348,6 +372,15 @@ public interface ISysBaseAPI extends CommonAPI {
@Override
@GetMapping("/sys/api/queryUserRoles")
Set<String> queryUserRoles(@RequestParam("username")String username);
/**
* 35查询用户角色信息
* @param userId
* @return
*/
@Override
@GetMapping("/sys/api/queryUserRolesById")
Set<String> queryUserRolesById(@RequestParam("userId")String userId);
/**
* 36查询用户权限信息
@ -387,6 +420,15 @@ public interface ISysBaseAPI extends CommonAPI {
@SensitiveDecode
@GetMapping("/sys/api/getUserByName")
LoginUser getUserByName(@RequestParam("username") String username);
/**
* 39根据用户账号查询用户ID CommonAPI中定义
* @param username
* @return 用户ID
*/
@Override
@GetMapping("/sys/api/getUserIdByName")
String getUserIdByName(@RequestParam("username") String username);
/**
* 40字典表的 翻译
@ -609,7 +651,7 @@ public interface ISysBaseAPI extends CommonAPI {
* @param dataLogDto
*/
@PostMapping("/sys/api/saveDataLog")
void saveDataLog(DataLogDTO dataLogDto);
void saveDataLog(@RequestBody DataLogDTO dataLogDto);
/**
* 更新头像

View File

@ -60,11 +60,21 @@ public class SysBaseAPIFallback implements ISysBaseAPI {
return null;
}
@Override
public List<String> getRolesByUserId(String userId) {
return null;
}
@Override
public List<String> getDepartIdsByUsername(String username) {
return null;
}
@Override
public List<String> getDepartIdsByUserId(String userId) {
return null;
}
@Override
public Set<String> getDepartParentIdsByUsername(String username) {
return null;
@ -193,6 +203,11 @@ public class SysBaseAPIFallback implements ISysBaseAPI {
return null;
}
@Override
public Set<String> getUserRoleSetById(String userId) {
return null;
}
@Override
public Set<String> getUserPermissionSet(String userId) {
return null;
@ -218,6 +233,11 @@ public class SysBaseAPIFallback implements ISysBaseAPI {
return null;
}
@Override
public Set<String> queryUserRolesById(String userId) {
return null;
}
@Override
public Set<String> queryUserAuths(String userId) {
return null;
@ -239,6 +259,11 @@ public class SysBaseAPIFallback implements ISysBaseAPI {
return null;
}
@Override
public String getUserIdByName(String username) {
return null;
}
@Override
public String translateDictFromTable(String table, String text, String code, String key) {
return null;

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>jeecg-system-api</artifactId>
<groupId>org.jeecgframework.boot</groupId>
<version>3.6.3</version>
<version>3.7.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -81,6 +81,13 @@ public interface ISysBaseAPI extends CommonAPI {
* @return
*/
List<String> getRolesByUsername(String username);
/**
* 7通过用户账号查询角色集合
* @param userId
* @return
*/
List<String> getRolesByUserId(String userId);
/**
* 8通过用户账号查询部门集合
@ -88,6 +95,12 @@ public interface ISysBaseAPI extends CommonAPI {
* @return 部门 id
*/
List<String> getDepartIdsByUsername(String username);
/**
* 8通过用户账号查询部门集合
* @param userId
* @return 部门 id
*/
List<String> getDepartIdsByUserId(String userId);
/**
* 8.2 通过用户账号查询部门父ID集合
@ -299,7 +312,13 @@ public interface ISysBaseAPI extends CommonAPI {
* @return
*/
Set<String> getUserRoleSet(String username);
/**
* 31获取用户的角色集合
* @param useId
* @return
*/
Set<String> getUserRoleSetById(String useId);
/**
* 32获取用户的权限集合
* @param userId

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>jeecg-module-system</artifactId>
<groupId>org.jeecgframework.boot</groupId>
<version>3.6.3</version>
<version>3.7.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-module-system</artifactId>
<version>3.6.3</version>
<version>3.7.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -29,17 +29,16 @@
<groupId>org.jeecgframework</groupId>
<artifactId>jeewx-api</artifactId>
</dependency>
<!-- 积木报表设计 -->
<!-- 积木报表 -->
<dependency>
<groupId>org.jeecgframework.jimureport</groupId>
<artifactId>jimureport-spring-boot-starter</artifactId>
</dependency>
<!-- 积木仪表盘 -->
<dependency>
<groupId>org.jeecgframework.jimureport</groupId>
<artifactId>jimureport-drag</artifactId>
</dependency>
<!-- 积木报表 mongo redis 支持包
<!-- 积木报表 mongo redis 支持包
<dependency>
<groupId>org.jeecgframework.jimureport</groupId>
<artifactId>jimureport-nosql-starter</artifactId>

View File

@ -15,6 +15,10 @@ import org.springframework.context.annotation.Configuration;
* 提醒: 达梦数据库需要修改下面的参数${spring.datasource.dynamic.datasource.master.url:}配置
* @author: scott
* @date: 2021年02月18日 16:30
*
* 重要说明:此类改路径或者名称,需要同步修改
* org/jeecg/interceptor/OnlineRepairCodeGenerateDbConfig.java里面的注解
* @ConditionalOnMissingClass("org.jeecg.config.init.CodeGenerateDbConfig")
*/
@Slf4j
@Configuration

View File

@ -36,7 +36,11 @@ public class JimuReportTokenService implements JmReportTokenServiceI {
@Override
public String getToken(HttpServletRequest request) {
return TokenUtils.getTokenByRequest(request);
try {
return TokenUtils.getTokenByRequest(request);
} catch (Exception e) {
return null;
}
}
@Override

View File

@ -8,6 +8,7 @@ import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.jeecg.common.api.dto.LogDTO;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.modules.base.service.BaseCommonService;
import org.jeecg.modules.system.entity.SysTenantPack;
@ -52,7 +53,7 @@ public class TenantPackUserLogAspect {
for(Object obj: args){
if(obj instanceof SysTenantPack){
// logType=3 租户操作日志
logType = 3;
logType = CommonConstant.LOG_TYPE_3;
SysTenantPack pack = (SysTenantPack)obj;
if(opType==2){
content = "创建了角色权限 "+ pack.getPackName();
@ -60,7 +61,7 @@ public class TenantPackUserLogAspect {
tenantId = pack.getTenantId();
break;
}else if(obj instanceof SysTenantPackUser){
logType = 3;
logType = CommonConstant.LOG_TYPE_3;
SysTenantPackUser packUser = (SysTenantPackUser)obj;
if(opType==2){
content = ""+packUser.getRealname()+" 添加到角色 "+ packUser.getPackName();

View File

@ -102,6 +102,17 @@ public class SystemApiController {
}
return loginUser;
}
/**
* 根据用户账号查询用户ID
* @param username
* @return
*/
@GetMapping("/getUserIdByName")
public String getUserIdByName(@RequestParam("username") String username){
String userId = sysBaseApi.getUserIdByName(username);
return userId;
}
/**
* 根据用户id查询用户信息
@ -129,6 +140,16 @@ public class SystemApiController {
List<String> getRolesByUsername(@RequestParam("username") String username){
return sysBaseApi.getRolesByUsername(username);
}
/**
* 通过用户账号查询角色集合
* @param userId
* @return
*/
@GetMapping("/getRolesByUserId")
List<String> getRolesByUserId(@RequestParam("userId") String userId){
return sysBaseApi.getRolesByUserId(userId);
}
/**
* 通过用户账号查询部门集合
@ -139,6 +160,16 @@ public class SystemApiController {
List<String> getDepartIdsByUsername(@RequestParam("username") String username){
return sysBaseApi.getDepartIdsByUsername(username);
}
/**
* 通过用户账号查询部门集合
* @param userId
* @return 部门 id
*/
@GetMapping("/getDepartIdsByUserId")
List<String> getDepartIdsByUserId(@RequestParam("userId") String userId){
return sysBaseApi.getDepartIdsByUserId(userId);
}
/**
* 通过用户账号查询部门父ID集合
@ -383,6 +414,16 @@ public class SystemApiController {
public Set<String> getUserRoleSet(@RequestParam("username")String username){
return sysBaseApi.getUserRoleSet(username);
}
/**
* 获取用户的角色集合
* @param userId
* @return
*/
@GetMapping("/getUserRoleSetById")
public Set<String> getUserRoleSetById(@RequestParam("userId")String userId){
return sysBaseApi.getUserRoleSetById(userId);
}
/**
* 获取用户的权限集合
@ -415,6 +456,16 @@ public class SystemApiController {
public Set<String> queryUserRoles(@RequestParam("username") String username){
return sysUserService.getUserRolesSet(username);
}
/**
* 查询用户角色信息
* @param userId
* @return
*/
@GetMapping("/queryUserRolesById")
public Set<String> queryUserRolesById(@RequestParam("userId") String userId){
return sysUserService.getUserRoleSetById(userId);
}
/**
@ -893,7 +944,7 @@ public class SystemApiController {
* @return
*/
@GetMapping("/getUserAccountsByDepCode")
public List<String> getUserAccountsByDepCode(String orgCode){
public List<String> getUserAccountsByDepCode(@RequestParam("orgCode") String orgCode){
return sysBaseApi.getUserAccountsByDepCode(orgCode);
}

View File

@ -173,7 +173,7 @@ public class SysMessageTemplateController extends JeecgController<SysMessageTemp
sysBaseApi.sendTemplateMessage(md);
return result.success("消息发送成功!");
} catch (Exception e) {
log.error("发送消息出错", e.getMessage());
log.error("发送消息出错" + e.getMessage(), e);
return result.error500("发送消息出错!");
}
}

View File

@ -79,7 +79,6 @@ public enum RangeDateEnum {
//本周
calendar1.set(Calendar.DAY_OF_WEEK, 2);
calendar2.set(Calendar.DAY_OF_WEEK,2);
calendar2.add(Calendar.WEEK_OF_MONTH,1);
calendar2.add(Calendar.DAY_OF_WEEK,-1);
} else if(SZ.key.equals(key)){

View File

@ -31,8 +31,13 @@ public class WebSocket {
* Redis触发监听名字
*/
public static final String REDIS_TOPIC_NAME = "socketHandler";
//避免初次调用出现空指针的情况
private static JeecgRedisClient jeecgRedisClient;
@Autowired
private JeecgRedisClient jeecgRedisClient;
private void setJeecgRedisClient(JeecgRedisClient jeecgRedisClient){
WebSocket.jeecgRedisClient = jeecgRedisClient;
}
//==========【websocket接受、推送消息等方法 —— 具体服务节点推送ws消息】========================================================================================
@ -109,6 +114,9 @@ public class WebSocket {
log.debug("【系统 WebSocket】收到客户端消息:" + message);
}else{
log.debug("【系统 WebSocket】收到客户端消息:" + message);
//update-begin---author:wangshuai---date:2024-05-07---for:【issues/1161】前端websocket因心跳导致监听不起作用---
this.sendMessage(userId, "ping");
//update-end---author:wangshuai---date:2024-05-07---for:【issues/1161】前端websocket因心跳导致监听不起作用---
}
// //------------------------------------------------------------------------------

View File

@ -0,0 +1,38 @@
package org.jeecg.modules.monitor.actuator;
import org.jeecg.modules.monitor.actuator.httptrace.CustomInMemoryHttpTraceRepository;
import org.springframework.boot.actuate.autoconfigure.trace.http.HttpTraceAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.trace.http.HttpTraceProperties;
import org.springframework.boot.actuate.trace.http.HttpTraceRepository;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 自定义健康监控配置类
*
* @Author: chenrui
* @Date: 2024/5/13 17:20
*/
@Configuration
@EnableConfigurationProperties(HttpTraceProperties.class)
@AutoConfigureBefore(HttpTraceAutoConfiguration.class)
public class CustomActuatorConfig {
/**
* 请求追踪
* @return
* @author chenrui
* @date 2024/5/14 14:52
*/
@Bean
@ConditionalOnProperty(prefix = "management.trace.http", name = "enabled", matchIfMissing = true)
@ConditionalOnMissingBean(HttpTraceRepository.class)
public CustomInMemoryHttpTraceRepository traceRepository() {
return new CustomInMemoryHttpTraceRepository();
}
}

View File

@ -0,0 +1,44 @@
package org.jeecg.modules.monitor.actuator.httptrace;
import lombok.Getter;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.annotation.Selector;
import org.springframework.boot.actuate.trace.http.HttpTrace;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import java.util.List;
import static org.springframework.boot.actuate.endpoint.annotation.Selector.Match.ALL_REMAINING;
/**
* @Description: ENDPOINT: 请求追踪(新),支持通过responseCode筛选
* @Author: chenrui
* @Date: 2024/5/13 17:02
*/
@Component
@Endpoint(id = "httptrace-new")
public class CustomHttpTraceEndpoint{
private final CustomInMemoryHttpTraceRepository repository;
public CustomHttpTraceEndpoint(CustomInMemoryHttpTraceRepository repository) {
Assert.notNull(repository, "Repository must not be null");
this.repository = repository;
}
@ReadOperation
public HttpTraceDescriptor traces(@Selector(match = ALL_REMAINING) String query) {
return new CustomHttpTraceEndpoint.HttpTraceDescriptor(this.repository.findAll(query));
}
@Getter
public static final class HttpTraceDescriptor {
private final List<HttpTrace> traces;
private HttpTraceDescriptor(List<HttpTrace> traces) {
this.traces = traces;
}
}
}

View File

@ -0,0 +1,94 @@
package org.jeecg.modules.monitor.actuator.httptrace;
import org.springframework.boot.actuate.trace.http.HttpTrace;
import org.springframework.boot.actuate.trace.http.InMemoryHttpTraceRepository;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @Description: 自定义内存请求追踪存储
* @Author: chenrui
* @Date: 2024/5/13 17:02
*/
public class CustomInMemoryHttpTraceRepository extends InMemoryHttpTraceRepository {
@Override
public List<HttpTrace> findAll() {
return super.findAll();
}
public List<HttpTrace> findAll(String query) {
List<HttpTrace> allTrace = super.findAll();
if (null != allTrace && !allTrace.isEmpty()) {
Stream<HttpTrace> stream = allTrace.stream();
String[] params = query.split(",");
stream = filter(params, stream);
stream = sort(params, stream);
allTrace = stream.collect(Collectors.toList());
}
return allTrace;
}
private Stream<HttpTrace> sort(String[] params, Stream<HttpTrace> stream) {
if (params.length < 2) {
return stream;
}
String sortBy = params[1];
String order;
if (params.length > 2) {
order = params[2];
} else {
order = "desc";
}
return stream.sorted((o1, o2) -> {
int i = 0;
if("timeTaken".equalsIgnoreCase(sortBy)) {
i = o1.getTimeTaken().compareTo(o2.getTimeTaken());
}else if("timestamp".equalsIgnoreCase(sortBy)){
i = o1.getTimestamp().compareTo(o2.getTimestamp());
}
if("desc".equalsIgnoreCase(order)){
i *=-1;
}
return i;
});
}
private static Stream<HttpTrace> filter(String[] params, Stream<HttpTrace> stream) {
if (params.length == 0) {
return stream;
}
String statusQuery = params[0];
if (null != statusQuery && !statusQuery.isEmpty()) {
statusQuery = statusQuery.toLowerCase().trim();
switch (statusQuery) {
case "error":
stream = stream.filter(httpTrace -> {
int status = httpTrace.getResponse().getStatus();
return status >= 404 && status < 501;
});
break;
case "warn":
stream = stream.filter(httpTrace -> {
int status = httpTrace.getResponse().getStatus();
return status >= 201 && status < 404;
});
break;
case "success":
stream = stream.filter(httpTrace -> {
int status = httpTrace.getResponse().getStatus();
return status == 200;
});
break;
case "all":
default:
break;
}
return stream;
}
return stream;
}
}

View File

@ -0,0 +1,52 @@
package org.jeecg.modules.monitor.controller;
import cn.hutool.core.util.NumberUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
import java.util.HashMap;
import java.util.Map;
/**
* @Description: 内存健康检查
* @author: chenrui
*/
@Slf4j
@RestController
@RequestMapping("/sys/actuator/memory")
public class ActuatorMemoryController {
/**
* 内存详情
* @return
* @throws Exception
*/
@GetMapping("/info")
public Result<?> getRedisInfo() throws Exception {
OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean();
JSONObject operatingSystemJson = JSONObject.parseObject(JSONObject.toJSONString(operatingSystemMXBean));
long totalPhysicalMemory = operatingSystemJson.getLongValue("totalPhysicalMemorySize");
long freePhysicalMemory = operatingSystemJson.getLongValue("freePhysicalMemorySize");
long usedPhysicalMemory = totalPhysicalMemory - freePhysicalMemory;
Runtime runtime = Runtime.getRuntime();
Map<String,Number> result = new HashMap<>();
result.put("memory.physical.total", totalPhysicalMemory);
result.put("memory.physical.used", freePhysicalMemory);
result.put("memory.physical.free", usedPhysicalMemory);
result.put("memory.physical.usage", NumberUtil.div(usedPhysicalMemory, totalPhysicalMemory));
result.put("memory.runtime.total", runtime.totalMemory());
result.put("memory.runtime.used", runtime.freeMemory());
result.put("memory.runtime.max", runtime.totalMemory() - runtime.freeMemory());
result.put("memory.runtime.free", runtime.maxMemory() - runtime.totalMemory() + runtime.freeMemory());
result.put("memory.runtime.usage", NumberUtil.div(runtime.totalMemory() - runtime.freeMemory(), runtime.totalMemory()));
return Result.ok(result);
}
}

View File

@ -43,6 +43,21 @@ public class ActuatorRedisController {
return Result.ok(infoList);
}
//update-begin---author:chenrui ---date:20240514 for[QQYUN-9247]系统监控功能优化------------
/**
* Redis历史性能指标查询(过去一小时)
* @return
* @throws Exception
* @author chenrui
* @date 2024/5/14 14:56
*/
@GetMapping(value = "/metrics/history")
public Result<?> getMetricsHistory() throws Exception {
Map<String,List<Map<String,Object>>> metricsHistory = this.redisService.getMetricsHistory();
return Result.OK(metricsHistory);
}
//update-end---author:chenrui ---date:20240514 for[QQYUN-9247]系统监控功能优化------------
@GetMapping("/keysSize")
public Map<String, Object> getKeysSize() throws Exception {
return redisService.getKeysSize();

View File

@ -44,4 +44,12 @@ public interface RedisService {
* @throws RedisConnectException
*/
Map<String, JSONArray> getMapForReport(String type) throws RedisConnectException ;
/**
* 获取历史性能指标
* @return
* @author chenrui
* @date 2024/5/14 14:57
*/
Map<String, List<Map<String, Object>>> getMetricsHistory();
}

View File

@ -1,10 +1,6 @@
package org.jeecg.modules.monitor.service.impl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.*;
import javax.annotation.Resource;
@ -12,13 +8,13 @@ import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.monitor.domain.RedisInfo;
import org.jeecg.modules.monitor.exception.RedisConnectException;
import org.jeecg.modules.monitor.service.RedisService;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
@ -40,6 +36,11 @@ public class RedisServiceImpl implements RedisService {
*/
private static final String REDIS_MESSAGE = "3";
/**
* redis性能信息记录
*/
private static final Map<String,List<Map<String, Object>>> REDIS_METRICS = new HashMap<>(2);
/**
* Redis详细信息
*/
@ -126,4 +127,48 @@ public class RedisServiceImpl implements RedisService {
mapJson.put("data",json);
return mapJson;
}
//update-begin---author:chenrui ---date:20240514 for[QQYUN-9247]系统监控功能优化------------
/**
* 获取历史性能指标
* @return
* @author chenrui
* @date 2024/5/14 14:57
*/
@Override
public Map<String, List<Map<String, Object>>> getMetricsHistory() {
return REDIS_METRICS;
}
/**
* 记录近一小时redis监控数据 <br/>
* 60s一次,,记录存储keysize和内存
* @throws RedisConnectException
* @author chenrui
* @date 2024/5/14 14:09
*/
@Scheduled(fixedRate = 60000)
public void recordCustomMetric() throws RedisConnectException {
List<Map<String, Object>> list= new ArrayList<>();
if(REDIS_METRICS.containsKey("dbSize")){
list = REDIS_METRICS.get("dbSize");
}else{
REDIS_METRICS.put("dbSize",list);
}
if(list.size()>60){
list.remove(0);
}
list.add(getKeysSize());
list= new ArrayList<>();
if(REDIS_METRICS.containsKey("memory")){
list = REDIS_METRICS.get("memory");
}else{
REDIS_METRICS.put("memory",list);
}
if(list.size()>60){
list.remove(0);
}
list.add(getMemoryInfo());
}
//update-end---author:chenrui ---date:20240514 for[QQYUN-9247]系统监控功能优化------------
}

View File

@ -79,7 +79,7 @@ public class QuartzJobController {
* @param quartzJob
* @return
*/
@RequiresRoles("admin")
//@RequiresRoles("admin")
@RequiresPermissions("system:quartzJob:add")
@RequestMapping(value = "/add", method = RequestMethod.POST)
public Result<?> add(@RequestBody QuartzJob quartzJob) {
@ -93,7 +93,7 @@ public class QuartzJobController {
* @param quartzJob
* @return
*/
@RequiresRoles("admin")
//@RequiresRoles("admin")
@RequiresPermissions("system:quartzJob:edit")
@RequestMapping(value = "/edit", method ={RequestMethod.PUT, RequestMethod.POST})
public Result<?> eidt(@RequestBody QuartzJob quartzJob) {
@ -112,7 +112,7 @@ public class QuartzJobController {
* @param id
* @return
*/
@RequiresRoles("admin")
//@RequiresRoles("admin")
@RequiresPermissions("system:quartzJob:delete")
@RequestMapping(value = "/delete", method = RequestMethod.DELETE)
public Result<?> delete(@RequestParam(name = "id", required = true) String id) {
@ -131,7 +131,7 @@ public class QuartzJobController {
* @param ids
* @return
*/
@RequiresRoles("admin")
//@RequiresRoles("admin")
@RequiresPermissions("system:quartzJob:deleteBatch")
@RequestMapping(value = "/deleteBatch", method = RequestMethod.DELETE)
public Result<?> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
@ -151,7 +151,7 @@ public class QuartzJobController {
* @param id
* @return
*/
@RequiresRoles("admin")
//@RequiresRoles("admin")
@RequiresPermissions("system:quartzJob:pause")
@GetMapping(value = "/pause")
@ApiOperation(value = "停止定时任务")
@ -170,7 +170,7 @@ public class QuartzJobController {
* @param id
* @return
*/
@RequiresRoles("admin")
//@RequiresRoles("admin")
@RequiresPermissions("system:quartzJob:resume")
@GetMapping(value = "/resume")
@ApiOperation(value = "启动定时任务")
@ -271,7 +271,7 @@ public class QuartzJobController {
* @param id
* @return
*/
@RequiresRoles("admin")
//@RequiresRoles("admin")
@RequiresPermissions("system:quartzJob:execute")
@GetMapping("/execute")
public Result<?> execute(@RequestParam(name = "id", required = true) String id) {

View File

@ -0,0 +1,69 @@
package org.jeecg.modules.system.cache;
import me.zhyd.oauth.cache.AuthCacheConfig;
import me.zhyd.oauth.cache.AuthStateCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import javax.annotation.PostConstruct;
import java.util.concurrent.TimeUnit;
public class AuthStateRedisCache implements AuthStateCache {
@Autowired
private RedisTemplate<String, String> redisTemplate;
private ValueOperations<String, String> valueOperations;
@PostConstruct
public void init() {
valueOperations = redisTemplate.opsForValue();
}
/**
* 存入缓存默认3分钟
*
* @param key 缓存key
* @param value 缓存内容
*/
@Override
public void cache(String key, String value) {
valueOperations.set(key, value, AuthCacheConfig.timeout, TimeUnit.MILLISECONDS);
}
/**
* 存入缓存
*
* @param key 缓存key
* @param value 缓存内容
* @param timeout 指定缓存过期时间(毫秒)
*/
@Override
public void cache(String key, String value, long timeout) {
valueOperations.set(key, value, timeout, TimeUnit.MILLISECONDS);
}
/**
* 获取缓存内容
*
* @param key 缓存key
* @return 缓存内容
*/
@Override
public String get(String key) {
return valueOperations.get(key);
}
/**
* 是否存在key如果对应key的value值已过期也返回false
*
* @param key 缓存key
* @return true存在key并且value没过期falsekey不存在或者已过期
*/
@Override
public boolean containsKey(String key) {
return redisTemplate.hasKey(key);
}
}

View File

@ -0,0 +1,15 @@
package org.jeecg.modules.system.config;
import me.zhyd.oauth.cache.AuthStateCache;
import org.jeecg.modules.system.cache.AuthStateRedisCache;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AuthStateConfiguration {
@Bean
public AuthStateCache authStateCache() {
return new AuthStateRedisCache();
}
}

View File

@ -10,6 +10,7 @@ import org.jeecg.common.util.CommonUtils;
import org.jeecg.common.util.filter.SsrfFileTypeFilter;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.*;

View File

@ -243,7 +243,7 @@ public class LoginController {
* 获取访问量
* @return
*/
@GetMapping("visitInfo")
@GetMapping("/visitInfo")
public Result<List<Map<String,Object>>> visitInfo() {
Result<List<Map<String,Object>>> result = new Result<List<Map<String,Object>>>();
Calendar calendar = new GregorianCalendar();
@ -295,12 +295,14 @@ public class LoginController {
* @return
*/
@PostMapping(value = "/sms")
public Result<String> sms(@RequestBody JSONObject jsonObject) {
public Result<String> sms(@RequestBody JSONObject jsonObject,HttpServletRequest request) {
Result<String> result = new Result<String>();
String clientIp = IpUtils.getIpAddr(request);
String mobile = jsonObject.get("mobile").toString();
//手机号模式 登录模式: "2" 注册模式: "1"
String smsmode=jsonObject.get("smsmode").toString();
log.info(mobile);
log.info("-------- IP:{}, 手机号:{},获取绑定验证码", clientIp, mobile);
if(oConvertUtils.isEmpty(mobile)){
result.setMessage("手机号不允许为空!");
result.setSuccess(false);
@ -318,6 +320,17 @@ public class LoginController {
return result;
}
//-------------------------------------------------------------------------------------
//增加 check防止恶意刷短信接口
if(!DySmsLimit.canSendSms(clientIp)){
log.warn("--------[警告] IP地址:{}, 短信接口请求太多-------", clientIp);
result.setMessage("短信接口请求太多,请稍后再试!");
result.setCode(CommonConstant.PHONE_SMS_FAIL_CODE);
result.setSuccess(false);
return result;
}
//-------------------------------------------------------------------------------------
//随机数
String captcha = RandomUtil.randomNumbers(6);
JSONObject obj = new JSONObject();
@ -734,4 +747,82 @@ public class LoginController {
redisUtil.set(key, ++val, 600);
}
/**
* 发送短信验证码接口(修改密码)
*
* @param jsonObject
* @return
*/
@PostMapping(value = "/sendChangePwdSms")
public Result<String> sendSms(@RequestBody JSONObject jsonObject) {
Result<String> result = new Result<>();
String mobile = jsonObject.get("mobile").toString();
if (oConvertUtils.isEmpty(mobile)) {
result.setMessage("手机号不允许为空!");
result.setSuccess(false);
return result;
}
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
String username = sysUser.getUsername();
LambdaQueryWrapper<SysUser> query = new LambdaQueryWrapper<>();
query.eq(SysUser::getUsername, username).eq(SysUser::getPhone, mobile);
SysUser user = sysUserService.getOne(query);
if (null == user) {
return Result.error("当前登录用户和绑定的手机号不匹配,无法修改密码!");
}
String redisKey = CommonConstant.PHONE_REDIS_KEY_PRE + mobile;
Object object = redisUtil.get(redisKey);
if (object != null) {
result.setMessage("验证码10分钟内仍然有效");
result.setSuccess(false);
return result;
}
//随机数
String captcha = RandomUtil.randomNumbers(6);
JSONObject obj = new JSONObject();
obj.put("code", captcha);
try {
boolean b = DySmsHelper.sendSms(mobile, obj, DySmsEnum.CHANGE_PASSWORD_TEMPLATE_CODE);
if (!b) {
result.setMessage("短信验证码发送失败,请稍后重试");
result.setSuccess(false);
return result;
}
//验证码5分钟内有效
redisUtil.set(redisKey, captcha, 300);
result.setSuccess(true);
} catch (ClientException e) {
e.printStackTrace();
result.error500(" 短信接口未配置,请联系管理员!");
return result;
}
return result;
}
/**
* 图形验证码
* @param sysLoginModel
* @return
*/
@RequestMapping(value = "/smsCheckCaptcha", method = RequestMethod.POST)
public Result<?> smsCheckCaptcha(@RequestBody SysLoginModel sysLoginModel, HttpServletRequest request){
String captcha = sysLoginModel.getCaptcha();
String checkKey = sysLoginModel.getCheckKey();
if(captcha==null){
return Result.error("验证码无效");
}
String lowerCaseCaptcha = captcha.toLowerCase();
String realKey = Md5Util.md5Encode(lowerCaseCaptcha+checkKey+jeecgBaseConfig.getSignatureSecret(), "utf-8");
Object checkCode = redisUtil.get(realKey);
if(checkCode==null || !checkCode.equals(lowerCaseCaptcha)) {
return Result.error("验证码错误");
}
String clientIp = IpUtils.getIpAddr(request);
//清空短信记录数量
DySmsLimit.clearSendSmsCount(clientIp);
redisUtil.removeAll(realKey);
return Result.ok();
}
}

View File

@ -1,6 +1,5 @@
package org.jeecg.modules.system.controller;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@ -18,10 +17,7 @@ import org.jeecg.common.constant.WebsocketConst;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.TokenUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.common.util.*;
import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
import org.jeecg.modules.message.enums.RangeDateEnum;
import org.jeecg.modules.message.websocket.WebSocket;
@ -40,6 +36,7 @@ import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
@ -51,7 +48,10 @@ import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
@ -86,7 +86,9 @@ public class SysAnnouncementController {
@Autowired
@Lazy
private RedisUtil redisUtil;
@Autowired
public RedisTemplate redisTemplate;
/**
* QQYUN-5072【性能优化】线上通知消息打开有点慢
*/
@ -332,12 +334,19 @@ public class SysAnnouncementController {
* @return
*/
@RequestMapping(value = "/listByUser", method = RequestMethod.GET)
public Result<Map<String, Object>> listByUser(@RequestParam(required = false, defaultValue = "5") Integer pageSize) {
public Result<Map<String, Object>> listByUser(@RequestParam(required = false, defaultValue = "5") Integer pageSize, HttpServletRequest request) {
long start = System.currentTimeMillis();
Result<Map<String,Object>> result = new Result<Map<String,Object>>();
Map<String,Object> sysMsgMap = new HashMap(5);
LoginUser sysUser = (LoginUser)SecurityUtils.getSubject().getPrincipal();
String userId = sysUser.getId();
//update-begin---author:scott ---date:2024-05-11 for【性能优化】优化系统通知只查近2个月的通知---
// 获取上个月的第一天(只查近两个月的通知)
Date lastMonthStartDay = DateRangeUtils.getLastMonthStartDay();
log.info("-----查询近两个月收到的未读通知-----近2月的第一天{}", lastMonthStartDay);
//update-end---author:scott ---date::2024-05-11 for【性能优化】优化系统通知只查近2个月的通知---
// //补推送数据(用户和通知的关系表)
// completeNoteThreadPool.execute(()->{
@ -347,19 +356,19 @@ public class SysAnnouncementController {
// 2.查询用户未读的系统消息
Page<SysAnnouncement> anntMsgList = new Page<SysAnnouncement>(0, pageSize);
//通知公告消息
anntMsgList = sysAnnouncementService.querySysCementPageByUserId(anntMsgList,userId,"1");
anntMsgList = sysAnnouncementService.querySysCementPageByUserId(anntMsgList,userId,"1",null, lastMonthStartDay);
sysMsgMap.put("anntMsgList", anntMsgList.getRecords());
sysMsgMap.put("anntMsgTotal", anntMsgList.getTotal());
log.info("begin 获取用户系统公告 (通知)" + (System.currentTimeMillis() - start) + "毫秒");
log.info("begin 获取用户近2个月的系统公告 (通知)" + (System.currentTimeMillis() - start) + "毫秒");
//系统消息
Page<SysAnnouncement> sysMsgList = new Page<SysAnnouncement>(0, pageSize);
sysMsgList = sysAnnouncementService.querySysCementPageByUserId(sysMsgList,userId,"2");
sysMsgList = sysAnnouncementService.querySysCementPageByUserId(sysMsgList,userId,"2",null, lastMonthStartDay);
sysMsgMap.put("sysMsgList", sysMsgList.getRecords());
sysMsgMap.put("sysMsgTotal", sysMsgList.getTotal());
log.info("end 获取用户系统公告 (系统消息)" + (System.currentTimeMillis() - start) + "毫秒");
log.info("end 获取用户2个月的系统公告 (系统消息)" + (System.currentTimeMillis() - start) + "毫秒");
result.setSuccess(true);
result.setResult(sysMsgMap);
@ -367,6 +376,24 @@ public class SysAnnouncementController {
}
/**
* 获取未读消息通知数量
*
* @return
*/
@RequestMapping(value = "/getUnreadMessageCount", method = RequestMethod.GET)
public Result<Integer> getUnreadMessageCount(@RequestParam(required = false, defaultValue = "5") Integer pageSize, HttpServletRequest request) {
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
String userId = sysUser.getId();
// 获取上个月的第一天(只查近两个月的通知)
Date lastMonthStartDay = DateRangeUtils.getLastMonthStartDay();
log.info(" ------查询近两个月收到的未读通知消息数量------近2月的第一天{}", lastMonthStartDay);
Integer unreadMessageCount = sysAnnouncementService.getUnreadMessageCountByUserId(userId, lastMonthStartDay);
return Result.ok(unreadMessageCount);
}
/**
* 导出excel
*
@ -570,15 +597,37 @@ public class SysAnnouncementController {
*/
@GetMapping("/getLastAnnountTime")
public Result<Page<SysAnnouncementSend>> getLastAnnountTime(@RequestParam(name = "userId") String userId){
Result<Page<SysAnnouncementSend>> result = new Result<>();
Page<SysAnnouncementSend> page = new Page<>(1,1);
Result<Page<SysAnnouncementSend>> result = new Result<>();
//----------------------------------------------------------------------------------------
// step.1 此接口过慢,可以采用缓存一小时方案
String keyString = String.format(CommonConstant.CACHE_KEY_USER_LAST_ANNOUNT_TIME_1HOUR, userId);
if (redisTemplate.hasKey(keyString)) {
log.info("[SysAnnouncementSend Redis] 通过Redis缓存查询用户最后一次收到系统通知时间userId={}", userId);
Page<SysAnnouncementSend> pageList = (Page<SysAnnouncementSend>) redisTemplate.opsForValue().get(keyString);
result.setSuccess(true);
result.setResult(pageList);
return result;
}
//----------------------------------------------------------------------------------------
Page<SysAnnouncementSend> page = new Page<>(1,1);
LambdaQueryWrapper<SysAnnouncementSend> query = new LambdaQueryWrapper<>();
query.eq(SysAnnouncementSend::getUserId,userId);
query.select(SysAnnouncementSend::getCreateTime);
//只查询上个月和本月,的通知的数据
query.ne(SysAnnouncementSend::getCreateTime, DateRangeUtils.getLastMonthStartDay());
query.select(SysAnnouncementSend::getCreateTime); // 提高查询效率
query.orderByDesc(SysAnnouncementSend::getCreateTime);
Page<SysAnnouncementSend> pageList = sysAnnouncementSendService.page(page, query);
result.setSuccess(true);
result.setResult(pageList);
//----------------------------------------------------------------------------------------
if (pageList != null && pageList.getSize() > 0) {
// step.3 保留1小时redis缓存
redisTemplate.opsForValue().set(keyString, pageList, 3600, TimeUnit.SECONDS);
}
//----------------------------------------------------------------------------------------
result.setSuccess(true);
result.setResult(pageList);
return result;
}

View File

@ -326,6 +326,13 @@ public class SysDictController {
@RequestParam(name="condition") String condition,
@RequestParam(value = "sign",required = false) String sign,HttpServletRequest request) {
Result<List<TreeSelectModel>> result = new Result<List<TreeSelectModel>>();
// 【QQYUN-9207】防止参数为空导致报错
if (oConvertUtils.isEmpty(tableName) || oConvertUtils.isEmpty(text) || oConvertUtils.isEmpty(code)) {
result.error500("字典Code格式不正确");
return result;
}
// 1.获取查询条件参数
Map<String, String> query = null;
if(oConvertUtils.isNotEmpty(condition)) {

View File

@ -17,9 +17,11 @@ import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.base.BaseMap;
import org.jeecg.common.config.TenantContext;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.modules.redis.client.JeecgRedisClient;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
@ -83,6 +85,8 @@ public class SysRoleController {
private ISysUserRoleService sysUserRoleService;
@Autowired
private BaseCommonService baseCommonService;
@Autowired
private JeecgRedisClient jeecgRedisClient;
/**
* 分页列表查询 【系统角色,不做租户隔离】
@ -124,7 +128,9 @@ public class SysRoleController {
HttpServletRequest req) {
Result<IPage<SysRole>> result = new Result<IPage<SysRole>>();
//此接口必须通过租户来隔离查询
role.setTenantId(oConvertUtils.getInt(!"0".equals(TenantContext.getTenant()) ? TenantContext.getTenant() : "", -1));
if(MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL) {
role.setTenantId(oConvertUtils.getInt(!"0".equals(TenantContext.getTenant()) ? TenantContext.getTenant() : "", -1));
}
QueryWrapper<SysRole> queryWrapper = QueryGenerator.initQueryWrapper(role, req.getParameterMap());
Page<SysRole> page = new Page<SysRole>(pageNo, pageSize);
@ -145,7 +151,9 @@ public class SysRoleController {
Result<SysRole> result = new Result<SysRole>();
try {
//开启多租户隔离,角色id自动生成10位
if(MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL){
//update-begin---author:wangshuai---date:2024-05-23---for:【TV360X-42】角色新增时设置的编码保存后不一致---
if(MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL && oConvertUtils.isEmpty(role.getRoleCode())){
//update-end---author:wangshuai---date:2024-05-23---for:【TV360X-42】角色新增时设置的编码保存后不一致---
role.setRoleCode(RandomUtil.randomString(10));
}
role.setCreateTime(new Date());
@ -222,6 +230,7 @@ public class SysRoleController {
//update-end---author:wangshuai---date:2024-01-16---for:【QQYUN-7974】禁止删除 admin 角色---
sysRoleService.deleteRole(id);
return Result.ok("删除角色成功");
}
@ -570,4 +579,5 @@ public class SysRoleController {
result.setResult(sysRoleCountPage);
return result;
}
}

View File

@ -1,6 +1,7 @@
package org.jeecg.modules.system.controller;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@ -351,10 +352,10 @@ public class SysTenantController {
* @param ids
* @return
*/
@DeleteMapping("/deletePackPermissions")
@DeleteMapping("/deleteTenantPack")
@RequiresPermissions("system:tenant:delete:pack")
public Result<String> deletePackPermissions(@RequestParam(value = "ids") String ids) {
sysTenantPackService.deletePackPermissions(ids);
public Result<String> deleteTenantPack(@RequestParam(value = "ids") String ids) {
sysTenantPackService.deleteTenantPack(ids);
return Result.ok("删除租户产品包成功");
}
@ -500,7 +501,7 @@ public class SysTenantController {
Integer tenantId = sysTenantService.joinTenantByHouseNumber(sysTenant, sysUser.getId());
Result<Integer> result = new Result<>();
if(tenantId != 0){
result.setMessage("申请租户成功");
result.setMessage("申请加入组织成功");
result.setSuccess(true);
result.setResult(tenantId);
return result;
@ -935,4 +936,39 @@ public class SysTenantController {
return Result.error("类型不匹配,禁止修改数据");
}
/**
* 目前只给敲敲云租户下删除用户使用
*
* 根据密码删除用户
*/
@DeleteMapping("/deleteUserByPassword")
public Result<String> deleteUserByPassword(@RequestBody SysUser sysUser,HttpServletRequest request){
Integer tenantId = oConvertUtils.getInteger(TokenUtils.getTenantIdByRequest(request), null);
sysTenantService.deleteUserByPassword(sysUser, tenantId);
return Result.ok("删除用户成功");
}
/**
* 查询当前用户的所有有效租户【知识库专用接口】
* @return
*/
@RequestMapping(value = "/getCurrentUserTenantForFile", method = RequestMethod.GET)
public Result<Map<String,Object>> getCurrentUserTenantForFile() {
Result<Map<String,Object>> result = new Result<Map<String,Object>>();
try {
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
List<SysTenant> tenantList = sysTenantService.getTenantListByUserId(sysUser.getId());
Map<String,Object> map = new HashMap<>(5);
//在开启saas租户隔离的时候并且租户数据不为空则返回租户信息
if (MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL && CollectionUtil.isNotEmpty(tenantList)) {
map.put("list", tenantList);
}
result.setSuccess(true);
result.setResult(map);
}catch(Exception e) {
log.error(e.getMessage(), e);
result.error500("查询失败!");
}
return result;
}
}

View File

@ -7,7 +7,6 @@ import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
@ -17,15 +16,17 @@ import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.PermissionData;
import org.jeecg.common.base.BaseMap;
import org.jeecg.common.config.TenantContext;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
import org.jeecg.modules.base.service.BaseCommonService;
import org.jeecg.common.modules.redis.client.JeecgRedisClient;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.*;
import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
import org.jeecg.modules.base.service.BaseCommonService;
import org.jeecg.modules.system.entity.*;
import org.jeecg.modules.system.model.DepartIdModel;
import org.jeecg.modules.system.model.SysUserSysDepartModel;
@ -101,6 +102,9 @@ public class SysUserController {
@Autowired
private ISysUserTenantService userTenantService;
@Autowired
private JeecgRedisClient jeecgRedisClient;
/**
* 获取租户下用户数据(支持租户隔离)
* @param user
@ -1129,7 +1133,7 @@ public class SysUserController {
}
sysUser = this.sysUserService.getOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUsername,username).eq(SysUser::getPhone,phone));
if (sysUser == null) {
result.setMessage("未找到用户");
result.setMessage("当前登录用户和绑定的手机号不匹配,无法修改密码");
result.setSuccess(false);
return result;
} else {
@ -1143,6 +1147,8 @@ public class SysUserController {
//update-end---author:wangshuai ---date:20220316 for[VUEN-234]密码重置添加敏感日志------------
result.setSuccess(true);
result.setMessage("密码重置完成!");
//修改完密码后清空redis
redisUtil.removeAll(redisKey);
return result;
}
}
@ -1551,15 +1557,15 @@ public class SysUserController {
@RequestParam(name = "departId", required = false) String departId,
@RequestParam(name = "roleId", required = false) String roleId,
@RequestParam(name="keyword",required=false) String keyword,
@RequestParam(name="excludeUserIdList",required = false) String excludeUserIdList) {
@RequestParam(name="excludeUserIdList",required = false) String excludeUserIdList,
HttpServletRequest req) {
//------------------------------------------------------------------------------------------------
Integer tenantId = null;
//是否开启系统管理模块的多租户数据隔离【SAAS多租户模式】
if(MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL){
String tenantStr = TenantContext.getTenant();
if(oConvertUtils.isNotEmpty(tenantStr)){
tenantId = Integer.parseInt(tenantStr);
}
tenantId = oConvertUtils.getInteger(tenantStr, oConvertUtils.getInt(TokenUtils.getTenantIdByRequest(req), -1));
log.info("---------简流中选择用户接口通过租户筛选租户ID={}", tenantId);
}
//------------------------------------------------------------------------------------------------
IPage<SysUser> pageList = sysUserDepartService.getUserInformation(tenantId, departId,roleId, keyword, pageSize, pageNo,excludeUserIdList);

View File

@ -61,8 +61,13 @@ public class ThirdAppController {
@GetMapping("/getEnabledType")
public Result getEnabledType() {
Map<String, Boolean> enabledMap = new HashMap(5);
//update-begin---author:wangshuai ---date:20230224 for[QQYUN-3440]通过租户模式隔离 ------------
int tenantId = oConvertUtils.getInt(TenantContext.getTenant(), 0);
int tenantId;
//是否开启系统管理模块的多租户数据隔离【SAAS多租户模式】
if (MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL) {
tenantId = oConvertUtils.getInt(TenantContext.getTenant(), -1);
} else {
tenantId = oConvertUtils.getInt(TenantContext.getTenant(), 0);
}
//查询当前租户下的第三方配置
List<SysThirdAppConfig> list = appConfigService.getThirdConfigListByThirdType(tenantId);
//钉钉是否已配置

View File

@ -74,6 +74,8 @@ public class SysDepart implements Serializable {
private String delFlag;
/**对接企业微信的ID*/
private String qywxIdentifier;
/**对接钉钉的部门ID*/
private String dingIdentifier;
/**创建人*/
private String createBy;
/**创建日期*/

View File

@ -108,6 +108,12 @@ public class SysLog implements Serializable {
*/
@Dict(dicCode = "operate_type")
private Integer operateType;
/**
* 客户终端类型 pc:电脑端 app:手机端 h5:移动网页端
*/
@Dict(dicCode = "client_type")
private String clientType;
/**
* 租户ID

View File

@ -26,7 +26,16 @@ public interface SysAnnouncementMapper extends BaseMapper<SysAnnouncement> {
* @param msgCategory 消息类型
* @return
*/
List<SysAnnouncement> querySysCementListByUserId(Page<SysAnnouncement> page, @Param("userId")String userId,@Param("msgCategory")String msgCategory);
List<SysAnnouncement> querySysCementListByUserId(Page<SysAnnouncement> page, @Param("userId")String userId,@Param("msgCategory")String msgCategory,
@Param("tenantId")Integer tenantId, @Param("beginDate")Date beginDate);
/**
* 获取用户未读消息数量
*
* @param userId 用户id
* @return
*/
Integer getUnreadMessageCountByUserId(@Param("userId") String userId, @Param("beginDate") Date beginDate);
/**
* 分页查询全部消息列表

View File

@ -40,6 +40,14 @@ public interface SysDepartMapper extends BaseMapper<SysDepart> {
* @return
*/
public List<SysDepart> queryDepartsByUsername(@Param("username") String username);
/**
* 根据用户名查询部门
*
* @param userId
* @return
*/
public List<String> queryDepartsByUserId(@Param("userId") String userId);
/**
* 通过部门编码获取部门id

View File

@ -20,4 +20,12 @@ public interface SysPackPermissionMapper extends BaseMapper<SysPackPermission> {
* @return
*/
List<String> getPermissionsByPackId(@Param("packId") String packId);
/**
* 删除产品包对应的菜单权限
*
* @param tenantIdList
*/
void deletePackPermByTenantIds(@Param("tenantIdList") List<Integer> tenantIdList);
}

View File

@ -37,7 +37,16 @@ public interface SysRoleMapper extends BaseMapper<SysRole> {
*/
@InterceptorIgnore(tenantLine = "true")
SysRole getRoleNoTenant(@Param("roleCode") String roleCode);
/**
* 根据用户id查询用户拥有的角色Code
*
* @param userId
* @param tenantId
* @return
*/
List<SysRole> getRoleCodeListByUserId(@Param("userId") String userId, @Param("tenantId") Integer tenantId);
/**
* 删除角色与用户关系
* @Author scott

View File

@ -127,4 +127,11 @@ public interface SysTenantMapper extends BaseMapper<SysTenant> {
*/
@Select("select count(1) from sys_tenant where id = #{tenantId} and del_flag = 0")
Long tenantIzExist(@Param("tenantId") Integer tenantId);
/**
* 根据用户id获取租户
* @param userId
* @return
*/
List<SysTenant> getTenantListByUserId(@Param("userId") String userId);
}

View File

@ -14,4 +14,10 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
*/
public interface SysTenantPackMapper extends BaseMapper<SysTenantPack> {
/**
* 删除租户产品包
*
* @param tenantIdList
*/
void deletePackByTenantIds(@Param("tenantIdList") List<Integer> tenantIdList);
}

View File

@ -31,5 +31,17 @@ public interface SysTenantPackUserMapper extends BaseMapper<SysTenantPackUser> {
* @param tenantId
* @return
*/
Long izHaveBuyAuth(@Param("userId") String userId, @Param("tenantId") String tenantId);
Long izHaveBuyAuth(@Param("userId") String userId, @Param("tenantId") Integer tenantId);
/**
* 根据租户id 删除租户产品包下的 用户
* @param tenantId
*/
void deletePackUserByTenantId(@Param("tenantId") Integer tenantId, @Param("userIds") List<String> userIds);
/**
* 根据多个租户id 删除租户产品包下的 用户
* @param
*/
void deletePackUserByTenantIds(@Param("tenantIds") List<Integer> tenantIds);
}

View File

@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
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.SysUser;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.jeecg.modules.system.model.SysUserSysDepartModel;
@ -27,6 +28,13 @@ public interface SysUserMapper extends BaseMapper<SysUser> {
* @return
*/
public SysUser getUserByName(@Param("username") String username);
/**
* 通过用户账号查询用户Id
* @param username
* @return
*/
public String getUserIdByName(@Param("username") String username);
/**
* 根据部门Id查询用户信息
@ -205,4 +213,13 @@ public interface SysUserMapper extends BaseMapper<SysUser> {
* @return
*/
List<SysUser> getUserByDepartsTenantId(@Param("departIds") List<String> departIds,@Param("tenantId") Integer tenantId);
/**
* 根据用户名和手机号获取用户
* @param phone
* @param username
* @return
*/
@Select("select id,phone from sys_user where phone = #{phone} and username = #{username}")
SysUser getUserByNameAndPhone(@Param("phone") String phone, @Param("username") String username);
}

View File

@ -23,6 +23,14 @@ public interface SysUserRoleMapper extends BaseMapper<SysUserRole> {
*/
@Select("select role_code from sys_role where id in (select role_id from sys_user_role where user_id = (select id from sys_user where username=#{username}))")
List<String> getRoleByUserName(@Param("username") String username);
/**
* 通过用户账号查询角色集合
* @param userId 用户id
* @return List<String>
*/
@Select("select role_code from sys_role where id in (select role_id from sys_user_role where user_id = #{userId})")
List<String> getRoleCodeByUserId(@Param("userId") String userId);
/**
* 通过用户账号查询角色Id集合

View File

@ -166,4 +166,12 @@ public interface SysUserTenantMapper extends BaseMapper<SysUserTenant> {
* @return
*/
List<JwUserDepartVo> getUsersByTenantIdAndName(@Param("tenantId") Integer tenantId);
/**
* 根据多个用户id获取租户id
*
* @param userIds
* @return
*/
List<Integer> getTenantIdsByUserIds(@Param("userIds") List<String> userIds);
}

View File

@ -37,11 +37,19 @@
where send_status = '1'
and del_flag = '0'
and msg_category = #{msgCategory}
and id IN ( select annt_id from sys_announcement_send where user_id = #{userId} and read_flag = 0)
and create_time &gt;= #{beginDate}
<if test="tenantId!=null and tenantId != 0">
and tenant_id = #{tenantId}
</if>
and id IN ( select annt_id from sys_announcement_send where user_id = #{userId} and read_flag = 0 and create_time &gt;= #{beginDate})
order by create_time DESC
</select>
<!-- 获取用户未读消息数量 -->
<select id="getUnreadMessageCountByUserId" resultType="java.lang.Integer">
select count(1) from sys_announcement_send where user_id = #{userId} and read_flag = 0 and create_time &gt;= #{beginDate}
</select>
<!-- 查询消息记录 -->
<select id="queryAllMessageList" resultMap="SysAnnouncement">
select
@ -72,6 +80,7 @@
<if test="beginDate!=null">
and a.create_time &gt;= #{beginDate}
and b.create_time &gt;= #{beginDate}
</if>
<if test="endDate!=null">
and a.create_time &lt;= #{endDate}

View File

@ -20,6 +20,17 @@
)
)
</select>
<!-- 根据username查询所拥有的部门 -->
<select id="queryDepartsByUserId" parameterType="String" resultType="java.lang.String">
SELECT id
FROM sys_depart
WHERE id IN (
SELECT dep_id
FROM sys_user_depart
WHERE user_id = #{userId}
)
</select>
<!-- 根据部门Id查询,当前和下级所有部门IDS -->
<select id="getSubDepIdsByDepId" resultType="java.lang.String">

View File

@ -7,5 +7,15 @@
where
pack_id = #{packId}
</select>
<!--删除产品包对应的菜单权限-->
<delete id="deletePackPermByTenantIds">
delete from sys_tenant_pack_perms
where pack_id in(
select id from sys_tenant_pack where tenant_id in
<foreach collection="tenantIdList" index="index" item="tenantId" open="(" separator="," close=")">
#{tenantId}
</foreach>
)
</delete>
</mapper>

View File

@ -136,6 +136,7 @@
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
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

View File

@ -20,5 +20,14 @@
SELECT * from sys_role
WHERE role_code = #{roleCode}
</select>
<!-- 根据用户id查询用户拥有的角色 -->
<select id="getRoleCodeListByUserId" resultType="org.jeecg.modules.system.entity.SysRole">
SELECT id, role_code from sys_role
WHERE id in (SELECT role_id from sys_user_role WHERE user_id = #{userId})
<if test="tenantId != null">
AND tenant_id = #{tenantId}
</if>
</select>
</mapper>

View File

@ -132,5 +132,12 @@
and a.tenant_id = #{tenantId}
and b.user_id = #{userId}
</select>
<!--根据用户id获取租户信息-->
<select id="getTenantListByUserId" resultType="org.jeecg.modules.system.entity.SysTenant">
SELECT st.id,st.name FROM sys_tenant st
LEFT JOIN sys_user_tenant sut on st.id= sut.tenant_id and st.status = 1 and sut.status='1'
WHERE sut.user_id = #{userId}
</select>
</mapper>

View File

@ -2,4 +2,12 @@
<!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.SysTenantPackMapper">
<!--删除租户产品包-->
<delete id="deletePackByTenantIds">
delete from sys_tenant_pack
where tenant_id in
<foreach collection="tenantIdList" index="index" item="tenantId" open="(" separator="," close=")">
#{tenantId}
</foreach>
</delete>
</mapper>

View File

@ -23,4 +23,22 @@
and stpu.user_id = #{userId}
and stp.pack_code in('superAdmin','accountAdmin')
</select>
<!--根据租户id 删除租户产品包下的 用户-->
<delete id="deletePackUserByTenantId">
delete from sys_tenant_pack_user where tenant_id = #{tenantId}
and user_id in
<foreach collection="userIds" index="index" item="userId" open="(" separator="," close=")">
#{userId}
</foreach>
</delete>
<!--根据多个租户id 删除租户产品包下的 用户-->
<delete id="deletePackUserByTenantIds">
delete from sys_tenant_pack_user
where tenant_id in
<foreach collection="tenantIds" index="index" item="tenantId" open="(" separator="," close=")">
#{tenantId}
</foreach>
</delete>
</mapper>

View File

@ -6,6 +6,11 @@
<select id="getUserByName" resultType="org.jeecg.modules.system.entity.SysUser">
select * from sys_user where username = #{username} and del_flag = 0
</select>
<!-- 根据用户名查询用户ID -->
<select id="getUserIdByName" resultType="String">
select id from sys_user where username = #{username} and del_flag = 0
</select>
<!-- 根据部门Id查询 -->
<select id="getUserByDepId" resultType="org.jeecg.modules.system.entity.SysUser">
@ -231,7 +236,7 @@
<!--获取租户下的用户离职列表信息-->
<select id="getTenantQuitList" resultType="org.jeecg.modules.system.entity.SysUser">
select su.id,su.username,su.realname,su.sex,su.avatar from sys_user su
select su.id,su.username,su.realname,su.sex,su.avatar,su.create_time,sut.create_by from sys_user su
join sys_user_tenant sut on sut.user_id = su.id and sut.status = '2'
where
su.status = 1

View File

@ -43,7 +43,15 @@ public interface ISysAnnouncementService extends IService<SysAnnouncement> {
* @param msgCategory 消息类型
* @return Page<SysAnnouncement>
*/
public Page<SysAnnouncement> querySysCementPageByUserId(Page<SysAnnouncement> page, String userId, String msgCategory);
public Page<SysAnnouncement> querySysCementPageByUserId(Page<SysAnnouncement> page, String userId, String msgCategory, Integer tenantId, Date beginDate);
/**
* 获取用户未读消息数量
*
* @param userId 用户id
* @return
*/
public Integer getUnreadMessageCountByUserId(String userId, Date beginDate);
/**

View File

@ -103,6 +103,14 @@ public interface ISysDepartService extends IService<SysDepart>{
* @return
*/
List<SysDepart> queryDepartsByUsername(String username);
/**
* 根据用户ID查询部门
*
* @param userId
* @return
*/
List<String> queryDepartsByUserId(String userId);
/**
* 根据部门id批量删除并删除其可能存在的子级部门

View File

@ -37,7 +37,7 @@ public interface ISysTenantPackService extends IService<SysTenantPack> {
* 删除租户产品包
* @param ids
*/
void deletePackPermissions(String ids);
void deleteTenantPack(String ids);
/**
* 退出租户

View File

@ -217,4 +217,19 @@ public interface ISysTenantService extends IService<SysTenant> {
* @param content
*/
void sendMsgForAgreeAndRefuseJoin(SysUser user, String content);
/**
* 根据密码删除当前用户
*
* @param sysUser
* @param tenantId
*/
void deleteUserByPassword(SysUser sysUser, Integer tenantId);
/**
* 根据用户id获取租户信息
* @param userId
* @return
*/
List<SysTenant> getTenantListByUserId(String userId);
}

View File

@ -89,7 +89,7 @@ public interface ISysUserService extends IService<SysUser> {
* @param user
* @param roles
*/
public void addUserWithRole(SysUser user,String roles);
public void addUserWithRole(SysUser user, String roles);
/**
@ -97,7 +97,7 @@ public interface ISysUserService extends IService<SysUser> {
* @param user
* @param roles
*/
public void editUserWithRole(SysUser user,String roles);
public void editUserWithRole(SysUser user, String roles);
/**
* 获取用户的授权角色
@ -113,7 +113,7 @@ public interface ISysUserService extends IService<SysUser> {
* @param version 前端UI版本
* @return
*/
public SysRoleIndex getDynamicIndexByUserRole(String username,String version);
public SysRoleIndex getDynamicIndexByUserRole(String username, String version);
/**
* 查询用户信息包括 部门信息
@ -177,7 +177,7 @@ public interface ISysUserService extends IService<SysUser> {
* @param username 用户账户名称
* @return
*/
public IPage<SysUser> getUserByRoleId(Page<SysUser> page,String roleId, String username);
public IPage<SysUser> getUserByRoleId(Page<SysUser> page, String roleId, String username);
/**
* 通过用户名获取用户角色集合
@ -186,6 +186,14 @@ public interface ISysUserService extends IService<SysUser> {
* @return 角色集合
*/
Set<String> getUserRolesSet(String username);
/**
* 通过用户名获取用户角色集合
*
* @param userId 用户id
* @return 角色集合
*/
Set<String> getUserRoleSetById(String userId);
/**
* 通过用户名获取用户权限集合
@ -347,7 +355,7 @@ public interface ISysUserService extends IService<SysUser> {
* @param sysUser
* @return
*/
Result<JSONObject> setLoginTenant(SysUser sysUser, JSONObject obj, String username, Result<JSONObject> result);
Result<JSONObject> setLoginTenant(SysUser sysUser, JSONObject obj, String username, Result<JSONObject> result);
//--- author:taoyan date:20221231 for: QQYUN-3515【应用】应用下的组织机构管理功能细节实现 ---
/**
@ -417,4 +425,21 @@ public interface ISysUserService extends IService<SysUser> {
* @param ids
*/
void checkUserAdminRejectDel(String ids);
/**
* 修改手机号
*
* @param json
* @param username
*/
void changePhone(JSONObject json, String username);
/**
* 发送短信验证码
*
* @param jsonObject
* @param username 用户名
* @param ipAddress ip地址
*/
void sendChangePhoneSms(JSONObject jsonObject, String username, String ipAddress);
}

View File

@ -8,6 +8,7 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.DateRangeUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
import org.jeecg.modules.system.entity.SysAnnouncement;
@ -139,14 +140,19 @@ public class SysAnnouncementServiceImpl extends ServiceImpl<SysAnnouncementMappe
}
@Override
public Page<SysAnnouncement> querySysCementPageByUserId(Page<SysAnnouncement> page, String userId, String msgCategory) {
public Page<SysAnnouncement> querySysCementPageByUserId(Page<SysAnnouncement> page, String userId, String msgCategory, Integer tenantId, Date beginDate) {
if (page.getSize() == -1) {
return page.setRecords(sysAnnouncementMapper.querySysCementListByUserId(null, userId, msgCategory));
return page.setRecords(sysAnnouncementMapper.querySysCementListByUserId(null, userId, msgCategory,tenantId,beginDate));
} else {
return page.setRecords(sysAnnouncementMapper.querySysCementListByUserId(page, userId, msgCategory));
return page.setRecords(sysAnnouncementMapper.querySysCementListByUserId(page, userId, msgCategory,tenantId,beginDate));
}
}
@Override
public Integer getUnreadMessageCountByUserId(String userId, Date beginDate) {
return sysAnnouncementMapper.getUnreadMessageCountByUserId(userId, beginDate);
}
@Override
public void completeAnnouncementSendInfo() {
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();

View File

@ -157,6 +157,18 @@ public class SysBaseApiImpl implements ISysBaseAPI {
return user;
}
@Override
@Cacheable(cacheNames=CommonConstant.SYS_USER_ID_MAPPING_CACHE, key="#username")
public String getUserIdByName(String username) {
if (oConvertUtils.isEmpty(username)) {
return null;
}
String userId = userMapper.getUserIdByName(username);
return userId;
}
@Override
public String translateDictFromTable(String table, String text, String code, String key) {
return sysDictService.queryTableDictTextByKey(table, text, code, key);
@ -265,9 +277,12 @@ public class SysBaseApiImpl implements ISysBaseAPI {
// }
if(user!=null) {
info.setSysUserId(user.getId());
info.setSysUserCode(user.getUsername());
info.setSysUserName(user.getRealname());
info.setSysOrgCode(user.getOrgCode());
info.setSysOrgId(user.getOrgId());
info.setSysRoleCode(user.getRoleCode());
}else{
return null;
}
@ -311,6 +326,11 @@ public class SysBaseApiImpl implements ISysBaseAPI {
public List<String> getRolesByUsername(String username) {
return sysUserRoleMapper.getRoleByUserName(username);
}
@Override
public List<String> getRolesByUserId(String userId) {
return sysUserRoleMapper.getRoleCodeByUserId(userId);
}
@Override
public List<String> getDepartIdsByUsername(String username) {
@ -321,6 +341,11 @@ public class SysBaseApiImpl implements ISysBaseAPI {
}
return result;
}
@Override
public List<String> getDepartIdsByUserId(String userId) {
return sysDepartService.queryDepartsByUserId(userId);
}
@Override
public Set<String> getDepartParentIdsByUsername(String username) {
@ -1092,6 +1117,20 @@ public class SysBaseApiImpl implements ISysBaseAPI {
log.info("-------通过数据库读取用户拥有的角色Rules------username " + username + ",Roles size: " + (roles == null ? 0 : roles.size()));
return new HashSet<>(roles);
}
/**
* 查询用户拥有的角色集合
* @param useId
* @return
*/
@Override
public Set<String> getUserRoleSetById(String useId) {
// 查询用户拥有的角色集合
List<String> roles = sysUserRoleMapper.getRoleCodeByUserId(useId);
log.info("-------通过数据库读取用户拥有的角色Rules------useId " + useId + ",Roles size: " + (roles == null ? 0 : roles.size()));
return new HashSet<>(roles);
}
/**
* 查询用户拥有的权限集合
@ -1175,6 +1214,11 @@ public class SysBaseApiImpl implements ISysBaseAPI {
return getUserRoleSet(username);
}
@Override
public Set<String> queryUserRolesById(String userId) {
return getUserRoleSetById(userId);
}
/**
* 查询用户拥有的权限集合 common api 里面的接口实现
* @param userId
@ -1588,25 +1632,33 @@ public class SysBaseApiImpl implements ISysBaseAPI {
@Override
public void saveDataLog(DataLogDTO dataLogDto) {
SysDataLog entity = new SysDataLog();
entity.setDataTable(dataLogDto.getTableName());
entity.setDataId(dataLogDto.getDataId());
entity.setDataContent(dataLogDto.getContent());
entity.setType(dataLogDto.getType());
entity.setDataVersion("1");
if (oConvertUtils.isNotEmpty(dataLogDto.getCreateName())) {
entity.setCreateBy(dataLogDto.getCreateName());
} else {
entity.autoSetCreateName();
try {
SysDataLog entity = new SysDataLog();
entity.setDataTable(dataLogDto.getTableName());
entity.setDataId(dataLogDto.getDataId());
entity.setDataContent(dataLogDto.getContent());
entity.setType(dataLogDto.getType());
entity.setDataVersion("1");
if (oConvertUtils.isNotEmpty(dataLogDto.getCreateName())) {
entity.setCreateBy(dataLogDto.getCreateName());
} else {
entity.autoSetCreateName();
}
sysDataLogService.save(entity);
} catch (Exception e) {
log.warn(e.getMessage(), e);
//e.printStackTrace();
}
sysDataLogService.save(entity);
}
@Override
public void updateAvatar(LoginUser loginUser) {
SysUser sysUser = new SysUser();
BeanUtils.copyProperties(loginUser, sysUser);
sysUserService.updateById(sysUser);
SysUser sysUser = new SysUser();
// 创建UpdateWrapper对象
UpdateWrapper<SysUser> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("id", loginUser.getId()); // 设置更新条件
sysUser.setAvatar(loginUser.getAvatar()); // 设置要更新的字段
sysUserService.update(sysUser, updateWrapper);
}
@Override

View File

@ -489,6 +489,12 @@ public class SysDepartServiceImpl extends ServiceImpl<SysDepartMapper, SysDepart
public List<SysDepart> queryDepartsByUsername(String username) {
return baseMapper.queryDepartsByUsername(username);
}
@Override
public List<String> queryDepartsByUserId(String userId) {
List<String> list = baseMapper.queryDepartsByUserId(userId);
return list;
}
/**
* 根据用户所负责部门ids获取父级部门编码

View File

@ -84,8 +84,7 @@ public class SysDictServiceImpl extends ServiceImpl<SysDictMapper, SysDict> impl
// 2.SQL注入check只限制非法串改数据库
//关联表字典举例sys_user,realname,id
SqlInjectionUtil.filterContent(table);
SqlInjectionUtil.filterContent(fieldName);
SqlInjectionUtil.filterContentMulti(table, fieldName);
String checkSql = table + SymbolConstant.COMMA + fieldName + SymbolConstant.COMMA;
// 【QQYUN-6533】表字典白名单check
@ -251,7 +250,7 @@ public class SysDictServiceImpl extends ServiceImpl<SysDictMapper, SysDict> impl
}
// 3.SQL注入check
SqlInjectionUtil.filterContent(table, text, code);
SqlInjectionUtil.filterContentMulti(table, text, code);
SqlInjectionUtil.specialFilterContentForDictSql(filterSql);
// 4.针对采用 ${}写法的表名和字段进行转义和check
@ -269,8 +268,7 @@ public class SysDictServiceImpl extends ServiceImpl<SysDictMapper, SysDict> impl
// 1.SQL注入校验只限制非法串改数据库
SqlInjectionUtil.specialFilterContentForDictSql(table);
SqlInjectionUtil.filterContent(text);
SqlInjectionUtil.filterContent(code);
SqlInjectionUtil.filterContentMulti(text, code);
SqlInjectionUtil.specialFilterContentForDictSql(filterSql);
String str = table+","+text+","+code;
@ -313,7 +311,7 @@ public class SysDictServiceImpl extends ServiceImpl<SysDictMapper, SysDict> impl
return null;
}
// 2.sql注入check
SqlInjectionUtil.filterContent(table, text, code, key);
SqlInjectionUtil.filterContentMulti(table, text, code, key);
// 3.针对采用 ${}写法的表名和字段进行转义和check
table = SqlInjectionUtil.getSqlInjectTableName(table);
@ -358,7 +356,7 @@ public class SysDictServiceImpl extends ServiceImpl<SysDictMapper, SysDict> impl
}
// 3.SQL注入check
SqlInjectionUtil.filterContent(table, text, code);
SqlInjectionUtil.filterContentMulti(table, text, code);
SqlInjectionUtil.specialFilterContentForDictSql(filterSql);
// 4.针对采用 ${}写法的表名和字段进行转义和check
@ -420,7 +418,7 @@ public class SysDictServiceImpl extends ServiceImpl<SysDictMapper, SysDict> impl
}
// 2.SQL注入check
SqlInjectionUtil.filterContent(table, text, code);
SqlInjectionUtil.filterContentMulti(table, text, code);
SqlInjectionUtil.specialFilterContentForDictSql(filterSql);
String str = table+","+text+","+code;
@ -626,9 +624,15 @@ public class SysDictServiceImpl extends ServiceImpl<SysDictMapper, SysDict> impl
pidField = SqlInjectionUtil.getSqlInjectField(pidField);
hasChildField = SqlInjectionUtil.getSqlInjectField(hasChildField);
if(oConvertUtils.isEmpty(text) || oConvertUtils.isEmpty(code)){
log.warn("text={}code={}", text, code);
log.warn("加载树字典参数有误text和code不允许为空");
return null;
}
// 2.检测最终SQL是否存在SQL注入风险
String dictCode = table + "," + text + "," + code;
SqlInjectionUtil.filterContent(dictCode);
SqlInjectionUtil.filterContentMulti(dictCode);
// 【QQYUN-6533】表字典白名单check
sysBaseAPI.dictTableWhiteListCheckByDict(table, text, code);
@ -697,7 +701,7 @@ public class SysDictServiceImpl extends ServiceImpl<SysDictMapper, SysDict> impl
}
// 3.SQL注入check
SqlInjectionUtil.filterContent(dictCode);
SqlInjectionUtil.filterContentMulti(dictCode);
Page<DictModel> pageList = baseMapper.queryDictTablePageList(page, query);
return pageList.getRecords();

View File

@ -113,10 +113,12 @@ public class SysTenantPackServiceImpl extends ServiceImpl<SysTenantPackMapper, S
@Override
@Transactional(rollbackFor = Exception.class)
public void deletePackPermissions(String ids) {
public void deleteTenantPack(String ids) {
String[] idsArray = ids.split(SymbolConstant.COMMA);
for (String id : idsArray) {
this.deletePackPermission(id,null);
//删除产品包下面的用户
this.deletePackUser(id);
sysTenantPackMapper.deleteById(id);
}
}
@ -251,4 +253,13 @@ public class SysTenantPackServiceImpl extends ServiceImpl<SysTenantPackMapper, S
}
}
/**
* 删除产品包下面的用户
* @param packId
*/
private void deletePackUser(String packId) {
LambdaQueryWrapper<SysTenantPackUser> query = new LambdaQueryWrapper<>();
query.eq(SysTenantPackUser::getPackId, packId);
sysTenantPackUserMapper.delete(query);
}
}

View File

@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.dto.message.BusMessageDTO;
import org.jeecg.common.api.dto.message.MessageDTO;
@ -14,18 +15,18 @@ import org.jeecg.common.config.TenantContext;
import org.jeecg.common.constant.CacheConstant;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.exception.JeecgBootBizTipException;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.PasswordUtil;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.constant.enums.SysAnnmentTypeEnum;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.aop.TenantLog;
import org.jeecg.modules.system.entity.*;
import org.jeecg.modules.system.mapper.SysTenantMapper;
import org.jeecg.modules.system.mapper.SysTenantPackUserMapper;
import org.jeecg.modules.system.mapper.SysUserDepartMapper;
import org.jeecg.modules.system.mapper.SysUserTenantMapper;
import org.jeecg.modules.system.mapper.*;
import org.jeecg.modules.system.service.ISysTenantPackService;
import org.jeecg.modules.system.service.ISysTenantService;
import org.jeecg.modules.system.service.ISysUserService;
@ -67,6 +68,12 @@ public class SysTenantServiceImpl extends ServiceImpl<SysTenantMapper, SysTenant
@Autowired
private SysUserDepartMapper sysUserDepartMapper;
@Autowired
private SysTenantPackMapper sysTenantPackMapper;
@Autowired
private SysPackPermissionMapper sysPackPermissionMapper;
@Override
public List<SysTenant> queryEffectiveTenant(Collection<Integer> idList) {
if(oConvertUtils.listIsEmpty(idList)){
@ -143,6 +150,8 @@ public class SysTenantServiceImpl extends ServiceImpl<SysTenantMapper, SysTenant
userTenantMapper.delete(query);
//update-end---author:wangshuai ---date:20221223 for[QQYUN-3371]租户逻辑改造,改成关系表------------
}
//租户移除用户,直接删除用户租户产品包
sysTenantPackUserMapper.deletePackUserByTenantId(Integer.valueOf(tenantId),Arrays.asList(userIds.split(SymbolConstant.COMMA)));
}
@Override
@ -192,11 +201,11 @@ public class SysTenantServiceImpl extends ServiceImpl<SysTenantMapper, SysTenant
if(CommonConstant.USER_TENANT_UNDER_REVIEW.equals(relation.getStatus())){
msg = ",状态:审核中";
}else if(CommonConstant.USER_TENANT_REFUSE.equals(relation.getStatus())){
throw new JeecgBootException("管理员已拒绝您加入租户,请联系租户管理员");
throw new JeecgBootBizTipException("管理员已拒绝您加入租户,请联系租户管理员");
}else if(CommonConstant.USER_TENANT_QUIT.equals(relation.getStatus())){
msg = ",状态:已离职";
}
throw new JeecgBootException("您已是该租户成员"+msg);
throw new JeecgBootBizTipException("您已是该租户成员"+msg);
}
//用户加入门牌号审核中状态
SysUserTenant tenant = new SysUserTenant();
@ -236,6 +245,11 @@ public class SysTenantServiceImpl extends ServiceImpl<SysTenantMapper, SysTenant
//删除租户下的用户
userTenantMapper.deleteUserByTenantId(list);
//update-ennd---author:wangshuai ---date:20230710 for【QQYUN-5723】3、租户彻底删除用户租户关系表也需要删除------------
//update-begin---author:wangshuai ---date:20230710 for【QQYUN-5723】3、租户彻底删除用户租户关系表也需要删除------------
//删除租户下的产品包
this.deleteTenantPackByTenantId(list);
//update-ennd---author:wangshuai ---date:20230710 for【QQYUN-5723】3、租户彻底删除用户租户关系表也需要删除------------
}
@Override
@ -833,4 +847,67 @@ public class SysTenantServiceImpl extends ServiceImpl<SysTenantMapper, SysTenant
}
return content;
}
/**
* 删除租户下的产品包
*
* @param tenantIdList
*/
private void deleteTenantPackByTenantId(List<Integer> tenantIdList) {
//1.删除产品包下的用户
sysTenantPackUserMapper.deletePackUserByTenantIds(tenantIdList);
//2.删除产品包对应的菜单权限
sysPackPermissionMapper.deletePackPermByTenantIds(tenantIdList);
//3.删除产品包
sysTenantPackMapper.deletePackByTenantIds(tenantIdList);
}
@Override
public void deleteUserByPassword(SysUser sysUser, Integer tenantId) {
//被删除人的用户id
String userId = sysUser.getId();
//被删除人的密码
String password = sysUser.getPassword();
//当前登录用户
LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
//step1 判断当前用户是否为当前租户的管理员(只有超级管理员和账号管理员可以删除)
Long isHaveAdmin = sysTenantPackUserMapper.izHaveBuyAuth(user.getId(), tenantId);
if(null == isHaveAdmin || 0 == isHaveAdmin){
throw new JeecgBootException("您不是当前组织的管理员,无法删除用户!");
}
//step2 离职状态下,并且无其他组织情况下,可以删除
SysUserTenant sysUserTenant = userTenantMapper.getUserTenantByTenantId(userId, tenantId);
if(null == sysUserTenant || !CommonConstant.USER_TENANT_QUIT.equals(sysUserTenant.getStatus())){
throw new JeecgBootException("用户没有离职,不允许删除!");
}
List<Integer> tenantIdsByUserId = userTenantMapper.getTenantIdsByUserId(userId);
if(CollectionUtils.isNotEmpty(tenantIdsByUserId) && tenantIdsByUserId.size()>0){
throw new JeecgBootException("用户尚有未退出的组织,无法删除!");
}
//step3 当天创建的用户和创建人可以删除
SysUser sysUserData = userService.getById(userId);
if(!sysUserData.getCreateBy().equals(user.getUsername())){
throw new JeecgBootException("您不是该用户的创建人,无法删除!");
}
Date createTime = sysUserData.getCreateTime();
boolean sameDay = DateUtils.isSameDay(createTime, new Date());
if(!sameDay){
throw new JeecgBootException("用户不是今天创建的,无法删除!");
}
//step4 验证密码
String passwordEncode = PasswordUtil.encrypt(sysUserData.getUsername(), password, sysUserData.getSalt());
if(!passwordEncode.equals(sysUserData.getPassword())){
throw new JeecgBootException("您输入的密码不正确,无法删除该用户!");
}
//step5 逻辑删除用户
userService.deleteUser(userId);
//step6 真实删除用户
userService.removeLogicDeleted(Collections.singletonList(userId));
}
@Override
public List<SysTenant> getTenantListByUserId(String userId) {
return tenantMapper.getTenantListByUserId(userId);
}
}

View File

@ -72,6 +72,7 @@ public class SysThirdAccountServiceImpl extends ServiceImpl<SysThirdAccountMappe
thirdQuery.eq(SysThirdAccount::getSysUserId,sysUser.getId());
thirdQuery.eq(SysThirdAccount::getThirdType,account.getThirdType());
thirdQuery.eq(SysThirdAccount::getThirdUserUuid,thirdUserUuid);
thirdQuery.eq(SysThirdAccount::getTenantId,CommonConstant.TENANT_ID_DEFAULT_VALUE);
SysThirdAccount sysThirdAccounts = sysThirdAccountMapper.selectOne(thirdQuery);
if(sysThirdAccounts!=null){
sysThirdAccount.setThirdUserId(sysThirdAccounts.getThirdUserId());

View File

@ -1,13 +1,12 @@
package org.jeecg.modules.system.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.exceptions.ClientException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
@ -24,11 +23,12 @@ import org.jeecg.common.constant.CacheConstant;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.FillRuleConstant;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.constant.enums.DySmsEnum;
import org.jeecg.common.constant.enums.MessageTypeEnum;
import org.jeecg.common.constant.enums.RoleIndexConfigEnum;
import org.jeecg.common.constant.enums.SysAnnmentTypeEnum;
import org.jeecg.common.desensitization.annotation.SensitiveEncode;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.system.vo.SysUserCacheInfo;
import org.jeecg.common.util.*;
@ -38,20 +38,22 @@ import org.jeecg.modules.message.handle.impl.SystemSendMsgHandle;
import org.jeecg.modules.system.entity.*;
import org.jeecg.modules.system.mapper.*;
import org.jeecg.modules.system.model.SysUserSysDepartModel;
import org.jeecg.modules.system.service.ISysRoleIndexService;
import org.jeecg.modules.system.service.ISysThirdAccountService;
import org.jeecg.modules.system.service.ISysUserService;
import org.jeecg.modules.system.vo.SysUserDepVo;
import org.jeecg.modules.system.vo.SysUserPositionVo;
import org.jeecg.modules.system.vo.UserAvatar;
import org.jeecg.modules.system.vo.lowapp.AppExportUserVo;
import org.jeecg.modules.system.vo.lowapp.DepartAndUserInfo;
import org.jeecg.modules.system.vo.lowapp.DepartInfo;
import org.jeecg.modules.system.vo.lowapp.AppExportUserVo;
import org.jeecg.modules.system.vo.lowapp.UpdateDepartInfo;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
@ -103,13 +105,13 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
@Autowired
private SysThirdAccountMapper sysThirdAccountMapper;
@Autowired
ThirdAppWechatEnterpriseServiceImpl wechatEnterpriseService;
ThirdAppWechatEnterpriseServiceImpl wechatEnterpriseService;
@Autowired
ThirdAppDingtalkServiceImpl dingtalkService;
ThirdAppDingtalkServiceImpl dingtalkService;
@Autowired
SysRoleIndexMapper sysRoleIndexMapper;
ISysRoleIndexService sysRoleIndexService;
@Autowired
SysTenantMapper sysTenantMapper;
SysTenantMapper sysTenantMapper;
@Autowired
private SysUserTenantMapper relationMapper;
@Autowired
@ -123,6 +125,9 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
@Autowired
private ISysThirdAccountService sysThirdAccountService;
@Autowired
private RedisUtil redisUtil;
@Override
public Result<IPage<SysUser>> queryPageList(HttpServletRequest req, QueryWrapper<SysUser> queryWrapper, Integer pageSize, Integer pageNo) {
@ -158,6 +163,20 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
}
//update-end-author:taoyan--date:20220104--for: JTC-372 【用户冻结问题】 online授权、用户组件选择用户都能看到被冻结的用户
//update-begin---author:wangshuai---date:2024-03-08---for:【QQYUN-8110】在线通讯录支持设置权限(只能看分配的技术支持)---
String tenantId = TokenUtils.getTenantIdByRequest(req);
String lowAppId = TokenUtils.getLowAppIdByRequest(req);
// Object bean = ResourceUtil.getImplementationClass(DataEnhanceEnum.getClassPath(tenantId,lowAppId));
// if(null != bean){
// UserFilterEnhance userEnhanceService = (UserFilterEnhance) bean;
// LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
// List<String> userIds = userEnhanceService.getUserIds(sysUser.getId());
// if(CollectionUtil.isNotEmpty(userIds)){
// queryWrapper.in("id", userIds);
// }
// }
//update-end---author:wangshuai---date:2024-03-08---for:【QQYUN-8110】在线通讯录支持设置权限(只能看分配的技术支持)---
//TODO 外部模拟登陆临时账号,列表不显示
queryWrapper.ne("username", "_reserve_user_external");
Page<SysUser> page = new Page<SysUser>(pageNo, pageSize);
@ -169,7 +188,6 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
List<String> userIds = pageList.getRecords().stream().map(SysUser::getId).collect(Collectors.toList());
if (userIds != null && userIds.size() > 0) {
Map<String, String> useDepNames = this.getDepNamesByUserIds(userIds);
int tenantId = oConvertUtils.getInt(TenantContext.getTenant(), 0);
pageList.getRecords().forEach(item -> {
item.setOrgCodeTxt(useDepNames.get(item.getId()));
//查询用户的租户ids
@ -181,14 +199,14 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
}
Integer posTenantId = null;
if (MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL) {
posTenantId = tenantId;
posTenantId = oConvertUtils.getInt(TenantContext.getTenant(), 0);;
}
//查询用户职位关系表(获取租户下面的)
//update-begin---author:wangshuai---date:2023-11-15---for:【QQYUN-7028】用户职务保存后未回显---
List<String> positionList = sysUserPositionMapper.getPositionIdByUserTenantId(item.getId(),posTenantId);
//update-end---author:wangshuai---date:2023-11-15---for:【QQYUN-7028】用户职务保存后未回显---
//update-end---author:wangshuai ---date:20230228 for[QQYUN-4354]加入更多字段:当前加入时间应该取当前租户的/职位也是当前租户下的------------
item.setPost(CommonUtils.getSplitText(positionList,SymbolConstant.COMMA));
item.setPost(CommonUtils.getSplitText(positionList, SymbolConstant.COMMA));
//update-begin---author:wangshuai---date:2023-10-08---for:【QQYUN-6668】钉钉部门和用户同步我怎么知道哪些用户是双向绑定成功的---
//是否根据租户隔离(敲敲云用户列表专用,用于展示是否同步钉钉)
@ -251,7 +269,13 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
@CacheEvict(value={CacheConstant.SYS_USERS_CACHE}, allEntries=true)
@Transactional(rollbackFor = Exception.class)
public boolean deleteUser(String userId) {
//1.删除用户
//update-begin---author:wangshuai---date:2024-01-16---for:【QQYUN-7974】admin用户禁止删除---
//1.验证当前用户是管理员账号 admin
//验证用户是否为管理员
this.checkUserAdminRejectDel(userId);
//update-end---author:wangshuai---date:2024-01-16---for:【QQYUN-7974】admin用户禁止删除---
//2.删除用户
this.removeById(userId);
return false;
}
@ -260,7 +284,9 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
@CacheEvict(value={CacheConstant.SYS_USERS_CACHE}, allEntries=true)
@Transactional(rollbackFor = Exception.class)
public boolean deleteBatchUsers(String userIds) {
//1.删除用户
//1.验证当前用户是管理员账号 admin
this.checkUserAdminRejectDel(userIds);
//2.删除用户
this.removeByIds(Arrays.asList(userIds.split(",")));
return false;
}
@ -318,33 +344,40 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
/**
* 获取动态首页路由配置
*
* @param username
* @param version
* @return
*/
@Override
public SysRoleIndex getDynamicIndexByUserRole(String username,String version) {
public SysRoleIndex getDynamicIndexByUserRole(String username, String version) {
List<String> roles = sysUserRoleMapper.getRoleByUserName(username);
String componentUrl = RoleIndexConfigEnum.getIndexByRoles(roles);
SysRoleIndex roleIndex = new SysRoleIndex(componentUrl);
boolean isV3 = CommonConstant.VERSION_V3.equals(version);
//只有 X-Version=v3 的时候才读取sys_role_index表获取角色首页配置
if (oConvertUtils.isNotEmpty(version) && roles!=null && roles.size()>0) {
LambdaQueryWrapper<SysRoleIndex> routeIndexQuery = new LambdaQueryWrapper();
if (isV3 && CollectionUtils.isNotEmpty(roles)) {
LambdaQueryWrapper<SysRoleIndex> routeIndexQuery = new LambdaQueryWrapper<>();
//用户所有角色
routeIndexQuery.in(SysRoleIndex::getRoleCode, roles);
//角色首页状态0未开启 1开启
routeIndexQuery.eq(SysRoleIndex::getStatus, CommonConstant.STATUS_1);
//优先级正序排序
routeIndexQuery.orderByAsc(SysRoleIndex::getPriority);
List<SysRoleIndex> list = sysRoleIndexMapper.selectList(routeIndexQuery);
if (null != list && list.size() > 0) {
List<SysRoleIndex> list = sysRoleIndexService.list(routeIndexQuery);
if (CollectionUtils.isNotEmpty(list)) {
roleIndex = list.get(0);
}
}
//如果componentUrl为空则返回空
if(oConvertUtils.isEmpty(roleIndex.getComponent())){
return null;
if (oConvertUtils.isEmpty(roleIndex.getComponent())) {
if (isV3) {
// 如果角色没有配置首页,则使用默认首页
return sysRoleIndexService.queryDefaultIndex();
} else {
// 非v3返回null
return null;
}
}
return roleIndex;
}
@ -361,17 +394,30 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
log.info("-------通过数据库读取用户拥有的角色Rules------username " + username + ",Roles size: " + (roles == null ? 0 : roles.size()));
return new HashSet<>(roles);
}
/**
* 通过用户名获取用户角色集合
* @param userId 用户ID
* @return 角色集合
*/
@Override
public Set<String> getUserRoleSetById(String userId) {
// 查询用户拥有的角色集合
List<String> roles = sysUserRoleMapper.getRoleCodeByUserId(userId);
log.info("-------通过数据库读取用户拥有的角色Rules------userId " + userId + ",Roles size: " + (roles == null ? 0 : roles.size()));
return new HashSet<>(roles);
}
/**
* 通过用户名获取用户权限集合
*
* @param username 用户
* @param userId 用户ID
* @return 权限集合
*/
@Override
public Set<String> getUserPermissionsSet(String username) {
public Set<String> getUserPermissionsSet(String userId) {
Set<String> permissionSet = new HashSet<>();
List<SysPermission> permissionList = sysPermissionMapper.queryByUser(username);
List<SysPermission> permissionList = sysPermissionMapper.queryByUser(userId);
//================= begin 开启租户的时候 如果没有test角色默认加入test角色================
if (MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL) {
if (permissionList == null) {
@ -390,7 +436,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
permissionSet.add(po.getPerms());
}
}
log.info("-------通过数据库读取用户拥有的权限Perms------username "+ username+",Perms size: "+ (permissionSet==null?0:permissionSet.size()) );
log.info("-------通过数据库读取用户拥有的权限Perms------userId "+ userId+",Perms size: "+ (permissionSet==null?0:permissionSet.size()) );
return permissionSet;
}
@ -444,7 +490,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
* @return
*/
@Override
public IPage<SysUser> getUserByDepId(Page<SysUser> page, String departId,String username) {
public IPage<SysUser> getUserByDepId(Page<SysUser> page, String departId, String username) {
return userMapper.getUserByDepId(page, departId,username);
}
@ -487,7 +533,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
//根据部门orgCode查询部门需要将职位id进行传递
for (SysUserSysDepartModel model:list) {
List<String> positionList = sysUserPositionMapper.getPositionIdByUserId(model.getId());
model.setPost(CommonUtils.getSplitText(positionList,SymbolConstant.COMMA));
model.setPost(CommonUtils.getSplitText(positionList, SymbolConstant.COMMA));
}
Integer total = baseMapper.getUserByOrgCodeTotal(orgCode, userParams);
@ -796,7 +842,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
}
@Override
@Cacheable(cacheNames=CacheConstant.SYS_USERS_CACHE, key="#username")
@Cacheable(cacheNames= CacheConstant.SYS_USERS_CACHE, key="#username")
@SensitiveEncode
public LoginUser getEncodeUserInfo(String username){
if(oConvertUtils.isEmpty(username)) {
@ -812,6 +858,10 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
return null;
}
BeanUtils.copyProperties(sysUser, loginUser);
// 查询当前登录用户的部门id
loginUser.setOrgId(this.getDepartIdByOrCode(sysUser.getOrgCode()));
// 查询当前登录用户的角色code多个逗号分割
loginUser.setRoleCode(this.getJoinRoleCodeByUserId(sysUser.getId()));
return loginUser;
}
@ -835,7 +885,6 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
SysUserTenant userTenant = new SysUserTenant();
userTenant.setStatus(CommonConstant.USER_TENANT_QUIT);
userTenantMapper.update(userTenant,query);
//update-end---author:wangshuai ---date:20230111 for[QQYUN-3951]租户用户离职重构------------
}
@Override
@ -854,7 +903,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
* @return
*/
@Override
public Result<JSONObject> setLoginTenant(SysUser sysUser, JSONObject obj, String username, Result<JSONObject> result){
public Result<JSONObject> setLoginTenant(SysUser sysUser, JSONObject obj, String username, Result<JSONObject> result){
// update-begin--Author:sunjianlei Date:20210802 for获取用户租户信息
//用户有哪些租户
// List<SysTenant> tenantList = null;
@ -1294,7 +1343,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
* @param orgName
* @param orgId
*/
private void getParentDepart(SysDepart depart,List<String> orgName,List<String> orgId){
private void getParentDepart(SysDepart depart, List<String> orgName, List<String> orgId){
String pid = depart.getParentId();
orgName.add(0, depart.getDepartName());
orgId.add(0, depart.getId());
@ -1422,17 +1471,58 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
private void userPositionId(SysUser sysUser) {
if(null != sysUser){
List<String> positionList = sysUserPositionMapper.getPositionIdByUserId(sysUser.getId());
sysUser.setPost(CommonUtils.getSplitText(positionList,SymbolConstant.COMMA));
sysUser.setPost(CommonUtils.getSplitText(positionList, SymbolConstant.COMMA));
}
}
/**
* 查询用户当前登录部门的id
*
* @param orgCode
*/
private @Nullable String getDepartIdByOrCode(String orgCode) {
if (oConvertUtils.isEmpty(orgCode)) {
return null;
}
LambdaQueryWrapper<SysDepart> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysDepart::getOrgCode, orgCode);
queryWrapper.select(SysDepart::getId);
SysDepart depart = sysDepartMapper.selectOne(queryWrapper);
if (depart == null || oConvertUtils.isEmpty(depart.getId())) {
return null;
}
return depart.getId();
}
/**
* 查询用户的角色code多个逗号分割
*
* @param userId
*/
private @Nullable String getJoinRoleCodeByUserId(String userId) {
if (oConvertUtils.isEmpty(userId)) {
return null;
}
// 判断是否开启saas模式根据租户id过滤
Integer tenantId = null;
if (MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL) {
// 开启了但是没有租户ID默认-1使其查询不到任何数据
tenantId = oConvertUtils.getInt(TenantContext.getTenant(), -1);
}
List<SysRole> roleList = sysRoleMapper.getRoleCodeListByUserId(userId, tenantId);
if (CollectionUtils.isEmpty(roleList)) {
return null;
}
return roleList.stream().map(SysRole::getRoleCode).collect(Collectors.joining(SymbolConstant.COMMA));
}
/**
* 移除部门负责人
* @param departChargeUserIdList
* @param departChargeUsers
* @param departId
*/
private void removeDepartmentManager(List<String> departChargeUserIdList,List<SysUser> departChargeUsers,String departId){
private void removeDepartmentManager(List<String> departChargeUserIdList, List<SysUser> departChargeUsers, String departId){
//移除部门负责人
for(String chargeUserId: departChargeUserIdList){
for(SysUser chargeUser: departChargeUsers){
@ -1796,6 +1886,9 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
String title = sysUser.getRealname() + " 邀请您加入 " + tenantName + "";
messageDTO.setTitle(title);
Map<String, Object> data = new HashMap<>();
//update-begin---author:wangshuai---date:2024-03-11---for:【QQYUN-8425】用户导入成功后 消息提醒 跳转至同意页面---
data.put(CommonConstant.NOTICE_MSG_BUS_TYPE, SysAnnmentTypeEnum.TENANT_INVITE.getType());
//update-end---author:wangshuai---date:2024-03-11---for:【QQYUN-8425】用户导入成功后 消息提醒 跳转至同意页面---
messageDTO.setData(data);
messageDTO.setContent(title);
messageDTO.setToUser(invitedUsername);
@ -1804,6 +1897,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
//update-end---author:wangshuai ---date:20230710 for【QQYUN-5731】导入用户时没有提醒------------
}
//======================================= end 用户与部门 用户列表导入 =========================================
@Override
public void checkUserAdminRejectDel(String userIds) {
LambdaQueryWrapper<SysUser> query = new LambdaQueryWrapper<>();
@ -1815,4 +1909,118 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
throw new JeecgBootException("admin用户不允许删除");
}
}
@Override
public void changePhone(JSONObject json, String username) {
String smscode = json.getString("smscode");
String phone = json.getString("phone");
String type = json.getString("type");
if(oConvertUtils.isEmpty(phone)){
throw new JeecgBootException("请填写原手机号!");
}
if(oConvertUtils.isEmpty(smscode)){
throw new JeecgBootException("请填写验证码!");
}
//step1 验证原手机号是否和当前用户匹配
SysUser sysUser = userMapper.getUserByNameAndPhone(phone,username);
if (null == sysUser){
throw new JeecgBootException("原手机号不匹配,无法修改密码!");
}
//step2 根据类型判断是验证原手机号的验证码还是新手机号的验证码
//验证原手机号
if(CommonConstant.VERIFY_ORIGINAL_PHONE.equals(type)){
this.verifyPhone(phone, smscode);
}else if(CommonConstant.UPDATE_PHONE.equals(type)){
//修改手机号
String newPhone = json.getString("newPhone");
//需要验证新手机号和原手机号是否一致,一致不让修改
if(newPhone.equals(phone)){
throw new JeecgBootException("新手机号与原手机号一致,无法修改!");
}
this.verifyPhone(newPhone, smscode);
//step3 新手机号验证码验证成功之后即可修改手机号
sysUser.setPhone(newPhone);
userMapper.updateById(sysUser);
}
}
/**
* 验证手机号
*
* @param phone
* @param smsCode
* @return
*/
public void verifyPhone(String phone, String smsCode){
String phoneKey = CommonConstant.CHANGE_PHONE_REDIS_KEY_PRE + phone;
Object phoneCode = redisUtil.get(phoneKey);
if(null == phoneCode){
throw new JeecgBootException("验证码失效,请重新发送验证码!");
}
if(!smsCode.equals(phoneCode.toString())) {
throw new JeecgBootException("短信验证码不匹配!");
}
//验证完成之后清空手机验证码
redisUtil.removeAll(phoneKey);
}
@Override
public void sendChangePhoneSms(JSONObject jsonObject, String username, String ipAddress) {
String type = jsonObject.getString("type");
String phone = jsonObject.getString("phone");
if(oConvertUtils.isEmpty(phone)){
throw new JeecgBootException("请填写手机号!");
}
//step1 根据类型判断是发送旧手机号验证码还是新的手机号验证码
if(CommonConstant.VERIFY_ORIGINAL_PHONE.equals(type)){
//step2 旧手机号验证码需要验证手机号是否匹配
SysUser sysUser = userMapper.getUserByNameAndPhone(phone, username);
if(null == sysUser){
throw new JeecgBootException("旧手机号不匹配,无法修改手机号!");
}
}else if(CommonConstant.UPDATE_PHONE.equals(type)){
//step3 新手机号需要验证手机号码是否已注册过
SysUser userByPhone = userMapper.getUserByPhone(phone);
if(null != userByPhone){
throw new JeecgBootException("手机号已被注册,请尝试其他手机号!");
}
}
//step4 发送短信验证码
this.sendPhoneSms(phone, ipAddress);
}
/**
* 发送短信验证码
* @param phone
*/
private void sendPhoneSms(String phone, String clientIp) {
String redisKey = CommonConstant.CHANGE_PHONE_REDIS_KEY_PRE+phone;
Object object = redisUtil.get(redisKey);
if (object != null) {
throw new JeecgBootException("验证码10分钟内仍然有效");
}
//增加 check防止恶意刷短信接口
if(!DySmsLimit.canSendSms(clientIp)){
log.warn("--------[警告] IP地址:{}, 短信接口请求太多-------", clientIp);
throw new JeecgBootException("短信接口请求太多,请稍后再试!", CommonConstant.PHONE_SMS_FAIL_CODE);
}
//随机数
String captcha = RandomUtil.randomNumbers(6);
JSONObject obj = new JSONObject();
obj.put("code", captcha);
try {
boolean sendSmsSuccess = DySmsHelper.sendSms(phone, obj, DySmsEnum.LOGIN_TEMPLATE_CODE);
if(!sendSmsSuccess){
throw new JeecgBootException("短信验证码发送失败,请稍后重试!");
}
//验证码10分钟内有效
redisUtil.set(redisKey, captcha, 600);
} catch (ClientException e) {
log.error(e.getMessage(),e);
throw new JeecgBootException("短信接口未配置,请联系管理员!");
}
}
}

View File

@ -30,10 +30,7 @@ import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.constant.enums.MessageTypeEnum;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.util.PasswordUtil;
import org.jeecg.common.util.RestUtil;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.common.util.*;
import org.jeecg.config.JeecgBaseConfig;
import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
import org.jeecg.modules.system.entity.*;
@ -233,7 +230,9 @@ public class ThirdAppDingtalkServiceImpl implements IThirdAppService {
for (JdtDepartmentTreeVo departmentTree : departmentTreeList) {
LambdaQueryWrapper<SysDepart> queryWrapper = new LambdaQueryWrapper<>();
// 根据 source_identifier 字段查询
queryWrapper.eq(SysDepart::getId, departmentTree.getSource_identifier());
//update-begin---author:wangshuai---date:2024-04-10---for:【issues/6017】钉钉同步部门时没有最顶层的部门名同步用户时用户没有部门信息---
queryWrapper.and(item -> item.eq(SysDepart::getId, departmentTree.getSource_identifier()).or().eq(SysDepart::getDingIdentifier,departmentTree.getDept_id()));
//update-end---author:wangshuai---date:2024-04-10---for:【issues/6017】钉钉同步部门时没有最顶层的部门名同步用户时用户没有部门信息---
SysDepart sysDepart = sysDepartService.getOne(queryWrapper);
if (sysDepart != null) {
// 执行更新操作
@ -270,14 +269,21 @@ public class ThirdAppDingtalkServiceImpl implements IThirdAppService {
newSysDepart.setOrgCategory("1");
}
try {
if(oConvertUtils.isEmpty(departmentTree.getParent_id())){
newSysDepart.setDingIdentifier(departmentTree.getDept_id().toString());
}
newSysDepart.setTenantId(tenantId);
sysDepartService.saveDepartData(newSysDepart, username);
// 更新钉钉 source_identifier
Department updateDtDepart = new Department();
updateDtDepart.setDept_id(departmentTree.getDept_id());
updateDtDepart.setSource_identifier(newSysDepart.getId());
Response response = JdtDepartmentAPI.update(updateDtDepart, accessToken);
if (!response.isSuccess()) {
throw new RuntimeException(response.getErrmsg());
//为空说明是最顶级部门,最顶级部门不允许修改操作
if(oConvertUtils.isNotEmpty(newSysDepart.getParentId())){
Response response = JdtDepartmentAPI.update(updateDtDepart, accessToken);
if (!response.isSuccess()) {
throw new RuntimeException(response.getErrmsg());
}
}
String str = String.format("部门 %s 创建成功!", newSysDepart.getDepartName());
syncInfo.addSuccessInfo(str);
@ -1234,7 +1240,7 @@ public class ThirdAppDingtalkServiceImpl implements IThirdAppService {
if(MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL){
Long count = tenantMapper.tenantIzExist(tenantId);
if(ObjectUtil.isEmpty(count) || 0 == count){
throw new JeecgBootException("租户不存在!");
throw new JeecgBootException("租户ID:" + tenantId + "无效,平台中不存在!");
}
}
}

View File

@ -284,7 +284,9 @@ public class ThirdAppWechatEnterpriseServiceImpl implements IThirdAppService {
if (sysDepart != null) {
// 执行更新操作
SysDepart updateSysDepart = this.qwDepartmentToSysDepart(departmentTree, sysDepart);
if (sysParentId != null) {
//update-begin---author:wangshuai---date:2024-04-10---for:【issues/6017】企业微信同步部门时没有最顶层的部门名同步用户时用户没有部门信息---
if (sysParentId != null && !"0".equals(sysParentId)) {
//update-end---author:wangshuai---date:2024-04-10---for:【issues/6017】企业微信同步部门时没有最顶层的部门名同步用户时用户没有部门信息---
updateSysDepart.setParentId(sysParentId);
}
try {
@ -302,7 +304,7 @@ public class ThirdAppWechatEnterpriseServiceImpl implements IThirdAppService {
} else {
// 执行新增操作
SysDepart newSysDepart = this.qwDepartmentToSysDepart(departmentTree, null);
if (sysParentId != null) {
if (sysParentId != null && !"0".equals(sysParentId)) {
newSysDepart.setParentId(sysParentId);
// 2 = 组织机构
newSysDepart.setOrgCategory("2");
@ -390,7 +392,7 @@ public class ThirdAppWechatEnterpriseServiceImpl implements IThirdAppService {
int errCode = JwUserAPI.updateUser(qwUser, accessToken);
// 收集错误信息
this.syncUserCollectErrInfo(errCode, sysUser, syncInfo);
this.thirdAccountSaveOrUpdate(sysThirdAccount, sysUser.getId(), qwUser.getUserid(),qwUser.getName());
this.thirdAccountSaveOrUpdate(sysThirdAccount, sysUser.getId(), qwUser.getUserid(),qwUser.getName(), null);
// 更新完成,直接跳到下一次外部循环继续
continue for1;
}
@ -400,7 +402,7 @@ public class ThirdAppWechatEnterpriseServiceImpl implements IThirdAppService {
// 收集错误信息
boolean apiSuccess = this.syncUserCollectErrInfo(errCode, sysUser, syncInfo);
if (apiSuccess) {
this.thirdAccountSaveOrUpdate(sysThirdAccount, sysUser.getId(), qwUser.getUserid(),qwUser.getName());
this.thirdAccountSaveOrUpdate(sysThirdAccount, sysUser.getId(), qwUser.getUserid(),qwUser.getName(), null);
}
}
return syncInfo;
@ -471,13 +473,16 @@ public class ThirdAppWechatEnterpriseServiceImpl implements IThirdAppService {
* @param qwUserId 企业微信用户ID
* @param wechatRealName 企业微信用户真实姓名
*/
private void thirdAccountSaveOrUpdate(SysThirdAccount sysThirdAccount, String sysUserId, String qwUserId, String wechatRealName) {
private void thirdAccountSaveOrUpdate(SysThirdAccount sysThirdAccount, String sysUserId, String qwUserId, String wechatRealName, Integer tenantId) {
if (sysThirdAccount == null) {
sysThirdAccount = new SysThirdAccount();
sysThirdAccount.setSysUserId(sysUserId);
sysThirdAccount.setStatus(1);
sysThirdAccount.setDelFlag(0);
sysThirdAccount.setThirdType(THIRD_TYPE);
if(oConvertUtils.isNotEmpty(tenantId)){
sysThirdAccount.setTenantId(tenantId);
}
}
sysThirdAccount.setThirdUserId(qwUserId);
sysThirdAccount.setThirdUserUuid(qwUserId);
@ -1184,7 +1189,7 @@ public class ThirdAppWechatEnterpriseServiceImpl implements IThirdAppService {
this.createUserTenant(sysUserId,false,tenantId);
//step 3 新建或更新第三方账号表
SysThirdAccount sysThirdAccount = sysThirdAccountService.getOneByUuidAndThirdType(wechatUserId, THIRD_TYPE, tenantId, wechatUserId);
this.thirdAccountSaveOrUpdate(sysThirdAccount,sysUserId,wechatUserId,wechatRealName);
this.thirdAccountSaveOrUpdate(sysThirdAccount,sysUserId,wechatUserId,wechatRealName,tenantId);
//step 4 新建或更新用户部门关系表
if(oConvertUtils.isNotEmpty(wechatDepartId)){
String wechatDepartIds = wechatDepartId.toString();

View File

@ -5,6 +5,7 @@ import org.springframework.beans.BeanUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* 钉钉树结构的部门
@ -48,8 +49,20 @@ public class JdtDepartmentTreeVo extends Department {
public static List<JdtDepartmentTreeVo> listToTree(List<Department> allDepartment) {
// 先找出所有的父级
List<JdtDepartmentTreeVo> treeList = getByParentId(1, allDepartment);
Optional<Department> departmentOptional = allDepartment.stream().filter(item -> item.getParent_id() == null).findAny();
Department department = new Department();
//判断是否找到数据
if(departmentOptional.isPresent()){
department = departmentOptional.get();
}
getChildrenRecursion(treeList, allDepartment);
return treeList;
//update-begin---author:wangshuai---date:2024-04-10---for:【issues/6017】钉钉同步部门时没有最顶层的部门名同步用户时用户没有部门信息---
JdtDepartmentTreeVo treeVo = new JdtDepartmentTreeVo(department);
treeVo.setChildren(treeList);
List<JdtDepartmentTreeVo> list = new ArrayList<>();
list.add(treeVo);
return list;
//update-end---author:wangshuai---date:2024-04-10---for:【issues/6017】钉钉同步部门时没有最顶层的部门名同步用户时用户没有部门信息---
}
private static List<JdtDepartmentTreeVo> getByParentId(Integer parentId, List<Department> allDepartment) {

View File

@ -5,6 +5,7 @@ import org.springframework.beans.BeanUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* 企业微信树结构的部门
@ -48,8 +49,20 @@ public class JwDepartmentTreeVo extends Department {
public static List<JwDepartmentTreeVo> listToTree(List<Department> allDepartment) {
// 先找出所有的父级
List<JwDepartmentTreeVo> treeList = getByParentId("1", allDepartment);
Optional<Department> departmentOptional = allDepartment.stream().filter(item -> "0".equals(item.getParentid())).findAny();
Department department = new Department();
//判断是否找到数据
if(departmentOptional.isPresent()){
department = departmentOptional.get();
}
getChildrenRecursion(treeList, allDepartment);
return treeList;
//update-begin---author:wangshuai---date:2024-04-10---for:【issues/6017】企业微信同步部门时没有最顶层的部门名同步用户时用户没有部门信息---
JwDepartmentTreeVo treeVo = new JwDepartmentTreeVo(department);
treeVo.setChildren(treeList);
List<JwDepartmentTreeVo> list = new ArrayList<>();
list.add(treeVo);
return list;
//update-begin---author:wangshuai---date:2024-04-10---for:【issues/6017】企业微信部门时没有最顶层的部门名同步用户时用户没有部门信息---
}
private static List<JwDepartmentTreeVo> getByParentId(String parentId, List<Department> allDepartment) {

View File

@ -31,7 +31,7 @@
TimePicker,
</#if>
<#if need_pca>
JAreaLinkage,
JAreaSelect,
</#if>
<#if need_upload>
JUpload,

View File

@ -8,104 +8,104 @@
<#elseif po.dictField?default("")?trim?length gt 1>
<#assign form_field_dictCode="${po.dictField}">
</#if>
<a-col :span="${form_span}">
<a-form-item label="${po.filedComment}" v-bind="validateInfos.${autoStringSuffixForModel(po)}">
<#if po.classType =='date'>
<a-date-picker placeholder="请选择${po.filedComment}" v-model:value="formData.${po.fieldName}" value-format="YYYY-MM-DD" style="width: 100%" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>/>
<#elseif po.classType =='datetime'>
<a-date-picker placeholder="请选择${po.filedComment}" v-model:value="formData.${po.fieldName}" showTime value-format="YYYY-MM-DD HH:mm:ss" style="width: 100%" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>/>
<#elseif po.classType =='time'>
<#assign need_time = true>
<time-picker placeholder="请选择${po.filedComment}" value-format="HH:mm:ss" v-model:value="formData.${po.fieldName}" style="width: 100%" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>/>
<#elseif po.classType =='popup'>
<#assign need_popup = true>
<#assign sourceFields = po.dictField?default("")?trim?split(",")/>
<#assign targetFields = po.dictText?default("")?trim?split(",")/>
<j-popup
placeholder="请选择${po.filedComment}"
v-model:value="formData.${po.fieldName}"
code="${po.dictTable}"
:fieldConfig="[
<#list sourceFields as fieldName>
{ source: '${fieldName}', target: '${dashedToCamel(targetFields[fieldName_index])}' },
</#list>
]"
:multi="${po.extendParams.popupMulti?c}"
:setFieldsValue="setFieldsValue"
<#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if><#rt>
/>
<#elseif po.classType =='sel_depart'>
<#assign need_dept = true>
<j-select-dept v-model:value="formData.${po.fieldName}" :multiple="${po.extendParams.multi?default('true')}" checkStrictly <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if> />
<#elseif po.classType =='switch'>
<#assign need_switch = true>
<j-switch v-model:value="formData.${po.fieldName}" <#if po.dictField != 'is_open'>:options="${po.dictField}"</#if> <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>></j-switch>
<#elseif po.classType =='pca'>
<#assign need_pca = true>
<j-area-select v-model:value="formData.${po.fieldName}" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if> />
<#elseif po.classType =='markdown'>
<#assign need_markdown = true>
<j-markdown-editor v-model:value="formData.${autoStringSuffixForModel(po)}" id="${po.fieldName}" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>></j-markdown-editor>
<#elseif po.classType =='password'>
<a-input-password v-model:value="formData.${po.fieldName}" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>/>
<#elseif po.classType =='sel_user'>
<#assign need_dept_user = true>
<#-- update-begin---author:chenrui ---date:20240102 for[issue/#5711]修复用户选择组件在生成代码后变成部门用户选择组件---------- -->
<j-select-user v-model:value="formData.${po.fieldName}" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>/>
<#-- update-end---author:chenrui ---date:20240102 for[issue/#5711]修复用户选择组件在生成代码后变成部门用户选择组件---------- -->
<#elseif po.classType =='textarea'>
<a-textarea v-model:value="formData.${autoStringSuffixForModel(po)}" :rows="4" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>/>
<#elseif po.classType=='radio'>
<#assign need_select_tag = true>
<j-dict-select-tag type='radio' v-model:value="formData.${po.fieldName}" dictCode="${form_field_dictCode}" placeholder="请选择${po.filedComment}" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>/>
<#elseif po.classType=='list'>
<#assign need_select_tag = true>
<j-dict-select-tag v-model:value="formData.${po.fieldName}" dictCode="${form_field_dictCode}" placeholder="请选择${po.filedComment}" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>/>
<#elseif po.classType=='list_multi'>
<#assign need_multi = true>
<j-select-multiple type="${po.classType}" v-model:value="formData.${po.fieldName}" dictCode="${form_field_dictCode}" placeholder="请选择${po.filedComment}" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if> :triggerChange="false"/>
<#elseif po.classType=='checkbox'>
<#assign need_checkbox = true>
<j-checkbox type="${po.classType}" v-model:value="formData.${po.fieldName}" dictCode="${form_field_dictCode}" placeholder="请选择${po.filedComment}" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>/>
<#elseif po.classType=='sel_search'>
<#assign need_search = true>
<j-search-select v-model:value="formData.${po.fieldName}" dict="${form_field_dictCode}" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if> />
<#elseif po.classType=='cat_tree'>
<#assign need_category = true>
<j-category-select v-model:value="formData.${po.fieldName}" pcode="${po.dictField?default("")}" placeholder="请选择${po.filedComment}" <#if po.dictText?default("")?trim?length gt 1>back="${dashedToCamel(po.dictText)}"</#if> <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if> @change="(value) => handleFormChange('${po.fieldName}', value)" />
<#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
<a-input-number v-model:value="formData.${po.fieldName}" placeholder="请输入${po.filedComment}" style="width: 100%" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>/>
<#elseif po.classType=='file'>
<#assign need_upload = true>
<j-upload v-model:value="formData.${po.fieldName}" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if> <#if po.uploadnum??>:maxCount=${po.uploadnum}</#if>></j-upload>
<#elseif po.classType=='image'>
<#assign need_image_upload = true>
<j-image-upload <#if po.uploadnum??>:fileMax=${po.uploadnum}</#if> v-model:value="formData.${po.fieldName}" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>></j-image-upload>
<#elseif po.classType=='umeditor'>
<#assign need_editor = true>
<j-editor v-model:value="formData.${autoStringSuffixForModel(po)}" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>/>
<#elseif po.fieldDbType=='Blob'>
<a-input v-model:value="formData.${autoStringSuffixForModel(po)}" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>></a-input>
<#elseif po.classType == 'sel_tree'>
<#assign need_select_tree = true>
<j-tree-select
<#if po.dictText??>
<#if po.dictText?split(',')[2]?? && po.dictText?split(',')[0]??>
dict="${po.dictTable},${po.dictText?split(',')[2]},${po.dictText?split(',')[0]}"
<#elseif po.dictText?split(',')[1]??>
pidField="${po.dictText?split(',')[1]}"
<#elseif po.dictText?split(',')[3]??>
hasChildField="${po.dictText?split(',')[3]}"
</#if>
</#if>
pidValue="${po.dictField}"
<#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>
v-model:value="formData.${po.fieldName}"
@change="(value) => handleFormChange('${po.fieldName}', value)">
</j-tree-select>
<#else>
<a-input v-model:value="formData.${po.fieldName}" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>></a-input>
</#if>
</a-form-item>
</a-col>
<a-col :span="${form_span}">
<a-form-item label="${po.filedComment}" v-bind="validateInfos.${autoStringSuffixForModel(po)}">
<#if po.classType =='date'>
<a-date-picker placeholder="请选择${po.filedComment}" v-model:value="formData.${po.fieldName}" value-format="YYYY-MM-DD" style="width: 100%" <#if po.readonly=='Y'>disabled</#if>/>
<#elseif po.classType =='datetime'>
<a-date-picker placeholder="请选择${po.filedComment}" v-model:value="formData.${po.fieldName}" showTime value-format="YYYY-MM-DD HH:mm:ss" style="width: 100%" <#if po.readonly=='Y'>disabled</#if>/>
<#elseif po.classType =='time'>
<#assign need_time = true>
<time-picker placeholder="请选择${po.filedComment}" value-format="HH:mm:ss" v-model:value="formData.${po.fieldName}" style="width: 100%" <#if po.readonly=='Y'>disabled</#if>/>
<#elseif po.classType =='popup'>
<#assign need_popup = true>
<#assign sourceFields = po.dictField?default("")?trim?split(",")/>
<#assign targetFields = po.dictText?default("")?trim?split(",")/>
<j-popup
placeholder="请选择${po.filedComment}"
v-model:value="formData.${po.fieldName}"
code="${po.dictTable}"
:fieldConfig="[
<#list sourceFields as fieldName>
{ source: '${fieldName}', target: '${dashedToCamel(targetFields[fieldName_index])}' },
</#list>
]"
:multi="${po.extendParams.popupMulti?c}"
:setFieldsValue="setFieldsValue"
<#if po.readonly=='Y'>disabled</#if><#rt>
/>
<#elseif po.classType =='sel_depart'>
<#assign need_dept = true>
<j-select-dept v-model:value="formData.${po.fieldName}" :multiple="${po.extendParams.multi?default('true')}" checkStrictly <#if po.readonly=='Y'>disabled</#if> />
<#elseif po.classType =='switch'>
<#assign need_switch = true>
<j-switch v-model:value="formData.${po.fieldName}" <#if po.dictField != 'is_open'>:options="${po.dictField}"</#if> <#if po.readonly=='Y'>disabled</#if>></j-switch>
<#elseif po.classType =='pca'>
<#assign need_pca = true>
<j-area-select v-model:value="formData.${po.fieldName}" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled</#if> />
<#elseif po.classType =='markdown'>
<#assign need_markdown = true>
<j-markdown-editor v-model:value="formData.${autoStringSuffixForModel(po)}" id="${po.fieldName}" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled</#if>></j-markdown-editor>
<#elseif po.classType =='password'>
<a-input-password v-model:value="formData.${po.fieldName}" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled</#if>/>
<#elseif po.classType =='sel_user'>
<#assign need_dept_user = true>
<#-- update-begin---author:chenrui ---date:20240102 for[issue/#5711]修复用户选择组件在生成代码后变成部门用户选择组件---------- -->
<j-select-user v-model:value="formData.${po.fieldName}" <#if po.readonly=='Y'>disabled</#if>/>
<#-- update-end---author:chenrui ---date:20240102 for[issue/#5711]修复用户选择组件在生成代码后变成部门用户选择组件---------- -->
<#elseif po.classType =='textarea'>
<a-textarea v-model:value="formData.${autoStringSuffixForModel(po)}" :rows="4" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled</#if>/>
<#elseif po.classType=='radio'>
<#assign need_select_tag = true>
<j-dict-select-tag type='radio' v-model:value="formData.${po.fieldName}" dictCode="${form_field_dictCode}" placeholder="请选择${po.filedComment}" <#if po.readonly=='Y'>disabled</#if>/>
<#elseif po.classType=='list'>
<#assign need_select_tag = true>
<j-dict-select-tag v-model:value="formData.${po.fieldName}" dictCode="${form_field_dictCode}" placeholder="请选择${po.filedComment}" <#if po.readonly=='Y'>disabled</#if>/>
<#elseif po.classType=='list_multi'>
<#assign need_multi = true>
<j-select-multiple type="${po.classType}" v-model:value="formData.${po.fieldName}" dictCode="${form_field_dictCode}" placeholder="请选择${po.filedComment}" <#if po.readonly=='Y'>disabled</#if> :triggerChange="false"/>
<#elseif po.classType=='checkbox'>
<#assign need_checkbox = true>
<j-checkbox type="${po.classType}" v-model:value="formData.${po.fieldName}" dictCode="${form_field_dictCode}" placeholder="请选择${po.filedComment}" <#if po.readonly=='Y'>disabled</#if>/>
<#elseif po.classType=='sel_search'>
<#assign need_search = true>
<j-search-select v-model:value="formData.${po.fieldName}" dict="${form_field_dictCode}" <#if po.readonly=='Y'>disabled</#if> />
<#elseif po.classType=='cat_tree'>
<#assign need_category = true>
<j-category-select v-model:value="formData.${po.fieldName}" pcode="${po.dictField?default("")}" placeholder="请选择${po.filedComment}" <#if po.dictText?default("")?trim?length gt 1>back="${dashedToCamel(po.dictText)}"</#if> <#if po.readonly=='Y'>disabled</#if> @change="(value) => handleFormChange('${po.fieldName}', value)" />
<#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
<a-input-number v-model:value="formData.${po.fieldName}" placeholder="请输入${po.filedComment}" style="width: 100%" <#if po.readonly=='Y'>disabled</#if>/>
<#elseif po.classType=='file'>
<#assign need_upload = true>
<j-upload v-model:value="formData.${po.fieldName}" <#if po.readonly=='Y'>disabled</#if> <#if po.uploadnum??>:maxCount=${po.uploadnum}</#if>></j-upload>
<#elseif po.classType=='image'>
<#assign need_image_upload = true>
<j-image-upload <#if po.uploadnum??>:fileMax=${po.uploadnum}</#if> v-model:value="formData.${po.fieldName}" <#if po.readonly=='Y'>disabled</#if>></j-image-upload>
<#elseif po.classType=='umeditor'>
<#assign need_editor = true>
<j-editor v-model:value="formData.${autoStringSuffixForModel(po)}" <#if po.readonly=='Y'>disabled</#if>/>
<#elseif po.fieldDbType=='Blob'>
<a-input v-model:value="formData.${autoStringSuffixForModel(po)}" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled</#if>></a-input>
<#elseif po.classType == 'sel_tree'>
<#assign need_select_tree = true>
<j-tree-select
<#if po.dictText??>
<#if po.dictText?split(',')[2]?? && po.dictText?split(',')[0]??>
dict="${po.dictTable},${po.dictText?split(',')[2]},${po.dictText?split(',')[0]}"
<#elseif po.dictText?split(',')[1]??>
pidField="${po.dictText?split(',')[1]}"
<#elseif po.dictText?split(',')[3]??>
hasChildField="${po.dictText?split(',')[3]}"
</#if>
</#if>
pidValue="${po.dictField}"
<#if po.readonly=='Y'>disabled</#if>
v-model:value="formData.${po.fieldName}"
@change="(value) => handleFormChange('${po.fieldName}', value)">
</j-tree-select>
<#else>
<a-input v-model:value="formData.${po.fieldName}" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled</#if>></a-input>
</#if>
</a-form-item>
</a-col>
</#if>

View File

@ -0,0 +1,21 @@
<#-- 原生查询区域样式 -->
.jeecg-basic-table-form-container {
padding: 0;
.table-page-search-submitButtons {
display: block;
margin-bottom: 24px;
white-space: nowrap;
}
.query-group-cust{
min-width: 100px !important;
}
.query-group-split-cust{
width: 30px;
display: inline-block;
text-align: center
}
.ant-form-item:not(.ant-form-item-with-help){
margin-bottom: 16px;
height: 32px;
}
}

View File

@ -3,7 +3,7 @@
<#if po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
${po.fieldName}: <#if po.defaultVal??>${po.defaultVal}<#else>undefined</#if>,
<#elseif po.fieldDbType=='Blob'>
${po.fieldName}String: <#if po.defaultVal??>${po.defaultVal}<#else>''</#if>,
${po.fieldName}String: <#if po.defaultVal??>'${po.defaultVal}'<#else>''</#if>,
<#else>
${po.fieldName}: <#if po.defaultVal??>'${po.defaultVal}'<#else>''</#if>,
</#if>

View File

@ -3,7 +3,7 @@
<#if po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
${po.fieldName}: <#if po.defaultVal??>${po.defaultVal}<#else>undefined</#if>,
<#elseif po.fieldDbType=='Blob'>
${po.fieldName}String: <#if po.defaultVal??>${po.defaultVal}<#else>''</#if>,
${po.fieldName}String: <#if po.defaultVal??>'${po.defaultVal}'<#else>''</#if>,
<#else>
${po.fieldName}: <#if po.defaultVal??>'${po.defaultVal}'<#else>''</#if>,
</#if>

View File

@ -3,7 +3,7 @@
<#if po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
${po.fieldName}: <#if po.defaultVal??>${po.defaultVal}<#else>undefined</#if>,
<#elseif po.fieldDbType=='Blob'>
${po.fieldName}String: <#if po.defaultVal??>${po.defaultVal}<#else>''</#if>,
${po.fieldName}String: <#if po.defaultVal??>'${po.defaultVal}'<#else>''</#if>,
<#else>
${po.fieldName}: <#if po.defaultVal??>'${po.defaultVal}'<#else>''</#if>,
</#if>

View File

@ -151,7 +151,7 @@
<#assign dictCode="dictCode: '${po.dictField}'">
</#if>
<#if po.classType=='list' || po.classType=='list_multi' || po.classType=='sel_search' || po.classType=='checkbox'>
<#if po.classType=='list' || po.classType=='list_multi' || po.classType=='sel_search' || po.classType=='checkbox' || po.classType=='radio'>
<#assign extAttrs="${dictCode},">
<#elseif po.classType=='cat_tree'>
<#-- 分类字典树 -->

View File

@ -14,13 +14,13 @@
{ validator: (rule, value, callback) => validateDuplicateValue(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}', value, this.model.id, callback)},
<#-- 6到16位数字 -->
<#elseif fieldValidType == 'n6-16'>
{ pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
{ pattern: /^\d{6,16}$|^(?=\d+\.\d+)[\d.]{7,17}$/, message: '请输入6到16位数字!'},
<#-- 6到16位任意字符 -->
<#elseif fieldValidType == '*6-16'>
{ pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
<#-- 6到18位字符串 -->
<#elseif fieldValidType == 's6-18'>
{ pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
{ pattern: /^[a-z|A-Z]{6,18}$/, message: '请输入6到18位字母!'},
<#-- 网址 -->
<#elseif fieldValidType == 'url'>
{ pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},

View File

@ -10,13 +10,13 @@
{ validator: ${po.fieldName}Duplicatevalidate }<#rt>
<#-- 6到16位数字 -->
<#elseif fieldValidType == 'n6-16'>
{ pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'}<#rt>,
{ pattern: /^\d{6,16}$|^(?=\d+\.\d+)[\d.]{7,17}$/, message: '请输入6到16位数字!'}<#rt>,
<#-- 6到16位任意字符 -->
<#elseif fieldValidType == '*6-16'>
{ pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'}<#rt>,
<#-- 6到18位字符串 -->
<#elseif fieldValidType == 's6-18'>
{ pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'}<#rt>,
{ pattern: /^[a-z|A-Z]{6,18}$/, message: '请输入6到18位字母!'},<#rt>,
<#-- 网址 -->
<#elseif fieldValidType == 'url'>
{ pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'}<#rt>,

View File

@ -132,7 +132,7 @@
//流程表单data
formData: {
type: Object,
default: ()=>{},
default: () => ({}),
required: false
},
//表单模式true流程表单 false普通表单

View File

@ -116,7 +116,9 @@
<#list columns as po>
<#if po.isQuery=='Y'>
<#if po.queryMode!='single'>
<#if po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
<#-- update-begin---author:chenrui ---date:20240527 for[TV360X-388]时间范围查询控件---------- -->
<#if po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal' || po.classType=='time'>
<#-- update-end---author:chenrui ---date:20240527 for[TV360X-388]时间范围查询控件---------- -->
['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end']],
</#if>
</#if>

View File

@ -122,6 +122,9 @@ export const searchFormSchema: FormSchema[] = [
},
<#elseif po.classType=='date'>
component: 'DatePicker',
componentProps: {
valueFormat: 'YYYY-MM-DD'
},
<#elseif po.classType=='datetime'>
component: 'DatePicker',
componentProps: {
@ -169,6 +172,10 @@ export const searchFormSchema: FormSchema[] = [
},
<#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
component: 'JRangeNumber',
<#-- update-begin---author:chenrui ---date:20240527 for[TV360X-388]时间范围查询控件---------- -->
<#elseif po.classType=='time'>
component: 'RangeTime',
<#-- update-end---author:chenrui ---date:20240527 for[TV360X-388]时间范围查询控件---------- -->
<#else>
component: 'Input', //TODO 范围查询
</#if>
@ -215,6 +222,9 @@ export const formSchema: FormSchema[] = [
<#-- update-end-author:taoyan date:2022-6-24 for: VUEN-1190【代码生成】默认值未生成 -->
<#if po.classType =='date'>
component: 'DatePicker',
componentProps: {
valueFormat: 'YYYY-MM-DD'
},
<#elseif po.classType =='datetime'>
component: 'DatePicker',
componentProps: {
@ -252,12 +262,18 @@ export const formSchema: FormSchema[] = [
},
<#elseif po.classType =='textarea'>
component: 'InputTextArea',
<#elseif po.classType=='list' || po.classType=='radio'>
<#elseif po.classType=='list'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}"
},
<#-- update-begin---author:chenrui ---date:20231228 for[QQYUN-7583] Vue3风格表单页面多选控件渲染成了下拉多选---------- -->
<#elseif po.classType=='radio'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}",
type: "radio"
},
<#-- update-begin---author:chenrui ---date:20231228 for[QQYUN-7583] Vue3风格表单页面多选控件渲染成了下拉多选---------- -->
<#elseif po.classType=='list_multi'>
component: 'JSelectMultiple',
componentProps:{
@ -334,7 +350,7 @@ export const formSchema: FormSchema[] = [
{...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema)[0]},
<#-- 6到16位数字 -->
<#elseif fieldValidType == 'n6-16'>
{ pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
{ pattern: /^\d{6,16}$|^(?=\d+\.\d+)[\d.]{7,17}$/, message: '请输入6到16位数字!'},
<#-- 6到16位任意字符 -->
<#elseif fieldValidType == '*6-16'>
{ pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},

View File

@ -355,7 +355,9 @@
* @param value
*/
function handleFormJoinChange(key, value) {
queryParam[key] = value.join(',');
if (typeof value != 'string') {
queryParam[key] = value.join(',');
}
}
</#if>
@ -408,20 +410,5 @@
</script>
<style lang="less" scoped>
.jeecg-basic-table-form-container {
padding: 0;
.table-page-search-submitButtons {
display: block;
margin-bottom: 24px;
white-space: nowrap;
}
.query-group-cust{
min-width: 100px !important;
}
.query-group-split-cust{
width: 30px;
display: inline-block;
text-align: center
}
}
<#include "/common/form/native/vueNativeSearchStyle.ftl">
</style>

View File

@ -121,6 +121,9 @@ export const searchFormSchema: FormSchema[] = [
},
<#elseif po.classType=='date'>
component: 'DatePicker',
componentProps: {
valueFormat: 'YYYY-MM-DD'
},
<#elseif po.classType=='datetime'>
component: 'DatePicker',
componentProps: {
@ -201,6 +204,9 @@ export const formSchema: FormSchema[] = [
field: ${autoStringSuffix(po)},
<#if po.classType =='date'>
component: 'DatePicker',
componentProps: {
valueFormat: 'YYYY-MM-DD'
},
<#elseif po.classType =='datetime'>
component: 'DatePicker',
componentProps: {
@ -238,11 +244,17 @@ export const formSchema: FormSchema[] = [
},
<#elseif po.classType =='textarea'>
component: 'InputTextArea',
<#elseif po.classType=='list' || po.classType=='radio'>
<#elseif po.classType=='list'>
component: 'JDictSelectTag',
componentProps:{
dictCode: "${form_field_dictCode}"
},
dictCode:"${form_field_dictCode}"
},
<#elseif po.classType=='radio'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}",
type: "radio"
},
<#-- update-begin---author:chenrui ---date:20231228 for[QQYUN-7583] Vue3风格表单页面多选控件渲染成了下拉多选---------- -->
<#elseif po.classType=='list_multi'>
component: 'JSelectMultiple',
@ -320,13 +332,13 @@ export const formSchema: FormSchema[] = [
{...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema)[0]},
<#-- 6到16位数字 -->
<#elseif fieldValidType == 'n6-16'>
{ pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
{ pattern: /^\d{6,16}$|^(?=\d+\.\d+)[\d.]{7,17}$/, message: '请输入6到16位数字!'},
<#-- 6到16位任意字符 -->
<#elseif fieldValidType == '*6-16'>
{ pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
<#-- 6到18位字符串 -->
<#elseif fieldValidType == 's6-18'>
{ pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
{ pattern: /^[a-z|A-Z]{6,18}$/, message: '请输入6到18位字母!'},
<#-- 网址 -->
<#elseif fieldValidType == 'url'>
{ pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},

View File

@ -1,8 +1,10 @@
<#include "/common/utils.ftl">
<template>
<a-spin :spinning="confirmLoading">
<a-form ref="formRef" class="antd-modal-form" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-row>
<JFormContainer :disabled="disabled">
<template #detail>
<a-form ref="formRef" class="antd-modal-form" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-row>
<#assign need_category = false>
<#assign bpm_flag=false>
<#assign need_pca = false>
@ -39,12 +41,14 @@
<#include "/common/form/native/vue3NativeForm.ftl">
</#list>
<#if bpm_flag>
<a-col v-if="showFlowSubmitButton" :span="24" style="width: 100%;text-align: center;">
<a-button preIcon="ant-design:check-outlined" style="width: 126px" type="primary" @click="submitForm">提 交</a-button>
</a-col>
<a-col v-if="showFlowSubmitButton" :span="24" style="width: 100%;text-align: center;">
<a-button preIcon="ant-design:check-outlined" style="width: 126px" type="primary" @click="submitForm">提 交</a-button>
</a-col>
</#if>
</a-row>
</a-form>
</a-row>
</a-form>
</template>
</JFormContainer>
</a-spin>
</template>
@ -56,13 +60,14 @@
import { getValueType } from '/@/utils';
import { saveOrUpdate } from '../${entityName}.api';
import { Form } from 'ant-design-vue';
import JFormContainer from '/@/components/Form/src/container/JFormContainer.vue';
<#if hasOnlyValidate == true>
import { duplicateValidate } from '/@/utils/helper/validator'
</#if>
const props = defineProps({
formDisabled: { type: Boolean, default: false },
formData: { type: Object, default: ()=>{} },
formData: { type: Object, default: () => ({})},
formBpm: { type: Boolean, default: true }
});
const formRef = ref();
@ -77,9 +82,9 @@
const wrapperCol = ref<any>({ xs: { span: 24 }, sm: { span: 16 } });
const confirmLoading = ref<boolean>(false);
//表单验证
const validatorRules = {
const validatorRules = reactive({
<#include "/common/validatorRulesTemplate/native/vue3MainNative.ftl">
};
});
const { resetFields, validate, validateInfos } = useForm(formData, validatorRules, { immediate: false });
// 表单禁用
@ -218,8 +223,6 @@
<style lang="less" scoped>
.antd-modal-form {
height: 500px !important;
overflow-y: auto;
padding: 14px;
}
</style>

View File

@ -1,12 +1,13 @@
<template>
<a-modal :title="title" :width="width" :visible="visible" @ok="handleOk" :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }" @cancel="handleCancel" cancelText="关闭">
<j-modal :title="title" :width="width" :visible="visible" @ok="handleOk" :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }" @cancel="handleCancel" cancelText="关闭">
<${entityName}Form ref="registerForm" @ok="submitCallback" :formDisabled="disableSubmit" :formBpm="false"></${entityName}Form>
</a-modal>
</j-modal>
</template>
<script lang="ts" setup>
import { ref, nextTick, defineExpose } from 'vue';
import ${entityName}Form from './${entityName}Form.vue'
import JModal from '/@/components/Modal/src/JModal/JModal.vue';
const title = ref<string>('');
const width = ref<number>(800);
@ -67,9 +68,10 @@
});
</script>
<style>
<style lang="less">
/**隐藏样式-modal确定按钮 */
.jee-hidden {
display: none !important;
}
</style>
<style lang="less" scoped></style>

View File

@ -405,7 +405,7 @@
//流程表单data
formData: {
type: Object,
default: ()=>{},
default: () => ({}),
required: false
},
//表单模式false流程表单 true普通表单

View File

@ -119,7 +119,9 @@
<#list columns as po>
<#if po.isQuery=='Y'>
<#if po.queryMode!='single'>
<#if po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
<#-- update-begin---author:chenrui ---date:20240527 for[TV360X-388]时间范围查询控件---------- -->
<#if po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal' || po.classType=='time'>
<#-- update-end---author:chenrui ---date:20240527 for[TV360X-388]时间范围查询控件---------- -->
['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end']],
</#if>
</#if>

View File

@ -125,11 +125,20 @@ export const searchFormSchema: FormSchema[] = [
},
<#elseif po.classType=='date'>
component: 'DatePicker',
componentProps: {
valueFormat: 'YYYY-MM-DD'
},
<#elseif po.classType=='datetime'>
component: 'DatePicker',
componentProps: {
showTime:true
},
<#elseif po.classType=='datetime'>
component: 'DatePicker',
componentProps: {
showTime:true,
valueFormat: 'YYYY-MM-DD HH:mm:ss'
},
<#elseif po.classType=='pca'>
component: 'JAreaLinkage',
<#elseif po.classType=='popup'>
@ -187,6 +196,10 @@ export const searchFormSchema: FormSchema[] = [
},
<#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
component: 'JRangeNumber',
<#-- update-begin---author:chenrui ---date:20240527 for[TV360X-388]时间范围查询控件---------- -->
<#elseif po.classType=='time'>
component: 'RangeTime',
<#-- update-end---author:chenrui ---date:20240527 for[TV360X-388]时间范围查询控件---------- -->
<#else>
component: 'Input', //TODO 范围查询
</#if>
@ -241,11 +254,14 @@ export const formSchema: FormSchema[] = [
},
<#elseif po.classType =='date'>
component: 'DatePicker',
componentProps: {
valueFormat: 'YYYY-MM-DD'
},
<#elseif po.classType =='datetime'>
component: 'DatePicker',
componentProps: {
showTime:true,
valueFormat: 'YYYY-MM-DD hh:mm:ss'
valueFormat: 'YYYY-MM-DD HH:mm:ss'
},
<#elseif po.classType =='time'>
component: 'TimePicker',
@ -278,11 +294,17 @@ export const formSchema: FormSchema[] = [
},
<#elseif po.classType =='textarea'>
component: 'InputTextArea',
<#elseif po.classType=='list' || po.classType=='radio'>
<#elseif po.classType=='list'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}"
},
<#elseif po.classType=='radio'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}",
type: "radio"
},
<#-- update-begin---author:chenrui ---date:20231228 for[QQYUN-7583] Vue3风格表单页面多选控件渲染成了下拉多选---------- -->
<#elseif po.classType=='list_multi'>
component: 'JSelectMultiple',
@ -363,7 +385,7 @@ export const formSchema: FormSchema[] = [
{...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema)[0]},
<#-- 6到16位数字 -->
<#elseif fieldValidType == 'n6-16'>
{ pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
{ pattern: /^\d{6,16}$|^(?=\d+\.\d+)[\d.]{7,17}$/, message: '请输入6到16位数字!'},
<#-- 6到16位任意字符 -->
<#elseif fieldValidType == '*6-16'>
{ pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},

View File

@ -527,7 +527,9 @@
* @param value
*/
function handleFormJoinChange(key, value) {
queryParam[key] = value.join(',');
if (typeof value != 'string') {
queryParam[key] = value.join(',');
}
}
</#if>
@ -565,20 +567,5 @@
</script>
<style lang="less" scoped>
.jeecg-basic-table-form-container {
padding: 0;
.table-page-search-submitButtons {
display: block;
margin-bottom: 24px;
white-space: nowrap;
}
.query-group-cust{
min-width: 100px !important;
}
.query-group-split-cust{
width: 30px;
display: inline-block;
text-align: center
}
}
<#include "/common/form/native/vueNativeSearchStyle.ftl">
</style>

View File

@ -127,6 +127,9 @@ export const searchFormSchema: FormSchema[] = [
},
<#elseif po.classType=='date'>
component: 'DatePicker',
componentProps: {
valueFormat: 'YYYY-MM-DD'
},
<#elseif po.classType=='datetime'>
component: 'DatePicker',
componentProps: {
@ -231,11 +234,14 @@ export const formSchema: FormSchema[] = [
},
<#elseif po.classType =='date'>
component: 'DatePicker',
componentProps: {
valueFormat: 'YYYY-MM-DD'
},
<#elseif po.classType =='datetime'>
component: 'DatePicker',
componentProps: {
showTime:true,
valueFormat: 'YYYY-MM-DD hh:mm:ss'
valueFormat: 'YYYY-MM-DD HH:mm:ss'
},
<#elseif po.classType =='time'>
component: 'TimePicker',
@ -268,10 +274,16 @@ export const formSchema: FormSchema[] = [
},
<#elseif po.classType =='textarea'>
component: 'InputTextArea',
<#elseif po.classType=='list' || po.classType=='radio'>
<#elseif po.classType=='list'>
component: 'JDictSelectTag',
componentProps:{
dictCode: "${form_field_dictCode}"
dictCode:"${form_field_dictCode}"
},
<#elseif po.classType=='radio'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}",
type: "radio"
},
<#-- update-begin---author:chenrui ---date:20231228 for[QQYUN-7583] Vue3风格表单页面多选控件渲染成了下拉多选---------- -->
<#elseif po.classType=='list_multi'>
@ -353,7 +365,7 @@ export const formSchema: FormSchema[] = [
{...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema)[0]},
<#-- 6到16位数字 -->
<#elseif fieldValidType == 'n6-16'>
{ pattern: /^\d{6,16}$/, message: '请输入6到16位数字!' },
{ pattern: /^\d{6,16}$|^(?=\d+\.\d+)[\d.]{7,17}$/, message: '请输入6到16位数字!' },
<#-- 6到16位任意字符 -->
<#elseif fieldValidType == '*6-16'>
{ pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!' },

View File

@ -1,8 +1,10 @@
<#include "/common/utils.ftl">
<template>
<a-spin :spinning="confirmLoading">
<a-form ref="formRef" class="antd-modal-form" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-row>
<JFormContainer :disabled="disabled">
<template #detail>
<a-form ref="formRef" class="antd-modal-form" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-row>
<#assign need_category = false>
<#assign bpm_flag=false>
<#assign need_pca = false>
@ -37,32 +39,34 @@
<#if po.isShow == 'Y' && po.fieldValidType?default("") == 'only'>
<#assign hasOnlyValidate = true>
</#if>
<#if po.fieldDbName == tableVo.extendParams.pidField>
<#assign pidFieldName = po.fieldName>
<#assign need_select_tree = true>
<a-col :span="${form_span}">
<a-form-item label="父级节点" v-bind="validateInfos.${autoStringSuffixForModel(po)}">
<j-tree-select
placeholder="请选择${po.filedComment}"
v-model:value="formData.${po.fieldName}"
dict="${tableVo.tableName},${tableVo.extendParams.textField},id"
pidField="${tableVo.extendParams.pidField}"
pidValue="0"
hasChildField="${tableVo.extendParams.hasChildren}"
<#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>>
</j-tree-select>
</a-form-item>
</a-col>
</#if>
<#if po.fieldDbName == tableVo.extendParams.pidField>
<#assign pidFieldName = po.fieldName>
<#assign need_select_tree = true>
<a-col :span="${form_span}">
<a-form-item label="父级节点" v-bind="validateInfos.${autoStringSuffixForModel(po)}">
<j-tree-select
placeholder="请选择${po.filedComment}"
v-model:value="formData.${po.fieldName}"
dict="${tableVo.tableName},${tableVo.extendParams.textField},id"
pidField="${tableVo.extendParams.pidField}"
pidValue="0"
hasChildField="${tableVo.extendParams.hasChildren}"
<#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>>
</j-tree-select>
</a-form-item>
</a-col>
</#if>
<#include "/common/form/native/vue3NativeForm.ftl">
</#list>
<#if bpm_flag>
<a-col v-if="showFlowSubmitButton" :span="24" style="width: 100%;text-align: center;">
<a-button preIcon="ant-design:check-outlined" style="width: 126px" type="primary" @click="submitForm">提 交</a-button>
</a-col>
</#if>
</a-row>
</a-form>
<#if bpm_flag>
<a-col v-if="showFlowSubmitButton" :span="24" style="width: 100%;text-align: center;">
<a-button preIcon="ant-design:check-outlined" style="width: 126px" type="primary" @click="submitForm">提 交</a-button>
</a-col>
</#if>
</a-row>
</a-form>
</template>
</JFormContainer>
</a-spin>
</template>
@ -74,6 +78,7 @@
import { getValueType } from '/@/utils';
import {loadTreeData, saveOrUpdateDict} from '../${entityName}.api';
import { Form } from 'ant-design-vue';
import JFormContainer from '/@/components/Form/src/container/JFormContainer.vue';
<#if hasOnlyValidate == true>
import { duplicateValidate } from '/@/utils/helper/validator'
</#if>
@ -95,13 +100,13 @@
const wrapperCol = ref<any>({ xs: { span: 24 }, sm: { span: 16 } });
const confirmLoading = ref<boolean>(false);
//表单验证
const validatorRules = {
const validatorRules = reactive({
<#include "/common/validatorRulesTemplate/native/vue3MainNative.ftl">
};
});
const { resetFields, validate, validateInfos } = useForm(formData, validatorRules, { immediate: false });
const props = defineProps({
formDisabled: { type: Boolean, default: false },
formData: { type: Object, default: ()=>{} },
formData: { type: Object, default: () => ({}) },
formBpm: { type: Boolean, default: true }
});
@ -270,8 +275,6 @@
<style lang="less" scoped>
.antd-modal-form {
height: 500px !important;
overflow-y: auto;
padding: 14px;
}
</style>

View File

@ -1,12 +1,13 @@
<template>
<a-modal :title="title" :width="width" :visible="visible" @ok="handleOk" :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }" @cancel="handleCancel" cancelText="关闭">
<j-modal :title="title" :width="width" :visible="visible" @ok="handleOk" :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }" @cancel="handleCancel" cancelText="关闭">
<${entityName}Form ref="registerForm" @ok="submitCallback" :formDisabled="disableSubmit" :formBpm="false"></${entityName}Form>
</a-modal>
</j-modal>
</template>
<script lang="ts" setup>
import { ref, nextTick, defineExpose } from 'vue';
import ${entityName}Form from './${entityName}Form.vue'
import JModal from '/@/components/Modal/src/JModal/JModal.vue';
const title = ref<string>('');
const width = ref<number>(800);
@ -73,9 +74,10 @@
});
</script>
<style>
<style lang="less">
/**隐藏样式-modal确定按钮 */
.jee-hidden {
display: none !important;
}
</style>
<style lang="less" scoped></style>

View File

@ -1,5 +1,5 @@
<template>
<div>
<div class="p-2 cgformErpList">
<#assign list_need_category=false>
<#assign list_need_pca=false>
<#assign bpm_flag=false>
@ -17,7 +17,8 @@
</#if>
</#list>
<#-- 结束循环 -->
<!--引用表格-->
<div class="content">
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection">
<!--插槽:table标题-->
<template #tableTitle>
@ -70,7 +71,7 @@
</template>
</BasicTable>
<!--子表表格tab-->
<a-tabs defaultActiveKey="1">
<a-tabs defaultActiveKey="1" style="margin: 10px">
<#assign sub_seq=1>
<#list subTables as sub>
<a-tab-pane tab="${sub.ftlDescription}" key="${sub_seq}" <#if sub_seq gt 1>forceRender</#if>>
@ -78,7 +79,8 @@
</a-tab-pane>
<#assign sub_seq=sub_seq+1>
</#list>
</a-tabs>
</a-tabs>
</div>
<!-- 表单区域 -->
<${entityName}Modal @register="registerModal" @success="handleSuccess"></${entityName}Modal>
</div>
@ -125,7 +127,9 @@
<#list columns as po>
<#if po.isQuery=='Y'>
<#if po.queryMode!='single'>
<#if po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
<#-- update-begin---author:chenrui ---date:20240527 for[TV360X-388]时间范围查询控件---------- -->
<#if po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal' || po.classType=='time'>
<#-- update-end---author:chenrui ---date:20240527 for[TV360X-388]时间范围查询控件---------- -->
['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end']],
</#if>
</#if>
@ -335,6 +339,14 @@
</#if>
</script>
<style scoped>
<style lang="less" scoped>
html[data-theme='light'] {
.cgformErpList {
height: 100%;
.content {
background-color: #fff;
height: 100%;
}
}
}
</style>

View File

@ -119,6 +119,9 @@ export const searchFormSchema: FormSchema[] = [
},
<#elseif po.classType=='date'>
component: 'DatePicker',
componentProps: {
valueFormat: 'YYYY-MM-DD'
},
<#elseif po.classType=='datetime'>
component: 'DatePicker',
componentProps: {
@ -161,6 +164,10 @@ export const searchFormSchema: FormSchema[] = [
},
<#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
component: 'JRangeNumber',
<#-- update-begin---author:chenrui ---date:20240527 for[TV360X-388]时间范围查询控件---------- -->
<#elseif po.classType=='time'>
component: 'RangeTime',
<#-- update-end---author:chenrui ---date:20240527 for[TV360X-388]时间范围查询控件---------- -->
<#else>
component: 'Input', //TODO 范围查询
</#if>
@ -208,6 +215,9 @@ export const formSchema: FormSchema[] = [
<#-- update-end-author:taoyan date:2022-6-24 for: VUEN-1190【代码生成】默认值未生成 -->
<#if po.classType =='date'>
component: 'DatePicker',
componentProps: {
valueFormat: 'YYYY-MM-DD'
},
<#elseif po.classType =='datetime'>
component: 'DatePicker',
componentProps: {
@ -245,11 +255,17 @@ export const formSchema: FormSchema[] = [
},
<#elseif po.classType =='textarea'>
component: 'InputTextArea',//TODO 注意string转换问题
<#elseif po.classType=='list' || po.classType=='radio'>
<#elseif po.classType=='list'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}"
},
<#elseif po.classType=='radio'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}",
type: "radio"
},
<#-- update-begin---author:chenrui ---date:20231228 for[QQYUN-7583] Vue3风格表单页面多选控件渲染成了下拉多选---------- -->
<#elseif po.classType=='list_multi'>
component: 'JSelectMultiple',
@ -328,7 +344,7 @@ export const formSchema: FormSchema[] = [
{...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema)[0]},
<#-- 6到16位数字 -->
<#elseif fieldValidType == 'n6-16'>
{ pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
{ pattern: /^\d{6,16}$|^(?=\d+\.\d+)[\d.]{7,17}$/, message: '请输入6到16位数字!'},
<#-- 6到16位任意字符 -->
<#elseif fieldValidType == '*6-16'>
{ pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
@ -488,6 +504,9 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
<#-- update-end-author:taoyan date:2022-6-24 for: VUEN-1190【代码生成】默认值未生成 -->
<#if po.classType =='date'>
component: 'DatePicker',
componentProps: {
valueFormat: 'YYYY-MM-DD'
},
<#elseif po.classType =='datetime'>
component: 'DatePicker',
componentProps: {
@ -608,7 +627,7 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
{...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema)[0]},
<#-- 6到16位数字 -->
<#elseif fieldValidType == 'n6-16'>
{ pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
{ pattern: /^\d{6,16}$|^(?=\d+\.\d+)[\d.]{7,17}$/, message: '请输入6到16位数字!'},
<#-- 6到16位任意字符 -->
<#elseif fieldValidType == '*6-16'>
{ pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},

View File

@ -1,6 +1,6 @@
<#include "/common/utils.ftl">
<template>
<div class="p-2">
<div class="p-2 erpNativeList">
<#assign query_field_no=0>
<#assign need_category = false>
<#assign need_pca = false>
@ -85,6 +85,7 @@
</a-form>
</div>
<#-- 结束循环 -->
<div class="content">
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection">
<!--插槽:table标题-->
@ -139,7 +140,7 @@
</template>
</BasicTable>
<!--子表表格tab-->
<a-tabs defaultActiveKey="1">
<a-tabs defaultActiveKey="1" style="margin: 10px">
<#assign sub_seq=1>
<#list subTables as sub>
<a-tab-pane tab="${sub.ftlDescription}" key="${sub_seq}" <#if sub_seq gt 1>forceRender</#if>>
@ -148,6 +149,7 @@
<#assign sub_seq=sub_seq+1>
</#list>
</a-tabs>
</div>
<!-- 表单区域 -->
<${entityName}Modal ref="registerModal" @success="handleSuccess" />
</div>
@ -423,25 +425,19 @@
* @param value
*/
function handleFormJoinChange(key, value) {
queryParam[key] = value.join(',');
if (typeof value != 'string') {
queryParam[key] = value.join(',');
}
}
</#if>
</script>
<style lang="less" scoped>
.jeecg-basic-table-form-container {
padding: 0;
.table-page-search-submitButtons {
display: block;
margin-bottom: 24px;
white-space: nowrap;
}
.query-group-cust{
min-width: 100px !important;
}
.query-group-split-cust{
width: 30px;
display: inline-block;
text-align: center
}
<#include "/common/form/native/vueNativeSearchStyle.ftl">
.erpNativeList {
height: 100%;
.content {
background-color: #fff;
height: 100%;
}
}
</style>

View File

@ -119,6 +119,9 @@ export const searchFormSchema: FormSchema[] = [
},
<#elseif po.classType=='date'>
component: 'DatePicker',
componentProps: {
valueFormat: 'YYYY-MM-DD'
},
<#elseif po.classType=='datetime'>
component: 'DatePicker',
componentProps: {
@ -210,6 +213,9 @@ export const formSchema: FormSchema[] = [
component: 'DatePicker',
<#elseif po.classType =='datetime'>
component: 'DatePicker',
componentProps: {
valueFormat: 'YYYY-MM-DD'
},
componentProps: {
showTime:true,
valueFormat: 'YYYY-MM-DD HH:mm:ss'
@ -245,11 +251,17 @@ export const formSchema: FormSchema[] = [
},
<#elseif po.classType =='textarea'>
component: 'InputTextArea',//TODO 注意string转换问题
<#elseif po.classType=='list' || po.classType=='radio'>
<#elseif po.classType=='list'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}"
},
dictCode:"${form_field_dictCode}"
},
<#elseif po.classType=='radio'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}",
type: "radio"
},
<#-- update-begin---author:chenrui ---date:20231228 for[QQYUN-7583] Vue3风格表单页面多选控件渲染成了下拉多选---------- -->
<#elseif po.classType=='list_multi'>
component: 'JSelectMultiple',
@ -328,7 +340,7 @@ export const formSchema: FormSchema[] = [
{...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema)[0]},
<#-- 6到16位数字 -->
<#elseif fieldValidType == 'n6-16'>
{ pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
{ pattern: /^\d{6,16}$|^(?=\d+\.\d+)[\d.]{7,17}$/, message: '请输入6到16位数字!'},
<#-- 6到16位任意字符 -->
<#elseif fieldValidType == '*6-16'>
{ pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
@ -488,6 +500,9 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
<#-- update-end-author:taoyan date:2022-6-24 for: VUEN-1190【代码生成】默认值未生成 -->
<#if po.classType =='date'>
component: 'DatePicker',
componentProps: {
valueFormat: 'YYYY-MM-DD'
},
<#elseif po.classType =='datetime'>
component: 'DatePicker',
componentProps: {
@ -525,11 +540,17 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
},
<#elseif po.classType =='textarea'>
component: 'InputTextArea',
<#elseif po.classType=='list' || po.classType=='radio'>
<#elseif po.classType=='list'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}"
},
dictCode:"${form_field_dictCode}"
},
<#elseif po.classType=='radio'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}",
type: "radio"
},
<#-- update-begin---author:chenrui ---date:20231228 for[QQYUN-7583]Vue3风格表单页面多选控件渲染成了下拉多选---------- -->
<#elseif po.classType=='list_multi'>
component: 'JSelectMultiple',
@ -608,7 +629,7 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
{...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema)[0]},
<#-- 6到16位数字 -->
<#elseif fieldValidType == 'n6-16'>
{ pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
{ pattern: /^\d{6,16}$|^(?=\d+\.\d+)[\d.]{7,17}$/, message: '请输入6到16位数字!'},
<#-- 6到16位任意字符 -->
<#elseif fieldValidType == '*6-16'>
{ pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},

View File

@ -1,5 +1,6 @@
<#list subTables as sub>
#segment#${sub.entityName}List.vue
<#assign need_pca = false>
<template>
<div class="p-2">
<#-- 结束循环 -->
@ -43,6 +44,7 @@
<a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
</#if>
<#if po.classType=='pca'>
<#assign need_pca = true>
<!--省市区字段回显插槽-->
{{ getAreaTextByCode(text) }}
</#if>
@ -66,6 +68,9 @@
import { isEmpty } from "/@/utils/is";
import { useMessage } from '/@/hooks/web/useMessage';
import { downloadFile } from '/@/utils/common/renderUtils';
<#if need_pca>
import { getAreaTextByCode } from '/@/components/Form/src/utils/Area';
</#if>
const toggleSearchStatus = ref<boolean>(false);
//接收主表id

View File

@ -1,8 +1,10 @@
<#include "/common/utils.ftl">
<template>
<a-spin :spinning="confirmLoading">
<a-form ref="formRef" class="antd-modal-form" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-row>
<JFormContainer :disabled="disabled">
<template #detail>
<a-form ref="formRef" class="antd-modal-form" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-row>
<#assign need_category = false>
<#assign bpm_flag=false>
<#assign need_pca = false>
@ -38,13 +40,15 @@
</#if>
<#include "/common/form/native/vue3NativeForm.ftl">
</#list>
<#if bpm_flag>
<a-col v-if="showFlowSubmitButton" :span="24" style="width: 100%;text-align: center;">
<a-button preIcon="ant-design:check-outlined" style="width: 126px" type="primary" @click="submitForm">提 交</a-button>
</a-col>
</#if>
</a-row>
</a-form>
<#if bpm_flag>
<a-col v-if="showFlowSubmitButton" :span="24" style="width: 100%;text-align: center;">
<a-button preIcon="ant-design:check-outlined" style="width: 126px" type="primary" @click="submitForm">提 交</a-button>
</a-col>
</#if>
</a-row>
</a-form>
</template>
</JFormContainer>
</a-spin>
</template>
@ -56,13 +60,14 @@
import { getValueType } from '/@/utils';
import { saveOrUpdate } from '../${entityName}.api';
import { Form } from 'ant-design-vue';
import JFormContainer from '/@/components/Form/src/container/JFormContainer.vue';
<#if hasOnlyValidate == true>
import { duplicateValidate } from '/@/utils/helper/validator'
</#if>
const props = defineProps({
formDisabled: { type: Boolean, default: false },
formData: { type: Object, default: ()=>{} },
formData: { type: Object, default: () => ({}) },
formBpm: { type: Boolean, default: true }
});
const useForm = Form.useForm;
@ -76,9 +81,9 @@
const wrapperCol = ref<any>({ xs: { span: 24 }, sm: { span: 16 } });
const confirmLoading = ref<boolean>(false);
//表单验证
const validatorRules = {
const validatorRules = reactive({
<#include "/common/validatorRulesTemplate/native/vue3MainNative.ftl">
};
});
const { resetFields, validate, validateInfos } = useForm(formData, validatorRules, { immediate: false });
const formRef = ref();
// 表单禁用
@ -217,8 +222,6 @@
<style lang="less" scoped>
.antd-modal-form {
height: 500px !important;
overflow-y: auto;
padding: 14px;
}
</style>

View File

@ -1,12 +1,13 @@
<template>
<a-modal :title="title" :width="width" :visible="visible" @ok="handleOk" :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }" @cancel="handleCancel" cancelText="关闭">
<j-modal :title="title" :width="width" :visible="visible" @ok="handleOk" :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }" @cancel="handleCancel" cancelText="关闭">
<${entityName}Form ref="registerForm" @ok="submitCallback" :formDisabled="disableSubmit" :formBpm="false"></${entityName}Form>
</a-modal>
</j-modal>
</template>
<script lang="ts" setup>
import { ref, nextTick, defineExpose } from 'vue';
import ${entityName}Form from './${entityName}Form.vue'
import JModal from '/@/components/Modal/src/JModal/JModal.vue';
const title = ref<string>('');
const width = ref<number>(800);
@ -72,4 +73,5 @@
.jee-hidden {
display: none !important;
}
</style>
</style>
<style lang="less" scoped></style>

Some files were not shown because too many files have changed in this diff Show More