mirror of
https://github.com/jeecgboot/JeecgBoot.git
synced 2025-12-08 17:12:28 +08:00
【v3.8.3开源版本发布】
Merge remote-tracking branch 'origin/master' into springboot3 # Conflicts: # README.md # jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/exception/JeecgBootExceptionHandler.java # jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/FileDownloadUtils.java # jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/SysUserAgentController.java # jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/ISysDepartService.java # jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/ISysUserService.java # jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/impl/SysAnnouncementServiceImpl.java # jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-sentinel/pom.xml # jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-test/jeecg-cloud-test-seata/pom.xml # jeecg-boot/pom.xml
This commit is contained in:
@ -2,13 +2,13 @@
|
||||
JeecgBoot AI低代码平台
|
||||
===============
|
||||
|
||||
当前最新版本: 3.8.2(发布日期:2025-08-04)
|
||||
当前最新版本: 3.8.3(发布日期:2025-09-22)
|
||||
|
||||
|
||||
[](https://github.com/jeecgboot/JeecgBoot/blob/master/LICENSE)
|
||||
[](https://jeecg.com)
|
||||
[](https://jeecg.blog.csdn.net)
|
||||
[](https://github.com/jeecgboot/JeecgBoot)
|
||||
[](https://github.com/jeecgboot/JeecgBoot)
|
||||
[](https://github.com/jeecgboot/JeecgBoot)
|
||||
[](https://github.com/jeecgboot/JeecgBoot)
|
||||
|
||||
@ -104,7 +104,7 @@ JeecgBoot平台提供了一套完善的AI应用管理系统模块,是一套类
|
||||
|
||||
为什么选择JeecgBoot?
|
||||
-----------------------------------
|
||||
- 1.采用最新主流前后分离框架(Spring Boot3 + MyBatis + Ant Design4 + Vue3),容易上手;代码生成器依赖性低,灵活的扩展能力,可快速实现二次开发。
|
||||
- 1.采用最新主流前后分离框架(Spring Boot3 + MyBatis + Shiro/SpringAuthorizationServer + Ant Design4 + Vue3),容易上手;代码生成器依赖性低,灵活的扩展能力,可快速实现二次开发。
|
||||
- 2.前端大版本换代,最新版采用 Vue3.0 + TypeScript + Vite6 + Ant Design Vue4 等新技术方案。
|
||||
- 3.支持微服务Spring Cloud Alibaba(Nacos、Gateway、Sentinel、Skywalking),提供简易机制,支持单体和微服务自由切换(这样可以满足各类项目需求)。
|
||||
- 4.开发效率高,支持在线建表和AI建表,提供强大代码生成器,单表、树列表、一对多、一对一等数据模型,增删改查功能一键生成,菜单配置直接使用。
|
||||
|
||||
@ -2,12 +2,12 @@
|
||||
JeecgBoot 低代码开发平台
|
||||
===============
|
||||
|
||||
当前最新版本: 3.8.2(发布日期:2025-08-04)
|
||||
当前最新版本: 3.8.3(发布日期:2025-09-22)
|
||||
|
||||
|
||||
[](https://github.com/zhangdaiscott/jeecg-boot/blob/master/LICENSE)
|
||||
[](http://jeecg.com/aboutusIndex)
|
||||
[](https://github.com/zhangdaiscott/jeecg-boot)
|
||||
[](https://github.com/zhangdaiscott/jeecg-boot)
|
||||
[](https://github.com/zhangdaiscott/jeecg-boot)
|
||||
[](https://github.com/zhangdaiscott/jeecg-boot)
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -1,5 +1,5 @@
|
||||
#
|
||||
# XXL-JOB v2.2.0
|
||||
# XXL-JOB v2.4.0
|
||||
# Copyright (c) 2015-present, xuxueli.
|
||||
|
||||
CREATE database if NOT EXISTS `xxl_job` default character set utf8mb4 collate utf8mb4_general_ci;
|
||||
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -24,8 +24,8 @@ services:
|
||||
|
||||
jeecg-boot-redis:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/jeecgdocker/redis:5.0
|
||||
ports:
|
||||
- 6379:6379
|
||||
# ports:
|
||||
# - 6379:6379
|
||||
restart: always
|
||||
hostname: jeecg-boot-redis
|
||||
container_name: jeecg-boot-redis
|
||||
@ -39,12 +39,26 @@ services:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: vector_db
|
||||
ports:
|
||||
- 5432:5432
|
||||
# ports:
|
||||
# - 5432:5432
|
||||
restart: always
|
||||
networks:
|
||||
- jeecg-boot
|
||||
|
||||
# jeecg-boot-rabbitmq:
|
||||
# image: rabbitmq:3.7.7-management
|
||||
## ports:
|
||||
## - 5672:5672
|
||||
## - 15672:15672
|
||||
# restart: always
|
||||
# container_name: jeecg-boot-rabbitmq
|
||||
# hostname: jeecg-boot-rabbitmq
|
||||
# environment:
|
||||
# RABBITMQ_DEFAULT_USER: guest
|
||||
# RABBITMQ_DEFAULT_PASS: guest
|
||||
# networks:
|
||||
# - jeecg-boot
|
||||
|
||||
jeecg-boot-system:
|
||||
build:
|
||||
context: ./jeecg-module-system/jeecg-system-start
|
||||
@ -59,6 +73,8 @@ services:
|
||||
- 8080:8080
|
||||
networks:
|
||||
- jeecg-boot
|
||||
volumes:
|
||||
- ./config:/jeecg-boot/config
|
||||
|
||||
networks:
|
||||
jeecg-boot:
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<artifactId>jeecg-boot-parent</artifactId>
|
||||
<version>3.8.2</version>
|
||||
<version>3.8.3</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jeecg-boot-base-core</artifactId>
|
||||
@ -49,16 +49,6 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-undertow</artifactId>
|
||||
</dependency>
|
||||
<!-- websocket -->
|
||||
<dependency>
|
||||
@ -147,7 +137,7 @@
|
||||
<!-- sqlserver-->
|
||||
<dependency>
|
||||
<groupId>com.microsoft.sqlserver</groupId>
|
||||
<artifactId>sqljdbc4</artifactId>
|
||||
<artifactId>mssql-jdbc</artifactId>
|
||||
<version>${sqljdbc4.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
@ -169,13 +159,13 @@
|
||||
<dependency>
|
||||
<groupId>org.jeecgframework</groupId>
|
||||
<artifactId>kingbase8</artifactId>
|
||||
<version>9.0.0</version>
|
||||
<version>${kingbase8.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<!--达梦数据库驱动 版本号1-3-26-2023.07.26-197096-20046-ENT -->
|
||||
<dependency>
|
||||
<groupId>com.dameng</groupId>
|
||||
<artifactId>Dm8JdbcDriver18</artifactId>
|
||||
<artifactId>DmJdbcDriver18</artifactId>
|
||||
<version>${dm8.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@ -303,17 +293,6 @@
|
||||
<dependency>
|
||||
<groupId>org.jeecgframework.boot3</groupId>
|
||||
<artifactId>autopoi-web</artifactId>
|
||||
<version>${autopoi-web.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>xercesImpl</artifactId>
|
||||
<groupId>xerces</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>xerces</groupId>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package org.jeecg.common.api;
|
||||
|
||||
import org.jeecg.common.api.dto.AiragFlowDTO;
|
||||
import org.jeecg.common.system.vo.*;
|
||||
|
||||
import java.util.List;
|
||||
@ -144,4 +145,15 @@ public interface CommonAPI {
|
||||
List<DictModel> translateDictFromTableByKeys(String table, String text, String code, String keys, String dataSource);
|
||||
//update-end---author:chenrui ---date:20231221 for:[issues/#5643]解决分布式下表字典跨库无法查询问题------------
|
||||
|
||||
/**
|
||||
* 16 运行AIRag流程
|
||||
* for [QQYUN-13634]在baseapi里面封装方法,方便其他模块调用
|
||||
*
|
||||
* @param airagFlowDTO
|
||||
* @return 流程执行结果,可能是String或者Map
|
||||
* @author chenrui
|
||||
* @date 2025/9/2 11:43
|
||||
*/
|
||||
Object runAiragFlow(AiragFlowDTO airagFlowDTO);
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,36 @@
|
||||
package org.jeecg.common.api.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 调用AI流程入参
|
||||
* for [QQYUN-13634]在baseapi里面封装方法,方便其他模块调用
|
||||
* @author chenrui
|
||||
* @date 2025/9/2 14:11
|
||||
*/
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public class AiragFlowDTO implements Serializable {
|
||||
|
||||
|
||||
private static final long serialVersionUID = 7431775881170684867L;
|
||||
|
||||
/**
|
||||
* 流程id
|
||||
*/
|
||||
private String flowId;
|
||||
|
||||
|
||||
/**
|
||||
* 输入参数
|
||||
*/
|
||||
private Map<String, Object> inputParams;
|
||||
}
|
||||
@ -642,11 +642,12 @@ public interface CommonConstant {
|
||||
|
||||
|
||||
/**
|
||||
* 自定义首页关联关系(ROLE:表示角色 USER:表示用户)
|
||||
* 自定义首页关联关系(ROLE:表示角色 USER:表示用户 DEFAULT:默认首页)
|
||||
*
|
||||
*/
|
||||
String HOME_RELATION_ROLE = "ROLE";
|
||||
String HOME_RELATION_USER = "USER";
|
||||
String HOME_RELATION_DEFAULT = "DEFAULT";
|
||||
|
||||
/**
|
||||
* 是否置顶(0否 1是)
|
||||
@ -659,4 +660,53 @@ public interface CommonConstant {
|
||||
String FLOW_FOCUS_NOTICE_PREFIX = "flow:runtimeData:focus:notice:";
|
||||
//任务缓办时间缓存前缀
|
||||
String FLOW_TASK_DELAY_PREFIX = "flow:runtimeData:task:delay:";
|
||||
/**
|
||||
* 用户代理类型:离职:quit 代理:agent
|
||||
*/
|
||||
String USER_AGENT_TYPE_QUIT = "quit";
|
||||
String USER_AGENT_TYPE_AGENT = "agent";
|
||||
/**
|
||||
* 督办流程首节点任务taskKey
|
||||
*/
|
||||
String SUPERVISE_FIRST_TASK_KEY = "Task_1bhxpt0";
|
||||
|
||||
/**
|
||||
* wps模板预览数据缓存前缀
|
||||
*/
|
||||
String EOA_WPS_TEMPLATE_VIEW_DATA ="eoa:wps:templateViewData:";
|
||||
|
||||
/**
|
||||
* wps模板预览版本号缓存前缀
|
||||
*/
|
||||
String EOA_WPS_TEMPLATE_VIEW_VERSION ="eoa:wps:templateViewVersion:";
|
||||
/**
|
||||
* 表单设计器oa新增字段
|
||||
* x_oa_timeout_date:逾期时间
|
||||
* x_oa_archive_status:归档状态
|
||||
*/
|
||||
String X_OA_TIMEOUT_DATE ="x_oa_timeout_date";
|
||||
String X_OA_ARCHIVE_STATUS ="x_oa_archive_status";
|
||||
/**
|
||||
* 流程状态
|
||||
* 待提交: 1
|
||||
* 处理中: 2
|
||||
* 已完成: 3
|
||||
* 已作废: 4
|
||||
* 已挂起: 5
|
||||
*/
|
||||
String BPM_STATUS_1 ="1";
|
||||
String BPM_STATUS_2 ="2";
|
||||
String BPM_STATUS_3 ="3";
|
||||
String BPM_STATUS_4 ="4";
|
||||
String BPM_STATUS_5 ="5";
|
||||
|
||||
/**
|
||||
* 默认租户产品包
|
||||
*/
|
||||
String TENANT_PACK_DEFAULT = "default";
|
||||
|
||||
/**
|
||||
* 部门名称redisKey(全路径)
|
||||
*/
|
||||
String DEPART_NAME_REDIS_KEY_PRE = "sys:cache:departPathName:";
|
||||
}
|
||||
|
||||
@ -0,0 +1,15 @@
|
||||
package org.jeecg.common.constant;
|
||||
|
||||
/**
|
||||
* @Description: 密码常量类
|
||||
*
|
||||
* @author: wangshuai
|
||||
* @date: 2025/8/27 20:10
|
||||
*/
|
||||
public interface PasswordConstant {
|
||||
|
||||
/**
|
||||
* 导入用户默认密码
|
||||
*/
|
||||
String DEFAULT_PASSWORD = "123456";
|
||||
}
|
||||
@ -121,7 +121,7 @@ public class ProvinceCityArea {
|
||||
|
||||
public void getAreaByCode(String code,List<String> ls){
|
||||
for(Area area: areaList){
|
||||
if(area.getId().equals(code)){
|
||||
if(null != area && area.getId().equals(code)){
|
||||
String pid = area.getPid();
|
||||
ls.add(0,area.getText());
|
||||
getAreaByCode(pid,ls);
|
||||
|
||||
@ -0,0 +1,97 @@
|
||||
package org.jeecg.common.constant.enums;
|
||||
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
|
||||
/**
|
||||
* @Description: 部门类型枚举类
|
||||
*
|
||||
* @author: wangshuai
|
||||
* @date: 2025/8/19 21:37
|
||||
*/
|
||||
public enum DepartCategoryEnum {
|
||||
|
||||
DEPART_CATEGORY_COMPANY("部门类型:公司","公司","1"),
|
||||
DEPART_CATEGORY_DEPART("部门类型:部门","部门","2"),
|
||||
DEPART_CATEGORY_POST("部门类型:岗位","岗位","3"),
|
||||
DEPART_CATEGORY_SUB_COMPANY("部门类型:子公司","子公司","4");
|
||||
|
||||
DepartCategoryEnum(String described, String name, String value) {
|
||||
this.value = value;
|
||||
this.name = name;
|
||||
this.described = described;
|
||||
}
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String described;
|
||||
/**
|
||||
* 值
|
||||
*/
|
||||
private String value;
|
||||
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
public String getDescribed() {
|
||||
return described;
|
||||
}
|
||||
|
||||
public void setDescribed(String described) {
|
||||
this.described = described;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据值获取名称
|
||||
*
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public static String getNameByValue(String value){
|
||||
if (oConvertUtils.isEmpty(value)) {
|
||||
return null;
|
||||
}
|
||||
for (DepartCategoryEnum val : values()) {
|
||||
if (val.getValue().equals(value)) {
|
||||
return val.getName();
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据名称获取值
|
||||
*
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public static String getValueByName(String name){
|
||||
if (oConvertUtils.isEmpty(name)) {
|
||||
return null;
|
||||
}
|
||||
for (DepartCategoryEnum val : values()) {
|
||||
if (val.getName().equals(name)) {
|
||||
return val.getValue();
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@ -8,18 +8,27 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
*
|
||||
* @author: jeecg-boot
|
||||
*/
|
||||
@EnumDict("messageType")
|
||||
public enum MessageTypeEnum {
|
||||
|
||||
/** 系统消息 */
|
||||
/**
|
||||
* 系统消息
|
||||
*/
|
||||
XT("system", "系统消息"),
|
||||
/** 邮件消息 */
|
||||
/**
|
||||
* 邮件消息
|
||||
*/
|
||||
YJ("email", "邮件消息"),
|
||||
/** 钉钉消息 */
|
||||
/**
|
||||
* 钉钉消息
|
||||
*/
|
||||
DD("dingtalk", "钉钉消息"),
|
||||
/** 企业微信 */
|
||||
/**
|
||||
* 企业微信
|
||||
*/
|
||||
QYWX("wechat_enterprise", "企业微信");
|
||||
|
||||
MessageTypeEnum(String type, String note) {
|
||||
@ -56,6 +65,7 @@ public enum MessageTypeEnum {
|
||||
|
||||
/**
|
||||
* 获取字典数据
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static List<DictModel> getDictList() {
|
||||
|
||||
@ -14,7 +14,16 @@ public enum NoticeTypeEnum {
|
||||
NOTICE_TYPE_PLAN("日程消息","plan"),
|
||||
//暂时没用到
|
||||
NOTICE_TYPE_MEETING("会议消息","meeting"),
|
||||
NOTICE_TYPE_SYSTEM("系统消息","system");
|
||||
NOTICE_TYPE_SYSTEM("系统消息","system"),
|
||||
/**
|
||||
* 协同工作
|
||||
* for [JHHB-136]【vue3】协同工作系统消息需要添加一个类型
|
||||
*/
|
||||
NOTICE_TYPE_COLLABORATION("协同工作", "collab"),
|
||||
/**
|
||||
* 督办
|
||||
*/
|
||||
NOTICE_TYPE_SUPERVISE("督办管理", "supe");
|
||||
|
||||
/**
|
||||
* 文件类型名称
|
||||
|
||||
@ -0,0 +1,180 @@
|
||||
package org.jeecg.common.constant.enums;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 职级枚举类
|
||||
*
|
||||
* 注意:此枚举仅适用于天津临港控股OA项目,职级的名称和等级均为写死(需要与数据库配置一致)
|
||||
* @date 2025-08-26
|
||||
* @author scott
|
||||
*/
|
||||
public enum PositionLevelEnum {
|
||||
|
||||
// 领导层级(等级1-3)
|
||||
CHAIRMAN("董事长", 1, PositionType.LEADER),
|
||||
GENERAL_MANAGER("总经理", 2, PositionType.LEADER),
|
||||
VICE_GENERAL_MANAGER("副总经理", 3, PositionType.LEADER),
|
||||
|
||||
// 职员层级(等级4-6)
|
||||
MINISTER("部长", 4, PositionType.STAFF),
|
||||
VICE_MINISTER("副部长", 5, PositionType.STAFF),
|
||||
STAFF("职员", 6, PositionType.STAFF);
|
||||
|
||||
private final String name;
|
||||
private final int level;
|
||||
private final PositionType type;
|
||||
|
||||
PositionLevelEnum(String name, int level, PositionType type) {
|
||||
this.name = name;
|
||||
this.level = level;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public PositionType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* 职级类型枚举
|
||||
*/
|
||||
public enum PositionType {
|
||||
STAFF("职员层级"),
|
||||
LEADER("领导层级");
|
||||
|
||||
private final String desc;
|
||||
|
||||
PositionType(String desc) {
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
return desc;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据职级名称获取枚举
|
||||
* @param name 职级名称
|
||||
* @return 职级枚举
|
||||
*/
|
||||
public static PositionLevelEnum getByName(String name) {
|
||||
for (PositionLevelEnum position : values()) {
|
||||
if (position.getName().equals(name)) {
|
||||
return position;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据职级等级获取枚举
|
||||
* @param level 职级等级
|
||||
* @return 职级枚举
|
||||
*/
|
||||
public static PositionLevelEnum getByLevel(int level) {
|
||||
for (PositionLevelEnum position : values()) {
|
||||
if (position.getLevel() == level) {
|
||||
return position;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据职级名称判断是否为职员层级
|
||||
* @param name 职级名称
|
||||
* @return true-职员层级,false-非职员层级
|
||||
*/
|
||||
public static boolean isStaffLevel(String name) {
|
||||
PositionLevelEnum position = getByName(name);
|
||||
return position != null && position.getType() == PositionType.STAFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据职级名称判断是否为领导层级
|
||||
* @param name 职级名称
|
||||
* @return true-领导层级,false-非领导层级
|
||||
*/
|
||||
public static boolean isLeaderLevel(String name) {
|
||||
PositionLevelEnum position = getByName(name);
|
||||
return position != null && position.getType() == PositionType.LEADER;
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较两个职级的等级高低
|
||||
* @param name1 职级名称1
|
||||
* @param name2 职级名称2
|
||||
* @return 正数表示name1等级更高,负数表示name2等级更高,0表示等级相同
|
||||
*/
|
||||
public static int compareLevel(String name1, String name2) {
|
||||
PositionLevelEnum pos1 = getByName(name1);
|
||||
PositionLevelEnum pos2 = getByName(name2);
|
||||
|
||||
if (pos1 == null || pos2 == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 等级数字越小代表职级越高
|
||||
return pos2.getLevel() - pos1.getLevel();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为更高等级
|
||||
* @param currentName 当前职级名称
|
||||
* @param targetName 目标职级名称
|
||||
* @return true-目标职级更高,false-目标职级不高于当前职级
|
||||
*/
|
||||
public static boolean isHigherLevel(String currentName, String targetName) {
|
||||
return compareLevel(targetName, currentName) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有职员层级名称
|
||||
* @return 职员层级名称列表
|
||||
*/
|
||||
public static List<String> getStaffLevelNames() {
|
||||
return Arrays.asList(MINISTER.getName(), VICE_MINISTER.getName(), STAFF.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有领导层级名称
|
||||
* @return 领导层级名称列表
|
||||
*/
|
||||
public static List<String> getLeaderLevelNames() {
|
||||
return Arrays.asList(CHAIRMAN.getName(), GENERAL_MANAGER.getName(), VICE_GENERAL_MANAGER.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有职级名称(按等级排序)
|
||||
* @return 所有职级名称列表
|
||||
*/
|
||||
public static List<String> getAllPositionNames() {
|
||||
return Arrays.asList(
|
||||
CHAIRMAN.getName(), GENERAL_MANAGER.getName(), VICE_GENERAL_MANAGER.getName(),
|
||||
MINISTER.getName(), VICE_MINISTER.getName(), STAFF.getName()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定等级范围的职级
|
||||
* @param minLevel 最小等级
|
||||
* @param maxLevel 最大等级
|
||||
* @return 职级名称列表
|
||||
*/
|
||||
public static List<String> getPositionsByLevelRange(int minLevel, int maxLevel) {
|
||||
return Arrays.stream(values())
|
||||
.filter(p -> p.getLevel() >= minLevel && p.getLevel() <= maxLevel)
|
||||
.map(PositionLevelEnum::getName)
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
}
|
||||
@ -23,7 +23,25 @@ public enum SysAnnmentTypeEnum {
|
||||
/**
|
||||
* 邀请用户跳转到个人设置
|
||||
*/
|
||||
TENANT_INVITE("tenant_invite", "url", "/system/usersetting");
|
||||
TENANT_INVITE("tenant_invite", "url", "/system/usersetting"),
|
||||
/**
|
||||
* 协同工作-待办通知
|
||||
* for [JHHB-136]【vue3】协同工作系统消息需要添加一个类型
|
||||
*/
|
||||
EOA_CO_NOTIFY("eoa_co_notify", "url", "/collaboration/pending"),
|
||||
/**
|
||||
* 协同工作-催办通知
|
||||
* for [JHHB-136]【vue3】协同工作系统消息需要添加一个类型
|
||||
*/
|
||||
EOA_CO_REMIND("eoa_co_remind", "url", "/collaboration/pending"),
|
||||
/**
|
||||
* 督办管理-催办
|
||||
*/
|
||||
EOA_SUP_REMIND("eoa_sup_remind", "url", "/superivse/list"),
|
||||
/**
|
||||
* 督办管理-通知
|
||||
*/
|
||||
EOA_SUP_NOTIFY("eoa_sup_notify", "url", "/superivse/list");
|
||||
|
||||
/**
|
||||
* 业务类型(email:邮件 bpm:流程)
|
||||
|
||||
@ -3,7 +3,6 @@ package org.jeecg.common.exception;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import io.undertow.server.RequestTooBigException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
@ -180,7 +179,7 @@ public class JeecgBootExceptionHandler {
|
||||
@ExceptionHandler(MultipartException.class)
|
||||
public Result<?> handleMaxUploadSizeExceededException(MultipartException e) {
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof IllegalStateException && cause.getCause() instanceof RequestTooBigException) {
|
||||
if (cause instanceof IllegalStateException) {
|
||||
log.error("文件大小超出限制: {}", cause.getMessage(), e);
|
||||
addSysLog(e);
|
||||
return Result.error("文件大小超出限制, 请压缩或降低文件质量!");
|
||||
@ -291,7 +290,7 @@ public class JeecgBootExceptionHandler {
|
||||
boolean isTooBigException = false;
|
||||
if(e instanceof MultipartException){
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof IllegalStateException && cause.getCause() instanceof RequestTooBigException){
|
||||
if (cause instanceof IllegalStateException){
|
||||
isTooBigException = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,11 @@ import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 将枚举类转化成字典数据
|
||||
*
|
||||
* <<使用说明>>
|
||||
* 1. 枚举类需以 `Enum` 结尾,并且在类上添加 `@EnumDict` 注解。
|
||||
* 2. 需要手动将枚举类所在包路径** 添加到 `org.jeecg.common.system.util.ResourceUtil.BASE_SCAN_PACKAGES` 配置数组中。
|
||||
*
|
||||
* @Author taoYan
|
||||
* @Date 2022/7/8 10:34
|
||||
**/
|
||||
|
||||
@ -18,6 +18,7 @@ import org.jeecgframework.poi.excel.entity.ExportParams;
|
||||
import org.jeecgframework.poi.excel.entity.ImportParams;
|
||||
import org.jeecgframework.poi.excel.entity.enmus.ExcelType;
|
||||
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
|
||||
import org.jeecgframework.poi.handler.inter.IExcelExportServer;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.multipart.MultipartHttpServletRequest;
|
||||
@ -127,6 +128,53 @@ public class JeecgController<T, S extends IService<T>> {
|
||||
return mv;
|
||||
}
|
||||
|
||||
/**
|
||||
* 大数据导出
|
||||
* @param request
|
||||
* @param object
|
||||
* @param clazz
|
||||
* @param title
|
||||
* @param pageSize 每次查询的数据量
|
||||
* @return
|
||||
* @author chenrui
|
||||
* @date 2025/8/11 16:11
|
||||
*/
|
||||
protected ModelAndView exportXlsForBigData(HttpServletRequest request, T object, Class<T> clazz, String title,Integer pageSize) {
|
||||
// 组装查询条件
|
||||
QueryWrapper<T> queryWrapper = QueryGenerator.initQueryWrapper(object, request.getParameterMap());
|
||||
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
|
||||
// 计算分页数
|
||||
double total = service.count();
|
||||
int count = (int) Math.ceil(total / pageSize);
|
||||
// 过滤选中数据
|
||||
String selections = request.getParameter("selections");
|
||||
if (oConvertUtils.isNotEmpty(selections)) {
|
||||
List<String> selectionList = Arrays.asList(selections.split(","));
|
||||
queryWrapper.in("id", selectionList);
|
||||
}
|
||||
|
||||
// 定义IExcelExportServer
|
||||
IExcelExportServer excelExportServer = (queryParams, pageNum) -> {
|
||||
if (pageNum > count) {
|
||||
return null;
|
||||
}
|
||||
Page<T> page = new Page<T>(pageNum, pageSize);
|
||||
IPage<T> pageList = service.page(page, (QueryWrapper<T>) queryParams);
|
||||
return new ArrayList<>(pageList.getRecords());
|
||||
};
|
||||
|
||||
// AutoPoi 导出Excel
|
||||
ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
|
||||
//此处设置的filename无效 ,前端会重更新设置一下
|
||||
mv.addObject(NormalExcelConstants.FILE_NAME, title);
|
||||
mv.addObject(NormalExcelConstants.CLASS, clazz);
|
||||
ExportParams exportParams = new ExportParams(title + "报表", "导出人:" + sysUser.getRealname(), title, jeecgBaseConfig.getPath().getUpload());
|
||||
mv.addObject(NormalExcelConstants.PARAMS, exportParams);
|
||||
mv.addObject(NormalExcelConstants.EXPORT_SERVER, excelExportServer);
|
||||
mv.addObject(NormalExcelConstants.QUERY_PARAMS, queryWrapper);
|
||||
return mv;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据权限导出excel,传入导出字段参数
|
||||
|
||||
@ -414,9 +414,11 @@ public class QueryGenerator {
|
||||
}
|
||||
// update-begin-author:sunjianlei date:20220119 for: 【JTC-573】 过滤空条件查询,防止 sql 拼接多余的 and
|
||||
List<QueryCondition> filterConditions = conditions.stream().filter(
|
||||
rule -> oConvertUtils.isNotEmpty(rule.getField())
|
||||
rule -> (oConvertUtils.isNotEmpty(rule.getField())
|
||||
&& oConvertUtils.isNotEmpty(rule.getRule())
|
||||
&& oConvertUtils.isNotEmpty(rule.getVal())
|
||||
)
|
||||
|| "empty".equals(rule.getRule())
|
||||
).collect(Collectors.toList());
|
||||
if (filterConditions.size() == 0) {
|
||||
return;
|
||||
@ -427,9 +429,12 @@ public class QueryGenerator {
|
||||
queryWrapper.and(andWrapper -> {
|
||||
for (int i = 0; i < filterConditions.size(); i++) {
|
||||
QueryCondition rule = filterConditions.get(i);
|
||||
if (oConvertUtils.isNotEmpty(rule.getField())
|
||||
&& oConvertUtils.isNotEmpty(rule.getRule())
|
||||
&& oConvertUtils.isNotEmpty(rule.getVal())) {
|
||||
if (
|
||||
(
|
||||
oConvertUtils.isNotEmpty(rule.getField()) && oConvertUtils.isNotEmpty(rule.getRule()) && oConvertUtils.isNotEmpty(rule.getVal())
|
||||
)
|
||||
|| "empty".equals(rule.getRule())
|
||||
) {
|
||||
|
||||
log.debug("SuperQuery ==> " + rule.toString());
|
||||
|
||||
@ -716,7 +721,11 @@ public class QueryGenerator {
|
||||
* @param value 查询条件值
|
||||
*/
|
||||
public static void addEasyQuery(QueryWrapper<?> queryWrapper, String name, QueryRuleEnum rule, Object value) {
|
||||
if (name==null || value == null || rule == null || oConvertUtils.isEmpty(value)) {
|
||||
if (
|
||||
(
|
||||
name==null || value == null || rule == null || oConvertUtils.isEmpty(value)
|
||||
)
|
||||
&& !QueryRuleEnum.EMPTY.equals(rule)) {
|
||||
return;
|
||||
}
|
||||
name = oConvertUtils.camelToUnderline(name);
|
||||
@ -728,6 +737,9 @@ public class QueryGenerator {
|
||||
case GE:
|
||||
queryWrapper.ge(name, value);
|
||||
break;
|
||||
case EMPTY:
|
||||
queryWrapper.isNull(name);
|
||||
break;
|
||||
case LT:
|
||||
queryWrapper.lt(name, value);
|
||||
break;
|
||||
|
||||
@ -51,20 +51,20 @@ public class JwtUtil {
|
||||
* @param code
|
||||
* @param errorMsg
|
||||
*/
|
||||
public static void responseError(ServletResponse response, Integer code, String errorMsg) {
|
||||
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
|
||||
// issues/I4YH95浏览器显示乱码问题
|
||||
httpServletResponse.setHeader("Content-type", "text/html;charset=UTF-8");
|
||||
public static void responseError(HttpServletResponse response, Integer code, String errorMsg) {
|
||||
try {
|
||||
Result jsonResult = new Result(code, errorMsg);
|
||||
jsonResult.setSuccess(false);
|
||||
OutputStream os = null;
|
||||
try {
|
||||
os = httpServletResponse.getOutputStream();
|
||||
httpServletResponse.setCharacterEncoding("UTF-8");
|
||||
httpServletResponse.setStatus(code);
|
||||
os.write(new ObjectMapper().writeValueAsString(jsonResult).getBytes("UTF-8"));
|
||||
os.flush();
|
||||
os.close();
|
||||
|
||||
// 设置响应头和内容类型
|
||||
response.setStatus(code);
|
||||
response.setHeader("Content-type", "text/html;charset=UTF-8");
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
// 使用 ObjectMapper 序列化为 JSON 字符串
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
String json = objectMapper.writeValueAsString(jsonResult);
|
||||
response.getWriter().write(json);
|
||||
response.getWriter().flush();
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
@ -101,7 +101,7 @@ public class JwtUtil {
|
||||
DecodedJWT jwt = JWT.decode(token);
|
||||
return jwt.getClaim("username").asString();
|
||||
} catch (JWTDecodeException e) {
|
||||
log.warn(e.getMessage(), e);
|
||||
log.error(e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,31 +13,33 @@ import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 资源加载工具类
|
||||
* 枚举字典数据 资源加载工具类
|
||||
*
|
||||
* @Author taoYan
|
||||
* @Date 2022/7/8 10:40
|
||||
**/
|
||||
@Slf4j
|
||||
public class ResourceUtil {
|
||||
|
||||
/**
|
||||
* 多个包扫描根路径
|
||||
*
|
||||
* 之所以让用户手工配置扫描路径,是为了避免不必要的类加载开销,提升启动性能。
|
||||
* 请务必将所有枚举类所在包路径添加到此配置中。
|
||||
*/
|
||||
private final static String[] BASE_SCAN_PACKAGES = {
|
||||
"org.jeecg.common.constant.enums",
|
||||
"org.jeecg.modules.message.enums"
|
||||
};
|
||||
|
||||
/**
|
||||
* 枚举字典数据
|
||||
*/
|
||||
private final static Map<String, List<DictModel>> enumDictData = new HashMap<>(5);
|
||||
|
||||
/**
|
||||
* 所有java类
|
||||
*/
|
||||
private final static String CLASS_PATTERN="/**/*.class";
|
||||
|
||||
/**
|
||||
* 所有枚举java类
|
||||
*/
|
||||
@ -45,9 +47,9 @@ public class ResourceUtil {
|
||||
private final static String CLASS_ENUM_PATTERN="/**/*Enum.class";
|
||||
|
||||
/**
|
||||
* 包路径 org.jeecg
|
||||
* 初始化状态标识
|
||||
*/
|
||||
private final static String BASE_PACKAGE = "org.jeecg";
|
||||
private static volatile boolean initialized = false;
|
||||
|
||||
/**
|
||||
* 枚举类中获取字典数据的方法名
|
||||
@ -55,56 +57,132 @@ public class ResourceUtil {
|
||||
private final static String METHOD_NAME = "getDictList";
|
||||
|
||||
/**
|
||||
* 获取枚举字典数据
|
||||
* 获取枚举类对应的字典数据 SysDictServiceImpl#queryAllDictItems()
|
||||
* @return
|
||||
*
|
||||
* @return 枚举字典数据
|
||||
*/
|
||||
public static Map<String, List<DictModel>> getEnumDictData() {
|
||||
if(enumDictData.keySet().size()>0){
|
||||
return enumDictData;
|
||||
}
|
||||
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
|
||||
String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(BASE_PACKAGE) + CLASS_ENUM_PATTERN;
|
||||
try {
|
||||
Resource[] resources = resourcePatternResolver.getResources(pattern);
|
||||
MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
|
||||
for (Resource resource : resources) {
|
||||
MetadataReader reader = readerFactory.getMetadataReader(resource);
|
||||
String classname = reader.getClassMetadata().getClassName();
|
||||
Class<?> clazz = Class.forName(classname);
|
||||
EnumDict enumDict = clazz.getAnnotation(EnumDict.class);
|
||||
if (enumDict != null) {
|
||||
EnumDict annotation = clazz.getAnnotation(EnumDict.class);
|
||||
String key = annotation.value();
|
||||
if(oConvertUtils.isNotEmpty(key)){
|
||||
List<DictModel> list = (List<DictModel>) clazz.getDeclaredMethod(METHOD_NAME).invoke(null);
|
||||
enumDictData.put(key, list);
|
||||
if (!initialized) {
|
||||
synchronized (ResourceUtil.class) {
|
||||
if (!initialized) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
log.info("【枚举字典加载】开始初始化枚举字典数据...");
|
||||
|
||||
initEnumDictData();
|
||||
initialized = true;
|
||||
|
||||
long endTime = System.currentTimeMillis();
|
||||
log.info("【枚举字典加载】枚举字典数据初始化完成,共加载 {} 个字典,总耗时: {}ms", enumDictData.size(), endTime - startTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}catch (Exception e){
|
||||
log.error("获取枚举类字典数据异常", e.getMessage());
|
||||
// e.printStackTrace();
|
||||
}
|
||||
return enumDictData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用多包路径扫描方式初始化枚举字典数据
|
||||
*/
|
||||
private static void initEnumDictData() {
|
||||
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
|
||||
|
||||
long scanStartTime = System.currentTimeMillis();
|
||||
List<Resource> allResources = new ArrayList<>();
|
||||
|
||||
// 扫描多个包路径
|
||||
for (String basePackage : BASE_SCAN_PACKAGES) {
|
||||
String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(basePackage) + CLASS_ENUM_PATTERN;
|
||||
|
||||
try {
|
||||
Resource[] resources = resourcePatternResolver.getResources(pattern);
|
||||
allResources.addAll(Arrays.asList(resources));
|
||||
log.debug("【枚举字典加载】扫描包 {} 找到 {} 个枚举类文件", basePackage, resources.length);
|
||||
} catch (Exception e) {
|
||||
log.warn("【枚举字典加载】扫描包 {} 时出现异常: {}", basePackage, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
long scanEndTime = System.currentTimeMillis();
|
||||
log.info("【枚举字典加载】文件扫描完成,总共找到 {} 个枚举类文件,扫描耗时: {}ms", allResources.size(), scanEndTime - scanStartTime);
|
||||
|
||||
MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
|
||||
|
||||
long processStartTime = System.currentTimeMillis();
|
||||
int processedCount = 0;
|
||||
|
||||
for (Resource resource : allResources) {
|
||||
try {
|
||||
MetadataReader reader = readerFactory.getMetadataReader(resource);
|
||||
String classname = reader.getClassMetadata().getClassName();
|
||||
|
||||
// 提前检查是否有@EnumDict注解,避免不必要的Class.forName
|
||||
if (hasEnumDictAnnotation(reader)) {
|
||||
processEnumClass(classname);
|
||||
processedCount++;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("处理资源异常: {} - {}", resource.getFilename(), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
long processEndTime = System.currentTimeMillis();
|
||||
log.info("【枚举字典加载】处理完成,实际处理 {} 个带注解的枚举类,处理耗时: {}ms", processedCount, processEndTime - processStartTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查类是否有EnumDict注解(通过元数据,避免类加载)
|
||||
*/
|
||||
private static boolean hasEnumDictAnnotation(MetadataReader reader) {
|
||||
try {
|
||||
return reader.getAnnotationMetadata().hasAnnotation(EnumDict.class.getName());
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理单个枚举类
|
||||
*/
|
||||
private static void processEnumClass(String classname) {
|
||||
try {
|
||||
Class<?> clazz = Class.forName(classname);
|
||||
EnumDict enumDict = clazz.getAnnotation(EnumDict.class);
|
||||
|
||||
if (enumDict != null) {
|
||||
String key = enumDict.value();
|
||||
if (oConvertUtils.isNotEmpty(key)) {
|
||||
Method method = clazz.getDeclaredMethod(METHOD_NAME);
|
||||
List<DictModel> list = (List<DictModel>) method.invoke(null);
|
||||
enumDictData.put(key, list);
|
||||
log.debug("成功加载枚举字典: {} -> {}", key, classname);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("处理枚举类异常: {} - {}", classname, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于后端字典翻译 SysDictServiceImpl#queryManyDictByKeys(java.util.List, java.util.List)
|
||||
* @param dictCodeList
|
||||
* @param keys
|
||||
* @return
|
||||
*
|
||||
* @param dictCodeList 字典编码列表
|
||||
* @param keys 键值列表
|
||||
* @return 字典数据映射
|
||||
*/
|
||||
public static Map<String, List<DictModel>> queryManyDictByKeys(List<String> dictCodeList, List<String> keys) {
|
||||
if(enumDictData.keySet().size()==0){
|
||||
getEnumDictData();
|
||||
}
|
||||
Map<String, List<DictModel>> enumDict = getEnumDictData();
|
||||
Map<String, List<DictModel>> map = new HashMap<>();
|
||||
for (String code : enumDictData.keySet()) {
|
||||
if(dictCodeList.indexOf(code)>=0){
|
||||
List<DictModel> dictItemList = enumDictData.get(code);
|
||||
|
||||
// 使用更高效的查找方式
|
||||
Set<String> dictCodeSet = new HashSet<>(dictCodeList);
|
||||
Set<String> keySet = new HashSet<>(keys);
|
||||
|
||||
for (String code : enumDict.keySet()) {
|
||||
if (dictCodeSet.contains(code)) {
|
||||
List<DictModel> dictItemList = enumDict.get(code);
|
||||
for (DictModel dm : dictItemList) {
|
||||
String value = dm.getValue();
|
||||
if(keys.indexOf(value)>=0){
|
||||
if (keySet.contains(value)) {
|
||||
List<DictModel> list = new ArrayList<>();
|
||||
list.add(new DictModel(value, dm.getText()));
|
||||
map.put(code, list);
|
||||
@ -116,21 +194,4 @@ public class ResourceUtil {
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实现类
|
||||
*
|
||||
* @param classPath
|
||||
*/
|
||||
public static Object getImplementationClass(String classPath){
|
||||
try {
|
||||
Class<?> aClass = Class.forName(classPath);
|
||||
return SpringContextUtils.getBean(aClass);
|
||||
} catch (ClassNotFoundException e) {
|
||||
log.error("类没有找到",e);
|
||||
return null;
|
||||
} catch (NoSuchBeanDefinitionException e){
|
||||
log.error(classPath + "没有实现",e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -144,4 +144,8 @@ public class LoginUser {
|
||||
/**设备id uniapp推送用*/
|
||||
private String clientId;
|
||||
|
||||
/**
|
||||
* 主岗位
|
||||
*/
|
||||
private String mainDepPostId;
|
||||
}
|
||||
|
||||
@ -25,6 +25,7 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.SQLException;
|
||||
@ -163,6 +164,10 @@ public class CommonUtils {
|
||||
}
|
||||
// 获取文件名
|
||||
String orgName = mf.getOriginalFilename();
|
||||
// 无中文情况下进行转码
|
||||
if (orgName != null && !CommonUtils.ifContainChinese(orgName)) {
|
||||
orgName = new String(orgName.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
|
||||
}
|
||||
orgName = CommonUtils.getFileName(orgName);
|
||||
if(orgName.indexOf(SymbolConstant.SPOT)!=-1){
|
||||
fileName = orgName.substring(0, orgName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + orgName.substring(orgName.lastIndexOf("."));
|
||||
@ -242,6 +247,10 @@ public class CommonUtils {
|
||||
try {
|
||||
DataSource dataSource = SpringContextUtils.getApplicationContext().getBean(DataSource.class);
|
||||
dbTypeEnum = JdbcUtils.getDbType(dataSource.getConnection().getMetaData().getURL());
|
||||
//【采用SQL_SERVER2005引擎】QQYUN-13298 解决升级mybatisPlus后SqlServer分页使用OFFSET,无排序字段报错问题
|
||||
if (dbTypeEnum == DbType.SQL_SERVER) {
|
||||
dbTypeEnum = DbType.SQL_SERVER2005;
|
||||
}
|
||||
return dbTypeEnum;
|
||||
} catch (SQLException e) {
|
||||
log.warn(e.getMessage(), e);
|
||||
|
||||
@ -13,6 +13,8 @@ import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
@ -814,4 +816,44 @@ public class DateUtils extends PropertyEditorSupport {
|
||||
return calendar1.get(Calendar.YEAR) == calendar2.get(Calendar.YEAR);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取两个日期之间的所有日期列表,包含开始和结束日期
|
||||
*
|
||||
* @param begin
|
||||
* @param end
|
||||
* @return
|
||||
*/
|
||||
public static List<Date> getDateRangeList(Date begin, Date end) {
|
||||
List<Date> dateList = new ArrayList<>();
|
||||
if (begin == null || end == null) {
|
||||
return dateList;
|
||||
}
|
||||
|
||||
// 清除时间部分,只比较日期
|
||||
Calendar beginCal = Calendar.getInstance();
|
||||
beginCal.setTime(begin);
|
||||
beginCal.set(Calendar.HOUR_OF_DAY, 0);
|
||||
beginCal.set(Calendar.MINUTE, 0);
|
||||
beginCal.set(Calendar.SECOND, 0);
|
||||
beginCal.set(Calendar.MILLISECOND, 0);
|
||||
|
||||
Calendar endCal = Calendar.getInstance();
|
||||
endCal.setTime(end);
|
||||
endCal.set(Calendar.HOUR_OF_DAY, 0);
|
||||
endCal.set(Calendar.MINUTE, 0);
|
||||
endCal.set(Calendar.SECOND, 0);
|
||||
endCal.set(Calendar.MILLISECOND, 0);
|
||||
|
||||
if (endCal.before(beginCal)) {
|
||||
return dateList;
|
||||
}
|
||||
|
||||
dateList.add(beginCal.getTime());
|
||||
while (beginCal.before(endCal)) {
|
||||
beginCal.add(Calendar.DAY_OF_YEAR, 1);
|
||||
dateList.add(beginCal.getTime());
|
||||
}
|
||||
return dateList;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,14 +1,22 @@
|
||||
package org.jeecg.common.util;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.exception.JeecgBootException;
|
||||
import org.jeecg.common.util.filter.SsrfFileTypeFilter;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
@ -203,4 +211,150 @@ public class FileDownloadUtils {
|
||||
dir.mkdirs();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 下载单个文件到ZIP流
|
||||
* 核心功能:获取文件流,写入ZIP条目
|
||||
* @param fileUrl 文件URL(可以是HTTP URL或本地路径)
|
||||
* @param fileName ZIP内的文件名
|
||||
* @param zous ZIP输出流
|
||||
*/
|
||||
public static void downLoadSingleFile(String fileUrl, String fileName, String uploadUrl,ZipArchiveOutputStream zous) {
|
||||
InputStream inputStream = null;
|
||||
try {
|
||||
// 创建ZIP条目:每个文件在ZIP中都是一个独立条目
|
||||
ZipArchiveEntry entry = new ZipArchiveEntry(fileName);
|
||||
zous.putArchiveEntry(entry);
|
||||
|
||||
// 获取文件输入流:区分普通文件和快捷方式
|
||||
if (fileUrl.endsWith(".url")) {
|
||||
// 处理快捷方式:生成.url文件内容
|
||||
inputStream = FileDownloadUtils.createInternetShortcut(fileName, fileUrl, "");
|
||||
} else {
|
||||
// 普通文件下载:从URL或本地路径获取流
|
||||
inputStream = getDownInputStream(fileUrl,uploadUrl);
|
||||
}
|
||||
|
||||
if (inputStream != null) {
|
||||
// 将文件流写入ZIP
|
||||
IOUtils.copy(inputStream, zous);
|
||||
}
|
||||
// 关闭当前ZIP条目
|
||||
zous.closeArchiveEntry();
|
||||
} catch (IOException e) {
|
||||
log.error("文件下载失败: {}", e);
|
||||
} finally {
|
||||
// 确保输入流关闭
|
||||
IoUtil.close(inputStream);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下载文件输入流
|
||||
* 功能:根据URL类型(HTTP或本地)获取文件流
|
||||
* @param fileUrl 文件URL(支持HTTP和本地路径)
|
||||
* @return 文件输入流,失败返回null
|
||||
*/
|
||||
public static InputStream getDownInputStream(String fileUrl, String uploadUrl) {
|
||||
try {
|
||||
// 处理HTTP URL:通过网络下载
|
||||
if (oConvertUtils.isNotEmpty(fileUrl) && fileUrl.startsWith(CommonConstant.STR_HTTP)) {
|
||||
URL url = new URL(fileUrl);
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setConnectTimeout(5000); // 连接超时5秒
|
||||
connection.setReadTimeout(30000); // 读取超时30秒
|
||||
return connection.getInputStream();
|
||||
} else {
|
||||
// 处理本地文件:直接读取文件系统
|
||||
String downloadFilePath = uploadUrl + File.separator + fileUrl;
|
||||
// 安全检查:防止下载危险文件类型
|
||||
SsrfFileTypeFilter.checkDownloadFileType(downloadFilePath);
|
||||
return new BufferedInputStream(new FileInputStream(downloadFilePath));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// 异常时返回null,上层会处理空流情况
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件扩展名
|
||||
* 功能:从文件名中提取扩展名
|
||||
* @param fileName 文件名
|
||||
* @return 文件扩展名(不含点),如"txt"、"png"
|
||||
*/
|
||||
public static String getFileExtension(String fileName) {
|
||||
int dotIndex = fileName.lastIndexOf('.');
|
||||
return (dotIndex == -1) ? "" : fileName.substring(dotIndex + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建快捷方式(.url文件内容)
|
||||
* 功能:生成Internet快捷方式文件内容
|
||||
* @param name 快捷方式名称
|
||||
* @param url 目标URL地址
|
||||
* @param icon 图标路径(可选)
|
||||
* @return 包含.url文件内容的输入流
|
||||
*/
|
||||
public static InputStream createInternetShortcut(String name, String url, String icon) {
|
||||
StringWriter sw = new StringWriter();
|
||||
try {
|
||||
// 按照Windows快捷方式格式写入内容
|
||||
sw.write("[InternetShortcut]\n");
|
||||
sw.write("URL=" + url + "\n");
|
||||
if (oConvertUtils.isNotEmpty(icon)) {
|
||||
sw.write("IconFile=" + icon + "\n");
|
||||
}
|
||||
// 将字符串内容转换为输入流
|
||||
return new ByteArrayInputStream(sw.toString().getBytes(StandardCharsets.UTF_8));
|
||||
} finally {
|
||||
IoUtil.close(sw);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 从URL中提取文件名
|
||||
* 功能:从HTTP URL或本地路径中提取纯文件名
|
||||
* @param fileUrl 文件URL
|
||||
* @return 文件名(不含路径)
|
||||
*/
|
||||
public static String getFileNameFromUrl(String fileUrl) {
|
||||
try {
|
||||
// 处理HTTP URL:从路径部分提取文件名
|
||||
if (fileUrl.startsWith(CommonConstant.STR_HTTP)) {
|
||||
URL url = new URL(fileUrl);
|
||||
String path = url.getPath();
|
||||
return path.substring(path.lastIndexOf('/') + 1);
|
||||
}
|
||||
|
||||
// 处理本地文件路径:从文件路径提取文件名
|
||||
return fileUrl.substring(fileUrl.lastIndexOf(File.separator) + 1);
|
||||
} catch (Exception e) {
|
||||
// 如果解析失败,使用时间戳作为文件名
|
||||
return "file_" + System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 生成ZIP中的文件名
|
||||
* 功能:避免文件名冲突,为多个文件添加序号
|
||||
* @param fileUrl 文件URL(用于提取原始文件名)
|
||||
* @param index 文件序号(从0开始)
|
||||
* @param total 文件总数
|
||||
* @return 处理后的文件名(带序号)
|
||||
*/
|
||||
public static String generateFileName(String fileUrl, int index, int total) {
|
||||
// 从URL中提取原始文件名
|
||||
String originalFileName = getFileNameFromUrl(fileUrl);
|
||||
|
||||
// 如果只有一个文件,直接使用原始文件名
|
||||
if (total == 1) {
|
||||
return originalFileName;
|
||||
}
|
||||
|
||||
// 多个文件时,使用序号+原始文件名
|
||||
String extension = getFileExtension(originalFileName);
|
||||
String nameWithoutExtension = originalFileName.replace("." + extension, "");
|
||||
|
||||
return String.format("%s_%d.%s", nameWithoutExtension, index + 1, extension);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ package org.jeecg.common.util;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
@ -16,6 +17,7 @@ import java.util.List;
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@Lazy(false)
|
||||
public class PmsUtil {
|
||||
|
||||
|
||||
|
||||
@ -221,6 +221,63 @@ public class RestUtil {
|
||||
return RT.exchange(url, method, request, responseType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送请求(支持自定义超时时间)
|
||||
*
|
||||
* @param url 请求地址
|
||||
* @param method 请求方式
|
||||
* @param headers 请求头 可空
|
||||
* @param variables 请求url参数 可空
|
||||
* @param params 请求body参数 可空
|
||||
* @param responseType 返回类型
|
||||
* @param timeout 超时时间(毫秒),如果为0或负数则使用默认超时
|
||||
* @return ResponseEntity<responseType>
|
||||
*/
|
||||
public static <T> ResponseEntity<T> request(String url, HttpMethod method, HttpHeaders headers,
|
||||
JSONObject variables, Object params, Class<T> responseType, int timeout) {
|
||||
log.info(" RestUtil --- request --- url = "+ url + ", timeout = " + timeout);
|
||||
|
||||
if (StringUtils.isEmpty(url)) {
|
||||
throw new RuntimeException("url 不能为空");
|
||||
}
|
||||
if (method == null) {
|
||||
throw new RuntimeException("method 不能为空");
|
||||
}
|
||||
if (headers == null) {
|
||||
headers = new HttpHeaders();
|
||||
}
|
||||
|
||||
// 创建自定义RestTemplate(如果需要设置超时)
|
||||
RestTemplate restTemplate = RT;
|
||||
if (timeout > 0) {
|
||||
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
|
||||
requestFactory.setConnectTimeout(timeout);
|
||||
requestFactory.setReadTimeout(timeout);
|
||||
restTemplate = new RestTemplate(requestFactory);
|
||||
// 解决乱码问题
|
||||
restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
// 请求体
|
||||
String body = "";
|
||||
if (params != null) {
|
||||
if (params instanceof JSONObject) {
|
||||
body = ((JSONObject) params).toJSONString();
|
||||
} else {
|
||||
body = params.toString();
|
||||
}
|
||||
}
|
||||
|
||||
// 拼接 url 参数
|
||||
if (variables != null && !variables.isEmpty()) {
|
||||
url += ("?" + asUrlVariables(variables));
|
||||
}
|
||||
|
||||
// 发送请求
|
||||
HttpEntity<String> request = new HttpEntity<>(body, headers);
|
||||
return restTemplate.exchange(url, method, request, responseType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取JSON请求头
|
||||
*/
|
||||
|
||||
@ -0,0 +1,37 @@
|
||||
package org.jeecg.common.util;
|
||||
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.mgt.SecurityManager;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
import org.apache.shiro.util.ThreadContext;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* @date 2025-09-04
|
||||
* @author scott
|
||||
*
|
||||
* @Description: 支持shiro的API,获取当前登录人方法的线程池
|
||||
*/
|
||||
public class ShiroThreadPoolExecutor extends ThreadPoolExecutor {
|
||||
|
||||
public ShiroThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
|
||||
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Runnable command) {
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
SecurityManager securityManager = SecurityUtils.getSecurityManager();
|
||||
super.execute(() -> {
|
||||
try {
|
||||
ThreadContext.bind(securityManager);
|
||||
ThreadContext.bind(subject);
|
||||
command.run();
|
||||
} finally {
|
||||
ThreadContext.unbindSubject();
|
||||
ThreadContext.unbindSecurityManager();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -65,6 +65,10 @@ public class TokenUtils {
|
||||
if (tenantId == null) {
|
||||
tenantId = oConvertUtils.getString(request.getHeader(CommonConstant.TENANT_ID));
|
||||
}
|
||||
|
||||
if (oConvertUtils.isNotEmpty(tenantId) && "undefined".equals(tenantId)) {
|
||||
return null;
|
||||
}
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
|
||||
@ -474,6 +474,23 @@ public class oConvertUtils {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断字符串是否为JSON格式
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
public static boolean isJson(String str) {
|
||||
if (str == null || str.trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
com.alibaba.fastjson.JSON.parse(str);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Map对象
|
||||
*/
|
||||
@ -1132,7 +1149,15 @@ public class oConvertUtils {
|
||||
* @date 2020/9/12 15:50
|
||||
*/
|
||||
public static <T> boolean isIn(T obj, T... objs) {
|
||||
return isIn(obj, objs);
|
||||
if (isEmpty(objs)) {
|
||||
return false;
|
||||
}
|
||||
for (T obj1 : objs) {
|
||||
if (isEqual(obj, obj1)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -3,13 +3,14 @@ package org.jeecg.config;
|
||||
import org.jeecgframework.core.util.ApplicationContextUtil;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
|
||||
/**
|
||||
* @Author: Scott
|
||||
* @Date: 2018/2/7
|
||||
* @description: autopoi 配置类
|
||||
*/
|
||||
|
||||
@Lazy(false)
|
||||
@Configuration
|
||||
public class AutoPoiConfig {
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
* @Version:1.0
|
||||
*/
|
||||
@Slf4j
|
||||
@Lazy(false)
|
||||
@Service
|
||||
public class AutoPoiDictConfig implements AutoPoiDictServiceI {
|
||||
final static String EXCEL_SPLIT_TAG = "_";
|
||||
|
||||
@ -76,11 +76,6 @@ public class JeecgBaseConfig {
|
||||
*/
|
||||
private BaiduApi baiduApi;
|
||||
|
||||
/**
|
||||
* 高德开放API配置
|
||||
*/
|
||||
private GaoDeApi gaoDeApi;
|
||||
|
||||
public String getCustomResourcePrefixPath() {
|
||||
return customResourcePrefixPath;
|
||||
}
|
||||
@ -177,11 +172,4 @@ public class JeecgBaseConfig {
|
||||
this.baiduApi = baiduApi;
|
||||
}
|
||||
|
||||
public GaoDeApi getGaoDeApi() {
|
||||
return gaoDeApi;
|
||||
}
|
||||
|
||||
public void setGaoDeApi(GaoDeApi gaoDeApi) {
|
||||
this.gaoDeApi = gaoDeApi;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
package org.jeecg.config;
|
||||
|
||||
import org.jeecg.config.vo.GaoDeApi;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
|
||||
/**
|
||||
* 高德账号配置
|
||||
*/
|
||||
@Lazy(false)
|
||||
@Configuration("jeecgGaodeBaseConfig")
|
||||
@ConfigurationProperties(prefix = "jeecg.jmreport")
|
||||
public class JeecgGaodeBaseConfig {
|
||||
|
||||
/**
|
||||
* 高德开放API配置
|
||||
*/
|
||||
private GaoDeApi gaoDeApi;
|
||||
|
||||
public GaoDeApi getGaoDeApi() {
|
||||
return gaoDeApi;
|
||||
}
|
||||
|
||||
public void setGaoDeApi(GaoDeApi gaoDeApi) {
|
||||
this.gaoDeApi = gaoDeApi;
|
||||
}
|
||||
|
||||
}
|
||||
@ -2,12 +2,14 @@ package org.jeecg.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 设置静态参数初始化
|
||||
* @author: jeecg-boot
|
||||
*/
|
||||
@Lazy(false)
|
||||
@Component
|
||||
@Data
|
||||
public class StaticConfig {
|
||||
|
||||
@ -117,7 +117,7 @@ public class Swagger3Config implements WebMvcConfigurer {
|
||||
return new OpenAPI()
|
||||
.info(new Info()
|
||||
.title("JeecgBoot 后台服务API接口文档")
|
||||
.version("3.8.2")
|
||||
.version("3.8.3")
|
||||
.contact(new Contact().name("北京国炬信息技术有限公司").url("www.jeccg.com").email("jeecgos@163.com"))
|
||||
.description("后台API接口")
|
||||
.termsOfService("NO terms of service")
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
package org.jeecg.config;
|
||||
|
||||
import io.undertow.server.DefaultByteBufferPool;
|
||||
import io.undertow.websockets.jsr.WebSocketDeploymentInfo;
|
||||
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
|
||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class UndertowCustomizer implements WebServerFactoryCustomizer<UndertowServletWebServerFactory> {
|
||||
@Override
|
||||
public void customize(UndertowServletWebServerFactory factory) {
|
||||
factory.addDeploymentInfoCustomizers(deploymentInfo -> {
|
||||
WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo();
|
||||
webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(false, 1024));
|
||||
deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo", webSocketDeploymentInfo);
|
||||
});
|
||||
}
|
||||
}
|
||||
//package org.jeecg.config;
|
||||
//
|
||||
//import io.undertow.server.DefaultByteBufferPool;
|
||||
//import io.undertow.websockets.jsr.WebSocketDeploymentInfo;
|
||||
//import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
|
||||
//import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
//import org.springframework.stereotype.Component;
|
||||
//
|
||||
//@Component
|
||||
//public class UndertowCustomizer implements WebServerFactoryCustomizer<UndertowServletWebServerFactory> {
|
||||
// @Override
|
||||
// public void customize(UndertowServletWebServerFactory factory) {
|
||||
// factory.addDeploymentInfoCustomizers(deploymentInfo -> {
|
||||
// WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo();
|
||||
// webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(false, 1024));
|
||||
// deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo", webSocketDeploymentInfo);
|
||||
// });
|
||||
// }
|
||||
//}
|
||||
|
||||
@ -9,11 +9,13 @@ import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
|
||||
/**
|
||||
* Minio文件上传配置文件
|
||||
* @author: jeecg-boot
|
||||
*/
|
||||
@Lazy(false)
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@ConditionalOnProperty(prefix = "jeecg.minio", name = "minio_url")
|
||||
|
||||
@ -6,11 +6,13 @@ import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
|
||||
/**
|
||||
* 云存储 配置
|
||||
* @author: jeecg-boot
|
||||
*/
|
||||
@Lazy(false)
|
||||
@Configuration
|
||||
@ConditionalOnProperty(prefix = "jeecg.oss", name = "endpoint")
|
||||
public class OssConfiguration {
|
||||
|
||||
@ -126,6 +126,7 @@ public class ShiroConfig {
|
||||
filterChainDefinitionMap.put("/**/*.ttf", "anon");
|
||||
filterChainDefinitionMap.put("/**/*.woff", "anon");
|
||||
filterChainDefinitionMap.put("/**/*.woff2", "anon");
|
||||
|
||||
filterChainDefinitionMap.put("/**/*.glb", "anon");
|
||||
filterChainDefinitionMap.put("/**/*.wasm", "anon");
|
||||
//update-end--Author:scott Date:20221116 for:排除静态资源后缀
|
||||
@ -177,6 +178,8 @@ public class ShiroConfig {
|
||||
filterChainDefinitionMap.put("/sys/version/app3version", "anon");
|
||||
//仪表盘(按钮通信)
|
||||
filterChainDefinitionMap.put("/dragChannelSocket/**","anon");
|
||||
//App vue3版本查询版本接口
|
||||
filterChainDefinitionMap.put("/sys/version/app3version", "anon");
|
||||
|
||||
//性能监控——安全隐患泄露TOEKN(durid连接池也有)
|
||||
//filterChainDefinitionMap.put("/actuator/**", "anon");
|
||||
@ -228,6 +231,7 @@ public class ShiroConfig {
|
||||
registration.addUrlPatterns("/airag/chat/send");
|
||||
registration.addUrlPatterns("/airag/app/debug");
|
||||
registration.addUrlPatterns("/airag/app/prompt/generate");
|
||||
registration.addUrlPatterns("/airag/chat/receive/**");
|
||||
//支持异步
|
||||
registration.setAsyncSupported(true);
|
||||
registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC);
|
||||
|
||||
@ -109,8 +109,8 @@ public class ShiroRealm extends AuthorizingRealm {
|
||||
try {
|
||||
loginUser = this.checkUserTokenIsEffect(token);
|
||||
} catch (AuthenticationException e) {
|
||||
log.error("—————校验 check token 失败——————————"+ e.getMessage(), e);
|
||||
JwtUtil.responseError(SpringContextUtils.getHttpServletResponse(),401,e.getMessage());
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
return new SimpleAuthenticationInfo(loginUser, token, getName());
|
||||
@ -125,7 +125,7 @@ public class ShiroRealm extends AuthorizingRealm {
|
||||
// 解密获得username,用于和数据库进行对比
|
||||
String username = JwtUtil.getUsername(token);
|
||||
if (username == null) {
|
||||
throw new AuthenticationException("token非法无效!");
|
||||
throw new AuthenticationException("Token非法无效!");
|
||||
}
|
||||
|
||||
// 查询用户信息
|
||||
|
||||
@ -56,7 +56,7 @@ public class JwtFilter extends BasicHttpAuthenticationFilter {
|
||||
executeLogin(request, response);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
JwtUtil.responseError(response,401,CommonConstant.TOKEN_IS_INVALID_MSG);
|
||||
JwtUtil.responseError((HttpServletResponse)response,401,CommonConstant.TOKEN_IS_INVALID_MSG);
|
||||
return false;
|
||||
//throw new AuthenticationException("Token失效,请重新登录", e);
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<artifactId>jeecg-boot-module</artifactId>
|
||||
<version>3.8.2</version>
|
||||
<version>3.8.3</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>jeecg-boot-module-airag</artifactId>
|
||||
@ -31,8 +31,6 @@
|
||||
</repositories>
|
||||
|
||||
<properties>
|
||||
<kotlin.version>1.6.21</kotlin.version>
|
||||
<liteflow.version>2.12.4.1</liteflow.version>
|
||||
<langchain4j.version>0.35.0</langchain4j.version>
|
||||
<apache-tika.version>2.9.1</apache-tika.version>
|
||||
</properties>
|
||||
@ -57,7 +55,7 @@
|
||||
<dependency>
|
||||
<groupId>org.jeecgframework.boot3</groupId>
|
||||
<artifactId>jeecg-aiflow</artifactId>
|
||||
<version>1.1.1</version>
|
||||
<version>1.2.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- beigin 这两个依赖太多每个包50M左右,如果你发布需要使用,请把<scope>provided</scope>删掉 -->
|
||||
@ -153,7 +151,7 @@
|
||||
<dependency>
|
||||
<groupId>org.jeecgframework</groupId>
|
||||
<artifactId>langchain4j-pgvector</artifactId>
|
||||
<version>0.35.0</version>
|
||||
<version>${langchain4j.version}</version>
|
||||
</dependency>
|
||||
<!-- langChain4j Document Parser -->
|
||||
<dependency>
|
||||
|
||||
@ -156,6 +156,20 @@ public class AiragChatController {
|
||||
return chatService.clearMessage(conversationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 继续接收消息
|
||||
*
|
||||
* @param requestId
|
||||
* @return
|
||||
* @author chenrui
|
||||
* @date 2025/8/11 17:49
|
||||
*/
|
||||
@IgnoreAuth
|
||||
@GetMapping(value = "/receive/{requestId}")
|
||||
public SseEmitter receiveByRequestId(@PathVariable(name = "requestId", required = true) String requestId) {
|
||||
return chatService.receiveByRequestId(requestId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据请求ID停止某个请求的处理
|
||||
|
||||
@ -102,4 +102,13 @@ public interface IAiragChatService {
|
||||
* @date 2025/4/21 14:17
|
||||
*/
|
||||
Result<?> initChat(String appId);
|
||||
|
||||
/**
|
||||
* 继续接收消息
|
||||
* @param requestId
|
||||
* @return
|
||||
* @author chenrui
|
||||
* @date 2025/8/11 17:39
|
||||
*/
|
||||
SseEmitter receiveByRequestId(String requestId);
|
||||
}
|
||||
|
||||
@ -8,13 +8,13 @@ import dev.langchain4j.service.TokenStream;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.exception.JeecgBootBizTipException;
|
||||
import org.jeecg.common.exception.JeecgBootException;
|
||||
import org.jeecg.common.system.api.ISysBaseAPI;
|
||||
import org.jeecg.common.system.util.JwtUtil;
|
||||
import org.jeecg.common.util.*;
|
||||
import org.jeecg.modules.airag.app.consts.AiAppConsts;
|
||||
import org.jeecg.modules.airag.app.entity.AiragApp;
|
||||
import org.jeecg.modules.airag.app.mapper.AiragAppMapper;
|
||||
import org.jeecg.modules.airag.app.service.IAiragAppService;
|
||||
import org.jeecg.modules.airag.app.service.IAiragChatService;
|
||||
import org.jeecg.modules.airag.app.vo.AppDebugParams;
|
||||
import org.jeecg.modules.airag.app.vo.ChatConversation;
|
||||
@ -31,6 +31,8 @@ import org.jeecg.modules.airag.flow.consts.FlowConsts;
|
||||
import org.jeecg.modules.airag.flow.service.IAiragFlowService;
|
||||
import org.jeecg.modules.airag.flow.vo.api.FlowRunParams;
|
||||
import org.jeecg.modules.airag.llm.entity.AiragModel;
|
||||
import org.jeecg.modules.airag.llm.handler.AIChatHandler;
|
||||
import org.jeecg.modules.airag.llm.handler.JeecgToolsProvider;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.BoundValueOperations;
|
||||
@ -41,8 +43,7 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -74,6 +75,14 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
||||
@Autowired
|
||||
private RedisUtil redisUtil;
|
||||
|
||||
@Autowired
|
||||
JeecgToolsProvider jeecgToolsProvider;
|
||||
|
||||
/**
|
||||
* 重新接收消息
|
||||
*/
|
||||
private static final ExecutorService SSE_THREAD_POOL = Executors.newFixedThreadPool(10); // 最大10个线程
|
||||
|
||||
@Override
|
||||
public SseEmitter send(ChatSendParams chatSendParams) {
|
||||
AssertUtils.assertNotEmpty("参数异常", chatSendParams);
|
||||
@ -148,7 +157,9 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
||||
// 发送完成事件
|
||||
emitter.send(SseEmitter.event().data(eventData));
|
||||
} catch (Exception e) {
|
||||
if(!e.getMessage().contains("ResponseBodyEmitter has already completed")){
|
||||
log.error("终止会话时发生错误", e);
|
||||
}
|
||||
try {
|
||||
// 防止异常冒泡
|
||||
emitter.completeWithError(e);
|
||||
@ -250,6 +261,96 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
||||
return Result.ok(app);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SseEmitter receiveByRequestId(String requestId) {
|
||||
AssertUtils.assertNotEmpty("请选择会话",requestId);
|
||||
if(AiragLocalCache.get(AiragConsts.CACHE_TYPE_SSE, requestId) == null){
|
||||
return null;
|
||||
}
|
||||
List<EventData> datas = AiragLocalCache.get(AiragConsts.CACHE_TYPE_SSE_HISTORY_MSG, requestId);
|
||||
if(null == datas){
|
||||
return null;
|
||||
}
|
||||
SseEmitter emitter = createSSE(requestId);
|
||||
// 120秒
|
||||
final long timeoutMillis = 120_000L;
|
||||
// 使用线程池提交任务
|
||||
SSE_THREAD_POOL.submit(() -> {
|
||||
int lastIndex = 0;
|
||||
long lastActiveTime = System.currentTimeMillis();
|
||||
try {
|
||||
while (true) {
|
||||
if(lastIndex < datas.size()) {
|
||||
try {
|
||||
EventData eventData = datas.get(lastIndex++);
|
||||
String eventStr = JSONObject.toJSONString(eventData);
|
||||
log.debug("[AI应用]继续接收-接收LLM返回消息:{}", eventStr);
|
||||
emitter.send(SseEmitter.event().data(eventStr));
|
||||
// 有新消息,重置计时
|
||||
lastActiveTime = System.currentTimeMillis();
|
||||
} catch (IOException e) {
|
||||
log.error("[AI应用]继续接收-发送消息失败");
|
||||
}
|
||||
} else {
|
||||
// 没有新消息了
|
||||
if (AiragLocalCache.get(AiragConsts.CACHE_TYPE_SSE, requestId) == null) {
|
||||
// 主线程sse已经被移除,退出线程.
|
||||
log.info("[AI应用]继续接收-SSE消息推送完成: {}", requestId);
|
||||
break;
|
||||
} else if (System.currentTimeMillis() - lastActiveTime > timeoutMillis) {
|
||||
// 主线程未结束,等待超时,
|
||||
log.warn("[AI应用]继续接收-等待消息更新超时,释放线程: {}", requestId);
|
||||
break;
|
||||
} else {
|
||||
// 主线程未结束, 未超时, 休眠一会再查
|
||||
log.warn("[AI应用]继续接收-等待消息更新: {}", requestId);
|
||||
Thread.sleep(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("SSE消息推送异常", e);
|
||||
} finally {
|
||||
try {
|
||||
// 发送完成事件
|
||||
emitter.send(SseEmitter.event().data(new EventData(requestId, null, EventData.EVENT_MESSAGE_END)));
|
||||
} catch (Exception e) {
|
||||
log.error("终止会话时发生错误", e);
|
||||
try {
|
||||
// 防止异常冒泡
|
||||
emitter.completeWithError(e);
|
||||
} catch (Exception ignore) {}
|
||||
} finally {
|
||||
// 关闭emitter
|
||||
try {
|
||||
emitter.complete();
|
||||
} catch (Exception ignore) {}
|
||||
}
|
||||
}
|
||||
});
|
||||
return emitter;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建SSE
|
||||
* @param requestId
|
||||
* @return
|
||||
* @author chenrui
|
||||
* @date 2025/8/12 15:30
|
||||
*/
|
||||
private static SseEmitter createSSE(String requestId) {
|
||||
SseEmitter emitter = new SseEmitter(-0L);
|
||||
emitter.onError(throwable -> {
|
||||
log.warn("SEE向客户端发送消息失败: {}", throwable.getMessage());
|
||||
AiragLocalCache.remove(AiragConsts.CACHE_TYPE_SSE, requestId);
|
||||
AiragLocalCache.remove(AiragConsts.CACHE_TYPE_SSE_SEND_TIME, requestId);
|
||||
try {
|
||||
emitter.complete();
|
||||
} catch (Exception ignore) {}
|
||||
});
|
||||
return emitter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<?> deleteConversation(String conversationId) {
|
||||
AssertUtils.assertNotEmpty("请选择要删除的会话", conversationId);
|
||||
@ -522,22 +623,14 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
||||
AiragApp aiApp = chatConversation.getApp();
|
||||
// 每次会话都生成一个新的,用来缓存emitter
|
||||
String requestId = UUIDGenerator.generate();
|
||||
SseEmitter emitter = new SseEmitter(-0L);
|
||||
emitter.onError(throwable -> {
|
||||
log.warn("SEE向客户端发送消息失败: {}", throwable.getMessage());
|
||||
AiragLocalCache.remove(AiragConsts.CACHE_TYPE_SSE, requestId);
|
||||
try {
|
||||
emitter.complete();
|
||||
} catch (Exception ignore) {}
|
||||
});
|
||||
EventData eventRequestId = new EventData(requestId, null, EventData.EVENT_INIT_REQUEST_ID, chatConversation.getId(), topicId);
|
||||
eventRequestId.setData(EventMessageData.builder().message("").build());
|
||||
sendMessage2Client(emitter, eventRequestId);
|
||||
SseEmitter emitter = createSSE(requestId);
|
||||
// 缓存emitter
|
||||
AiragLocalCache.put(AiragConsts.CACHE_TYPE_SSE, requestId, emitter);
|
||||
// 缓存开始发送时间
|
||||
log.info("[AI-CHAT]开始发送消息,requestId:{}", requestId);
|
||||
AiragLocalCache.put(AiragConsts.CACHE_TYPE_SSE_SEND_TIME, requestId, System.currentTimeMillis());
|
||||
// 初始化历史消息缓存
|
||||
AiragLocalCache.put(AiragConsts.CACHE_TYPE_SSE_HISTORY_MSG, requestId, new CopyOnWriteArrayList<>());
|
||||
try {
|
||||
// 组装用户消息
|
||||
UserMessage userMessage = aiChatHandler.buildUserMessage(sendParams.getContent(), sendParams.getImages());
|
||||
@ -561,6 +654,10 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
||||
// 发消息
|
||||
sendWithDefault(requestId, chatConversation, topicId, null, messages, null);
|
||||
}
|
||||
// 发送就绪消息
|
||||
EventData eventRequestId = new EventData(requestId, null, EventData.EVENT_INIT_REQUEST_ID, chatConversation.getId(), topicId);
|
||||
eventRequestId.setData(EventMessageData.builder().message("").build());
|
||||
sendMessage2Client(emitter, eventRequestId);
|
||||
} catch (Throwable e) {
|
||||
log.error(e.getMessage(), e);
|
||||
EventData eventData = new EventData(requestId, null, EventData.EVENT_FLOW_ERROR, chatConversation.getId(), topicId);
|
||||
@ -725,6 +822,10 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
||||
if (null == aiChatParams) {
|
||||
aiChatParams = new AIChatParams();
|
||||
}
|
||||
// 如果是默认app,加载系统默认工具
|
||||
if(chatConversation.getApp().getId().equals(AiAppConsts.DEFAULT_APP_ID)){
|
||||
aiChatParams.setTools(jeecgToolsProvider.getDefaultTools());
|
||||
}
|
||||
aiChatParams.setKnowIds(chatConversation.getApp().getKnowIds());
|
||||
aiChatParams.setMaxMsgNumber(oConvertUtils.getInt(chatConversation.getApp().getMsgNum(), 5));
|
||||
HttpServletRequest httpRequest = SpringContextUtils.getHttpServletRequest();
|
||||
@ -739,6 +840,19 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
// sse
|
||||
SseEmitter emitter = AiragLocalCache.get(AiragConsts.CACHE_TYPE_SSE, requestId);
|
||||
if (null == emitter) {
|
||||
log.warn("[AI应用]接收LLM返回会话已关闭{}", requestId);
|
||||
return;
|
||||
}
|
||||
String errMsg = "调用大模型接口失败,详情请查看后台日志。";
|
||||
if(e instanceof JeecgBootException){
|
||||
errMsg = e.getMessage();
|
||||
}
|
||||
EventData eventData = new EventData(requestId, null, EventData.EVENT_FLOW_ERROR, chatConversation.getId(), topicId);
|
||||
eventData.setData(EventFlowData.builder().success(false).message(errMsg).build());
|
||||
closeSSE(emitter, eventData);
|
||||
throw new JeecgBootBizTipException("调用大模型接口失败:" + e.getMessage());
|
||||
}
|
||||
/**
|
||||
@ -808,7 +922,7 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
||||
// 异常结束
|
||||
log.error("调用模型异常:" + respText);
|
||||
if (respText.contains("insufficient Balance")) {
|
||||
respText = "大预言模型账号余额不足!";
|
||||
respText = "大语言模型账号余额不足!";
|
||||
}
|
||||
EventData eventData = new EventData(requestId, null, EventData.EVENT_FLOW_ERROR, chatConversation.getId(), topicId);
|
||||
eventData.setData(EventFlowData.builder().success(false).message(respText).build());
|
||||
@ -837,6 +951,14 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
||||
//update-end---author:chenrui ---date:20250425 for:[QQYUN-12203]AI 聊天,超时或者服务器报错,给个友好提示------------
|
||||
} else {
|
||||
errMsg = "调用大模型接口失败,详情请查看后台日志。";
|
||||
// 根据常见异常关键字做细致翻译
|
||||
for (Map.Entry<String, String> entry : AIChatHandler.MODEL_ERROR_MAP.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String value = entry.getValue();
|
||||
if (error.getMessage().contains(key)) {
|
||||
errMsg = value;
|
||||
}
|
||||
}
|
||||
EventData eventData = new EventData(requestId, null, EventData.EVENT_FLOW_ERROR, chatConversation.getId(), topicId);
|
||||
eventData.setData(EventFlowData.builder().success(false).message(errMsg).build());
|
||||
closeSSE(emitter, eventData);
|
||||
@ -858,6 +980,12 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
||||
String eventStr = JSONObject.toJSONString(eventData);
|
||||
log.debug("[AI应用]接收LLM返回消息:{}", eventStr);
|
||||
emitter.send(SseEmitter.event().data(eventStr));
|
||||
List<EventData> historyMsg = AiragLocalCache.get(AiragConsts.CACHE_TYPE_SSE_HISTORY_MSG, eventData.getRequestId());
|
||||
if (null == historyMsg) {
|
||||
historyMsg = new CopyOnWriteArrayList<>();
|
||||
AiragLocalCache.put(AiragConsts.CACHE_TYPE_SSE_HISTORY_MSG, eventData.getRequestId(), historyMsg);
|
||||
}
|
||||
historyMsg.add(eventData);
|
||||
} catch (IOException e) {
|
||||
log.error("发送消息失败", e);
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@ import org.jeecg.common.system.base.controller.JeecgController;
|
||||
import org.jeecg.common.system.query.QueryGenerator;
|
||||
import org.jeecg.common.util.AssertUtils;
|
||||
import org.jeecg.common.util.TokenUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
|
||||
import org.jeecg.modules.airag.llm.consts.LLMConsts;
|
||||
import org.jeecg.modules.airag.llm.entity.AiragModel;
|
||||
@ -77,6 +78,11 @@ public class AiragModelController extends JeecgController<AiragModel, IAiragMode
|
||||
AssertUtils.assertNotEmpty("模型名称不能为空", airagModel.getName());
|
||||
AssertUtils.assertNotEmpty("模型类型不能为空", airagModel.getModelType());
|
||||
AssertUtils.assertNotEmpty("基础模型不能为空", airagModel.getModelName());
|
||||
// 默认未激活
|
||||
if(oConvertUtils.isObjectEmpty(airagModel.getActivateFlag())){
|
||||
airagModel.setActivateFlag(0);
|
||||
}
|
||||
airagModel.setActivateFlag(0);
|
||||
airagModelService.save(airagModel);
|
||||
return Result.OK("添加成功!");
|
||||
}
|
||||
@ -164,7 +170,7 @@ public class AiragModelController extends JeecgController<AiragModel, IAiragMode
|
||||
AssertUtils.assertNotEmpty("基础模型不能为空", airagModel.getModelName());
|
||||
try {
|
||||
if(LLMConsts.MODEL_TYPE_LLM.equals(airagModel.getModelType())){
|
||||
aiChatHandler.completions(airagModel, Collections.singletonList(UserMessage.from("test connection")), null);
|
||||
aiChatHandler.completions(airagModel, Collections.singletonList(UserMessage.from("To test whether it can be successfully called, simply return success")), null);
|
||||
}else{
|
||||
AiModelOptions aiModelOptions = EmbeddingHandler.buildModelOptions(airagModel);
|
||||
EmbeddingModel embeddingModel = AiModelFactory.createEmbeddingModel(aiModelOptions);
|
||||
@ -172,9 +178,12 @@ public class AiragModelController extends JeecgController<AiragModel, IAiragMode
|
||||
}
|
||||
}catch (Exception e){
|
||||
log.error("测试模型连接失败", e);
|
||||
return Result.error("测试模型连接失败" + e.getMessage());
|
||||
return Result.error("测试模型连接失败:" + e.getMessage());
|
||||
}
|
||||
return Result.OK("测试模型连接成功");
|
||||
// 测试成功激活数据
|
||||
airagModel.setActivateFlag(1);
|
||||
airagModelService.updateById(airagModel);
|
||||
return Result.OK("");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -121,4 +121,11 @@ public class AiragModel implements Serializable {
|
||||
@Excel(name = "模型参数", width = 15)
|
||||
@Schema(description = "模型参数")
|
||||
private String modelParams;
|
||||
|
||||
/**
|
||||
* 是否激活(0=未激活,1=已激活)
|
||||
*/
|
||||
@Excel(name = "是否激活", width = 15)
|
||||
@Schema(description = "是否激活")
|
||||
private Integer activateFlag;
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import dev.langchain4j.rag.query.router.QueryRouter;
|
||||
import dev.langchain4j.service.TokenStream;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.ai.handler.LLMHandler;
|
||||
import org.jeecg.common.exception.JeecgBootException;
|
||||
import org.jeecg.common.util.AssertUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.airag.common.handler.AIChatParams;
|
||||
@ -22,9 +23,7 @@ import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
/**
|
||||
@ -83,6 +82,7 @@ public class AIChatHandler implements IAIChatHandler {
|
||||
AssertUtils.assertNotEmpty("请选择模型", modelId);
|
||||
|
||||
AiragModel airagModel = airagModelMapper.getByIdIgnoreTenant(modelId);
|
||||
AssertUtils.assertSame("模型未激活,请先在[AI模型配置]中[测试激活]模型", airagModel.getActivateFlag(), 1);
|
||||
return completions(airagModel, messages, params);
|
||||
}
|
||||
|
||||
@ -98,7 +98,25 @@ public class AIChatHandler implements IAIChatHandler {
|
||||
*/
|
||||
public String completions(AiragModel airagModel, List<ChatMessage> messages, AIChatParams params) {
|
||||
params = mergeParams(airagModel, params);
|
||||
String resp = llmHandler.completions(messages, params);
|
||||
String resp;
|
||||
try {
|
||||
resp = llmHandler.completions(messages, params);
|
||||
} catch (Exception e) {
|
||||
// langchain4j 异常友好提示
|
||||
String errMsg = "调用大模型接口失败,详情请查看后台日志。";
|
||||
if (oConvertUtils.isNotEmpty(e.getMessage())) {
|
||||
// // 根据常见异常关键字做细致翻译
|
||||
// for (Map.Entry<String, String> entry : MODEL_ERROR_MAP.entrySet()) {
|
||||
// String key = entry.getKey();
|
||||
// String value = entry.getValue();
|
||||
// if (errMsg.contains(key)) {
|
||||
// errMsg = value;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
log.error("AI模型调用异常: {}", errMsg, e);
|
||||
throw new JeecgBootException(errMsg);
|
||||
}
|
||||
if (resp.contains("</think>")
|
||||
&& (null == params.getNoThinking() || params.getNoThinking())) {
|
||||
String[] thinkSplit = resp.split("</think>");
|
||||
@ -151,6 +169,7 @@ public class AIChatHandler implements IAIChatHandler {
|
||||
AssertUtils.assertNotEmpty("请选择模型", modelId);
|
||||
|
||||
AiragModel airagModel = airagModelMapper.getByIdIgnoreTenant(modelId);
|
||||
AssertUtils.assertSame("模型未激活,请先在[AI模型配置]中[测试激活]模型", airagModel.getActivateFlag(), 1);
|
||||
return chat(airagModel, messages, params);
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
package org.jeecg.modules.airag.llm.handler;
|
||||
|
||||
import dev.langchain4j.agent.tool.ToolSpecification;
|
||||
import dev.langchain4j.service.tool.ToolExecutor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* for [QQYUN-13565]【AI助手】新增创建用户和查询用户的工具扩展
|
||||
* @Description: jeecg llm工具提供者
|
||||
* @Author: chenrui
|
||||
* @Date: 2025/8/26 18:06
|
||||
*/
|
||||
public interface JeecgToolsProvider {
|
||||
|
||||
/**
|
||||
* 获取默认的工具列表
|
||||
* @return
|
||||
* @author chenrui
|
||||
* @date 2025/8/27 09:49
|
||||
*/
|
||||
public Map<ToolSpecification, ToolExecutor> getDefaultTools();
|
||||
|
||||
/**
|
||||
* jeecgLlm工具类
|
||||
* @author chenrui
|
||||
* @date 2025/8/27 09:49
|
||||
*/
|
||||
@Getter
|
||||
class JeecgLlmTools{
|
||||
ToolSpecification toolSpecification;
|
||||
ToolExecutor toolExecutor;
|
||||
|
||||
public JeecgLlmTools(ToolSpecification toolSpecification, ToolExecutor toolExecutor) {
|
||||
this.toolSpecification = toolSpecification;
|
||||
this.toolExecutor = toolExecutor;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>jeecg-boot-module</artifactId>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<version>3.8.2</version>
|
||||
<version>3.8.3</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@ -64,7 +64,7 @@ public class JeecgDemoController extends JeecgController<JeecgDemo, IJeecgDemoSe
|
||||
*/
|
||||
@Operation(summary = "获取Demo数据列表")
|
||||
@GetMapping(value = "/list")
|
||||
@PermissionData(pageComponent = "jeecg/JeecgDemoList")
|
||||
@PermissionData(pageComponent = "system/examples/demo/index")
|
||||
public Result<?> list(JeecgDemo jeecgDemo, @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo, @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
|
||||
HttpServletRequest req) {
|
||||
QueryWrapper<JeecgDemo> queryWrapper = QueryGenerator.initQueryWrapper(jeecgDemo, req.getParameterMap());
|
||||
|
||||
@ -409,4 +409,30 @@ public class DlMockController {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取车辆最后一个位置
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/findLatestCarLngLat")
|
||||
public List findLatestCarLngLat() {
|
||||
// 模拟JSON数据路径
|
||||
String path = "classpath:org/jeecg/modules/dlglong/json/CarLngLat.json";
|
||||
// 读取JSON数据
|
||||
return readJsonData(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取车辆最后一个位置
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/findCarTrace")
|
||||
public List findCarTrace() {
|
||||
// 模拟JSON数据路径
|
||||
String path = "classpath:org/jeecg/modules/dlglong/json/CarTrace.json";
|
||||
// 读取JSON数据
|
||||
return readJsonData(path);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
[
|
||||
{
|
||||
"id": "6891ba44421aa907bcb7390c",
|
||||
"alarm": "0",
|
||||
"altitude": "13",
|
||||
"direction": "0",
|
||||
"latitude": "38.918739",
|
||||
"longitude": "117.758737",
|
||||
"speed": "11",
|
||||
"status": "4980739",
|
||||
"timestamp": "2025-08-05T16:01:07",
|
||||
"imei": "18441136860"
|
||||
}
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>jeecg-boot-parent</artifactId>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<version>3.8.2</version>
|
||||
<version>3.8.3</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>jeecg-system-api</artifactId>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<version>3.8.2</version>
|
||||
<version>3.8.3</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@ package org.jeecg.common.system.api;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import org.jeecg.common.api.CommonAPI;
|
||||
import org.jeecg.common.api.dto.AiragFlowDTO;
|
||||
import org.jeecg.common.api.dto.DataLogDTO;
|
||||
import org.jeecg.common.api.dto.OnlineAuthDTO;
|
||||
import org.jeecg.common.api.dto.message.*;
|
||||
@ -13,6 +14,7 @@ import org.jeecg.common.system.api.factory.SysBaseAPIFallbackFactory;
|
||||
import org.jeecg.common.system.vo.*;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@ -681,7 +683,7 @@ public interface ISysBaseAPI extends CommonAPI {
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/sys/api/getRoleCode")
|
||||
String getRoleCodeById(String id);
|
||||
String getRoleCodeById(@RequestParam(name = "id") String id);
|
||||
|
||||
/**
|
||||
* 根据roleCode查询角色信息,可逗号分隔多个
|
||||
@ -709,7 +711,7 @@ public interface ISysBaseAPI extends CommonAPI {
|
||||
* @return JSONObject
|
||||
*/
|
||||
@GetMapping("/sys/api/queryUserById")
|
||||
JSONObject queryUserById(String id);
|
||||
JSONObject queryUserById(@RequestParam(name="id") String id);
|
||||
|
||||
|
||||
/**
|
||||
@ -770,7 +772,7 @@ public interface ISysBaseAPI extends CommonAPI {
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/sys/api/queryUserIdsByPositionIds")
|
||||
List<String> queryUserIdsByPositionIds(List<String> positionIds);
|
||||
List<String> queryUserIdsByPositionIds(@RequestParam("positionIds") List<String> positionIds);
|
||||
|
||||
/**
|
||||
* 根据部门和子部门下的所有用户账号
|
||||
@ -815,4 +817,56 @@ public interface ISysBaseAPI extends CommonAPI {
|
||||
@RequestParam(value = "currentUserName") String currentUserName
|
||||
);
|
||||
|
||||
/**
|
||||
* 根据部门编码查询公司信息
|
||||
* @param orgCode 部门编码
|
||||
* @return
|
||||
* @author chenrui
|
||||
* @date 2025/8/12 14:45
|
||||
*/
|
||||
@GetMapping(value = "/sys/api/queryCompByOrgCode")
|
||||
SysDepartModel queryCompByOrgCode(@RequestParam(name = "sysCode") String orgCode);
|
||||
|
||||
/**
|
||||
* 根据部门编码和层次查询上级公司
|
||||
*
|
||||
* @param orgCode 部门编码
|
||||
* @param level 可以传空 默认为1级 最小值为1
|
||||
* @return
|
||||
*/
|
||||
@GetMapping(value = "/sys/api/queryCompByOrgCodeAndLevel")
|
||||
SysDepartModel queryCompByOrgCodeAndLevel(@RequestParam("orgCode") String orgCode, @RequestParam("level") Integer level);
|
||||
|
||||
/**
|
||||
* 16 运行AIRag流程
|
||||
* for [QQYUN-13634]在baseapi里面封装方法,方便其他模块调用
|
||||
*
|
||||
* @param airagFlowDTO
|
||||
* @return 流程执行结果,可能是String或者Map
|
||||
* @author chenrui
|
||||
* @date 2025/9/2 11:43
|
||||
*/
|
||||
@PostMapping(value = "/sys/api/runAiragFlow")
|
||||
Object runAiragFlow(@RequestBody AiragFlowDTO airagFlowDTO);
|
||||
|
||||
/**
|
||||
* 根据部门code或部门id获取部门名称(当前和上级部门)
|
||||
*
|
||||
* @param orgCode 部门编码
|
||||
* @param depId 部门id
|
||||
* @return String 部门名称
|
||||
*/
|
||||
@GetMapping("/getDepartPathNameByOrgCode")
|
||||
String getDepartPathNameByOrgCode(@RequestParam(name = "orgCode", required = false) String orgCode, @RequestParam(name = "depId", required = false) String depId);
|
||||
|
||||
|
||||
/**
|
||||
* 根据部门ID查询部门及其子部门下用户ID <br/>
|
||||
* @param deptIds
|
||||
* @return
|
||||
* @author chenrui
|
||||
* @date 2025/09/08 15:28
|
||||
*/
|
||||
@GetMapping("/sys/api/queryUserIdsByCascadeDeptIds")
|
||||
List<String> queryUserIdsByCascadeDeptIds(@RequestParam("deptIds") List<String> deptIds);
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ package org.jeecg.common.system.api.fallback;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.common.api.dto.AiragFlowDTO;
|
||||
import org.jeecg.common.api.dto.DataLogDTO;
|
||||
import org.jeecg.common.api.dto.OnlineAuthDTO;
|
||||
import org.jeecg.common.api.dto.message.*;
|
||||
@ -475,4 +476,28 @@ public class SysBaseAPIFallback implements ISysBaseAPI {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public SysDepartModel queryCompByOrgCode(String orgCode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SysDepartModel queryCompByOrgCodeAndLevel(String orgCode, Integer level) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object runAiragFlow(AiragFlowDTO airagFlowDTO) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDepartPathNameByOrgCode(String orgCode, String depId) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> queryUserIdsByCascadeDeptIds(List<String> deptIds) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>jeecg-system-api</artifactId>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<version>3.8.2</version>
|
||||
<version>3.8.3</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@ -2,12 +2,14 @@ package org.jeecg.common.system.api;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import org.jeecg.common.api.CommonAPI;
|
||||
import org.jeecg.common.api.dto.AiragFlowDTO;
|
||||
import org.jeecg.common.api.dto.DataLogDTO;
|
||||
import org.jeecg.common.api.dto.OnlineAuthDTO;
|
||||
import org.jeecg.common.api.dto.message.*;
|
||||
import org.jeecg.common.constant.enums.DySmsEnum;
|
||||
import org.jeecg.common.constant.enums.EmailTemplateEnum;
|
||||
import org.jeecg.common.system.vo.*;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -505,6 +507,14 @@ public interface ISysBaseAPI extends CommonAPI {
|
||||
*/
|
||||
List<String> queryUserIdsByDeptIds(List<String> deptIds);
|
||||
|
||||
|
||||
/**
|
||||
* 根据部门ID查询部门及其子部门下用户ID <br/>
|
||||
* @param deptIds
|
||||
* @return
|
||||
*/
|
||||
List<String> queryUserIdsByCascadeDeptIds(List<String> deptIds);
|
||||
|
||||
/**
|
||||
* 根据部门ID查询用户账号
|
||||
* @param deptIds
|
||||
@ -557,4 +567,42 @@ public interface ISysBaseAPI extends CommonAPI {
|
||||
* @param currentUserName
|
||||
*/
|
||||
void announcementAutoRelease(String dataId, String currentUserName);
|
||||
|
||||
/**
|
||||
* 根据部门编码查询公司信息
|
||||
* @param orgCode 部门编码
|
||||
* @return
|
||||
* @author chenrui
|
||||
* @date 2025/8/12 14:53
|
||||
*/
|
||||
SysDepartModel queryCompByOrgCode(@RequestParam(name = "sysCode") String orgCode);
|
||||
|
||||
/**
|
||||
* 根据部门编码和层次查询上级公司
|
||||
*
|
||||
* @param orgCode 部门编码
|
||||
* @param level 可以传空 默认为1级 最小值为1
|
||||
* @return
|
||||
*/
|
||||
SysDepartModel queryCompByOrgCodeAndLevel(String orgCode, Integer level);
|
||||
|
||||
/**
|
||||
* 16 运行AIRag流程
|
||||
* for [QQYUN-13634]在baseapi里面封装方法,方便其他模块调用
|
||||
*
|
||||
* @param airagFlowDTO
|
||||
* @return 流程执行结果,可能是String或者Map
|
||||
* @author chenrui
|
||||
* @date 2025/9/2 11:43
|
||||
*/
|
||||
Object runAiragFlow(AiragFlowDTO airagFlowDTO);
|
||||
|
||||
/**
|
||||
* 根据部门code或部门id获取部门名称(当前和上级部门)
|
||||
*
|
||||
* @param orgCode 部门编码
|
||||
* @param depId 部门id
|
||||
* @return String 部门名称
|
||||
*/
|
||||
String getDepartPathNameByOrgCode(String orgCode, String depId);
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>jeecg-module-system</artifactId>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<version>3.8.2</version>
|
||||
<version>3.8.3</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<artifactId>jeecg-module-system</artifactId>
|
||||
<version>3.8.2</version>
|
||||
<version>3.8.3</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ public class ShiroCacheClearRunner implements ApplicationRunner {
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
// 清空所有授权redis缓存
|
||||
log.info("———————清空所有用户授权缓存———————clearAllCache——————— ");
|
||||
log.info("——— Service restart, clearing all user shiro authorization cache ——— ");
|
||||
redisUtil.removeAll(CommonConstant.PREFIX_USER_SHIRO_CACHE);
|
||||
|
||||
}
|
||||
|
||||
@ -1,33 +1,33 @@
|
||||
//package org.jeecg.config.init;
|
||||
//
|
||||
//import org.apache.catalina.Context;
|
||||
//import org.apache.tomcat.util.scan.StandardJarScanner;
|
||||
//import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
|
||||
//import org.springframework.context.annotation.Bean;
|
||||
//import org.springframework.context.annotation.Configuration;
|
||||
//
|
||||
///**
|
||||
// * @Description: TomcatFactoryConfig
|
||||
// * @author: scott
|
||||
// * @date: 2021年01月25日 11:40
|
||||
// */
|
||||
//@Configuration
|
||||
//public class TomcatFactoryConfig {
|
||||
// /**
|
||||
// * tomcat-embed-jasper引用后提示jar找不到的问题
|
||||
// */
|
||||
// @Bean
|
||||
// public TomcatServletWebServerFactory tomcatFactory() {
|
||||
// TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory() {
|
||||
// @Override
|
||||
// protected void postProcessContext(Context context) {
|
||||
// ((StandardJarScanner) context.getJarScanner()).setScanManifest(false);
|
||||
// }
|
||||
// };
|
||||
// factory.addConnectorCustomizers(connector -> {
|
||||
// connector.setProperty("relaxedPathChars", "[]{}");
|
||||
// connector.setProperty("relaxedQueryChars", "[]{}");
|
||||
// });
|
||||
// return factory;
|
||||
// }
|
||||
//}
|
||||
package org.jeecg.config.init;
|
||||
|
||||
import org.apache.catalina.Context;
|
||||
import org.apache.tomcat.util.scan.StandardJarScanner;
|
||||
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @Description: TomcatFactoryConfig
|
||||
* @author: scott
|
||||
* @date: 2021年01月25日 11:40
|
||||
*/
|
||||
@Configuration
|
||||
public class TomcatFactoryConfig {
|
||||
/**
|
||||
* tomcat-embed-jasper引用后提示jar找不到的问题
|
||||
*/
|
||||
@Bean
|
||||
public TomcatServletWebServerFactory tomcatFactory() {
|
||||
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory() {
|
||||
@Override
|
||||
protected void postProcessContext(Context context) {
|
||||
((StandardJarScanner) context.getJarScanner()).setScanManifest(false);
|
||||
}
|
||||
};
|
||||
factory.addConnectorCustomizers(connector -> {
|
||||
connector.setProperty("relaxedPathChars", "[]{}");
|
||||
connector.setProperty("relaxedQueryChars", "[]{}");
|
||||
});
|
||||
return factory;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,48 +1,48 @@
|
||||
package org.jeecg.config.init;
|
||||
|
||||
import io.undertow.UndertowOptions;
|
||||
import io.undertow.server.DefaultByteBufferPool;
|
||||
import io.undertow.server.handlers.BlockingHandler;
|
||||
import io.undertow.websockets.jsr.WebSocketDeploymentInfo;
|
||||
import org.jeecg.modules.monitor.actuator.undertow.CustomUndertowMetricsHandler;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
|
||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Undertow配置
|
||||
*
|
||||
* 解决启动提示: WARN io.undertow.websockets.jsr:68 - UT026010: Buffer pool was not set on WebSocketDeploymentInfo, the default pool will be used
|
||||
*/
|
||||
@Configuration
|
||||
public class UndertowConfiguration implements WebServerFactoryCustomizer<UndertowServletWebServerFactory> {
|
||||
|
||||
/**
|
||||
* 自定义undertow监控指标工具类
|
||||
* for [QQYUN-11902]tomcat 替换undertow 这里的功能还没修改
|
||||
*/
|
||||
@Autowired
|
||||
private CustomUndertowMetricsHandler customUndertowMetricsHandler;
|
||||
|
||||
@Override
|
||||
public void customize(UndertowServletWebServerFactory factory) {
|
||||
// 设置 Undertow 服务器参数(底层网络配置)
|
||||
factory.addBuilderCustomizers(builder -> {
|
||||
builder.setServerOption(UndertowOptions.MAX_HEADER_SIZE, 65536); // header 最大64KB
|
||||
builder.setServerOption(UndertowOptions.MAX_PARAMETERS, 10000); // 最大参数数
|
||||
});
|
||||
factory.addDeploymentInfoCustomizers(deploymentInfo -> {
|
||||
|
||||
WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo();
|
||||
|
||||
// 设置合理的参数
|
||||
webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(true, 8192));
|
||||
|
||||
deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo", webSocketDeploymentInfo);
|
||||
|
||||
// 添加自定义 监控 handler
|
||||
deploymentInfo.addInitialHandlerChainWrapper(next -> new BlockingHandler(customUndertowMetricsHandler.wrap(next)));
|
||||
});
|
||||
}
|
||||
}
|
||||
//package org.jeecg.config.init;
|
||||
//
|
||||
//import io.undertow.UndertowOptions;
|
||||
//import io.undertow.server.DefaultByteBufferPool;
|
||||
//import io.undertow.server.handlers.BlockingHandler;
|
||||
//import io.undertow.websockets.jsr.WebSocketDeploymentInfo;
|
||||
//import org.jeecg.modules.monitor.actuator.undertow.CustomUndertowMetricsHandler;
|
||||
//import org.springframework.beans.factory.annotation.Autowired;
|
||||
//import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
|
||||
//import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
//import org.springframework.context.annotation.Configuration;
|
||||
//
|
||||
///**
|
||||
// * Undertow配置
|
||||
// *
|
||||
// * 解决启动提示: WARN io.undertow.websockets.jsr:68 - UT026010: Buffer pool was not set on WebSocketDeploymentInfo, the default pool will be used
|
||||
// */
|
||||
//@Configuration
|
||||
//public class UndertowConfiguration implements WebServerFactoryCustomizer<UndertowServletWebServerFactory> {
|
||||
//
|
||||
// /**
|
||||
// * 自定义undertow监控指标工具类
|
||||
// * for [QQYUN-11902]tomcat 替换undertow 这里的功能还没修改
|
||||
// */
|
||||
// @Autowired
|
||||
// private CustomUndertowMetricsHandler customUndertowMetricsHandler;
|
||||
//
|
||||
// @Override
|
||||
// public void customize(UndertowServletWebServerFactory factory) {
|
||||
// // 设置 Undertow 服务器参数(底层网络配置)
|
||||
// factory.addBuilderCustomizers(builder -> {
|
||||
// builder.setServerOption(UndertowOptions.MAX_HEADER_SIZE, 65536); // header 最大64KB
|
||||
// builder.setServerOption(UndertowOptions.MAX_PARAMETERS, 10000); // 最大参数数
|
||||
// });
|
||||
// factory.addDeploymentInfoCustomizers(deploymentInfo -> {
|
||||
//
|
||||
// WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo();
|
||||
//
|
||||
// // 设置合理的参数
|
||||
// webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(true, 8192));
|
||||
//
|
||||
// deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo", webSocketDeploymentInfo);
|
||||
//
|
||||
// // 添加自定义 监控 handler
|
||||
// deploymentInfo.addInitialHandlerChainWrapper(next -> new BlockingHandler(customUndertowMetricsHandler.wrap(next)));
|
||||
// });
|
||||
// }
|
||||
//}
|
||||
@ -0,0 +1,258 @@
|
||||
package org.jeecg.modules.airag;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import dev.langchain4j.agent.tool.JsonSchemaProperty;
|
||||
import dev.langchain4j.agent.tool.ToolSpecification;
|
||||
import dev.langchain4j.service.tool.ToolExecutor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.system.api.ISysBaseAPI;
|
||||
import org.jeecg.common.util.PasswordUtil;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.airag.llm.handler.JeecgToolsProvider;
|
||||
import org.jeecg.modules.base.service.BaseCommonService;
|
||||
import org.jeecg.modules.system.controller.SysUserController;
|
||||
import org.jeecg.modules.system.entity.SysRole;
|
||||
import org.jeecg.modules.system.entity.SysUser;
|
||||
import org.jeecg.modules.system.mapper.SysUserMapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* for [QQYUN-13565]【AI助手】新增创建用户和查询用户的工具扩展
|
||||
* @Description: jeecg llm工具提供者
|
||||
* @Author: chenrui
|
||||
* @Date: 2025/8/26 18:06
|
||||
*/
|
||||
@Component
|
||||
public class JeecgBizToolsProvider implements JeecgToolsProvider {
|
||||
|
||||
@Autowired
|
||||
SysUserController sysUserController;
|
||||
|
||||
@Autowired
|
||||
SysUserMapper userMapper;
|
||||
|
||||
@Autowired
|
||||
private BaseCommonService baseCommonService;
|
||||
|
||||
@Autowired
|
||||
private org.jeecg.modules.system.service.ISysRoleService sysRoleService;
|
||||
|
||||
@Autowired
|
||||
private org.jeecg.modules.system.service.ISysUserRoleService sysUserRoleService;
|
||||
|
||||
@Autowired
|
||||
private org.jeecg.modules.system.service.ISysUserService sysUserService;
|
||||
|
||||
public Map<ToolSpecification, ToolExecutor> getDefaultTools(){
|
||||
Map<ToolSpecification, ToolExecutor> tools = new HashMap<>();
|
||||
JeecgLlmTools userTool = queryUserTool();
|
||||
tools.put(userTool.getToolSpecification(), userTool.getToolExecutor());
|
||||
JeecgLlmTools addUser = addUserTool();
|
||||
tools.put(addUser.getToolSpecification(), addUser.getToolExecutor());
|
||||
// 新增:查询所有角色
|
||||
JeecgLlmTools queryRoles = queryAllRolesTool();
|
||||
tools.put(queryRoles.getToolSpecification(), queryRoles.getToolExecutor());
|
||||
// 新增:给用户授予角色
|
||||
JeecgLlmTools grantRoles = grantUserRolesTool();
|
||||
tools.put(grantRoles.getToolSpecification(), grantRoles.getToolExecutor());
|
||||
return tools;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加用户
|
||||
* @return
|
||||
* @author chenrui
|
||||
* @date 2025/8/27 09:51
|
||||
*/
|
||||
private JeecgLlmTools addUserTool(){
|
||||
ToolSpecification toolSpecification = ToolSpecification.builder()
|
||||
.name("add_user")
|
||||
.description("添加用户,返回添加结果;" +
|
||||
"\n\n - 缺少必要字段时,请向用户索要." +
|
||||
"\n\n - 你应该提前判断用户的输入是否合法,比如用户名是否符合规范,手机号和邮箱是否正确等." +
|
||||
"\n\n - 提前使用用户名查询用户是否存在,如果存在则不能添加." +
|
||||
"\n\n - 添加成功后返回成功消息,如果失败则返回失败原因." +
|
||||
"\n\n - 用户名,工号,邮箱,手机号均要求唯一,提前通过查询用户工具确认唯一性." )
|
||||
.addParameter("username", JsonSchemaProperty.STRING, JsonSchemaProperty.description("用户名,必填,只允许使用字母、数字、下划线,且必须以字母开头,唯一"))
|
||||
.addParameter("password", JsonSchemaProperty.STRING, JsonSchemaProperty.description("用户密码,必填"))
|
||||
.addParameter("realname", JsonSchemaProperty.STRING, JsonSchemaProperty.description("真实姓名,必填"))
|
||||
.addParameter("workNo", JsonSchemaProperty.STRING, JsonSchemaProperty.description("工号,必填,唯一"))
|
||||
.addParameter("email", JsonSchemaProperty.STRING, JsonSchemaProperty.description("邮箱,必填,唯一"))
|
||||
.addParameter("phone", JsonSchemaProperty.STRING, JsonSchemaProperty.description("手机号,必填,唯一"))
|
||||
.build();
|
||||
ToolExecutor toolExecutor = (toolExecutionRequest, memoryId) -> {
|
||||
JSONObject arguments = JSONObject.parseObject(toolExecutionRequest.arguments());
|
||||
arguments.put("confirmPassword",arguments.get("password"));
|
||||
arguments.put("userIdentity",1);
|
||||
arguments.put("activitiSync",1);
|
||||
arguments.put("departIds","");
|
||||
String selectedRoles = arguments.getString("selectedroles");
|
||||
String selectedDeparts = arguments.getString("selecteddeparts");
|
||||
String msg = "添加用户失败";
|
||||
try {
|
||||
SysUser user = JSON.parseObject(arguments.toJSONString(), SysUser.class);
|
||||
user.setCreateTime(new Date());//设置创建时间
|
||||
String salt = oConvertUtils.randomGen(8);
|
||||
user.setSalt(salt);
|
||||
String passwordEncode = PasswordUtil.encrypt(user.getUsername(), user.getPassword(), salt);
|
||||
user.setPassword(passwordEncode);
|
||||
user.setStatus(1);
|
||||
user.setDelFlag(CommonConstant.DEL_FLAG_0);
|
||||
//用户表字段org_code不能在这里设置他的值
|
||||
user.setOrgCode(null);
|
||||
// 保存用户走一个service 保证事务
|
||||
//获取租户ids
|
||||
String relTenantIds = arguments.getString("relTenantIds");
|
||||
sysUserService.saveUser(user, selectedRoles, selectedDeparts, relTenantIds, false);
|
||||
baseCommonService.addLog("添加用户,username: " +user.getUsername() ,CommonConstant.LOG_TYPE_2, 2);
|
||||
msg = "添加用户成功";
|
||||
// 用户变更,触发同步工作流
|
||||
} catch (Exception e) {
|
||||
msg = "添加用户失败";
|
||||
}
|
||||
return msg;
|
||||
};
|
||||
return new JeecgLlmTools(toolSpecification,toolExecutor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询用户信息
|
||||
*
|
||||
* @return 用户列表JSON字符串
|
||||
* @author chenrui
|
||||
* @date 2025/8/26 18:52
|
||||
*/
|
||||
private JeecgLlmTools queryUserTool() {
|
||||
ToolSpecification toolSpecification = ToolSpecification.builder()
|
||||
.name("query_user_by_name")
|
||||
.description("查询用户详细信息,返回json数组。支持用户名、真实姓名、邮箱、手机号、工号多字段组合查询,用户名、真实姓名、邮箱、手机号均为模糊查询,工号为精确查询。无条件则返回全部用户。")
|
||||
.addParameter("username", JsonSchemaProperty.STRING, JsonSchemaProperty.description("用户名"))
|
||||
.addParameter("realname", JsonSchemaProperty.STRING, JsonSchemaProperty.description("真实姓名"))
|
||||
.addParameter("email", JsonSchemaProperty.STRING, JsonSchemaProperty.description("电子邮件"))
|
||||
.addParameter("phone", JsonSchemaProperty.STRING, JsonSchemaProperty.description("手机号"))
|
||||
.addParameter("workNo", JsonSchemaProperty.STRING, JsonSchemaProperty.description("工号"))
|
||||
.build();
|
||||
ToolExecutor toolExecutor = (toolExecutionRequest, memoryId) -> {
|
||||
SysUser args = JSONObject.parseObject(toolExecutionRequest.arguments(), SysUser.class);
|
||||
QueryWrapper<SysUser> qw = new QueryWrapper<>();
|
||||
if (StringUtils.isNotBlank(args.getUsername())) {
|
||||
qw.like("username", args.getUsername());
|
||||
}
|
||||
if (StringUtils.isNotBlank(args.getRealname())) {
|
||||
qw.like("realname", args.getRealname());
|
||||
}
|
||||
if (StringUtils.isNotBlank(args.getEmail())) {
|
||||
qw.like("email", args.getEmail());
|
||||
}
|
||||
if (StringUtils.isNotBlank(args.getPhone())) {
|
||||
qw.like("phone", args.getPhone());
|
||||
}
|
||||
if (StringUtils.isNotBlank(args.getWorkNo())) {
|
||||
qw.eq("work_no", args.getWorkNo());
|
||||
}
|
||||
qw.eq("del_flag", 0);
|
||||
List<SysUser> users = userMapper.selectList(qw);
|
||||
users.forEach(u -> { u.setPassword(null); u.setSalt(null); });
|
||||
return JSONObject.toJSONString(users);
|
||||
};
|
||||
return new JeecgLlmTools(toolSpecification, toolExecutor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有角色
|
||||
* @return
|
||||
* @author chenrui
|
||||
* @date 2025/8/27 09:52
|
||||
*/
|
||||
private JeecgLlmTools queryAllRolesTool() {
|
||||
ToolSpecification spec = ToolSpecification.builder()
|
||||
.name("query_all_roles")
|
||||
.description("查询所有角色,返回json数组。包含字段:id、roleName、roleCode;默认按创建时间/排序号规则由后端决定。")
|
||||
.addParameter("roleName", JsonSchemaProperty.STRING, JsonSchemaProperty.description("角色姓名"))
|
||||
.addParameter("roleCode", JsonSchemaProperty.STRING, JsonSchemaProperty.description("角色编码"))
|
||||
.build();
|
||||
ToolExecutor exec = (toolExecutionRequest, memoryId) -> {
|
||||
// 做租户隔离查询(若开启)
|
||||
SysRole sysRole = JSONObject.parseObject(toolExecutionRequest.arguments(), SysRole.class);
|
||||
QueryWrapper<SysRole> qw = Wrappers.query();
|
||||
if (StringUtils.isNotBlank(sysRole.getRoleName())) {
|
||||
qw.like("role_name", sysRole.getRoleName());
|
||||
}
|
||||
if (StringUtils.isNotBlank(sysRole.getRoleCode())) {
|
||||
qw.like("role_code", sysRole.getRoleCode());
|
||||
}
|
||||
// 未删除
|
||||
List<org.jeecg.modules.system.entity.SysRole> roles = sysRoleService.list(qw);
|
||||
// 仅返回核心字段
|
||||
JSONArray arr = new JSONArray();
|
||||
for (org.jeecg.modules.system.entity.SysRole r : roles) {
|
||||
JSONObject o = new JSONObject();
|
||||
o.put("id", r.getId());
|
||||
o.put("roleName", r.getRoleName());
|
||||
o.put("roleCode", r.getRoleCode());
|
||||
arr.add(o);
|
||||
}
|
||||
return arr.toJSONString();
|
||||
};
|
||||
return new JeecgLlmTools(spec, exec);
|
||||
}
|
||||
|
||||
/**
|
||||
* 给用户授予角色
|
||||
* @return
|
||||
* @author chenrui
|
||||
* @date 2025/8/27 09:52
|
||||
*/
|
||||
private JeecgLlmTools grantUserRolesTool() {
|
||||
ToolSpecification spec = ToolSpecification.builder()
|
||||
.name("grant_user_roles")
|
||||
.description("给用户授予角色,支持一次授予多个角色;如果关系已存在则跳过。返回授予结果统计。")
|
||||
.addParameter("userId", JsonSchemaProperty.STRING, JsonSchemaProperty.description("用户ID,必填"))
|
||||
.addParameter("roleIds", JsonSchemaProperty.STRING, JsonSchemaProperty.description("角色ID列表,必填,使用英文逗号分隔"))
|
||||
.build();
|
||||
ToolExecutor exec = (toolExecutionRequest, memoryId) -> {
|
||||
JSONObject args = JSONObject.parseObject(toolExecutionRequest.arguments());
|
||||
String userId = args.getString("userId");
|
||||
String roleIdsStr = args.getString("roleIds");
|
||||
if (org.apache.commons.lang3.StringUtils.isAnyBlank(userId, roleIdsStr)) {
|
||||
return "参数缺失:userId 或 roleIds";
|
||||
}
|
||||
org.jeecg.modules.system.entity.SysUser user = sysUserService.getById(userId);
|
||||
if (user == null) {
|
||||
return "用户不存在:" + userId;
|
||||
}
|
||||
String[] roleIds = roleIdsStr.split(",");
|
||||
int added = 0, existed = 0, invalid = 0;
|
||||
for (String roleId : roleIds) {
|
||||
roleId = roleId.trim();
|
||||
if (roleId.isEmpty()) continue;
|
||||
org.jeecg.modules.system.entity.SysRole role = sysRoleService.getById(roleId);
|
||||
if (role == null) { invalid++; continue; }
|
||||
com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<org.jeecg.modules.system.entity.SysUserRole> q = new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<>();
|
||||
q.eq("role_id", roleId).eq("user_id", userId);
|
||||
org.jeecg.modules.system.entity.SysUserRole one = sysUserRoleService.getOne(q);
|
||||
if (one == null) {
|
||||
org.jeecg.modules.system.entity.SysUserRole rel = new org.jeecg.modules.system.entity.SysUserRole(userId, roleId);
|
||||
boolean ok = sysUserRoleService.save(rel);
|
||||
if (ok) { added++; } else { invalid++; }
|
||||
} else {
|
||||
existed++;
|
||||
}
|
||||
}
|
||||
return String.format("授予完成:新增%d,已存在%d,无效/失败%d", added, existed, invalid);
|
||||
};
|
||||
return new JeecgLlmTools(spec, exec);
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,7 @@ package org.jeecg.modules.api.controller;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.common.api.dto.AiragFlowDTO;
|
||||
import org.jeecg.common.api.dto.DataLogDTO;
|
||||
import org.jeecg.common.api.dto.OnlineAuthDTO;
|
||||
import org.jeecg.common.api.dto.message.*;
|
||||
@ -1014,4 +1015,61 @@ public class SystemApiController {
|
||||
sysBaseApi.announcementAutoRelease(dataId, currentUserName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据部门编码查询公司信息
|
||||
* @param orgCode 部门编码
|
||||
* @return
|
||||
* @author chenrui
|
||||
* @date 2025/8/12 14:45
|
||||
*/
|
||||
@GetMapping(value = "/queryCompByOrgCode")
|
||||
SysDepartModel queryCompByOrgCode(@RequestParam(name = "sysCode") String orgCode) {
|
||||
return sysBaseApi.queryCompByOrgCode(orgCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据部门编码和层次查询上级公司
|
||||
*
|
||||
* @param orgCode 部门编码
|
||||
* @param level 可以传空 默认为1级 最小值为1
|
||||
* @return
|
||||
*/
|
||||
@GetMapping(value = "/queryCompByOrgCodeAndLevel")
|
||||
SysDepartModel queryCompByOrgCodeAndLevel(@RequestParam("orgCode") String orgCode, @RequestParam("level") Integer level){
|
||||
return sysBaseApi.queryCompByOrgCodeAndLevel(orgCode,level);
|
||||
}
|
||||
|
||||
/**
|
||||
* 运行AIRag流程
|
||||
* for [QQYUN-13634]在baseapi里面封装方法,方便其他模块调用
|
||||
* @param airagFlowDTO
|
||||
* @return 流程执行结果,可能是String或者Map
|
||||
* @return
|
||||
*/
|
||||
@PostMapping(value = "/runAiragFlow")
|
||||
Object runAiragFlow(@RequestBody AiragFlowDTO airagFlowDTO) {
|
||||
return sysBaseApi.runAiragFlow(airagFlowDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据部门code或部门id获取部门名称(当前和上级部门)
|
||||
*
|
||||
* @param orgCode 部门编码
|
||||
* @param depId 部门id
|
||||
* @return String 部门名称
|
||||
*/
|
||||
@GetMapping(value = "/getDepartPathNameByOrgCode")
|
||||
String getDepartPathNameByOrgCode(@RequestParam(name = "orgCode", required = false) String orgCode, @RequestParam(name = "depId", required = false) String depId) {
|
||||
return sysBaseApi.getDepartPathNameByOrgCode(orgCode, depId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据部门ID查询用户ID
|
||||
* @param deptIds
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/queryUserIdsByCascadeDeptIds")
|
||||
public List<String> queryUserIdsByCascadeDeptIds(@RequestParam("deptIds") List<String> deptIds){
|
||||
return sysBaseApi.queryUserIdsByCascadeDeptIds(deptIds);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,90 +1,88 @@
|
||||
package org.jeecg.modules.monitor.actuator.undertow;
|
||||
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.undertow.server.HttpHandler;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import io.undertow.server.session.*;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.LongAdder;
|
||||
|
||||
/**
|
||||
* 自定义undertow监控指标工具类
|
||||
* for [QQYUN-11902]tomcat 替换undertow 这里的功能还没修改
|
||||
* @author chenrui
|
||||
* @date 2025/4/8 19:06
|
||||
*/
|
||||
@Component("jeecgCustomUndertowMetricsHandler")
|
||||
public class CustomUndertowMetricsHandler {
|
||||
|
||||
// 用于统计已创建的 session 数量
|
||||
private final LongAdder sessionsCreated = new LongAdder();
|
||||
|
||||
// 用于统计已销毁的 session 数量
|
||||
private final LongAdder sessionsExpired = new LongAdder();
|
||||
|
||||
// 当前活跃的 session 数量
|
||||
private final AtomicInteger activeSessions = new AtomicInteger();
|
||||
|
||||
// 历史最大活跃 session 数
|
||||
private final AtomicInteger maxActiveSessions = new AtomicInteger();
|
||||
|
||||
// Undertow 内存 session 管理器(用于创建与管理 session)
|
||||
private final InMemorySessionManager sessionManager = new InMemorySessionManager("undertow-session-manager");
|
||||
|
||||
// 使用 Cookie 存储 session ID
|
||||
private final SessionConfig sessionConfig = new SessionCookieConfig();
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param meterRegistry
|
||||
* @author chenrui
|
||||
* @date 2025/4/8 19:07
|
||||
*/
|
||||
public CustomUndertowMetricsHandler(MeterRegistry meterRegistry) {
|
||||
// 注册 Micrometer 指标
|
||||
meterRegistry.gauge("undertow.sessions.created", sessionsCreated, LongAdder::longValue);
|
||||
meterRegistry.gauge("undertow.sessions.expired", sessionsExpired, LongAdder::longValue);
|
||||
meterRegistry.gauge("undertow.sessions.active.current", activeSessions, AtomicInteger::get);
|
||||
meterRegistry.gauge("undertow.sessions.active.max", maxActiveSessions, AtomicInteger::get);
|
||||
|
||||
// 添加 session 生命周期监听器,统计 session 创建与销毁
|
||||
sessionManager.registerSessionListener(new SessionListener() {
|
||||
@Override
|
||||
public void sessionCreated(Session session, HttpServerExchange exchange) {
|
||||
sessionsCreated.increment();
|
||||
int now = activeSessions.incrementAndGet();
|
||||
maxActiveSessions.getAndUpdate(max -> Math.max(max, now));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sessionDestroyed(Session session, HttpServerExchange exchange, SessionDestroyedReason reason) {
|
||||
sessionsExpired.increment();
|
||||
activeSessions.decrementAndGet();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 包装 Undertow 的 HttpHandler,实现 session 自动创建逻辑
|
||||
* @param next
|
||||
* @return
|
||||
* @author chenrui
|
||||
* @date 2025/4/8 19:07
|
||||
*/
|
||||
public HttpHandler wrap(HttpHandler next) {
|
||||
return exchange -> {
|
||||
// 获取当前 session,如果不存在则创建
|
||||
Session session = sessionManager.getSession(exchange, sessionConfig);
|
||||
if (session == null) {
|
||||
try {
|
||||
sessionManager.createSession(exchange, sessionConfig);
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
|
||||
// 执行下一个 Handler
|
||||
next.handleRequest(exchange);
|
||||
};
|
||||
}
|
||||
}
|
||||
//package org.jeecg.modules.monitor.actuator.undertow;
|
||||
//
|
||||
//import io.micrometer.core.instrument.MeterRegistry;
|
||||
//import io.undertow.server.HttpHandler;
|
||||
//import io.undertow.server.HttpServerExchange;
|
||||
//import io.undertow.server.session.*;
|
||||
//import org.springframework.stereotype.Component;
|
||||
//
|
||||
//import java.util.concurrent.atomic.AtomicInteger;
|
||||
//import java.util.concurrent.atomic.LongAdder;
|
||||
//
|
||||
///**
|
||||
// * 自定义undertow监控指标工具类
|
||||
// * for [QQYUN-11902]tomcat 替换undertow 这里的功能还没修改
|
||||
// * @author chenrui
|
||||
// * @date 2025/4/8 19:06
|
||||
// */
|
||||
//@Component("jeecgCustomUndertowMetricsHandler")
|
||||
//public class CustomUndertowMetricsHandler {
|
||||
//
|
||||
// // 用于统计已创建的 session 数量
|
||||
// private final LongAdder sessionsCreated = new LongAdder();
|
||||
//
|
||||
// // 用于统计已销毁的 session 数量
|
||||
// private final LongAdder sessionsExpired = new LongAdder();
|
||||
//
|
||||
// // 当前活跃的 session 数量
|
||||
// private final AtomicInteger activeSessions = new AtomicInteger();
|
||||
//
|
||||
// // 历史最大活跃 session 数
|
||||
// private final AtomicInteger maxActiveSessions = new AtomicInteger();
|
||||
//
|
||||
// // Undertow 内存 session 管理器(用于创建与管理 session)
|
||||
// private final InMemorySessionManager sessionManager = new InMemorySessionManager("undertow-session-manager");
|
||||
//
|
||||
// // 使用 Cookie 存储 session ID
|
||||
// private final SessionConfig sessionConfig = new SessionCookieConfig();
|
||||
//
|
||||
// /**
|
||||
// * 构造函数
|
||||
// * @param meterRegistry
|
||||
// * @author chenrui
|
||||
// * @date 2025/4/8 19:07
|
||||
// */
|
||||
// public CustomUndertowMetricsHandler(MeterRegistry meterRegistry) {
|
||||
// // 注册 Micrometer 指标
|
||||
// meterRegistry.gauge("undertow.sessions.created", sessionsCreated, LongAdder::longValue);
|
||||
// meterRegistry.gauge("undertow.sessions.expired", sessionsExpired, LongAdder::longValue);
|
||||
// meterRegistry.gauge("undertow.sessions.active.current", activeSessions, AtomicInteger::get);
|
||||
// meterRegistry.gauge("undertow.sessions.active.max", maxActiveSessions, AtomicInteger::get);
|
||||
//
|
||||
// // 添加 session 生命周期监听器,统计 session 创建与销毁
|
||||
// sessionManager.registerSessionListener(new SessionListener() {
|
||||
// @Override
|
||||
// public void sessionCreated(Session session, HttpServerExchange exchange) {
|
||||
// sessionsCreated.increment();
|
||||
// int now = activeSessions.incrementAndGet();
|
||||
// maxActiveSessions.getAndUpdate(max -> Math.max(max, now));
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void sessionDestroyed(Session session, HttpServerExchange exchange, SessionDestroyedReason reason) {
|
||||
// sessionsExpired.increment();
|
||||
// activeSessions.decrementAndGet();
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 包装 Undertow 的 HttpHandler,实现 session 自动创建逻辑
|
||||
// * @param next
|
||||
// * @return
|
||||
// * @author chenrui
|
||||
// * @date 2025/4/8 19:07
|
||||
// */
|
||||
// public HttpHandler wrap(HttpHandler next) {
|
||||
// return exchange -> {
|
||||
// // 获取当前 session,如果不存在则创建
|
||||
// Session session = sessionManager.getSession(exchange, sessionConfig);
|
||||
// if (session == null) {
|
||||
// sessionManager.createSession(exchange, sessionConfig);
|
||||
// }
|
||||
//
|
||||
// // 执行下一个 Handler
|
||||
// next.handleRequest(exchange);
|
||||
// };
|
||||
// }
|
||||
//}
|
||||
@ -382,7 +382,7 @@ public class OpenApiController extends JeecgController<OpenApi, OpenApiService>
|
||||
SwaggerInfo info = new SwaggerInfo();
|
||||
|
||||
info.setDescription("OpenAPI 接口列表");
|
||||
info.setVersion("3.8.2");
|
||||
info.setVersion("3.8.3");
|
||||
info.setTitle("OpenAPI 接口列表");
|
||||
info.setTermsOfService("https://jeecg.com");
|
||||
|
||||
|
||||
@ -13,7 +13,10 @@ 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.*;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.multipart.MultipartHttpServletRequest;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
@ -213,9 +216,7 @@ public class CommonController {
|
||||
if(oConvertUtils.isEmpty(imgPath) || CommonConstant.STRING_NULL.equals(imgPath)){
|
||||
return;
|
||||
}
|
||||
// 其余处理略
|
||||
InputStream inputStream = null;
|
||||
OutputStream outputStream = null;
|
||||
|
||||
try {
|
||||
imgPath = imgPath.replace("..", "").replace("../","");
|
||||
if (imgPath.endsWith(SymbolConstant.COMMA)) {
|
||||
@ -236,33 +237,21 @@ public class CommonController {
|
||||
// 设置强制下载不打开
|
||||
response.setContentType("application/force-download");
|
||||
response.addHeader("Content-Disposition", "attachment;fileName=" + new String(file.getName().getBytes("UTF-8"),"iso-8859-1"));
|
||||
inputStream = new BufferedInputStream(new FileInputStream(filePath));
|
||||
outputStream = response.getOutputStream();
|
||||
byte[] buf = new byte[1024];
|
||||
|
||||
// 结合 StreamingResponseBody 的流式写法
|
||||
try (InputStream inputStream = new BufferedInputStream(new FileInputStream(file));
|
||||
OutputStream outputStream = response.getOutputStream()) {
|
||||
byte[] buf = new byte[8192];
|
||||
int len;
|
||||
while ((len = inputStream.read(buf)) > 0) {
|
||||
while ((len = inputStream.read(buf)) != -1) {
|
||||
outputStream.write(buf, 0, len);
|
||||
}
|
||||
response.flushBuffer();
|
||||
outputStream.flush();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("预览文件失败" + e.getMessage());
|
||||
response.setStatus(404);
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (inputStream != null) {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
if (outputStream != null) {
|
||||
try {
|
||||
outputStream.close();
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -31,6 +31,7 @@ import org.jeecg.modules.system.service.impl.SysBaseApiImpl;
|
||||
import org.jeecg.modules.system.util.RandImageUtil;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.task.AsyncTaskExecutor;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@ -38,6 +39,9 @@ import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@ -67,8 +71,11 @@ public class LoginController {
|
||||
private BaseCommonService baseCommonService;
|
||||
@Autowired
|
||||
private JeecgBaseConfig jeecgBaseConfig;
|
||||
|
||||
private final String BASE_CHECK_CODES = "qwertyuiplkjhgfdsazxcvbnmQWERTYUPLKJHGFDSAZXCVBNM1234567890";
|
||||
/**
|
||||
* 线程池用于异步发送纪要
|
||||
*/
|
||||
public static ExecutorService cachedThreadPool = new ShiroThreadPoolExecutor(0, 1024, 60L, TimeUnit.SECONDS, new SynchronousQueue<>());
|
||||
|
||||
@Operation(summary="登录接口")
|
||||
@RequestMapping(value = "/login", method = RequestMethod.POST)
|
||||
@ -192,17 +199,7 @@ public class LoginController {
|
||||
String username = JwtUtil.getUsername(token);
|
||||
LoginUser sysUser = sysBaseApi.getUserByName(username);
|
||||
if(sysUser!=null) {
|
||||
//update-begin--Author:wangshuai Date:20200714 for:登出日志没有记录人员
|
||||
baseCommonService.addLog("用户名: "+sysUser.getRealname()+",退出成功!", CommonConstant.LOG_TYPE_1, null,sysUser);
|
||||
//update-end--Author:wangshuai Date:20200714 for:登出日志没有记录人员
|
||||
log.info(" 用户名: "+sysUser.getRealname()+",退出成功! ");
|
||||
//清空用户登录Token缓存
|
||||
redisUtil.del(CommonConstant.PREFIX_USER_TOKEN + token);
|
||||
//清空用户登录Shiro权限缓存
|
||||
redisUtil.del(CommonConstant.PREFIX_USER_SHIRO_CACHE + sysUser.getId());
|
||||
//清空用户的缓存信息(包括部门信息),例如sys:cache:user::<username>
|
||||
redisUtil.del(String.format("%s::%s", CacheConstant.SYS_USERS_CACHE, sysUser.getUsername()));
|
||||
//调用shiro的logout
|
||||
asyncClearLogoutCache(token, sysUser); // 异步清理
|
||||
SecurityUtils.getSubject().logout();
|
||||
return Result.ok("退出登录成功!");
|
||||
}else {
|
||||
@ -210,6 +207,26 @@ public class LoginController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理用户缓存
|
||||
*
|
||||
* @param token
|
||||
* @param sysUser
|
||||
*/
|
||||
private void asyncClearLogoutCache(String token, LoginUser sysUser) {
|
||||
cachedThreadPool.execute(()->{
|
||||
//清空用户登录Token缓存
|
||||
redisUtil.del(CommonConstant.PREFIX_USER_TOKEN + token);
|
||||
//清空用户登录Shiro权限缓存
|
||||
redisUtil.del(CommonConstant.PREFIX_USER_SHIRO_CACHE + sysUser.getId());
|
||||
//清空用户的缓存信息(包括部门信息),例如sys:cache:user::<username>
|
||||
redisUtil.del(String.format("%s::%s", CacheConstant.SYS_USERS_CACHE, sysUser.getUsername()));
|
||||
baseCommonService.addLog("用户名: "+sysUser.getRealname()+",退出成功!", CommonConstant.LOG_TYPE_1, null, sysUser);
|
||||
log.info("【退出成功操作】异步处理,退出后,清理用户缓存: "+sysUser.getRealname());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取访问量
|
||||
* @return
|
||||
|
||||
@ -322,7 +322,7 @@ public class SysAnnouncementController {
|
||||
try {
|
||||
// 同步企业微信、钉钉的消息通知
|
||||
Response<String> dtResponse = dingtalkService.sendActionCardMessage(sysAnnouncement, null, true);
|
||||
wechatEnterpriseService.sendTextCardMessage(sysAnnouncement, true);
|
||||
wechatEnterpriseService.sendTextCardMessage(sysAnnouncement, null,true);
|
||||
|
||||
if (dtResponse != null && dtResponse.isSuccess()) {
|
||||
String taskId = dtResponse.getResult();
|
||||
@ -726,6 +726,18 @@ public class SysAnnouncementController {
|
||||
return Result.ok("公告消息访问次数+1次");
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量下载文件
|
||||
* @param id
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
@GetMapping("/downLoadFiles")
|
||||
public void downLoadFiles(@RequestParam(name="id") String id,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response){
|
||||
sysAnnouncementService.downLoadFiles(id,request,response);
|
||||
}
|
||||
/**
|
||||
* 根据异常信息确定友好的错误提示
|
||||
*/
|
||||
|
||||
@ -2,7 +2,6 @@ package org.jeecg.modules.system.controller;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
@ -11,22 +10,22 @@ import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
import org.jeecg.common.constant.CacheConstant;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
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.ImportExcelUtil;
|
||||
import org.jeecg.common.util.RedisUtil;
|
||||
import org.jeecg.common.util.YouBianCodeUtil;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
|
||||
import org.jeecg.modules.system.entity.SysDepart;
|
||||
import org.jeecg.modules.system.entity.SysUser;
|
||||
import org.jeecg.modules.system.excelstyle.ExcelExportSysUserStyle;
|
||||
import org.jeecg.modules.system.model.DepartIdModel;
|
||||
import org.jeecg.modules.system.model.SysDepartTreeModel;
|
||||
import org.jeecg.modules.system.service.ISysDepartService;
|
||||
import org.jeecg.modules.system.service.ISysUserDepartService;
|
||||
import org.jeecg.modules.system.service.ISysUserService;
|
||||
import org.jeecg.modules.system.vo.SysDepartExportVo;
|
||||
import org.jeecg.modules.system.vo.SysPositionSelectTreeVo;
|
||||
import org.jeecg.modules.system.vo.lowapp.ExportDepartVo;
|
||||
import org.jeecgframework.poi.excel.ExcelImportUtil;
|
||||
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
|
||||
@ -148,6 +147,32 @@ public class SysDepartController {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步查询部门和岗位list
|
||||
* @param parentId 父节点 异步加载时传递
|
||||
* @param ids 前端回显是传递
|
||||
* @param primaryKey 主键字段(id或者orgCode)
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(value = "/queryDepartAndPostTreeSync", method = RequestMethod.GET)
|
||||
public Result<List<SysDepartTreeModel>> queryDepartAndPostTreeSync(@RequestParam(name = "pid", required = false) String parentId,
|
||||
@RequestParam(name = "ids", required = false) String ids,
|
||||
@RequestParam(name = "primaryKey", required = false) String primaryKey,
|
||||
@RequestParam(name = "departIds", required = false) String departIds,
|
||||
@RequestParam(name = "name", required = false) String orgName) {
|
||||
Result<List<SysDepartTreeModel>> result = new Result<>();
|
||||
try {
|
||||
List<SysDepartTreeModel> list = sysDepartService.queryDepartAndPostTreeSync(parentId,ids, primaryKey, departIds, orgName);
|
||||
result.setResult(list);
|
||||
result.setSuccess(true);
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(),e);
|
||||
result.setSuccess(false);
|
||||
result.setMessage("查询失败");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某个部门的所有父级部门的ID
|
||||
*
|
||||
@ -321,7 +346,10 @@ public class SysDepartController {
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(value = "/searchBy", method = RequestMethod.GET)
|
||||
public Result<List<SysDepartTreeModel>> searchBy(@RequestParam(name = "keyWord", required = true) String keyWord,@RequestParam(name = "myDeptSearch", required = false) String myDeptSearch) {
|
||||
public Result<List<SysDepartTreeModel>> searchBy(@RequestParam(name = "keyWord", required = true) String keyWord,
|
||||
@RequestParam(name = "myDeptSearch", required = false) String myDeptSearch,
|
||||
@RequestParam(name = "orgCategory", required = false) String orgCategory,
|
||||
@RequestParam(name = "departIds", required = false) String depIds) {
|
||||
Result<List<SysDepartTreeModel>> result = new Result<List<SysDepartTreeModel>>();
|
||||
//部门查询,myDeptSearch为1时为我的部门查询,登录用户为上级时查只查负责部门下数据
|
||||
LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
|
||||
@ -329,7 +357,7 @@ public class SysDepartController {
|
||||
if(oConvertUtils.isNotEmpty(user.getUserIdentity()) && user.getUserIdentity().equals( CommonConstant.USER_IDENTITY_2 )){
|
||||
departIds = user.getDepartIds();
|
||||
}
|
||||
List<SysDepartTreeModel> treeList = this.sysDepartService.searchByKeyWord(keyWord,myDeptSearch,departIds);
|
||||
List<SysDepartTreeModel> treeList = this.sysDepartService.searchByKeyWord(keyWord,myDeptSearch,departIds,orgCategory,depIds);
|
||||
if (treeList == null || treeList.size() == 0) {
|
||||
result.setSuccess(false);
|
||||
result.setMessage("未查询匹配数据!");
|
||||
@ -382,11 +410,14 @@ public class SysDepartController {
|
||||
mv.addObject(NormalExcelConstants.FILE_NAME, "部门列表");
|
||||
mv.addObject(NormalExcelConstants.CLASS, SysDepartExportVo.class);
|
||||
LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
|
||||
mv.addObject(NormalExcelConstants.PARAMS, new ExportParams("导入规则:\n" +
|
||||
ExportParams exportParams = new ExportParams("导入规则:\n" +
|
||||
"1、标题为第三行,部门路径和部门名称的标题不允许修改,否则会匹配失败;第四行为数据填写范围;\n" +
|
||||
"2、部门路径用英文字符/分割,部门名称为部门路径的最后一位;\n" +
|
||||
"3、部门从一级名称开始创建,如果有同级就需要多添加一行,如研发部/研发一部;研发部/研发二部;\n" +
|
||||
"4、自定义的部门编码需要满足规则才能导入。如一级部门编码为A01,那么子部门为A01A01,同级子部门为A01A02,编码固定为三位,首字母为A-Z,后两位为数字0-99,依次递增;", "导出人:"+user.getRealname(), "导出信息"));
|
||||
"4、自定义的部门编码需要满足规则才能导入。如一级部门编码为A01,那么子部门为A01A01,同级子部门为A01A02,编码固定为三位,首字母为A-Z,后两位为数字0-99,依次递增;", "导出人:" + user.getRealname(), "导出信息");
|
||||
exportParams.setTitleHeight((short)70);
|
||||
exportParams.setStyle(ExcelExportSysUserStyle.class);
|
||||
mv.addObject(NormalExcelConstants.PARAMS, exportParams);
|
||||
mv.addObject(NormalExcelConstants.DATA_LIST, sysDepartExportVos);
|
||||
//update-end---author:wangshuai---date:2023-10-19---for:【QQYUN-5482】系统的部门导入导出也可以改成敲敲云模式的部门路径---
|
||||
|
||||
@ -580,6 +611,11 @@ public class SysDepartController {
|
||||
String[] ids = deptIds.split(",");
|
||||
Collection<String> idList = Arrays.asList(ids);
|
||||
Collection<SysDepart> deptList = sysDepartService.listByIds(idList);
|
||||
// 设置部门路径名称
|
||||
for (SysDepart depart : deptList) {
|
||||
String departPathName = sysDepartService.getDepartPathNameByOrgCode(depart.getOrgCode(),null);
|
||||
depart.setDepartPathName(departPathName);
|
||||
}
|
||||
result.setSuccess(true);
|
||||
result.setResult(deptList);
|
||||
return result;
|
||||
@ -686,4 +722,39 @@ public class SysDepartController {
|
||||
return Result.error("文件导入失败!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据部门id和职级id获取岗位信息
|
||||
*/
|
||||
@GetMapping("/getPositionByDepartId")
|
||||
public Result<List<SysPositionSelectTreeVo>> getPositionByDepartId(@RequestParam(name = "parentId") String parentId,
|
||||
@RequestParam(name = "departId",required = false) String departId,
|
||||
@RequestParam(name = "positionId") String positionId){
|
||||
List<SysPositionSelectTreeVo> positionByDepartId = sysDepartService.getPositionByDepartId(parentId, departId, positionId);
|
||||
return Result.OK(positionByDepartId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取职级关系
|
||||
* @param departId
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/getRankRelation")
|
||||
public Result<List<SysPositionSelectTreeVo>> getRankRelation(@RequestParam(name = "departId") String departId){
|
||||
List<SysPositionSelectTreeVo> list = sysDepartService.getRankRelation(departId);
|
||||
return Result.ok(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据部门code获取当前和上级部门名称
|
||||
*
|
||||
* @param orgCode
|
||||
* @param depId
|
||||
* @return String 部门名称
|
||||
*/
|
||||
@GetMapping("/getDepartPathNameByOrgCode")
|
||||
public Result<String> getDepartPathNameByOrgCode(@RequestParam(name = "orgCode", required = false) String orgCode,
|
||||
@RequestParam(name = "depId", required = false) String depId) {
|
||||
String departName = sysDepartService.getDepartPathNameByOrgCode(orgCode, depId);
|
||||
return Result.OK(departName);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.system.base.controller.JeecgController;
|
||||
import org.jeecg.common.system.query.QueryGenerator;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.system.entity.SysLog;
|
||||
@ -24,6 +24,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@ -36,7 +37,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
@RestController
|
||||
@RequestMapping("/sys/log")
|
||||
@Slf4j
|
||||
public class SysLogController {
|
||||
public class SysLogController extends JeecgController<SysLog, ISysLogService> {
|
||||
|
||||
@Autowired
|
||||
private ISysLogService sysLogService;
|
||||
@ -123,5 +124,16 @@ public class SysLogController {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出excel
|
||||
* for [QQYUN-13431]【jeecg】日志管理中添加大数据导出功能
|
||||
* @param request
|
||||
* @param syslog
|
||||
*/
|
||||
@RequestMapping(value = "/exportXls")
|
||||
public ModelAndView exportXls(HttpServletRequest request, SysLog syslog) {
|
||||
return super.exportXlsForBigData(request, syslog, SysLog.class, "syslog", 10000);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -90,6 +90,8 @@ public class SysPositionController {
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------
|
||||
QueryWrapper<SysPosition> queryWrapper = QueryGenerator.initQueryWrapper(sysPosition, req.getParameterMap());
|
||||
queryWrapper.orderByAsc("post_level");
|
||||
queryWrapper.orderByDesc("create_time");
|
||||
Page<SysPosition> page = new Page<SysPosition>(pageNo, pageSize);
|
||||
IPage<SysPosition> pageList = sysPositionService.page(page, queryWrapper);
|
||||
result.setSuccess(true);
|
||||
|
||||
@ -1001,4 +1001,26 @@ public class SysTenantController {
|
||||
sysTenantService.deleteUser(sysUser, tenantId);
|
||||
return Result.ok("删除用户成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据租户id和用户id获取用户的产品包列表和当前用户下的产品包id
|
||||
*
|
||||
* @param tenantId
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/listPackByTenantUserId")
|
||||
public Result<Map<String, Object>> listPackByTenantUserId(@RequestParam("tenantId") String tenantId,
|
||||
@RequestParam("userId") String userId,
|
||||
HttpServletRequest request) {
|
||||
if (null == tenantId) {
|
||||
return null;
|
||||
}
|
||||
List<SysTenantPack> list = sysTenantPackService.getPackListByTenantId(tenantId);
|
||||
List<String> userPackIdList = sysTenantPackService.getPackIdByUserIdAndTenantId(userId, oConvertUtils.getInt(tenantId));
|
||||
Map<String, Object> map = new HashMap<>(5);
|
||||
map.put("packList", list);
|
||||
map.put("userPackIdList", userPackIdList);
|
||||
return Result.ok(map);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,265 +0,0 @@
|
||||
package org.jeecg.modules.system.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.system.query.QueryGenerator;
|
||||
import org.jeecg.common.system.vo.LoginUser;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.system.entity.SysUserAgent;
|
||||
import org.jeecg.modules.system.service.ISysUserAgentService;
|
||||
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.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.multipart.MultipartHttpServletRequest;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* @Title: Controller
|
||||
* @Description: 用户代理人设置
|
||||
* @Author: jeecg-boot
|
||||
* @Date: 2019-04-17
|
||||
* @Version: V1.0
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/sys/sysUserAgent")
|
||||
@Slf4j
|
||||
public class SysUserAgentController {
|
||||
@Autowired
|
||||
private ISysUserAgentService sysUserAgentService;
|
||||
|
||||
@Value("${jeecg.path.upload}")
|
||||
private String upLoadPath;
|
||||
|
||||
/**
|
||||
* 分页列表查询
|
||||
* @param sysUserAgent
|
||||
* @param pageNo
|
||||
* @param pageSize
|
||||
* @param req
|
||||
* @return
|
||||
*/
|
||||
@GetMapping(value = "/list")
|
||||
public Result<IPage<SysUserAgent>> queryPageList(SysUserAgent sysUserAgent,
|
||||
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
|
||||
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
|
||||
HttpServletRequest req) {
|
||||
Result<IPage<SysUserAgent>> result = new Result<IPage<SysUserAgent>>();
|
||||
QueryWrapper<SysUserAgent> queryWrapper = QueryGenerator.initQueryWrapper(sysUserAgent, req.getParameterMap());
|
||||
Page<SysUserAgent> page = new Page<SysUserAgent>(pageNo, pageSize);
|
||||
IPage<SysUserAgent> pageList = sysUserAgentService.page(page, queryWrapper);
|
||||
result.setSuccess(true);
|
||||
result.setResult(pageList);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加
|
||||
* @param sysUserAgent
|
||||
* @return
|
||||
*/
|
||||
@PostMapping(value = "/add")
|
||||
public Result<SysUserAgent> add(@RequestBody SysUserAgent sysUserAgent) {
|
||||
Result<SysUserAgent> result = new Result<SysUserAgent>();
|
||||
try {
|
||||
sysUserAgentService.save(sysUserAgent);
|
||||
result.success("代理人设置成功!");
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(),e);
|
||||
result.error500("操作失败");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
* @param sysUserAgent
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
|
||||
public Result<SysUserAgent> edit(@RequestBody SysUserAgent sysUserAgent) {
|
||||
Result<SysUserAgent> result = new Result<SysUserAgent>();
|
||||
SysUserAgent sysUserAgentEntity = sysUserAgentService.getById(sysUserAgent.getId());
|
||||
if(sysUserAgentEntity==null) {
|
||||
result.error500("未找到对应实体");
|
||||
}else {
|
||||
boolean ok = sysUserAgentService.updateById(sysUserAgent);
|
||||
//TODO 返回false说明什么?
|
||||
if(ok) {
|
||||
result.success("代理人设置成功!");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过id删除
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<SysUserAgent> delete(@RequestParam(name="id",required=true) String id) {
|
||||
Result<SysUserAgent> result = new Result<SysUserAgent>();
|
||||
SysUserAgent sysUserAgent = sysUserAgentService.getById(id);
|
||||
if(sysUserAgent==null) {
|
||||
result.error500("未找到对应实体");
|
||||
}else {
|
||||
boolean ok = sysUserAgentService.removeById(id);
|
||||
if(ok) {
|
||||
result.success("删除成功!");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除
|
||||
* @param ids
|
||||
* @return
|
||||
*/
|
||||
@DeleteMapping(value = "/deleteBatch")
|
||||
public Result<SysUserAgent> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
|
||||
Result<SysUserAgent> result = new Result<SysUserAgent>();
|
||||
if(ids==null || "".equals(ids.trim())) {
|
||||
result.error500("参数不识别!");
|
||||
}else {
|
||||
this.sysUserAgentService.removeByIds(Arrays.asList(ids.split(",")));
|
||||
result.success("删除成功!");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过id查询
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
@GetMapping(value = "/queryById")
|
||||
public Result<SysUserAgent> queryById(@RequestParam(name="id",required=true) String id) {
|
||||
Result<SysUserAgent> result = new Result<SysUserAgent>();
|
||||
SysUserAgent sysUserAgent = sysUserAgentService.getById(id);
|
||||
if(sysUserAgent==null) {
|
||||
result.error500("未找到对应实体");
|
||||
}else {
|
||||
result.setResult(sysUserAgent);
|
||||
result.setSuccess(true);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过userName查询
|
||||
* @param userName
|
||||
* @return
|
||||
*/
|
||||
@GetMapping(value = "/queryByUserName")
|
||||
public Result<SysUserAgent> queryByUserName(@RequestParam(name="userName",required=true) String userName) {
|
||||
Result<SysUserAgent> result = new Result<SysUserAgent>();
|
||||
LambdaQueryWrapper<SysUserAgent> queryWrapper = new LambdaQueryWrapper<SysUserAgent>();
|
||||
queryWrapper.eq(SysUserAgent::getUserName, userName);
|
||||
SysUserAgent sysUserAgent = sysUserAgentService.getOne(queryWrapper);
|
||||
if(sysUserAgent==null) {
|
||||
result.error500("未找到对应实体");
|
||||
}else {
|
||||
result.setResult(sysUserAgent);
|
||||
result.setSuccess(true);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出excel
|
||||
*
|
||||
* @param sysUserAgent
|
||||
* @param request
|
||||
*/
|
||||
@RequestMapping(value = "/exportXls")
|
||||
public ModelAndView exportXls(SysUserAgent sysUserAgent,HttpServletRequest request) {
|
||||
// Step.1 组装查询条件
|
||||
QueryWrapper<SysUserAgent> queryWrapper = QueryGenerator.initQueryWrapper(sysUserAgent, request.getParameterMap());
|
||||
//Step.2 AutoPoi 导出Excel
|
||||
ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
|
||||
List<SysUserAgent> pageList = sysUserAgentService.list(queryWrapper);
|
||||
//导出文件名称
|
||||
mv.addObject(NormalExcelConstants.FILE_NAME, "用户代理人设置列表");
|
||||
mv.addObject(NormalExcelConstants.CLASS, SysUserAgent.class);
|
||||
LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
|
||||
ExportParams exportParams = new ExportParams("用户代理人设置列表数据", "导出人:"+user.getRealname(), "导出信息");
|
||||
exportParams.setImageBasePath(upLoadPath);
|
||||
mv.addObject(NormalExcelConstants.PARAMS, exportParams);
|
||||
mv.addObject(NormalExcelConstants.DATA_LIST, pageList);
|
||||
return mv;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过excel导入数据
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
|
||||
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
|
||||
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
|
||||
Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
|
||||
for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
|
||||
// 获取上传文件对象
|
||||
MultipartFile file = entity.getValue();
|
||||
ImportParams params = new ImportParams();
|
||||
params.setTitleRows(2);
|
||||
params.setHeadRows(1);
|
||||
params.setNeedSave(true);
|
||||
try {
|
||||
List<SysUserAgent> listSysUserAgents = ExcelImportUtil.importExcel(file.getInputStream(), SysUserAgent.class, params);
|
||||
for (SysUserAgent sysUserAgentExcel : listSysUserAgents) {
|
||||
sysUserAgentService.save(sysUserAgentExcel);
|
||||
}
|
||||
return Result.ok("文件导入成功!数据行数:" + listSysUserAgents.size());
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(),e);
|
||||
return Result.error("文件导入失败!");
|
||||
} finally {
|
||||
try {
|
||||
file.getInputStream().close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return Result.error("文件导入失败!");
|
||||
}
|
||||
|
||||
}
|
||||
@ -3,7 +3,6 @@ package org.jeecg.modules.system.controller;
|
||||
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
@ -13,10 +12,8 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.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;
|
||||
@ -28,23 +25,23 @@ 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.excelstyle.ExcelExportSysUserStyle;
|
||||
import org.jeecg.modules.system.model.DepartIdModel;
|
||||
import org.jeecg.modules.system.model.SysUserSysDepPostModel;
|
||||
import org.jeecg.modules.system.model.SysUserSysDepartModel;
|
||||
import org.jeecg.modules.system.service.*;
|
||||
import org.jeecg.modules.system.util.ImportSysUserCache;
|
||||
import org.jeecg.modules.system.vo.SysDepartUsersVO;
|
||||
import org.jeecg.modules.system.vo.SysUserExportVo;
|
||||
import org.jeecg.modules.system.vo.SysUserRoleVO;
|
||||
import org.jeecg.modules.system.vo.lowapp.DepartAndUserInfo;
|
||||
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.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.multipart.MultipartHttpServletRequest;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
@ -93,9 +90,6 @@ public class SysUserController {
|
||||
@Autowired
|
||||
private BaseCommonService baseCommonService;
|
||||
|
||||
@Autowired
|
||||
private ISysUserAgentService sysUserAgentService;
|
||||
|
||||
@Autowired
|
||||
private ISysPositionService sysPositionService;
|
||||
|
||||
@ -170,7 +164,7 @@ public class SysUserController {
|
||||
// 保存用户走一个service 保证事务
|
||||
//获取租户ids
|
||||
String relTenantIds = jsonObject.getString("relTenantIds");
|
||||
sysUserService.saveUser(user, selectedRoles, selectedDeparts, relTenantIds);
|
||||
sysUserService.saveUser(user, selectedRoles, selectedDeparts, relTenantIds, false);
|
||||
baseCommonService.addLog("添加用户,username: " +user.getUsername() ,CommonConstant.LOG_TYPE_2, 2);
|
||||
result.success("添加成功!");
|
||||
} catch (Exception e) {
|
||||
@ -484,15 +478,25 @@ public class SysUserController {
|
||||
}
|
||||
//update-end--Author:kangxiaolin Date:20180825 for:[03]用户导出,如果选择数据则只导出相关数据----------------------
|
||||
List<SysUser> pageList = sysUserService.list(queryWrapper);
|
||||
List<SysUserExportVo> list = sysUserService.getDepartAndRoleExportMsg(pageList);
|
||||
|
||||
//导出文件名称
|
||||
mv.addObject(NormalExcelConstants.FILE_NAME, "用户列表");
|
||||
mv.addObject(NormalExcelConstants.CLASS, SysUser.class);
|
||||
mv.addObject(NormalExcelConstants.CLASS, SysUserExportVo.class);
|
||||
LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
|
||||
ExportParams exportParams = new ExportParams("用户列表数据", "导出人:"+user.getRealname(), "导出信息");
|
||||
ExportParams exportParams = new ExportParams("导入规则:\n" +
|
||||
"1. 用户名为必填项,仅支持新增数据导入;\n" +
|
||||
"2. 多个部门、角色或负责部门请用英文分号 ; 分隔,如:财务部;研发部;\n" +
|
||||
"3. 部门层级请用英文斜杠 / 分隔,如:北京公司/财务部/财务一部;\n" +
|
||||
"4. 部门类型需与部门层级一致,也用 / 分隔,如:公司/部门/部门 或 1/3/3,多个类型用 ; 分隔。机构类型编码:公司(1),子公司(4),部门(3);\n" +
|
||||
"5. 部门根据用户名匹配,若存在多个则关联最新创建的部门,不存在时自动新增;\n" +
|
||||
"6. 负责部门与所属部门导入规则一致,若所属部门不包含负责部门,则不关联负责部门;\n" +
|
||||
"7. 用户主岗位导入时会在部门下自动创建新岗位,职级为空时默认不与岗位建立关联。", "导出人:" + user.getRealname(), "导出信息");
|
||||
exportParams.setTitleHeight((short)70);
|
||||
exportParams.setStyle(ExcelExportSysUserStyle.class);
|
||||
exportParams.setImageBasePath(upLoadPath);
|
||||
mv.addObject(NormalExcelConstants.PARAMS, exportParams);
|
||||
mv.addObject(NormalExcelConstants.DATA_LIST, pageList);
|
||||
mv.addObject(NormalExcelConstants.DATA_LIST, list);
|
||||
return mv;
|
||||
}
|
||||
|
||||
@ -506,78 +510,8 @@ public class SysUserController {
|
||||
@RequiresPermissions("system:user:import")
|
||||
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
|
||||
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response)throws IOException {
|
||||
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
|
||||
Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
|
||||
// 错误信息
|
||||
List<String> errorMessage = new ArrayList<>();
|
||||
int successLines = 0, errorLines = 0;
|
||||
for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
|
||||
MultipartFile file = entity.getValue();// 获取上传文件对象
|
||||
ImportParams params = new ImportParams();
|
||||
params.setTitleRows(2);
|
||||
params.setHeadRows(1);
|
||||
params.setNeedSave(true);
|
||||
try {
|
||||
List<SysUser> listSysUsers = ExcelImportUtil.importExcel(file.getInputStream(), SysUser.class, params);
|
||||
for (int i = 0; i < listSysUsers.size(); i++) {
|
||||
SysUser sysUserExcel = listSysUsers.get(i);
|
||||
if (StringUtils.isBlank(sysUserExcel.getPassword())) {
|
||||
// 密码默认为 “123456”
|
||||
sysUserExcel.setPassword("123456");
|
||||
}
|
||||
// 密码加密加盐
|
||||
String salt = oConvertUtils.randomGen(8);
|
||||
sysUserExcel.setSalt(salt);
|
||||
String passwordEncode = PasswordUtil.encrypt(sysUserExcel.getUsername(), sysUserExcel.getPassword(), salt);
|
||||
sysUserExcel.setPassword(passwordEncode);
|
||||
try {
|
||||
sysUserService.save(sysUserExcel);
|
||||
successLines++;
|
||||
} catch (Exception e) {
|
||||
errorLines++;
|
||||
String message = e.getMessage().toLowerCase();
|
||||
int lineNumber = i + 1;
|
||||
// 通过索引名判断出错信息
|
||||
if (message.contains(CommonConstant.SQL_INDEX_UNIQ_SYS_USER_USERNAME)) {
|
||||
errorMessage.add("第 " + lineNumber + " 行:用户名已经存在,忽略导入。");
|
||||
} else if (message.contains(CommonConstant.SQL_INDEX_UNIQ_SYS_USER_WORK_NO)) {
|
||||
errorMessage.add("第 " + lineNumber + " 行:工号已经存在,忽略导入。");
|
||||
} else if (message.contains(CommonConstant.SQL_INDEX_UNIQ_SYS_USER_PHONE)) {
|
||||
errorMessage.add("第 " + lineNumber + " 行:手机号已经存在,忽略导入。");
|
||||
} else if (message.contains(CommonConstant.SQL_INDEX_UNIQ_SYS_USER_EMAIL)) {
|
||||
errorMessage.add("第 " + lineNumber + " 行:电子邮件已经存在,忽略导入。");
|
||||
} else if (message.contains(CommonConstant.SQL_INDEX_UNIQ_SYS_USER)) {
|
||||
errorMessage.add("第 " + lineNumber + " 行:违反表唯一性约束。");
|
||||
} else {
|
||||
errorMessage.add("第 " + lineNumber + " 行:未知错误,忽略导入");
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
// 批量将部门和用户信息建立关联关系
|
||||
String departIds = sysUserExcel.getDepartIds();
|
||||
if (StringUtils.isNotBlank(departIds)) {
|
||||
String userId = sysUserExcel.getId();
|
||||
String[] departIdArray = departIds.split(",");
|
||||
List<SysUserDepart> userDepartList = new ArrayList<>(departIdArray.length);
|
||||
for (String departId : departIdArray) {
|
||||
userDepartList.add(new SysUserDepart(userId, departId));
|
||||
}
|
||||
sysUserDepartService.saveBatch(userDepartList);
|
||||
}
|
||||
|
||||
}
|
||||
} catch (Exception e) {
|
||||
errorMessage.add("发生异常:" + e.getMessage());
|
||||
log.error(e.getMessage(), e);
|
||||
} finally {
|
||||
try {
|
||||
file.getInputStream().close();
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ImportExcelUtil.imporReturnRes(errorLines,successLines,errorMessage);
|
||||
//return ImportOldUserUtil.importOldSysUser(request);
|
||||
return sysUserService.importSysUser(request);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -614,6 +548,17 @@ public class SysUserController {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @功能:根据userName查询用户以及部门信息
|
||||
* @param userName
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(value = "/queryUserAndDeptByName", method = RequestMethod.GET)
|
||||
public Result<Map<String,String>> queryUserAndDeptByName(@RequestParam(name = "userName") String userName) {
|
||||
Map<String,String> userInfo= sysUserService.queryUserAndDeptByName(userName);
|
||||
return Result.ok(userInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 首页用户重置密码
|
||||
*/
|
||||
@ -646,7 +591,8 @@ public class SysUserController {
|
||||
Page<SysUser> page = new Page<SysUser>(pageNo, pageSize);
|
||||
String roleId = req.getParameter("roleId");
|
||||
String username = req.getParameter("username");
|
||||
IPage<SysUser> pageList = sysUserService.getUserByRoleId(page,roleId,username);
|
||||
String realname = req.getParameter("realname");
|
||||
IPage<SysUser> pageList = sysUserService.getUserByRoleId(page,roleId,username,realname);
|
||||
result.setSuccess(true);
|
||||
result.setResult(pageList);
|
||||
return result;
|
||||
@ -811,38 +757,8 @@ public class SysUserController {
|
||||
SysUser userParams
|
||||
) {
|
||||
IPage page = new Page(pageNo, pageSize);
|
||||
IPage<SysUserSysDepartModel> pageList = sysUserService.queryUserByOrgCode(orgCode, userParams, page);
|
||||
List<SysUserSysDepartModel> list = pageList.getRecords();
|
||||
|
||||
// 记录所有出现过的 user, key = userId
|
||||
Map<String, JSONObject> hasUser = new HashMap<>(list.size());
|
||||
|
||||
JSONArray resultJson = new JSONArray(list.size());
|
||||
|
||||
for (SysUserSysDepartModel item : list) {
|
||||
String userId = item.getId();
|
||||
// userId
|
||||
JSONObject getModel = hasUser.get(userId);
|
||||
// 之前已存在过该用户,直接合并数据
|
||||
if (getModel != null) {
|
||||
String departName = getModel.get("departName").toString();
|
||||
getModel.put("departName", (departName + " | " + item.getDepartName()));
|
||||
} else {
|
||||
// 将用户对象转换为json格式,并将部门信息合并到 json 中
|
||||
JSONObject json = JSON.parseObject(JSON.toJSONString(item));
|
||||
json.remove("id");
|
||||
json.put("userId", userId);
|
||||
json.put("departId", item.getDepartId());
|
||||
json.put("departName", item.getDepartName());
|
||||
// json.put("avatar", item.getSysUser().getAvatar());
|
||||
resultJson.add(json);
|
||||
hasUser.put(userId, json);
|
||||
}
|
||||
}
|
||||
|
||||
IPage<JSONObject> result = new Page<>(pageNo, pageSize, pageList.getTotal());
|
||||
result.setRecords(resultJson.toJavaList(JSONObject.class));
|
||||
return Result.ok(result);
|
||||
IPage<SysUserSysDepPostModel> pageList = sysUserService.queryDepartPostUserByOrgCode(orgCode, userParams, page);
|
||||
return Result.ok(pageList);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1227,6 +1143,14 @@ public class SysUserController {
|
||||
map.put("sysUserName", sysUser.getRealname()); // 当前登录用户真实名称
|
||||
map.put("sysOrgCode", sysUser.getOrgCode()); // 当前登录用户部门编号
|
||||
|
||||
// 【QQYUN-12930】设置部门名称
|
||||
if (oConvertUtils.isNotEmpty(sysUser.getOrgCode())) {
|
||||
SysDepart sysDepart = sysDepartService.lambdaQuery().select(SysDepart::getDepartName).eq(SysDepart::getOrgCode, sysUser.getOrgCode()).one();
|
||||
if (sysDepart != null) {
|
||||
map.put("sysOrgName", sysDepart.getDepartName()); // 当前登录用户部门名称
|
||||
}
|
||||
}
|
||||
|
||||
log.debug(" ------ 通过令牌获取部分用户信息,已获取的用户信息: " + map);
|
||||
|
||||
return Result.ok(map);
|
||||
@ -1619,22 +1543,6 @@ public class SysUserController {
|
||||
return Result.OK(pageList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户离职(新增代理人和用户状态变更操作)【低代码应用专用接口】
|
||||
* @param sysUserAgent
|
||||
* @return
|
||||
*/
|
||||
@PutMapping("/userQuitAgent")
|
||||
public Result<String> userQuitAgent(@RequestBody SysUserAgent sysUserAgent){
|
||||
//判断id是否为空
|
||||
if(oConvertUtils.isNotEmpty(sysUserAgent.getId())){
|
||||
sysUserAgentService.updateById(sysUserAgent);
|
||||
}else{
|
||||
sysUserAgentService.save(sysUserAgent);
|
||||
}
|
||||
sysUserService.userQuit(sysUserAgent.getUserName());
|
||||
return Result.ok("离职成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取被逻辑删除的用户列表,无分页【低代码应用专用接口】
|
||||
@ -1656,29 +1564,6 @@ public class SysUserController {
|
||||
return Result.ok(quitList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新刪除状态和离职状态【低代码应用专用接口】
|
||||
* @param jsonObject
|
||||
* @return Result<String>
|
||||
*/
|
||||
@PutMapping("/putCancelQuit")
|
||||
public Result<String> putCancelQuit(@RequestBody JSONObject jsonObject, HttpServletRequest request){
|
||||
String userIds = jsonObject.getString("userIds");
|
||||
String usernames = jsonObject.getString("usernames");
|
||||
Integer tenantId = oConvertUtils.getInt(TokenUtils.getTenantIdByRequest(request),0);
|
||||
//将状态改成未删除
|
||||
if (StringUtils.isNotBlank(userIds)) {
|
||||
userTenantService.putCancelQuit(Arrays.asList(userIds.split(SymbolConstant.COMMA)),tenantId);
|
||||
}
|
||||
if(StringUtils.isNotEmpty(usernames)){
|
||||
//根据用户名删除代理人
|
||||
LambdaQueryWrapper<SysUserAgent> query = new LambdaQueryWrapper<>();
|
||||
query.in(SysUserAgent::getUserName,Arrays.asList(usernames.split(SymbolConstant.COMMA)));
|
||||
sysUserAgentService.remove(query);
|
||||
}
|
||||
return Result.ok("取消离职成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息(vue3用户设置专用)【低代码应用专用接口】
|
||||
* @return
|
||||
@ -1929,4 +1814,53 @@ public class SysUserController {
|
||||
result.setMessage("发送验证码成功!");
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 没有绑定手机号 直接修改密码
|
||||
* @param oldPassword
|
||||
* @param password
|
||||
* @return
|
||||
*/
|
||||
@PutMapping("/updatePasswordNotBindPhone")
|
||||
public Result<String> updatePasswordNotBindPhone(@RequestParam(value="oldPassword") String oldPassword,
|
||||
@RequestParam(value="password") String password,
|
||||
@RequestParam(value="username") String username){
|
||||
sysUserService.updatePasswordNotBindPhone(oldPassword, password, username);
|
||||
return Result.OK("修改密码成功!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据部门岗位选择用户【部门岗位选择用户专用】
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/queryDepartPostUserPageList")
|
||||
public Result<IPage<SysUser>> queryDepartPostUserPageList( @RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
|
||||
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
|
||||
@RequestParam(name = "departId", required = false) String departId,
|
||||
@RequestParam(name="realname",required=false) String realname,
|
||||
@RequestParam(name="username",required=false) String username,
|
||||
@RequestParam(name="isMultiTranslate",required=false) String isMultiTranslate,
|
||||
@RequestParam(name="id",required = false) String id){
|
||||
String[] arr = new String[]{departId, realname, username, id};
|
||||
SqlInjectionUtil.filterContent(arr, SymbolConstant.SINGLE_QUOTATION_MARK);
|
||||
IPage<SysUser> pageList = sysUserDepartService.queryDepartPostUserPageList(departId, username, realname, pageSize, pageNo,id,isMultiTranslate);
|
||||
return Result.OK(pageList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上传文件的进度
|
||||
*
|
||||
* @param fileKey
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/getUploadFileProgress")
|
||||
public Result<Double> getUploadFileProgress(@RequestParam(name = "fileKey") String fileKey,
|
||||
@RequestParam("type") String type){
|
||||
Double progress = ImportSysUserCache.getImportSysUserMap(fileKey, type);
|
||||
if(progress == 100){
|
||||
ImportSysUserCache.removeImportLowAppMap(fileKey);
|
||||
}
|
||||
return Result.ok(progress);
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,8 +8,11 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.jeecg.common.config.mqtoken.UserTokenContext;
|
||||
import org.jeecg.common.system.util.JwtUtil;
|
||||
import org.jeecg.common.system.vo.LoginUser;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
@ -96,8 +99,14 @@ public class SysDataLog implements Serializable {
|
||||
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
|
||||
this.setCreateName(sysUser.getRealname());
|
||||
} catch (Exception e) {
|
||||
// QQYUN-13669 进一步优化:解决某些异步场景下获取用户信息为空的问题
|
||||
String token = UserTokenContext.getToken();
|
||||
if (StringUtils.hasText(token)) {
|
||||
this.setCreateName(JwtUtil.getUsername(token));
|
||||
} else {
|
||||
log.warn("SecurityUtils.getSubject() 获取用户信息异常:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -35,6 +35,9 @@ public class SysDepart implements Serializable {
|
||||
/**机构/部门名称*/
|
||||
@Excel(name="机构/部门名称",width=15)
|
||||
private String departName;
|
||||
/**机构/部门路径名称(非持久化字段)*/
|
||||
@TableField(exist = false)
|
||||
private String departPathName;
|
||||
/**英文名*/
|
||||
@Excel(name="英文名",width=15)
|
||||
private String departNameEn;
|
||||
@ -46,7 +49,7 @@ public class SysDepart implements Serializable {
|
||||
/**描述*/
|
||||
@Excel(name="描述",width=15)
|
||||
private String description;
|
||||
/**机构类别 1=公司,2=组织机构,3=岗位*/
|
||||
/**机构类别 1=公司,2=组织机构,3=岗位 4=子公司*/
|
||||
@Excel(name="机构类别",width=15,dicCode="org_category")
|
||||
private String orgCategory;
|
||||
/**机构类型*/
|
||||
@ -103,6 +106,20 @@ public class SysDepart implements Serializable {
|
||||
private String oldDirectorUserIds;
|
||||
//update-end---author:wangshuai ---date:20200308 for:[JTC-119]新增字段负责人ids和旧的负责人ids
|
||||
|
||||
/**
|
||||
* 职级id
|
||||
*/
|
||||
@Excel(name="职级",width=15,dictTable = "sys_position", dicCode = "id", dicText = "name")
|
||||
@Dict(dictTable = "sys_position", dicCode = "id", dicText = "name")
|
||||
private String positionId;
|
||||
|
||||
/**
|
||||
* 部门岗位id
|
||||
*/
|
||||
@Excel(name="上级岗位",width=15,dictTable = "sys_depart", dicCode = "id", dicText = "depart_name")
|
||||
@Dict(dictTable = "sys_depart", dicCode = "id", dicText = "depart_name")
|
||||
private String depPostParentId;
|
||||
|
||||
/**
|
||||
* 重写equals方法
|
||||
*/
|
||||
|
||||
@ -3,6 +3,7 @@ package org.jeecg.modules.system.entity;
|
||||
import java.util.Date;
|
||||
|
||||
import org.jeecg.common.aspect.annotation.Dict;
|
||||
import org.jeecgframework.poi.excel.annotation.Excel;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
@ -41,6 +42,7 @@ public class SysLog implements Serializable {
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@Excel(name = "创建时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
@ -58,11 +60,13 @@ public class SysLog implements Serializable {
|
||||
/**
|
||||
* 耗时
|
||||
*/
|
||||
@Excel(name = "耗时(毫秒)", width = 15)
|
||||
private Long costTime;
|
||||
|
||||
/**
|
||||
* IP
|
||||
*/
|
||||
@Excel(name = "IP", width = 15)
|
||||
private String ip;
|
||||
|
||||
/**
|
||||
@ -91,10 +95,12 @@ public class SysLog implements Serializable {
|
||||
/**
|
||||
* 操作人用户账户
|
||||
*/
|
||||
@Excel(name = "操作人", width = 15)
|
||||
private String userid;
|
||||
/**
|
||||
* 操作详细日志
|
||||
*/
|
||||
@Excel(name = "日志内容", width = 50)
|
||||
private String logContent;
|
||||
|
||||
/**
|
||||
@ -112,6 +118,7 @@ public class SysLog implements Serializable {
|
||||
/**
|
||||
* 客户终端类型 pc:电脑端 app:手机端 h5:移动网页端
|
||||
*/
|
||||
@Excel(name = "客户端类型", width = 15, dicCode = "client_type")
|
||||
@Dict(dicCode = "client_type")
|
||||
private String clientType;
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ import org.jeecgframework.poi.excel.annotation.Excel;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
/**
|
||||
* @Description: 职务表
|
||||
* @Description: 职务级别
|
||||
* @Author: jeecg-boot
|
||||
* @Date: 2019-09-19
|
||||
* @Version: V1.0
|
||||
@ -23,7 +23,7 @@ import org.springframework.format.annotation.DateTimeFormat;
|
||||
@TableName("sys_position")
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@Accessors(chain = true)
|
||||
@Schema(description="职务表")
|
||||
@Schema(description="职务级别表")
|
||||
public class SysPosition {
|
||||
|
||||
/**
|
||||
@ -39,18 +39,17 @@ public class SysPosition {
|
||||
@Schema(description = "职务编码")
|
||||
private java.lang.String code;
|
||||
/**
|
||||
* 职务名称
|
||||
* 职务级别名称
|
||||
*/
|
||||
@Excel(name = "职务名称", width = 15)
|
||||
@Schema(description = "职务名称")
|
||||
@Excel(name = "职务级别名称", width = 15)
|
||||
@Schema(description = "职务级别名称")
|
||||
private java.lang.String name;
|
||||
/**
|
||||
* 职级
|
||||
*/
|
||||
//@Excel(name = "职级", width = 15,dicCode ="position_rank")
|
||||
@Schema(description = "职级")
|
||||
@Dict(dicCode = "position_rank")
|
||||
private java.lang.String postRank;
|
||||
@Schema(description = "职务等级")
|
||||
private java.lang.Integer postLevel;
|
||||
/**
|
||||
* 公司id
|
||||
*/
|
||||
|
||||
@ -68,6 +68,13 @@ public class SysTenantPack implements Serializable {
|
||||
@Schema(description = "产品包类型")
|
||||
private String packType;
|
||||
|
||||
/**
|
||||
* 是否自动分配给用户(0 否 1是)
|
||||
*/
|
||||
@Excel(name = "是否自动分配给用户(0 否 1是)", width = 15)
|
||||
@Schema(description = "是否自动分配给用户")
|
||||
private String izSysn;
|
||||
|
||||
/**菜单id 临时字段用于新增编辑菜单id传递*/
|
||||
@TableField(exist = false)
|
||||
private String permissionIds;
|
||||
|
||||
@ -211,4 +211,36 @@ public class SysUser implements Serializable {
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private boolean izBindThird;
|
||||
|
||||
/**
|
||||
* 个性签名
|
||||
*/
|
||||
private String sign;
|
||||
|
||||
/**
|
||||
* 是否开启个性签名
|
||||
*/
|
||||
private Integer signEnable;
|
||||
|
||||
/**
|
||||
* 主岗位
|
||||
*/
|
||||
@Excel(name="主岗位",width = 15,dictTable ="sys_depart",dicText = "depart_name",dicCode = "id")
|
||||
@Dict(dictTable ="sys_depart",dicText = "depart_name",dicCode = "id")
|
||||
private String mainDepPostId;
|
||||
|
||||
/**
|
||||
* 兼职岗位
|
||||
*/
|
||||
@Excel(name="兼职岗位",width = 15,dictTable ="sys_depart",dicText = "depart_name",dicCode = "id")
|
||||
@Dict(dictTable ="sys_depart",dicText = "depart_name",dicCode = "id")
|
||||
@TableField(exist = false)
|
||||
private String otherDepPostId;
|
||||
|
||||
/**
|
||||
* 职务(字典)
|
||||
*/
|
||||
@Excel(name = "职务", width = 15, dicCode = "position_type")
|
||||
@Dict(dicCode = "position_type")
|
||||
private String positionType;
|
||||
}
|
||||
|
||||
@ -1,74 +0,0 @@
|
||||
package org.jeecg.modules.system.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.jeecgframework.poi.excel.annotation.Excel;
|
||||
|
||||
/**
|
||||
* @Description: 用户代理人设置
|
||||
* @Author: jeecg-boot
|
||||
* @Date: 2019-04-17
|
||||
* @Version: V1.0
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_user_agent")
|
||||
public class SysUserAgent implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**序号*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private java.lang.String id;
|
||||
/**用户名*/
|
||||
@Excel(name = "用户名", width = 15)
|
||||
private java.lang.String userName;
|
||||
/**代理人用户名*/
|
||||
@Excel(name = "代理人用户名", width = 15)
|
||||
private java.lang.String agentUserName;
|
||||
/**代理开始时间*/
|
||||
@Excel(name = "代理开始时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
|
||||
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
|
||||
private java.util.Date startTime;
|
||||
/**代理结束时间*/
|
||||
@Excel(name = "代理结束时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
|
||||
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
|
||||
private java.util.Date endTime;
|
||||
/**状态0无效1有效*/
|
||||
@Excel(name = "状态0无效1有效", width = 15)
|
||||
private java.lang.String status;
|
||||
/**创建人名称*/
|
||||
@Excel(name = "创建人名称", width = 15)
|
||||
private java.lang.String createName;
|
||||
/**创建人登录名称*/
|
||||
@Excel(name = "创建人登录名称", width = 15)
|
||||
private java.lang.String createBy;
|
||||
/**创建日期*/
|
||||
@Excel(name = "创建日期", width = 20, format = "yyyy-MM-dd HH:mm:ss")
|
||||
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
|
||||
private java.util.Date createTime;
|
||||
/**更新人名称*/
|
||||
@Excel(name = "更新人名称", width = 15)
|
||||
private java.lang.String updateName;
|
||||
/**更新人登录名称*/
|
||||
@Excel(name = "更新人登录名称", width = 15)
|
||||
private java.lang.String updateBy;
|
||||
/**更新日期*/
|
||||
@Excel(name = "更新日期", width = 20, format = "yyyy-MM-dd HH:mm:ss")
|
||||
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
|
||||
private java.util.Date updateTime;
|
||||
/**所属部门*/
|
||||
@Excel(name = "所属部门", width = 15)
|
||||
private java.lang.String sysOrgCode;
|
||||
/**所属公司*/
|
||||
@Excel(name = "所属公司", width = 15)
|
||||
private java.lang.String sysCompanyCode;
|
||||
}
|
||||
@ -0,0 +1,85 @@
|
||||
package org.jeecg.modules.system.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.jeecgframework.poi.excel.annotation.Excel;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @Description: 部门岗位用户
|
||||
* @author: wangshuai
|
||||
* @date: 2025/9/5 11:45
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_user_dep_post")
|
||||
public class SysUserDepPost implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键id
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
@Schema(description = "主键id")
|
||||
private String id;
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
@Schema(description = "用户id")
|
||||
private String userId;
|
||||
/**
|
||||
* 部门岗位id
|
||||
*/
|
||||
@Schema(description = "部门岗位id")
|
||||
private String depId;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
@Schema(description = "创建人")
|
||||
private String createBy;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Schema(description = "创建时间")
|
||||
private Date createTime;
|
||||
/**
|
||||
* 更新人
|
||||
*/
|
||||
@Schema(description = "更新人")
|
||||
private String updateBy;
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Schema(description = "更新时间")
|
||||
private Date updateTime;
|
||||
/**
|
||||
* 机构编码
|
||||
*/
|
||||
@Excel(name = "机构编码", width = 15)
|
||||
@Schema(description = "机构编码")
|
||||
private String orgCode;
|
||||
|
||||
public SysUserDepPost(String id, String userId, String depId) {
|
||||
super();
|
||||
this.id = id;
|
||||
this.userId = userId;
|
||||
this.depId = depId;
|
||||
}
|
||||
|
||||
public SysUserDepPost(String userId, String departId) {
|
||||
this.userId = userId;
|
||||
this.depId = departId;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package org.jeecg.modules.system.excelstyle;
|
||||
|
||||
import org.apache.poi.ss.usermodel.*;
|
||||
import org.jeecgframework.poi.excel.export.styler.ExcelExportStylerDefaultImpl;
|
||||
|
||||
/**
|
||||
* @Description: 导入用户获取标题头部样式 覆盖默认样式
|
||||
*
|
||||
* @author: wangshuai
|
||||
* @date: 2025/8/28 14:05
|
||||
*/
|
||||
public class ExcelExportSysUserStyle extends ExcelExportStylerDefaultImpl {
|
||||
|
||||
public ExcelExportSysUserStyle(Workbook workbook) {
|
||||
super(workbook);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取标题样式
|
||||
*
|
||||
* @param color
|
||||
* @return
|
||||
*/
|
||||
public CellStyle getHeaderStyle(short color) {
|
||||
CellStyle titleStyle = this.workbook.createCellStyle();
|
||||
Font font = this.workbook.createFont();
|
||||
font.setFontHeightInPoints((short)12);
|
||||
titleStyle.setFont(font);
|
||||
titleStyle.setAlignment(HorizontalAlignment.LEFT);
|
||||
titleStyle.setVerticalAlignment(VerticalAlignment.CENTER);
|
||||
titleStyle.setWrapText(true);
|
||||
return titleStyle;
|
||||
}
|
||||
}
|
||||
@ -7,9 +7,8 @@ import org.apache.ibatis.annotations.Select;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
import org.jeecg.modules.system.entity.SysDepart;
|
||||
import org.jeecg.modules.system.entity.SysUser;
|
||||
import org.jeecg.modules.system.model.SysDepartTreeModel;
|
||||
import org.jeecg.modules.system.model.TreeModel;
|
||||
import org.jeecg.modules.system.vo.SysDepartExportVo;
|
||||
import org.jeecg.modules.system.vo.SysDepartPositionVo;
|
||||
import org.jeecg.modules.system.vo.SysUserDepVo;
|
||||
import org.jeecg.modules.system.vo.lowapp.ExportDepartVo;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
@ -195,4 +194,61 @@ public interface SysDepartMapper extends BaseMapper<SysDepart> {
|
||||
*/
|
||||
@InterceptorIgnore(tenantLine = "true")
|
||||
List<SysUserDepVo> getUserDepartByUserId(@Param("userList")List<SysUser> userList);
|
||||
|
||||
/**
|
||||
* 根据父级id/职级/部门id获取部门岗位信息
|
||||
*
|
||||
* @param parentId
|
||||
* @param postLevel
|
||||
* @param departId
|
||||
*/
|
||||
List<SysDepart> getDepartPositionByParentId(@Param("parentId") String parentId, @Param("postLevel") Integer postLevel, @Param("departId") String departId);
|
||||
|
||||
/**
|
||||
* 根据父级id获取部门中的数据
|
||||
* @param parentId
|
||||
* @return
|
||||
*/
|
||||
@Select("select id, depart_name, parent_id, iz_leaf, org_category, org_code from sys_depart where parent_id = #{parentId} order by depart_order,create_time desc")
|
||||
List<SysDepart> getDepartByParentId(@Param("parentId") String parentId);
|
||||
|
||||
/**
|
||||
* 根据部门id查询部门信息
|
||||
|
||||
* @param departId
|
||||
* @return 部门岗位信息
|
||||
*/
|
||||
SysDepartPositionVo getDepartPostByDepartId(@Param("departId") String departId);
|
||||
|
||||
/**
|
||||
* 根据父级部门id查询部门信息
|
||||
|
||||
* @param orgCode
|
||||
* @return 部门岗位信息
|
||||
*/
|
||||
List<SysDepartPositionVo> getDepartPostByOrgCode(@Param("orgCode") String orgCode);
|
||||
|
||||
/**
|
||||
* 根据部门id获取部门code
|
||||
* @param idList
|
||||
* @return
|
||||
*/
|
||||
List<String> getDepCodeByDepIds(@Param("idList") List<String> idList);
|
||||
|
||||
/**
|
||||
* 根据父级部门id和职务名称查找部门id
|
||||
*
|
||||
* @param parentId
|
||||
* @param postName
|
||||
* @return
|
||||
*/
|
||||
String getDepIdByDepIdAndPostName(@Param("parentId") String parentId, @Param("postName") String postName);
|
||||
|
||||
/**
|
||||
* 根据部门id 获取职级名称
|
||||
*
|
||||
* @param depId
|
||||
* @return
|
||||
*/
|
||||
String getPostNameByPostId(@Param("depId") String depId);
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ package org.jeecg.modules.system.mapper;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.jeecg.modules.system.entity.SysTenantPack;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
@ -20,4 +21,12 @@ public interface SysTenantPackMapper extends BaseMapper<SysTenantPack> {
|
||||
* @param tenantIdList
|
||||
*/
|
||||
void deletePackByTenantIds(@Param("tenantIdList") List<Integer> tenantIdList);
|
||||
|
||||
/**
|
||||
* 根据租户id和产品包的code获取租户套餐id
|
||||
*
|
||||
* @param tenantId
|
||||
*/
|
||||
@Select("select id from sys_tenant_pack where tenant_id = #{tenantId} and (pack_code not in('superAdmin','accountAdmin','appAdmin') or pack_code is null) and iz_sysn = '1'")
|
||||
List<String> getPackIdByPackCodeAndTenantId(@Param("tenantId") Integer tenantId);
|
||||
}
|
||||
|
||||
@ -3,6 +3,8 @@ package org.jeecg.modules.system.mapper;
|
||||
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.jeecg.modules.system.entity.SysTenantPack;
|
||||
import org.jeecg.modules.system.entity.SysTenantPackUser;
|
||||
|
||||
import java.util.List;
|
||||
@ -44,4 +46,23 @@ public interface SysTenantPackUserMapper extends BaseMapper<SysTenantPackUser> {
|
||||
* @param
|
||||
*/
|
||||
void deletePackUserByTenantIds(@Param("tenantIds") List<Integer> tenantIds);
|
||||
|
||||
/**
|
||||
* 根据用户id和租户id获取当前租户用户下的产品包id
|
||||
*
|
||||
* @param tenantId
|
||||
* @param userId
|
||||
* @return
|
||||
*/
|
||||
@Select("select pack_id from sys_tenant_pack_user where tenant_id = #{tenantId} and user_id = #{userId}")
|
||||
List<String> getPackIdByTenantIdAndUserId(@Param("tenantId") Integer tenantId, @Param("userId") String userId);
|
||||
|
||||
/**
|
||||
* 根据租户id获取用户的产品包列表
|
||||
*
|
||||
* @param tenantId
|
||||
* @return
|
||||
*/
|
||||
@Select("select id,pack_name,pack_code,pack_type from sys_tenant_pack where tenant_id = #{tenantId}")
|
||||
List<SysTenantPack> getPackListByTenantId(@Param("tenantId") Integer tenantId);
|
||||
}
|
||||
|
||||
@ -1,17 +0,0 @@
|
||||
package org.jeecg.modules.system.mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.jeecg.modules.system.entity.SysUserAgent;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* @Description: 用户代理人设置
|
||||
* @Author: jeecg-boot
|
||||
* @Date: 2019-04-17
|
||||
* @Version: V1.0
|
||||
*/
|
||||
public interface SysUserAgentMapper extends BaseMapper<SysUserAgent> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package org.jeecg.modules.system.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.jeecg.modules.system.entity.SysUserDepPost;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Description: 部门岗位用户关联表 Mapper
|
||||
* @author: wangshuai
|
||||
* @date: 2025/9/5 12:01
|
||||
*/
|
||||
public interface SysUserDepPostMapper extends BaseMapper<SysUserDepPost> {
|
||||
|
||||
/**
|
||||
* 通过用户id查询部门岗位用户
|
||||
*
|
||||
* @param userId
|
||||
* @return
|
||||
*/
|
||||
@Select("select dep_id from sys_user_dep_post where user_id = #{userId}")
|
||||
List<String> getDepPostByUserId(@Param("userId") String userId);
|
||||
}
|
||||
@ -8,6 +8,7 @@ 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.SysUserSysDepPostModel;
|
||||
import org.jeecg.modules.system.model.SysUserSysDepartModel;
|
||||
import org.jeecg.modules.system.vo.SysUserDepVo;
|
||||
|
||||
@ -74,9 +75,10 @@ public interface SysUserMapper extends BaseMapper<SysUser> {
|
||||
* @param page
|
||||
* @param roleId 角色id
|
||||
* @param username 用户登录账户
|
||||
* @param realname 用户姓名
|
||||
* @return
|
||||
*/
|
||||
IPage<SysUser> getUserByRoleId(Page page, @Param("roleId") String roleId, @Param("username") String username);
|
||||
IPage<SysUser> getUserByRoleId(Page page, @Param("roleId") String roleId, @Param("username") String username, @Param("realname") String realname);
|
||||
|
||||
/**
|
||||
* 根据用户名设置部门ID
|
||||
@ -222,4 +224,34 @@ public interface SysUserMapper extends BaseMapper<SysUser> {
|
||||
*/
|
||||
@Select("select id,phone from sys_user where phone = #{phone} and username = #{username}")
|
||||
SysUser getUserByNameAndPhone(@Param("phone") String phone, @Param("username") String username);
|
||||
|
||||
/**
|
||||
* 查询部门、岗位下的用户 包括子部门下的用户
|
||||
*
|
||||
* @param page
|
||||
* @param orgCode
|
||||
* @param userParams
|
||||
* @return
|
||||
*/
|
||||
List<SysUserSysDepPostModel> queryDepartPostUserByOrgCode(@Param("page") IPage page, @Param("orgCode") String orgCode, @Param("userParams") SysUser userParams);
|
||||
|
||||
/**
|
||||
* 根据部门id和用户名获取部门岗位用户分页列表
|
||||
*
|
||||
* @param page
|
||||
* @param userIdList
|
||||
* @return
|
||||
*/
|
||||
IPage<SysUser> getDepPostListByIdUserName(@Param("page") Page<SysUser> page, @Param("userIdList") List<String> userIdList, @Param("userId") String userId, @Param("userName") String userName, @Param("userNameList") List<String> userNameList);
|
||||
|
||||
/**
|
||||
* 根据部门id、用户名和真实姓名获取部门岗位用户分页列表
|
||||
*
|
||||
* @param page
|
||||
* @param username
|
||||
* @param realname
|
||||
* @param orgCode
|
||||
* @return
|
||||
*/
|
||||
IPage<SysUser> getDepartPostListByIdUserRealName(@Param("page") Page<SysUser> page, @Param("username") String username, @Param("realname") String realname, @Param("orgCode") String orgCode);
|
||||
}
|
||||
|
||||
@ -170,7 +170,7 @@
|
||||
|
||||
<!--系统后台导出,根据父级id和租户id查询部门数据-->
|
||||
<select id="getSysDepartList" resultType="org.jeecg.modules.system.vo.SysDepartExportVo">
|
||||
SELECT id,depart_name,parent_id,depart_name_en,depart_order,description,org_category,org_code,mobile,fax,address,memo FROM sys_depart
|
||||
SELECT id,depart_name,parent_id,depart_name_en,depart_order,description,org_category,org_code,mobile,fax,address,memo, position_id FROM sys_depart
|
||||
WHERE
|
||||
1=1
|
||||
<if test="null != tenantId and 0 != tenantId">
|
||||
@ -209,7 +209,7 @@
|
||||
|
||||
<!--根据用户id获取部门信息-->
|
||||
<select id="getUserDepartByUserId" resultType="org.jeecg.modules.system.vo.SysUserDepVo">
|
||||
SELECT sd.depart_name, sud.user_id, sd.id as deptId, sd.parent_id
|
||||
SELECT sd.depart_name, sud.user_id, sd.id as deptId, sd.parent_id, sd.org_category
|
||||
FROM sys_depart sd
|
||||
RIGHT JOIN sys_user_depart sud on sd.id = sud.dep_id and sd.del_flag = 0
|
||||
WHERE sud.user_id IN
|
||||
@ -218,4 +218,54 @@
|
||||
</foreach>
|
||||
order by sd.org_code;
|
||||
</select>
|
||||
|
||||
<!--根据父级id/职级/部门id获取部门岗位信息-->
|
||||
<select id="getDepartPositionByParentId" resultType="org.jeecg.modules.system.entity.SysDepart">
|
||||
select sd.depart_name, sd.id, sd.iz_leaf, sd.org_category, sd.parent_id, sd.org_code from sys_depart sd left join sys_position sp on sd.position_id = sp.id
|
||||
where sd.parent_id = #{parentId}
|
||||
and sd.org_category = '3'
|
||||
<if test="postLevel != null">
|
||||
and sp.post_level <![CDATA[ < ]]> #{postLevel}
|
||||
</if>
|
||||
<if test="departId != null and departId != ''">
|
||||
and sd.id != #{departId}
|
||||
</if>
|
||||
order by sd.depart_order,sd.create_time desc
|
||||
</select>
|
||||
|
||||
<!--根据部门id获取部门信息-->
|
||||
<select id="getDepartPostByDepartId" resultType="org.jeecg.modules.system.vo.SysDepartPositionVo">
|
||||
select depart_name as positionName,id,iz_leaf,parent_id,org_category,org_code, dep_post_parent_id from sys_depart
|
||||
where id = #{departId}
|
||||
</select>
|
||||
|
||||
<!--根据部门编码查询部门信息-->
|
||||
<select id="getDepartPostByOrgCode" resultType="org.jeecg.modules.system.vo.SysDepartPositionVo">
|
||||
select depart_name as positionName,id,iz_leaf,parent_id,org_category,org_code, dep_post_parent_id from sys_depart
|
||||
where org_code like #{orgCode}
|
||||
and org_category = '3'
|
||||
order by depart_order,create_time desc
|
||||
</select>
|
||||
|
||||
<!--根据部门id获取部门编码-->
|
||||
<select id="getDepCodeByDepIds" resultType="java.lang.String">
|
||||
SELECT org_code
|
||||
FROM sys_depart
|
||||
WHERE id IN
|
||||
<foreach collection="idList" index="index" item="item" open="(" separator="," close=")">
|
||||
#{item}
|
||||
</foreach>
|
||||
</select>
|
||||
|
||||
<!-- 根据父级部门id和职务名称查找部门id -->
|
||||
<select id="getDepIdByDepIdAndPostName" resultType="java.lang.String">
|
||||
SELECT id FROM sys_depart
|
||||
WHERE parent_id = #{parentId} and depart_name = #{postName}
|
||||
</select>
|
||||
|
||||
<!--根据部门id获取职级名称-->
|
||||
<select id="getPostNameByPostId" resultType="java.lang.String">
|
||||
select sp.name from sys_depart sd join sys_position sp on sd.position_id = sp.id
|
||||
where sd.id = #{depId}
|
||||
</select>
|
||||
</mapper>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user