JeecgBoot低代码平台 2.4.5 版本发布,钉钉与企业微信集成版本

This commit is contained in:
zhangdaiscott 2021-06-02 15:51:19 +08:00
parent 1bc7ee3345
commit 6840772959
120 changed files with 4734 additions and 239599 deletions

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

File diff suppressed because one or more lines are too long

View File

@ -1,81 +0,0 @@
-- redis监控菜单sql
INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `redirect`, `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_route`, `is_leaf`, `keep_alive`, `hidden`, `description`, `status`, `del_flag`, `rule_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `internal_or_external`) VALUES ('1352200630711652354', 'f0675b52d89100ee88472b6800754a08', 'redis监控', '{{ window._CONFIG[\'domianURL\'] }}/jmreport/view/1352160857479581696', 'layouts/IframePageView', NULL, NULL, '1', NULL, '1', '1.00', '0', '', '1', '1', '0', '0', NULL, '1', '0', '0', 'admin', '2021-01-21 18:25:28', 'admin', '2021-01-22 16:49:43', '0');
-- 新增开关字典
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `type`) VALUES ('1356445645198135298', '开关', 'is_open', '', '0', 'admin', '2021-02-02 11:33:38', 'admin', '2021-02-02 15:28:12', '0');
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `description`, `sort_order`, `status`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES ('1356445705549975553', '1356445645198135298', '是', 'Y', '', '1', '1', 'admin', '2021-02-02 11:33:52', NULL, NULL);
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `description`, `sort_order`, `status`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES ('1356445754212290561', '1356445645198135298', '否', 'N', '', '1', '1', 'admin', '2021-02-02 11:34:04', NULL, NULL);
-- author:liusq----date:20210202----for: 新增开关字典
-- 是否字典
INSERT INTO `sys_dict`(`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `type`) VALUES ('a7adbcd86c37f7dbc9b66945c82ef9e6', '1是0否', 'yn', '', 0, 'admin', '2019-05-22 19:29:29', NULL, NULL, 0);
INSERT INTO `sys_dict_item`(`id`, `dict_id`, `item_text`, `item_value`, `description`, `sort_order`, `status`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES ('51222413e5906cdaf160bb5c86fb827c', 'a7adbcd86c37f7dbc9b66945c82ef9e6', '是', '1', '', 1, 1, 'admin', '2019-05-22 19:29:45', NULL, NULL);
INSERT INTO `sys_dict_item`(`id`, `dict_id`, `item_text`, `item_value`, `description`, `sort_order`, `status`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES ('c5700a71ad08994d18ad1dacc37a71a9', 'a7adbcd86c37f7dbc9b66945c82ef9e6', '否', '0', '', 1, 1, 'admin', '2019-05-22 19:29:55', NULL, NULL);
-- author:scott----date:20210202----for: 是否字典
-- 精简积木报表的示例表由22个减至4个----
drop table rep_demo_dadong;
drop table rep_demo_daibu;
drop table rep_demo_deliveryorder;
drop table rep_demo_huizong;
drop table rep_demo_income;
drop table rep_demo_jiehsao;
drop table rep_demo_kaoqin;
drop table rep_demo_salesrate;
drop table rep_demo_xiaoshou;
drop table xianlu1_wxtl;
drop table xianlu_wxtl;
drop table yanshi_duozu;
drop table yanshi_jdcx;
drop table yanshi_qipaosandian;
drop table yanshi_sandian;
drop table yanshi_tima;
drop table yanshi_wxtl;
drop table yanshi_yipan;
alter table yanshi_dxtj rename to rep_demo_dxtj;
delete from jimu_report_data_source where report_id in ('1331429368098066432','1333962561053396992','1334028738995818496','1334074491629867008','1337271712059887616','1339478701846433792','1339859143477039104','8e69f5396643b199c92d6f401be4d833');
delete from jimu_report_db where jimu_report_id in('1331429368098066432','1333962561053396992','1334028738995818496','1334074491629867008','1337271712059887616','1339478701846433792','1339859143477039104','8e69f5396643b199c92d6f401be4d833');
delete from jimu_report_db_param where jimu_report_head_id in ('1331429368098066432','1333962561053396992','1334028738995818496','1334074491629867008','1337271712059887616','1339478701846433792','1339859143477039104','8e69f5396643b199c92d6f401be4d833');
delete from jimu_report_db_field where jimu_report_db_id not in (select id from jimu_report_db);
delete from jimu_report where id in ('1331429368098066432','1333962561053396992','1334028738995818496','1334074491629867008','1337271712059887616','1339478701846433792','1339859143477039104','8e69f5396643b199c92d6f401be4d833');
-- 添加一对多JVxeTable案例
INSERT INTO sys_permission (`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `redirect`, `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_route`, `is_leaf`, `keep_alive`, `hidden`, `description`, `status`, `del_flag`, `rule_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `internal_or_external`) VALUES ('1365187528377102337', '2a470fc0c3954d9dbb61de6d80846549', '一对多JVxeTable', '/jeecg/JeecgOrderMainListForJVxeTable', 'jeecg/JeecgOrderMainListForJVxeTable', NULL, NULL, 1, NULL, '1', 2.00, 0, NULL, 1, 1, 0, 0, NULL, '1', 0, 0, 'admin', '2021-02-26 14:30:45', 'admin', '2021-02-26 14:32:05', 0);
-- 积木权限表
CREATE TABLE `jimu_report_share` (
`id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '主键',
`report_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '在线excel设计器id',
`preview_url` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '预览地址',
`preview_lock` varchar(4) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '密码锁',
`last_update_time` datetime(0) NULL DEFAULT NULL COMMENT '最后更新时间',
`term_of_validity` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '有效期(0:永久有效1:1天2:7天)',
`status` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '是否过期(0未过期1已过期)',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '积木报表预览权限表';
-- 积木报表链接表
CREATE TABLE `jimu_report_link` (
`id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '主键id',
`report_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '积木设计器id',
`parameter` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '参数',
`eject_type` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '弹出方式0 当前页面 1 新窗口',
`link_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '链接名称',
`api_method` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '请求方法0-get,1-post',
`link_type` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '链接方式(0 网络报表 1 网络连接 2 图表联动)',
`api_url` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '外网api',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '超链接配置表';
update jimu_report_link set link_type = '0';

View File

@ -0,0 +1,62 @@
-- online 新增部门授权 扩展字段
ALTER TABLE `onl_auth_relation`
ADD COLUMN `auth_mode` varchar(50) NULL COMMENT '授权方式role角色depart部门user人' AFTER `cgform_id`;
update onl_auth_relation set auth_mode = 'role';
-- 部门表新增 qywx_identifier 字段
ALTER TABLE `sys_depart`
ADD COLUMN `qywx_identifier` varchar(100) NULL COMMENT '对接企业微信的ID' AFTER `del_flag`;
-- sys_third_account 表新增 third_user_id 字段
ALTER TABLE `sys_third_account`
ADD COLUMN `third_user_id` varchar(100) NULL COMMENT '第三方app用户账号' AFTER `third_user_uuid`;
-- 新增第三方APP消息测试菜单
INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`, `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`, `hidden`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `rule_flag`, `status`, `internal_or_external`) VALUES ('1387612436586065922', '2a470fc0c3954d9dbb61de6d80846549', '第三方APP消息测试', '/jeecg/ThirdAppMessageTest', 'jeecg/ThirdAppMessageTest', '1', NULL, NULL, '1', NULL, '1', '3', '0', NULL, '1', '0', '0', NULL, 'admin', '2021-04-29 11:39:20', 'admin', '2021-04-29 11:39:27', '0', '0', '1', '0');
-- 定时任务一个类允许配置多个调度
-- 删除定时任务表唯一索引
ALTER TABLE `sys_quartz_job`
DROP INDEX `uniq_job_class_name`;
-- 停止所有的定时任务用于旧数据只执行一次即可执行完毕后需要重启后台项目[手动]再次打开定时任务
DELETE FROM `qrtz_cron_triggers`;
DELETE FROM `qrtz_fired_triggers`;
DELETE FROM `qrtz_triggers`;
DELETE FROM `qrtz_job_details`;
UPDATE `sys_quartz_job` SET `status` = '-1';
-- 不支持mariaDB数据库处理 issues/I3QID1
update sys_dict_item set item_text = 'MariaDB' where id ='1349250340104474626';
update sys_dict_item set item_text = 'Postgresql' where id ='1349254569766457345';
-- sys_announcement新增dt_task_id字段
ALTER TABLE `sys_announcement`
ADD COLUMN `dt_task_id` varchar(100) NULL COMMENT '钉钉task_id用于撤回消息' AFTER `msg_abstract`;
-- 模板类型注释错误
ALTER TABLE `jimu_report`
MODIFY COLUMN `template` tinyint(1) NULL DEFAULT NULL COMMENT '是否是模板 0不是,1是' AFTER `api_code`;
-- 查询支持默认值
ALTER TABLE `jimu_report_db_field`
ADD COLUMN `search_value` varchar(100) NULL COMMENT '查询默认值' AFTER `dict_code`;
-- jimu_report_db增加JSON数据字段
ALTER TABLE `jimu_report_db`
ADD COLUMN `json_data` text NULL COMMENT 'json数据直接解析json内容' AFTER `db_source_type`;
-- 连接失败次数
ALTER TABLE `jimu_report_data_source`
ADD COLUMN `connect_times` int(1) NULL COMMENT '连接失败次数' AFTER `update_time`;
-- 联动图表ID
ALTER TABLE `jimu_report_link`
ADD COLUMN `link_chart_id` varchar(50) NULL COMMENT '联动图表的ID' AFTER `api_url`;
-- 修复脚本的一些问题---
update sys_permission set url="{{ window._CONFIG['domianURL'] }}/jmreport/view/1352160857479581696?token=${token}" where id ='1352200630711652354';
UPDATE `jimu_report_db` SET `jimu_report_id` = '1352160857479581696', `create_by` = 'admin', `update_by` = 'admin', `create_time` = '2021-05-19 19:20:44', `update_time` = '2021-05-19 19:20:44', `db_code` = 'infoForReport', `db_ch_name` = '信息', `db_type` = '1', `db_table_name` = NULL, `db_dyn_sql` = NULL, `db_key` = NULL, `tb_db_key` = NULL, `tb_db_table_name` = NULL, `java_type` = NULL, `java_value` = NULL, `api_url` = '{{ domainURL }}/sys/actuator/redis/infoForReport', `api_method` = '0', `is_list` = 1, `is_page` = '1', `db_source` = '', `db_source_type` = NULL, `json_data` = NULL WHERE `id` = '60b3feffadc55eb49baa5a48fdf1ff0e';
UPDATE `jimu_report_db` SET `jimu_report_id` = '1352160857479581696', `create_by` = 'admin', `update_by` = 'admin', `create_time` = '2021-05-19 19:20:50', `update_time` = '2021-05-19 19:20:50', `db_code` = 'memoryForReport', `db_ch_name` = '内存', `db_type` = '1', `db_table_name` = NULL, `db_dyn_sql` = NULL, `db_key` = NULL, `tb_db_key` = NULL, `tb_db_table_name` = NULL, `java_type` = NULL, `java_value` = NULL, `api_url` = '{{ domainURL }}/sys/actuator/redis/memoryForReport', `api_method` = '0', `is_list` = 1, `is_page` = '0', `db_source` = '', `db_source_type` = NULL, `json_data` = NULL WHERE `id` = '6a1d22ca4c95e8fab655d3ceed43a84d';
UPDATE `jimu_report_db` SET `jimu_report_id` = '1352160857479581696', `create_by` = 'admin', `update_by` = 'admin', `create_time` = '2021-05-19 19:21:03', `update_time` = '2021-05-19 19:21:03', `db_code` = 'keysSizeForReport', `db_ch_name` = '数量', `db_type` = '1', `db_table_name` = NULL, `db_dyn_sql` = NULL, `db_key` = NULL, `tb_db_key` = NULL, `tb_db_table_name` = NULL, `java_type` = NULL, `java_value` = NULL, `api_url` = '{{ domainURL }}/sys/actuator/redis/keysSizeForReport', `api_method` = '0', `is_list` = 1, `is_page` = '0', `db_source` = '', `db_source_type` = NULL, `json_data` = NULL WHERE `id` = 'd4a29dfda94357308faf62be2b94db08';

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>jeecg-boot-base-api</artifactId>
<groupId>org.jeecgframework.boot</groupId>
<version>2.4.3</version>
<version>2.4.5</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>jeecg-boot-base-api</artifactId>
<groupId>org.jeecgframework.boot</groupId>
<version>2.4.3</version>
<version>2.4.5</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>jeecg-boot-base</artifactId>
<groupId>org.jeecgframework.boot</groupId>
<version>2.4.3</version>
<version>2.4.5</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-base</artifactId>
<version>2.4.3</version>
<version>2.4.5</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -114,7 +114,7 @@
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>hibernate-re</artifactId>
<version>2.4.3-RC</version>
<version>2.4.5-RC</version>
</dependency>
<!--mysql-->

View File

@ -0,0 +1,47 @@
package org.jeecg.common.api.desform;
import org.jeecg.common.system.vo.DictModel;
import java.util.List;
import java.util.Map;
/**
* 表单设计器System翻译API接口
*
* @author sunjianlei
*/
public interface ISysTranslateAPI {
/**
* 查询分类字典翻译
*/
List<String> categoryLoadDictItem(String ids);
/**
* 根据字典code加载字典text
*
* @param dictCode 顺序tableName,text,code
* @param keys 要查询的key
* @return
*/
List<String> dictLoadDictItem(String dictCode, String keys);
/**
* 获取字典数据
*
* @param dictCode 顺序tableName,text,code
* @param dictCode 要查询的key
* @return
*/
List<DictModel> dictGetDictItems(String dictCode);
/**
* JSearchSelectTag下拉搜索组件专用接口
* 大数据量的字典表 走异步加载 即前端输入内容过滤数据
*
* @param dictCode 字典code格式table,text,code
* @return
*/
List<DictModel> dictLoadDict(String dictCode, String keyword, Integer pageSize);
}

View File

@ -23,6 +23,11 @@ public class MessageDTO implements Serializable {
*/
protected String toUser;
/**
* 发送给所有人
*/
protected boolean toAll;
/**
* 消息主题
*/

View File

@ -1,12 +1,12 @@
package org.jeecg.common.api.vo;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.jeecg.common.constant.CommonConstant;
import lombok.Data;
import org.jeecg.common.constant.CommonConstant;
import java.io.Serializable;
/**
* 接口返回数据格式
@ -113,6 +113,15 @@ public class Result<T> implements Serializable {
return r;
}
public static<T> Result<T> error(String msg, T data) {
Result<T> r = new Result<T>();
r.setSuccess(false);
r.setCode(CommonConstant.SC_INTERNAL_SERVER_ERROR_500);
r.setMessage(msg);
r.setResult(data);
return r;
}
public static Result<Object> error(String msg) {
return error(CommonConstant.SC_INTERNAL_SERVER_ERROR_500, msg);
}

View File

@ -14,16 +14,15 @@ import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.enums.ModuleType;
import org.jeecg.modules.base.service.BaseCommonService;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.IPUtils;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.base.service.BaseCommonService;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

View File

@ -17,6 +17,7 @@ import org.jeecg.common.constant.CommonConstant;
import org.jeecg.modules.base.service.BaseCommonService;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.lang.reflect.Field;
@ -38,6 +39,8 @@ public class DictAspect {
@Autowired
private CommonAPI commonAPI;
@Autowired
public RedisTemplate redisTemplate;
// 定义切点Pointcut
@Pointcut("execution(public * org.jeecg.modules..*.*Controller.*(..))")
@ -144,12 +147,33 @@ public class DictAspect {
if (k.trim().length() == 0) {
continue; //跳过循环
}
//update-begin--Author:scott -- Date:20210531 ----for !56 优化微服务应用下存在表字段需要字典翻译时加载缓慢问题-----
if (!StringUtils.isEmpty(table)){
log.debug("--DictAspect------dicTable="+ table+" ,dicText= "+text+" ,dicCode="+code);
log.info("--DictAspect------dicTable="+ table+" ,dicText= "+text+" ,dicCode="+code);
String keyString = String.format("sys:cache:dictTable::SimpleKey [%s,%s,%s,%s]",table,text,code,k.trim());
if (redisTemplate.hasKey(keyString)){
try {
tmpValue = oConvertUtils.getString(redisTemplate.opsForValue().get(keyString));
} catch (Exception e) {
log.warn(e.getMessage());
}
}else {
tmpValue= commonAPI.translateDictFromTable(table,text,code,k.trim());
}
}else {
String keyString = String.format("sys:cache:dict::%s:%s",code,k.trim());
if (redisTemplate.hasKey(keyString)){
try {
tmpValue = oConvertUtils.getString(redisTemplate.opsForValue().get(keyString));
} catch (Exception e) {
log.warn(e.getMessage());
}
}else {
tmpValue = commonAPI.translateDict(code, k.trim());
}
}
//update-end--Author:scott -- Date:20210531 ----for !56 优化微服务应用下存在表字段需要字典翻译时加载缓慢问题-----
if (tmpValue != null) {
if (!"".equals(textValue.toString())) {
textValue.append(",");

View File

@ -305,4 +305,21 @@ public interface CommonConstant {
* 第三方登录 验证密码/创建用户 都需要设置一个操作码 防止被恶意调用
*/
public final static String THIRD_LOGIN_CODE = "third_login_code";
/**
* 第三方APP同步方向本地 --> 第三方APP
*/
String THIRD_SYNC_TO_APP = "SYNC_TO_APP";
/**
* 第三方APP同步方向第三方APP --> 本地
*/
String THIRD_SYNC_TO_LOCAL = "SYNC_TO_LOCAL";
/** 系统通告消息状态0=未发布 */
String ANNOUNCEMENT_SEND_STATUS_0 = "0";
/** 系统通告消息状态1=已发布 */
String ANNOUNCEMENT_SEND_STATUS_1 = "1";
/** 系统通告消息状态2=已撤销 */
String ANNOUNCEMENT_SEND_STATUS_2 = "2";
}

View File

@ -9,12 +9,14 @@ public interface DataBaseConstant {
public static final String DB_TYPE_DM = "DM";//达梦数据库
public static final String DB_TYPE_POSTGRESQL = "POSTGRESQL";
public static final String DB_TYPE_SQLSERVER = "SQLSERVER";
public static final String DB_TYPE_MARIADB = "MARIADB";
// 数据库类型对应 database_type 字典
public static final String DB_TYPE_MYSQL_NUM = "1";
public static final String DB_TYPE_ORACLE_NUM = "2";
public static final String DB_TYPE_SQLSERVER_NUM = "3";
public static final String DB_TYPE_POSTGRESQL_NUM = "4";
public static final String DB_TYPE_MARIADB_NUM = "5";
//*********系统上下文变量****************************************
/**
* 数据-所属机构编码
@ -65,6 +67,10 @@ public interface DataBaseConstant {
* 系统时间"yyyy-MM-dd HH:mm"
*/
public static final String SYS_TIME_TABLE = "sys_time";
/**
* 数据-所属机构编码
*/
public static final String SYS_BASE_PATH = "sys_base_path";
//*********系统上下文变量****************************************

View File

@ -221,7 +221,7 @@ public class QueryGenerator {
if(parameterMap!=null&& parameterMap.containsKey(ORDER_TYPE)) {
order = parameterMap.get(ORDER_TYPE)[0];
}
log.debug("排序规则>>列:"+column+",排序方式:"+order);
log.info("排序规则>>列:" + column + ",排序方式:" + order);
if (oConvertUtils.isNotEmpty(column) && oConvertUtils.isNotEmpty(order)) {
//字典字段去掉字典翻译文本后缀
if(column.endsWith(CommonConstant.DICT_TEXT_SUFFIX)) {
@ -230,11 +230,22 @@ public class QueryGenerator {
//SQL注入check
SqlInjectionUtil.filterContent(column);
//update-begin--Author:scott Date:20210531 for36 多条件排序无效问题修正-------
// 排序规则修改
// 将现有排序 _ 前端传递排序条件{....,column: 'column1,column2',order: 'desc'} 翻译成sql "column1,column2 desc"
// 修改为 _ 前端传递排序条件{....,column: 'column1,column2',order: 'desc'} 翻译成sql "column1 desc,column2 desc"
if (order.toUpperCase().indexOf(ORDER_TYPE_ASC)>=0) {
queryWrapper.orderByAsc(oConvertUtils.camelToUnderline(column));
String columnStr = oConvertUtils.camelToUnderline(column);
String[] columnArray = columnStr.split(",");
queryWrapper.orderByAsc(columnArray);
} else {
queryWrapper.orderByDesc(oConvertUtils.camelToUnderline(column));
String columnStr = oConvertUtils.camelToUnderline(column);
String[] columnArray = columnStr.split(",");
queryWrapper.orderByDesc(columnArray);
}
//update-end--Author:scott Date:20210531 for36 多条件排序无效问题修正-------
}
}
@ -1073,7 +1084,7 @@ public class QueryGenerator {
* @Return: java.lang.String
*/
private static String specialStrConvert(String value) {
if (DataBaseConstant.DB_TYPE_MYSQL.equals(getDbType())) {
if (DataBaseConstant.DB_TYPE_MYSQL.equals(getDbType()) || DataBaseConstant.DB_TYPE_MARIADB.equals(getDbType())) {
String[] special_str = QueryGenerator.LIKE_MYSQL_SPECIAL_STRS.split(",");
for (String str : special_str) {
if (value.indexOf(str) !=-1) {

View File

@ -6,6 +6,11 @@ import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.google.common.base.Joiner;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.DataBaseConstant;
@ -16,10 +21,6 @@ import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.oConvertUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Date;
/**
* @Author Scott
* @Date 2018-07-12 14:23

View File

@ -14,6 +14,7 @@ import org.springframework.web.multipart.MultipartFile;
import javax.sql.DataSource;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
@ -74,7 +75,8 @@ public class CommonUtils {
fileName = fileName.substring(pos + 1);
}
//替换上传文件名字的特殊字符
fileName = fileName.replace("=","").replace(",","").replace("&","").replace("#", "");
fileName = fileName.replace("=","").replace(",","").replace("&","")
.replace("#", "").replace("", "").replace("", "");
//替换上传文件名字中的空格
fileName=fileName.replaceAll("\\s","");
return fileName;
@ -106,6 +108,44 @@ public class CommonUtils {
}
return url;
}
/**
* 本地文件上传
* @param mf 文件
* @param bizPath 自定义路径
* @return
*/
public static String uploadLocal(MultipartFile mf,String bizPath,String uploadpath){
try {
String fileName = null;
File file = new File(uploadpath + File.separator + bizPath + File.separator );
if (!file.exists()) {
file.mkdirs();// 创建文件根目录
}
String orgName = mf.getOriginalFilename();// 获取文件名
orgName = CommonUtils.getFileName(orgName);
if(orgName.indexOf(".")!=-1){
fileName = orgName.substring(0, orgName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + orgName.substring(orgName.lastIndexOf("."));
}else{
fileName = orgName+ "_" + System.currentTimeMillis();
}
String savePath = file.getPath() + File.separator + fileName;
File savefile = new File(savePath);
FileCopyUtils.copy(mf.getBytes(), savefile);
String dbpath = null;
if(oConvertUtils.isNotEmpty(bizPath)){
dbpath = bizPath + File.separator + fileName;
}else{
dbpath = fileName;
}
if (dbpath.contains("\\")) {
dbpath = dbpath.replace("\\", "/");
}
return dbpath;
} catch (IOException e) {
log.error(e.getMessage(), e);
}
return "";
}
/**
* 统一全局上传 带桶
@ -132,7 +172,7 @@ public class CommonUtils {
return getDatabaseTypeByDataSource(dataSource);
} catch (SQLException e) {
//e.printStackTrace();
log.warn(e.getMessage());
log.warn(e.getMessage(),e);
return "";
}
}
@ -157,8 +197,11 @@ public class CommonUtils {
DB_TYPE = DataBaseConstant.DB_TYPE_SQLSERVER;
}else if(dbType.indexOf("postgresql")>=0) {
DB_TYPE = DataBaseConstant.DB_TYPE_POSTGRESQL;
}else if(dbType.indexOf("mariadb")>=0) {
DB_TYPE = DataBaseConstant.DB_TYPE_MARIADB;
}else {
throw new JeecgBootException("数据库类型:["+dbType+"]不识别!");
log.error("数据库类型:[" + dbType + "]不识别!");
//throw new JeecgBootException("数据库类型:["+dbType+"]不识别!");
}
} catch (Exception e) {
log.error(e.getMessage(), e);

View File

@ -75,7 +75,11 @@ public class MinioUtil {
orgName=file.getName();
}
orgName = CommonUtils.getFileName(orgName);
String objectName = bizPath+"/"+orgName.substring(0, orgName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + orgName.substring(orgName.lastIndexOf("."));
String objectName = bizPath+"/"
+( orgName.indexOf(".")==-1
?orgName + "_" + System.currentTimeMillis()
:orgName.substring(0, orgName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + orgName.substring(orgName.lastIndexOf("."))
);
// 使用putObject上传一个本地文件到存储桶中
if(objectName.startsWith("/")){

View File

@ -13,6 +13,7 @@ import java.util.Map;
public class SqlUtils {
public static final String DATABSE_TYPE_MYSQL = "mysql";
public static final String DATABSE_TYPE_MARIADB = "mariadb";
public static final String DATABSE_TYPE_POSTGRE = "postgresql";
public static final String DATABSE_TYPE_ORACLE = "oracle";
public static final String DATABSE_TYPE_SQLSERVER = "sqlserver";
@ -47,7 +48,7 @@ public class SqlUtils {
*/
public static boolean dbTypeIsMySQL(String dbType) {
return dbTypeIf(dbType, DATABSE_TYPE_MYSQL, DataBaseConstant.DB_TYPE_MYSQL_NUM);
return dbTypeIf(dbType, DATABSE_TYPE_MYSQL, DataBaseConstant.DB_TYPE_MYSQL_NUM) || dbTypeIf(dbType, DATABSE_TYPE_MARIADB, DataBaseConstant.DB_TYPE_MARIADB_NUM);
}
public static boolean dbTypeIsOracle(String dbType) {

View File

@ -111,7 +111,9 @@ public class OssBootUtil {
orgName=file.getName();
}
orgName = CommonUtils.getFileName(orgName);
String fileName = orgName.substring(0, orgName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + orgName.substring(orgName.lastIndexOf("."));
String fileName = orgName.indexOf(".")==-1
?orgName + "_" + System.currentTimeMillis()
:orgName.substring(0, orgName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + orgName.substring(orgName.lastIndexOf("."));
if (!fileDir.endsWith("/")) {
fileDir = fileDir.concat("/");
}

View File

@ -23,7 +23,7 @@ import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
import java.util.ArrayList;
import java.util.Collections;
@ -33,7 +33,7 @@ import java.util.List;
* @Author scott
*/
@Configuration
@EnableSwagger2
@EnableSwagger2WebMvc
@EnableKnife4j
@Import(BeanValidatorPluginsConfiguration.class)
public class Swagger2Config implements WebMvcConfigurer {

View File

@ -19,7 +19,7 @@ public class OssConfiguration {
private String accessKeySecret;
@Value("${jeecg.oss.bucketName}")
private String bucketName;
@Value("${jeecg.oss.staticDomain}")
@Value("${jeecg.oss.staticDomain:}")
private String staticDomain;

View File

@ -1,6 +1,7 @@
package org.jeecg.config.shiro;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
@ -110,6 +111,11 @@ public class ShiroConfig {
filterChainDefinitionMap.put("/webjars/**", "anon");
filterChainDefinitionMap.put("/v2/**", "anon");
// update-begin--Author:sunjianlei Date:20210510 for排除消息通告查看详情页面用于第三方APP
filterChainDefinitionMap.put("/sys/annountCement/show/**", "anon");
// update-end--Author:sunjianlei Date:20210510 for排除消息通告查看详情页面用于第三方APP
//积木报表排除
filterChainDefinitionMap.put("/jmreport/**", "anon");
filterChainDefinitionMap.put("/**/*.js.map", "anon");
@ -127,7 +133,7 @@ public class ShiroConfig {
filterChainDefinitionMap.put("/websocket/**", "anon");//系统通知和公告
filterChainDefinitionMap.put("/newsWebsocket/**", "anon");//CMS模块
filterChainDefinitionMap.put("/vxeSocket/**", "anon");//JVxeTable无痕刷新示例
filterChainDefinitionMap.put("/eoaSocket/**","anon");//我的聊天
//性能监控 TODO 存在安全漏洞泄露TOEKNdurid连接池也有
filterChainDefinitionMap.put("/actuator/**", "anon");
@ -241,8 +247,17 @@ public class ShiroConfig {
RedisClusterManager redisManager = new RedisClusterManager();
Set<HostAndPort> portSet = new HashSet<>();
lettuceConnectionFactory.getClusterConfiguration().getClusterNodes().forEach(node -> portSet.add(new HostAndPort(node.getHost() , node.getPort())));
//update-begin--Author:scott Date:20210531 for修改集群模式下未设置redis密码的bug issues/I3QNIC
if (oConvertUtils.isNotEmpty(lettuceConnectionFactory.getPassword())) {
JedisCluster jedisCluster = new JedisCluster(portSet, 2000, 2000, 5,
lettuceConnectionFactory.getPassword(), new GenericObjectPoolConfig());
redisManager.setPassword(lettuceConnectionFactory.getPassword());
redisManager.setJedisCluster(jedisCluster);
} else {
JedisCluster jedisCluster = new JedisCluster(portSet);
redisManager.setJedisCluster(jedisCluster);
}
//update-end--Author:scott Date:20210531 for修改集群模式下未设置redis密码的bug issues/I3QNIC
manager = redisManager;
}
return manager;

View File

@ -0,0 +1,79 @@
package org.jeecg.config.thirdapp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
/**
* 第三方App对接配置
*/
@Configuration
public class ThirdAppConfig {
/**
* 钉钉
*/
public final static String DINGTALK = "DINGTALK";
/**
* 企业微信
*/
public final static String WECHAT_ENTERPRISE = "WECHAT_ENTERPRISE";
/**
* 是否启用 第三方App对接
*/
@Value("${third-app.enabled:false}")
private boolean enabled;
/**
* 系统类型目前支持WECHAT_ENTERPRISE企业微信DINGTALK 钉钉
*/
@Autowired
private ThirdAppTypeConfig type;
public boolean isEnabled() {
return enabled;
}
public ThirdAppConfig setEnabled(boolean enabled) {
this.enabled = enabled;
return this;
}
/**
* 获取企业微信配置
*/
public ThirdAppTypeItemVo getWechatEnterprise() {
return this.type.getWECHAT_ENTERPRISE();
}
/**
* 获取钉钉配置
*/
public ThirdAppTypeItemVo getDingtalk() {
return this.type.getDINGTALK();
}
/**
* 获取企业微信是否启用
*/
public boolean isWechatEnterpriseEnabled() {
try {
return this.enabled && this.getWechatEnterprise().isEnabled();
} catch (Exception e) {
return false;
}
}
/**
* 获取钉钉是否启用
*/
public boolean isDingtalkEnabled() {
try {
return this.enabled && this.getDingtalk().isEnabled();
} catch (Exception e) {
return false;
}
}
}

View File

@ -0,0 +1,26 @@
package org.jeecg.config.thirdapp;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* 第三方APP配置
*
* @author sunjianlei
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "third-app.type")
public class ThirdAppTypeConfig {
/**
* 对应企业微信配置
*/
private ThirdAppTypeItemVo WECHAT_ENTERPRISE;
/**
* 对应钉钉配置
*/
private ThirdAppTypeItemVo DINGTALK;
}

View File

@ -0,0 +1,36 @@
package org.jeecg.config.thirdapp;
import lombok.Data;
/**
* 第三方App对接
*/
@Data
public class ThirdAppTypeItemVo {
/**
* 是否启用
*/
private boolean enabled;
/**
* 应用Key
*/
private String clientId;
/**
* 应用Secret
*/
private String clientSecret;
/**
* 应用ID
*/
private String agentId;
/**
* 目前仅企业微信用到自建应用Secret
*/
private String agentAppSecret;
public int getAgentIdInt() {
return Integer.parseInt(agentId);
}
}

View File

@ -4,7 +4,7 @@
<parent>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-base</artifactId>
<version>2.4.3</version>
<version>2.4.5</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<description>公共模块</description>

View File

@ -81,4 +81,14 @@ public interface CacheConstant {
* online表单页配置信息缓存key
*/
public static final String ONLINE_FORM = "sys:cache:online:form";
/**
* online报表
*/
public static final String ONLINE_RP = "sys:cache:online:rp";
/**
* online图表
*/
public static final String ONLINE_GRAPH = "sys:cache:online:graph";
}

View File

@ -77,7 +77,8 @@ public class RedisConfig extends CachingConfigurerSupport {
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = jacksonSerializer();
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
redisTemplate.setConnectionFactory(lettuceConnectionFactory);
RedisSerializer<?> stringSerializer = new StringRedisSerializer();
RedisSerializer<String> stringSerializer = new StringRedisSerializer();
// key序列化
redisTemplate.setKeySerializer(stringSerializer);
// value序列化
@ -98,10 +99,13 @@ public class RedisConfig extends CachingConfigurerSupport {
*/
@Bean
public CacheManager cacheManager(LettuceConnectionFactory factory) {
// 配置序列化缓存默认有效期 6小时
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = jacksonSerializer();
// 配置序列化解决乱码的问题,并且配置缓存默认有效期 6小时
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(6));
RedisCacheConfiguration redisCacheConfiguration = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer));
//.disableCachingNullValues();
// 以锁写入的方式创建RedisCacheWriter对象
//update-begin-author:taoyan date:20210316 for:注解CacheEvict根据key删除redis支持通配符*
RedisCacheWriter writer = new JeecgRedisCacheWriter(factory, Duration.ofMillis(50L));
@ -111,6 +115,9 @@ public class RedisConfig extends CachingConfigurerSupport {
//RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(1));
/* 自定义配置test:demo 的超时时间为 5分钟*/
RedisCacheManager cacheManager = RedisCacheManager.builder(writer).cacheDefaults(redisCacheConfiguration)
.withInitialCacheConfigurations(singletonMap(CacheConstant.SYS_DICT_TABLE_CACHE,
RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(10)).disableCachingNullValues()
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))))
.withInitialCacheConfigurations(singletonMap(CacheConstant.TEST_DEMO_CACHE, RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(5)).disableCachingNullValues()))
.withInitialCacheConfigurations(singletonMap(CacheConstant.PLUGIN_MALL_RANKING, RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(24)).disableCachingNullValues()))
.withInitialCacheConfigurations(singletonMap(CacheConstant.PLUGIN_MALL_PAGE_LIST, RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(24)).disableCachingNullValues()))

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>jeecg-boot-parent</artifactId>
<groupId>org.jeecgframework.boot</groupId>
<version>2.4.3</version>
<version>2.4.5</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>jeecg-boot-parent</artifactId>
<groupId>org.jeecgframework.boot</groupId>
<version>2.4.3</version>
<version>2.4.5</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -10,6 +10,6 @@ WORKDIR /jeecg-boot
EXPOSE 8080
ADD ./target/jeecg-boot-module-system-2.4.3.jar ./
ADD ./target/jeecg-boot-module-system-2.4.5.jar ./
CMD sleep 60;java -Djava.security.egd=file:/dev/./urandom -jar jeecg-boot-module-system-2.4.3.jar
CMD sleep 60;java -Djava.security.egd=file:/dev/./urandom -jar jeecg-boot-module-system-2.4.5.jar

View File

@ -4,7 +4,7 @@
<parent>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-parent</artifactId>
<version>2.4.3</version>
<version>2.4.5</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -34,16 +34,21 @@
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-system-local-api</artifactId>
</dependency>
<dependency>
<groupId>org.jeecgframework</groupId>
<artifactId>jeewx-api</artifactId>
<version>1.4.2</version>
</dependency>
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-module-demo</artifactId>
<version>${jeecgboot.version}</version>
</dependency>
<!-- 积木报表 -->
<dependency>
<groupId>org.jeecgframework.jimureport</groupId>
<artifactId>spring-boot-starter-jimureport</artifactId>
<version>1.3.1-beta4</version>
<version>1.3.3-beta</version>
<exclusions>
<exclusion>
<groupId>org.jeecgframework</groupId>

View File

@ -1,18 +1,14 @@
package org.jeecg.modules.quartz.controller;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.util.ImportExcelUtil;
import org.jeecg.modules.quartz.entity.QuartzJob;
@ -22,28 +18,21 @@ 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.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
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.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
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 io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* @Description: 定时任务在线管理
@ -89,10 +78,6 @@ public class QuartzJobController {
//@RequiresRoles("admin")
@RequestMapping(value = "/add", method = RequestMethod.POST)
public Result<?> add(@RequestBody QuartzJob quartzJob) {
List<QuartzJob> list = quartzJobService.findByJobClassName(quartzJob.getJobClassName());
if (list != null && list.size() > 0) {
return Result.error("该定时任务类名已存在");
}
quartzJobService.saveAndScheduleJob(quartzJob);
return Result.ok("创建定时任务成功");
}
@ -155,15 +140,14 @@ public class QuartzJobController {
/**
* 暂停定时任务
*
* @param jobClassName
* @param id
* @return
*/
//@RequiresRoles("admin")
@GetMapping(value = "/pause")
@ApiOperation(value = "暂停定时任务")
public Result<Object> pauseJob(@RequestParam(name = "jobClassName", required = true) String jobClassName) {
QuartzJob job = null;
job = quartzJobService.getOne(new LambdaQueryWrapper<QuartzJob>().eq(QuartzJob::getJobClassName, jobClassName));
public Result<Object> pauseJob(@RequestParam(name = "id") String id) {
QuartzJob job = quartzJobService.getById(id);
if (job == null) {
return Result.error("定时任务不存在!");
}
@ -174,14 +158,14 @@ public class QuartzJobController {
/**
* 启动定时任务
*
* @param jobClassName
* @param id
* @return
*/
//@RequiresRoles("admin")
@GetMapping(value = "/resume")
@ApiOperation(value = "恢复定时任务")
public Result<Object> resumeJob(@RequestParam(name = "jobClassName", required = true) String jobClassName) {
QuartzJob job = quartzJobService.getOne(new LambdaQueryWrapper<QuartzJob>().eq(QuartzJob::getJobClassName, jobClassName));
public Result<Object> resumeJob(@RequestParam(name = "id") String id) {
QuartzJob job = quartzJobService.getById(id);
if (job == null) {
return Result.error("定时任务不存在!");
}

View File

@ -26,7 +26,7 @@ public class SampleParamJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
log.info(" Job Execution key"+jobExecutionContext.getJobDetail().getKey());
log.info( String.format("welcome %s! Jeecg-Boot 带参数定时任务 SampleParamJob ! 时间:" + DateUtils.now(), this.parameter));
}
}

View File

@ -1,8 +1,7 @@
package org.jeecg.modules.quartz.service.impl;
import java.util.Date;
import java.util.List;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.util.DateUtils;
@ -13,9 +12,8 @@ import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import java.util.Date;
import java.util.List;
/**
* @Description: 定时任务在线管理
@ -46,13 +44,16 @@ public class QuartzJobServiceImpl extends ServiceImpl<QuartzJobMapper, QuartzJob
*/
@Override
public boolean saveAndScheduleJob(QuartzJob quartzJob) {
if (CommonConstant.STATUS_NORMAL.equals(quartzJob.getStatus())) {
// 定时器添加
this.schedulerAdd(quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim(), quartzJob.getParameter());
}
// DB设置修改
quartzJob.setDelFlag(CommonConstant.DEL_FLAG_0);
return this.save(quartzJob);
boolean success = this.save(quartzJob);
if (success) {
if (CommonConstant.STATUS_NORMAL.equals(quartzJob.getStatus())) {
// 定时器添加
this.schedulerAdd(quartzJob.getId(), quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim(), quartzJob.getParameter());
}
}
return success;
}
/**
@ -60,8 +61,8 @@ public class QuartzJobServiceImpl extends ServiceImpl<QuartzJobMapper, QuartzJob
*/
@Override
public boolean resumeJob(QuartzJob quartzJob) {
schedulerDelete(quartzJob.getJobClassName().trim());
schedulerAdd(quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim(), quartzJob.getParameter());
schedulerDelete(quartzJob.getId());
schedulerAdd(quartzJob.getId(), quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim(), quartzJob.getParameter());
quartzJob.setStatus(CommonConstant.STATUS_NORMAL);
return this.updateById(quartzJob);
}
@ -73,10 +74,10 @@ public class QuartzJobServiceImpl extends ServiceImpl<QuartzJobMapper, QuartzJob
@Override
public boolean editAndScheduleJob(QuartzJob quartzJob) throws SchedulerException {
if (CommonConstant.STATUS_NORMAL.equals(quartzJob.getStatus())) {
schedulerDelete(quartzJob.getJobClassName().trim());
schedulerAdd(quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim(), quartzJob.getParameter());
schedulerDelete(quartzJob.getId());
schedulerAdd(quartzJob.getId(), quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim(), quartzJob.getParameter());
}else{
scheduler.pauseJob(JobKey.jobKey(quartzJob.getJobClassName().trim()));
scheduler.pauseJob(JobKey.jobKey(quartzJob.getId()));
}
return this.updateById(quartzJob);
}
@ -86,7 +87,7 @@ public class QuartzJobServiceImpl extends ServiceImpl<QuartzJobMapper, QuartzJob
*/
@Override
public boolean deleteAndStopJob(QuartzJob job) {
schedulerDelete(job.getJobClassName().trim());
schedulerDelete(job.getId());
boolean ok = this.removeById(job.getId());
return ok;
}
@ -98,7 +99,9 @@ public class QuartzJobServiceImpl extends ServiceImpl<QuartzJobMapper, QuartzJob
String ymd = DateUtils.date2Str(startDate,DateUtils.yyyymmddhhmmss.get());
String identity = jobName + ymd;
//3秒后执行 只执行一次
startDate.setTime(startDate.getTime()+3000L);
// update-begin--author:sunjianlei ---- date:20210511--- for定时任务立即执行延迟3秒改成0.1秒-------
startDate.setTime(startDate.getTime() + 100L);
// update-end--author:sunjianlei ---- date:20210511--- for定时任务立即执行延迟3秒改成0.1秒-------
// 定义一个Trigger
SimpleTrigger trigger = (SimpleTrigger)TriggerBuilder.newTrigger()
.withIdentity(identity, JOB_TEST_GROUP)
@ -114,7 +117,7 @@ public class QuartzJobServiceImpl extends ServiceImpl<QuartzJobMapper, QuartzJob
@Override
public void pause(QuartzJob quartzJob){
schedulerDelete(quartzJob.getJobClassName().trim());
schedulerDelete(quartzJob.getId());
quartzJob.setStatus(CommonConstant.STATUS_DISABLE);
this.updateById(quartzJob);
}
@ -126,19 +129,19 @@ public class QuartzJobServiceImpl extends ServiceImpl<QuartzJobMapper, QuartzJob
* @param cronExpression
* @param parameter
*/
private void schedulerAdd(String jobClassName, String cronExpression, String parameter) {
private void schedulerAdd(String id, String jobClassName, String cronExpression, String parameter) {
try {
// 启动调度器
scheduler.start();
// 构建job信息
JobDetail jobDetail = JobBuilder.newJob(getClass(jobClassName).getClass()).withIdentity(jobClassName).usingJobData("parameter", parameter).build();
JobDetail jobDetail = JobBuilder.newJob(getClass(jobClassName).getClass()).withIdentity(id).usingJobData("parameter", parameter).build();
// 表达式调度构建器(即任务执行的时间)
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
// 按新的cronExpression表达式构建一个新的trigger
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobClassName).withSchedule(scheduleBuilder).build();
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(id).withSchedule(scheduleBuilder).build();
scheduler.scheduleJob(jobDetail, trigger);
} catch (SchedulerException e) {
@ -153,13 +156,13 @@ public class QuartzJobServiceImpl extends ServiceImpl<QuartzJobMapper, QuartzJob
/**
* 删除定时任务
*
* @param jobClassName
* @param id
*/
private void schedulerDelete(String jobClassName) {
private void schedulerDelete(String id) {
try {
scheduler.pauseTrigger(TriggerKey.triggerKey(jobClassName));
scheduler.unscheduleJob(TriggerKey.triggerKey(jobClassName));
scheduler.deleteJob(JobKey.jobKey(jobClassName));
scheduler.pauseTrigger(TriggerKey.triggerKey(id));
scheduler.unscheduleJob(TriggerKey.triggerKey(id));
scheduler.deleteJob(JobKey.jobKey(id));
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new JeecgBootException("删除定时任务失败");

View File

@ -133,7 +133,7 @@ public class CommonController {
String orgName = mf.getOriginalFilename();// 获取文件名
orgName = CommonUtils.getFileName(orgName);
if(orgName.indexOf(".")!=-1){
fileName = orgName.substring(0, orgName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + orgName.substring(orgName.indexOf("."));
fileName = orgName.substring(0, orgName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + orgName.substring(orgName.lastIndexOf("."));
}else{
fileName = orgName+ "_" + System.currentTimeMillis();
}

View File

@ -1,54 +1,54 @@
package org.jeecg.modules.system.controller;
import java.io.IOException;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.CommonSendStatus;
import org.jeecg.common.constant.WebsocketConst;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.message.websocket.WebSocket;
import org.jeecg.modules.system.entity.SysAnnouncement;
import org.jeecg.modules.system.entity.SysAnnouncementSend;
import org.jeecg.modules.system.service.ISysAnnouncementSendService;
import org.jeecg.modules.system.service.ISysAnnouncementService;
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.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.JSONObject;
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 com.jeecg.dingtalk.api.core.response.Response;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.CommonSendStatus;
import org.jeecg.common.constant.WebsocketConst;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.TokenUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.message.websocket.WebSocket;
import org.jeecg.modules.system.entity.SysAnnouncement;
import org.jeecg.modules.system.entity.SysAnnouncementSend;
import org.jeecg.modules.system.service.ISysAnnouncementSendService;
import org.jeecg.modules.system.service.ISysAnnouncementService;
import org.jeecg.modules.system.service.impl.ThirdAppDingtalkServiceImpl;
import org.jeecg.modules.system.service.impl.ThirdAppWechatEnterpriseServiceImpl;
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.context.annotation.Lazy;
import org.springframework.http.HttpStatus;
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 javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.jeecg.common.constant.CommonConstant.ANNOUNCEMENT_SEND_STATUS_1;
/**
* @Title: Controller
@ -67,6 +67,15 @@ public class SysAnnouncementController {
private ISysAnnouncementSendService sysAnnouncementSendService;
@Resource
private WebSocket webSocket;
@Autowired
ThirdAppWechatEnterpriseServiceImpl wechatEnterpriseService;
@Autowired
ThirdAppDingtalkServiceImpl dingtalkService;
@Autowired
private ISysBaseAPI sysBaseAPI;
@Autowired
@Lazy
private RedisUtil redisUtil;
/**
* 分页列表查询
@ -242,6 +251,19 @@ public class SysAnnouncementController {
obj.put(WebsocketConst.MSG_TXT, sysAnnouncement.getTitile());
webSocket.sendMessage(userIds, obj.toJSONString());
}
try {
// 同步企业微信钉钉的消息通知
Response<String> dtResponse = dingtalkService.sendActionCardMessage(sysAnnouncement, true);
wechatEnterpriseService.sendTextCardMessage(sysAnnouncement, true);
if (dtResponse != null && dtResponse.isSuccess()) {
String taskId = dtResponse.getResult();
sysAnnouncement.setDtTaskId(taskId);
sysAnnouncementService.updateById(sysAnnouncement);
}
} catch (Exception e) {
log.error("同步发送第三方APP消息失败", e);
}
}
}
@ -265,6 +287,13 @@ public class SysAnnouncementController {
boolean ok = sysAnnouncementService.updateById(sysAnnouncement);
if(ok) {
result.success("该系统通知撤销成功");
if (oConvertUtils.isNotEmpty(sysAnnouncement.getDtTaskId())) {
try {
dingtalkService.recallMessage(sysAnnouncement.getDtTaskId());
} catch (Exception e) {
log.error("第三方APP撤回消息失败", e);
}
}
}
}
@ -423,4 +452,33 @@ public class SysAnnouncementController {
}
return result;
}
/**
* 通告查看详情页面用于第三方APP
* @param modelAndView
* @param id
* @return
*/
@GetMapping("/show/{id}")
public ModelAndView showContent(ModelAndView modelAndView, @PathVariable("id") String id, HttpServletRequest request) {
SysAnnouncement announcement = sysAnnouncementService.getById(id);
if (announcement != null) {
boolean tokenOK = false;
try {
// 验证Token有效性
tokenOK = TokenUtils.verifyToken(request, sysBaseAPI, redisUtil);
} catch (Exception ignored) {
}
// 判断是否传递了Token并且Token有效如果传了就不做查看限制直接返回
// 如果Token无效就做查看限制只能查看已发布的
if (tokenOK || ANNOUNCEMENT_SEND_STATUS_1.equals(announcement.getSendStatus())) {
modelAndView.addObject("data", announcement);
modelAndView.setViewName("announcement/showContent");
return modelAndView;
}
}
modelAndView.setStatus(HttpStatus.NOT_FOUND);
return modelAndView;
}
}

View File

@ -409,10 +409,11 @@ public class SysCategoryController {
* 分类字典控件数据回显[表单页面]
*
* @param ids
* @param delNotExist 是否移除不存在的项默认为true设为false如果某个key不存在数据库中则直接返回key本身
* @return
*/
@RequestMapping(value = "/loadDictItem", method = RequestMethod.GET)
public Result<List<String>> loadDictItem(@RequestParam(name = "ids") String ids) {
public Result<List<String>> loadDictItem(@RequestParam(name = "ids") String ids, @RequestParam(name = "delNotExist", required = false, defaultValue = "true") boolean delNotExist) {
Result<List<String>> result = new Result<>();
// 非空判断
if (StringUtils.isBlank(ids)) {
@ -420,13 +421,8 @@ public class SysCategoryController {
result.setMessage("ids 不能为空");
return result;
}
String[] idArray = ids.split(",");
LambdaQueryWrapper<SysCategory> query = new LambdaQueryWrapper<>();
query.in(SysCategory::getId, Arrays.asList(idArray));
// 查询数据
List<SysCategory> list = this.sysCategoryService.list(query);
// 取出name并返回
List<String> textList = list.stream().map(SysCategory::getName).collect(Collectors.toList());
List<String> textList = sysCategoryService.loadDictItem(ids, delNotExist);
result.setSuccess(true);
result.setResult(textList);
return result;

View File

@ -1,12 +1,9 @@
package org.jeecg.modules.system.controller;
import java.io.IOException;
import java.util.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
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.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.jeecg.common.api.vo.Result;
@ -23,28 +20,25 @@ import org.jeecg.modules.system.entity.SysUser;
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.ISysPositionService;
import org.jeecg.modules.system.service.ISysUserDepartService;
import org.jeecg.modules.system.service.ISysUserService;
import org.jeecg.modules.system.util.FindsDepartsChildrenUtil;
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.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;
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 com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
/**
* <p>
@ -132,6 +126,33 @@ public class SysDepartController {
return result;
}
/**
* 获取某个部门的所有父级部门的ID
*
* @param departId 根据departId查
* @param orgCode 根据orgCode查departId和orgCode必须有一个不为空
*/
@GetMapping("/queryAllParentId")
public Result queryParentIds(
@RequestParam(name = "departId", required = false) String departId,
@RequestParam(name = "orgCode", required = false) String orgCode
) {
try {
JSONObject data;
if (oConvertUtils.isNotEmpty(departId)) {
data = sysDepartService.queryAllParentIdByDepartId(departId);
} else if (oConvertUtils.isNotEmpty(orgCode)) {
data = sysDepartService.queryAllParentIdByOrgCode(orgCode);
} else {
return Result.error("departId 和 orgCode 不能都为空!");
}
return Result.OK(data);
} catch (Exception e) {
log.error(e.getMessage(), e);
return Result.error(e.getMessage());
}
}
/**
* 添加新数据 添加用户新建的部门对象数据,并保存到数据库
*

View File

@ -31,7 +31,6 @@ 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.entity.result.ExcelImportResult;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
@ -122,35 +121,12 @@ public class SysDictController {
public Result<List<DictModel>> getDictItems(@PathVariable String dictCode, @RequestParam(value = "sign",required = false) String sign,HttpServletRequest request) {
log.info(" dictCode : "+ dictCode);
Result<List<DictModel>> result = new Result<List<DictModel>>();
List<DictModel> ls = null;
try {
if(dictCode.indexOf(",")!=-1) {
//关联表字典举例sys_user,realname,id
String[] params = dictCode.split(",");
if(params.length<3) {
List<DictModel> ls = sysDictService.getDictItems(dictCode);
if (ls == null) {
result.error500("字典Code格式不正确");
return result;
}
//SQL注入校验只限制非法串改数据库
final String[] sqlInjCheck = {params[0],params[1],params[2]};
SqlInjectionUtil.filterContent(sqlInjCheck);
if(params.length==4) {
//SQL注入校验查询条件SQL 特殊check此方法仅供此处使用
SqlInjectionUtil.specialFilterContent(params[3]);
ls = sysDictService.queryTableDictItemsByCodeAndFilter(params[0],params[1],params[2],params[3]);
}else if (params.length==3) {
ls = sysDictService.queryTableDictItemsByCode(params[0],params[1],params[2]);
}else{
result.error500("字典Code格式不正确");
return result;
}
}else {
//字典表
ls = sysDictService.queryDictItemsByCode(dictCode);
}
result.setSuccess(true);
result.setResult(ls);
log.debug(result.toString());
@ -159,7 +135,6 @@ public class SysDictController {
result.error500("操作失败");
return result;
}
return result;
}
@ -198,8 +173,9 @@ public class SysDictController {
}
/**
* JSearchSelectTag下拉搜索组件专用接口
* 大数据量的字典表 走异步加载 即前端输入内容过滤数据
* @param dictCode
* @param dictCode 字典code格式table,text,code
* @return
*/
@RequestMapping(value = "/loadDict/{dictCode}", method = RequestMethod.GET)
@ -209,39 +185,75 @@ public class SysDictController {
@RequestParam(value = "pageSize", required = false) Integer pageSize) {
log.info(" 加载字典表数据,加载关键字: "+ keyword);
Result<List<DictModel>> result = new Result<List<DictModel>>();
List<DictModel> ls = null;
try {
if(dictCode.indexOf(",")!=-1) {
String[] params = dictCode.split(",");
if(params.length!=3) {
List<DictModel> ls = sysDictService.loadDict(dictCode, keyword, pageSize);
if (ls == null) {
result.error500("字典Code格式不正确");
return result;
}
if(pageSize!=null){
ls = sysDictService.queryLittleTableDictItems(params[0],params[1],params[2],keyword, pageSize);
}else{
ls = sysDictService.queryTableDictItems(params[0],params[1],params[2],keyword);
}
result.setSuccess(true);
result.setResult(ls);
log.info(result.toString());
}else {
result.error500("字典Code格式不正确");
}
return result;
} catch (Exception e) {
log.error(e.getMessage(),e);
result.error500("操作失败");
return result;
}
}
/**
* 给表单设计器的表字典使用下拉搜索模式有值时动态拼接数据
* @param dictCode
* @param keyword 当前控件的值可以逗号分割
* @param sign
* @param pageSize
* @return
*/
@RequestMapping(value = "/loadDictOrderByValue/{dictCode}", method = RequestMethod.GET)
public Result<List<DictModel>> loadDictOrderByValue(
@PathVariable String dictCode,
@RequestParam(name = "keyword") String keyword,
@RequestParam(value = "sign", required = false) String sign,
@RequestParam(value = "pageSize", required = false) Integer pageSize) {
// 首次查询查出来用户选中的值并且不分页
Result<List<DictModel>> firstRes = this.loadDict(dictCode, keyword, sign, null);
if (!firstRes.isSuccess()) {
return firstRes;
}
// 然后再查询出第一页的数据
Result<List<DictModel>> result = this.loadDict(dictCode, "", sign, pageSize);
if (!result.isSuccess()) {
return result;
}
// 合并两次查询的数据
List<DictModel> firstList = firstRes.getResult();
List<DictModel> list = result.getResult();
for (DictModel firstItem : firstList) {
// anyMatch 表示判断的条件里任意一个元素匹配成功返回true
// allMatch 表示判断条件里的元素所有的都匹配成功返回true
// noneMatch allMatch 相反表示判断条件里的元素所有的都匹配失败返回true
boolean none = list.stream().noneMatch(item -> item.getValue().equals(firstItem.getValue()));
// 当元素不存在时再添加到集合里
if (none) {
list.add(0, firstItem);
}
}
return result;
}
/**
*
* 根据字典code加载字典text 返回
* @param dictCode 顺序tableName,text,code
* @param keys 要查询的key
* @param sign
* @param delNotExist 是否移除不存在的项默认为true设为false如果某个key不存在数据库中则直接返回key本身
* @param request
* @return
*/
@RequestMapping(value = "/loadDictItem/{dictCode}", method = RequestMethod.GET)
public Result<List<String>> loadDictItem(@PathVariable String dictCode,@RequestParam(name="key") String keys, @RequestParam(value = "sign",required = false) String sign,HttpServletRequest request) {
public Result<List<String>> loadDictItem(@PathVariable String dictCode,@RequestParam(name="key") String keys, @RequestParam(value = "sign",required = false) String sign,@RequestParam(value = "delNotExist",required = false,defaultValue = "true") boolean delNotExist,HttpServletRequest request) {
Result<List<String>> result = new Result<>();
try {
if(dictCode.indexOf(",")!=-1) {
@ -250,7 +262,7 @@ public class SysDictController {
result.error500("字典Code格式不正确");
return result;
}
List<String> texts = sysDictService.queryTableDictByKeys(params[0], params[1], params[2], keys);
List<String> texts = sysDictService.queryTableDictByKeys(params[0], params[1], params[2], keys, delNotExist);
result.setSuccess(true);
result.setResult(texts);
@ -407,12 +419,14 @@ public class SysDictController {
//清空字典缓存
Set keys = redisTemplate.keys(CacheConstant.SYS_DICT_CACHE + "*");
Set keys2 = redisTemplate.keys(CacheConstant.SYS_DICT_TABLE_CACHE + "*");
Set keys21 = redisTemplate.keys(CacheConstant.SYS_DICT_TABLE_BY_KEYS_CACHE + "*");
Set keys3 = redisTemplate.keys(CacheConstant.SYS_DEPARTS_CACHE + "*");
Set keys4 = redisTemplate.keys(CacheConstant.SYS_DEPART_IDS_CACHE + "*");
Set keys5 = redisTemplate.keys( "jmreport:cache:dict*");
Set keys6 = redisTemplate.keys( "jmreport:cache:dictTable*");
redisTemplate.delete(keys);
redisTemplate.delete(keys2);
redisTemplate.delete(keys21);
redisTemplate.delete(keys3);
redisTemplate.delete(keys4);
redisTemplate.delete(keys5);

View File

@ -145,8 +145,8 @@ public class SysUserController {
user.setPassword(passwordEncode);
user.setStatus(1);
user.setDelFlag(CommonConstant.DEL_FLAG_0);
sysUserService.addUserWithRole(user, selectedRoles);
sysUserService.addUserWithDepart(user, selectedDeparts);
// 保存用户走一个service 保证事务
sysUserService.saveUser(user, selectedRoles, selectedDeparts);
result.success("添加成功!");
} catch (Exception e) {
log.error(e.getMessage(), e);
@ -172,9 +172,8 @@ public class SysUserController {
user.setPassword(sysUser.getPassword());
String roles = jsonObject.getString("selectedroles");
String departs = jsonObject.getString("selecteddeparts");
sysUserService.editUserWithRole(user, roles);
sysUserService.editUserWithDepart(user, departs);
sysUserService.updateNullPhoneEmail();
// 修改用户走一个service 保证事务
sysUserService.editUser(user, roles, departs);
result.success("修改成功!");
}
} catch (Exception e) {
@ -279,6 +278,7 @@ public class SysUserController {
result.setResult(true);
try {
//通过传入信息查询新的用户信息
sysUser.setPassword(null);
SysUser user = sysUserService.getOne(new QueryWrapper<SysUser>(sysUser));
if (user != null) {
result.setSuccess(false);
@ -298,6 +298,7 @@ public class SysUserController {
/**
* 修改密码
*/
//@RequiresRoles({"admin"})
@RequestMapping(value = "/changePassword", method = RequestMethod.PUT)
public Result<?> changePassword(@RequestBody SysUser sysUser) {
SysUser u = this.sysUserService.getOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUsername, sysUser.getUsername()));
@ -388,6 +389,23 @@ public class SysUserController {
}
}
/**
* 用户选择组件 专用 根据用户账号或部门分页查询
* @param departId
* @param username
* @return
*/
@RequestMapping(value = "/queryUserComponentData", method = RequestMethod.GET)
public Result<IPage<SysUser>> queryUserComponentData(
@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) {
IPage<SysUser> pageList = sysUserDepartService.queryDepartUserPageList(departId, username, realname, pageSize, pageNo);
return Result.OK(pageList);
}
/**
* 导出excel
*
@ -523,11 +541,15 @@ public class SysUserController {
*/
//@RequiresRoles({"admin"})
@RequestMapping(value = "/updatePassword", method = RequestMethod.PUT)
public Result<?> changPassword(@RequestBody JSONObject json) {
public Result<?> updatePassword(@RequestBody JSONObject json) {
String username = json.getString("username");
String oldpassword = json.getString("oldpassword");
String password = json.getString("password");
String confirmpassword = json.getString("confirmpassword");
LoginUser sysUser = (LoginUser)SecurityUtils.getSubject().getPrincipal();
if(!sysUser.getUsername().equals(username)){
return Result.error("只允许修改自己的密码!");
}
SysUser user = this.sysUserService.getOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUsername, username));
if(user==null) {
return Result.error("用户不存在!");
@ -1338,4 +1360,6 @@ public class SysUserController {
sysUserService.updateById(user);
return Result.ok("手机号设置成功!");
}
}

View File

@ -0,0 +1,261 @@
package org.jeecg.modules.system.controller;
import com.alibaba.fastjson.JSONObject;
import com.jeecg.dingtalk.api.core.response.Response;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.dto.message.MessageDTO;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.config.thirdapp.ThirdAppConfig;
import org.jeecg.modules.system.service.impl.ThirdAppDingtalkServiceImpl;
import org.jeecg.modules.system.service.impl.ThirdAppWechatEnterpriseServiceImpl;
import org.jeecg.modules.system.vo.thirdapp.SyncInfoVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
/**
* 第三方App对接
*/
@Slf4j
@RestController("thirdAppController")
@RequestMapping("/sys/thirdApp")
public class ThirdAppController {
@Autowired
ThirdAppConfig thirdAppConfig;
@Autowired
ThirdAppWechatEnterpriseServiceImpl wechatEnterpriseService;
@Autowired
ThirdAppDingtalkServiceImpl dingtalkService;
/**
* 获取启用的系统
*/
@GetMapping("/getEnabledType")
public Result getEnabledType() {
Map<String, Boolean> enabledMap = new HashMap<>();
enabledMap.put("wechatEnterprise", thirdAppConfig.isWechatEnterpriseEnabled());
enabledMap.put("dingtalk", thirdAppConfig.isDingtalkEnabled());
return Result.OK(enabledMap);
}
/**
* 同步本地[用户]企业微信
*
* @param ids
* @return
*/
@GetMapping("/sync/wechatEnterprise/user/toApp")
public Result syncWechatEnterpriseUserToApp(@RequestParam(value = "ids", required = false) String ids) {
if (thirdAppConfig.isWechatEnterpriseEnabled()) {
SyncInfoVo syncInfo = wechatEnterpriseService.syncLocalUserToThirdApp(ids);
if (syncInfo.getFailInfo().size() == 0) {
return Result.OK("同步成功", syncInfo);
} else {
return Result.error("同步失败", syncInfo);
}
}
return Result.error("企业微信同步功能已禁用");
}
/**
* 同步企业微信[用户]到本地
*
* @param ids 作废
* @return
*/
@GetMapping("/sync/wechatEnterprise/user/toLocal")
public Result syncWechatEnterpriseUserToLocal(@RequestParam(value = "ids", required = false) String ids) {
if (thirdAppConfig.isWechatEnterpriseEnabled()) {
SyncInfoVo syncInfo = wechatEnterpriseService.syncThirdAppUserToLocal();
if (syncInfo.getFailInfo().size() == 0) {
return Result.OK("同步成功", syncInfo);
} else {
return Result.error("同步失败", syncInfo);
}
}
return Result.error("企业微信同步功能已禁用");
}
/**
* 同步本地[部门]企业微信
*
* @param ids
* @return
*/
@GetMapping("/sync/wechatEnterprise/depart/toApp")
public Result syncWechatEnterpriseDepartToApp(@RequestParam(value = "ids", required = false) String ids) {
if (thirdAppConfig.isWechatEnterpriseEnabled()) {
boolean flag = wechatEnterpriseService.syncLocalDepartmentToThirdApp(ids);
return flag ? Result.OK("同步成功", null) : Result.error("同步失败");
}
return Result.error("企业微信同步功能已禁用");
}
/**
* 同步企业微信[部门]到本地
*
* @param ids
* @return
*/
@GetMapping("/sync/wechatEnterprise/depart/toLocal")
public Result syncWechatEnterpriseDepartToLocal(@RequestParam(value = "ids", required = false) String ids) {
if (thirdAppConfig.isWechatEnterpriseEnabled()) {
SyncInfoVo syncInfo = wechatEnterpriseService.syncThirdAppDepartmentToLocal(ids);
if (syncInfo.getFailInfo().size() == 0) {
return Result.OK("同步成功", syncInfo);
} else {
return Result.error("同步失败", syncInfo);
}
}
return Result.error("企业微信同步功能已禁用");
}
/**
* 同步本地[部门]钉钉
*
* @param ids
* @return
*/
@GetMapping("/sync/dingtalk/depart/toApp")
public Result syncDingtalkDepartToApp(@RequestParam(value = "ids", required = false) String ids) {
if (thirdAppConfig.isDingtalkEnabled()) {
boolean flag = dingtalkService.syncLocalDepartmentToThirdApp(ids);
return flag ? Result.OK("同步成功", null) : Result.error("同步失败");
}
return Result.error("钉钉同步功能已禁用");
}
/**
* 同步钉钉[部门]到本地
*
* @param ids
* @return
*/
@GetMapping("/sync/dingtalk/depart/toLocal")
public Result syncDingtalkDepartToLocal(@RequestParam(value = "ids", required = false) String ids) {
if (thirdAppConfig.isDingtalkEnabled()) {
SyncInfoVo syncInfo = dingtalkService.syncThirdAppDepartmentToLocal(ids);
if (syncInfo.getFailInfo().size() == 0) {
return Result.OK("同步成功", syncInfo);
} else {
return Result.error("同步失败", syncInfo);
}
}
return Result.error("钉钉同步功能已禁用");
}
/**
* 同步本地[用户]钉钉
*
* @param ids
* @return
*/
@GetMapping("/sync/dingtalk/user/toApp")
public Result syncDingtalkUserToApp(@RequestParam(value = "ids", required = false) String ids) {
if (thirdAppConfig.isDingtalkEnabled()) {
SyncInfoVo syncInfo = dingtalkService.syncLocalUserToThirdApp(ids);
if (syncInfo.getFailInfo().size() == 0) {
return Result.OK("同步成功", syncInfo);
} else {
return Result.error("同步失败", syncInfo);
}
}
return Result.error("钉钉同步功能已禁用");
}
/**
* 同步钉钉[用户]到本地
*
* @param ids 作废
* @return
*/
@GetMapping("/sync/dingtalk/user/toLocal")
public Result syncDingtalkUserToLocal(@RequestParam(value = "ids", required = false) String ids) {
if (thirdAppConfig.isDingtalkEnabled()) {
SyncInfoVo syncInfo = dingtalkService.syncThirdAppUserToLocal();
if (syncInfo.getFailInfo().size() == 0) {
return Result.OK("同步成功", syncInfo);
} else {
return Result.error("同步失败", syncInfo);
}
}
return Result.error("钉钉同步功能已禁用");
}
/**
* 发送消息测试
*
* @return
*/
@PostMapping("/sendMessageTest")
public Result sendMessageTest(@RequestBody JSONObject params, HttpServletRequest request) {
/* 获取前台传递的参数 */
// 第三方app的类型
String app = params.getString("app");
// 是否发送给全部人
boolean sendAll = params.getBooleanValue("sendAll");
// 消息接收者传sys_user表的username字段多个用逗号分割
String receiver = params.getString("receiver");
// 消息内容
String content = params.getString("content");
String fromUser = JwtUtil.getUserNameByToken(request);
String title = "第三方APP消息测试";
MessageDTO message = new MessageDTO(fromUser, receiver, title, content);
message.setToAll(sendAll);
if (ThirdAppConfig.WECHAT_ENTERPRISE.equals(app)) {
if (thirdAppConfig.isWechatEnterpriseEnabled()) {
JSONObject response = wechatEnterpriseService.sendMessageResponse(message, false);
return Result.OK(response);
}
return Result.error("企业微信已被禁用");
} else if (ThirdAppConfig.DINGTALK.equals(app)) {
if (thirdAppConfig.isDingtalkEnabled()) {
Response<String> response = dingtalkService.sendMessageResponse(message, false);
return Result.OK(response);
}
return Result.error("钉钉已被禁用");
}
return Result.error("不识别的第三方APP");
}
/**
* 撤回消息测试
*
* @return
*/
@PostMapping("/recallMessageTest")
public Result recallMessageTest(@RequestBody JSONObject params) {
/* 获取前台传递的参数 */
// 第三方app的类型
String app = params.getString("app");
// 消息id
String msg_task_id = params.getString("msg_task_id");
if (ThirdAppConfig.WECHAT_ENTERPRISE.equals(app)) {
if (thirdAppConfig.isWechatEnterpriseEnabled()) {
return Result.error("企业微信不支持撤回消息");
}
return Result.error("企业微信已被禁用");
} else if (ThirdAppConfig.DINGTALK.equals(app)) {
if (thirdAppConfig.isDingtalkEnabled()) {
Response<JSONObject> response = dingtalkService.recallMessageResponse(msg_task_id);
if (response.isSuccess()) {
return Result.OK("撤回成功", response);
} else {
return Result.error("撤回失败:" + response.getErrcode() + "——" + response.getErrmsg(), response);
}
}
return Result.error("钉钉已被禁用");
}
return Result.error("不识别的第三方APP");
}
}

View File

@ -1,17 +1,16 @@
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 lombok.Data;
import org.jeecg.common.aspect.annotation.Dict;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
/**
* @Description: 系统通告表
* @Author: jeecg-boot
@ -144,4 +143,8 @@ public class SysAnnouncement implements Serializable {
* 摘要
*/
private java.lang.String msgAbstract;
/**
* 钉钉task_id用于撤回消息
*/
private java.lang.String dtTaskId;
}

View File

@ -6,13 +6,11 @@ import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.jeecg.common.aspect.annotation.Dict;
import org.jeecg.modules.system.model.SysDepartTreeModel;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import java.util.Objects;
/**
@ -73,6 +71,8 @@ public class SysDepart implements Serializable {
/**删除状态0正常1已删除*/
@Dict(dicCode = "del_flag")
private String delFlag;
/**对接企业微信的ID*/
private String qywxIdentifier;
/**创建人*/
private String createBy;
/**创建日期*/

View File

@ -1,18 +1,13 @@
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 com.baomidou.mybatisplus.annotation.TableField;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
/**
@ -60,4 +55,8 @@ public class SysThirdAccount {
@Excel(name = "真实姓名", width = 15)
@ApiModelProperty(value = "真实姓名")
private java.lang.String thirdUserUuid;
/**真实姓名*/
@Excel(name = "第三方用户账号", width = 15)
@ApiModelProperty(value = "第三方用户账号")
private java.lang.String thirdUserId;
}

View File

@ -6,7 +6,6 @@ import java.util.Map;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.ResultType;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.jeecg.common.system.vo.DictModel;
@ -132,4 +131,26 @@ public interface SysDictMapper extends BaseMapper<SysDict> {
*/
@Deprecated
public Page<DictModel> queryDictTablePageList(Page page, @Param("query") DictQuery query);
/**
* 查询 字典表数据 支持查询条件 分页
* @param page
* @param table
* @param text
* @param code
* @param filterSql
* @return
*/
IPage<DictModel> queryTableDictWithFilter(Page<DictModel> page, @Param("table") String table, @Param("text") String text, @Param("code") String code, @Param("filterSql") String filterSql);
/**
* 查询 字典表数据 支持查询条件 查询所有
* @param table
* @param text
* @param code
* @param filterSql
* @return
*/
List<DictModel> queryAllTableDictItems(@Param("table") String table, @Param("text") String text, @Param("code") String code, @Param("filterSql") String filterSql);
}

View File

@ -1,10 +1,10 @@
package org.jeecg.modules.system.mapper;
import java.util.List;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.jeecg.modules.system.entity.SysThirdAccount;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import java.util.List;
/**
* @Description: 第三方登录账号表
@ -14,4 +14,13 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
*/
public interface SysThirdAccountMapper extends BaseMapper<SysThirdAccount> {
/**
* 通过 sysUsername 集合批量查询
*
* @param sysUsernameArr username集合
* @param thirdType 第三方类型
* @return
*/
List<SysThirdAccount> selectThirdIdsByUsername(@Param("sysUsernameArr") String[] sysUsernameArr, @Param("thirdType") String thirdType);
}

View File

@ -1,11 +1,32 @@
package org.jeecg.modules.system.mapper;
import java.util.List;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import org.jeecg.modules.system.entity.SysUser;
import org.jeecg.modules.system.entity.SysUserDepart;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface SysUserDepartMapper extends BaseMapper<SysUserDepart>{
List<SysUserDepart> getUserDepartByUid(@Param("userId") String userId);
/**
* 查询指定部门下的用户 并且支持用户真实姓名模糊查询
* @param orgCode
* @param realname
* @return
*/
List<SysUser> queryDepartUserList(@Param("orgCode") String orgCode, @Param("realname") String realname);
/**
* 根据部门查询部门用户
* @param page
* @param orgCode
* @param username
* @param realname
* @return
*/
IPage<SysUser> queryDepartUserPageList(Page<SysUser> page, @Param("orgCode") String orgCode, @Param("username") String username, @Param("realname") String realname);
}

View File

@ -106,4 +106,22 @@
</if>
</select>
<!--通过查询指定table的 text code 获取字典数据,且支持关键字和自定义查询条件查询 分页-->
<select id="queryTableDictWithFilter" parameterType="String" resultType="org.jeecg.common.system.vo.DictModel">
select ${text} as "text", ${code} as "value" from ${table}
<if test="filterSql != null and filterSql != ''">
${filterSql}
</if>
</select>
<!--通过查询指定table的 text code 获取字典数据,且支持关键字和自定义查询条件查询 获取所有 -->
<select id="queryAllTableDictItems" parameterType="String" resultType="org.jeecg.common.system.vo.DictModel">
select ${text} as "text", ${code} as "value" from ${table}
<if test="filterSql != null and filterSql != ''">
${filterSql}
</if>
</select>
</mapper>

View File

@ -24,7 +24,7 @@
<!-- 首页访问统计 -->
<select id="findVisitCount" resultType="java.util.HashMap">
<if test="dbType == 'MYSQL'">
<if test="dbType == 'MYSQL' || dbType == 'MARIADB'">
select count(*) as visit
,count(distinct(ip)) as ip
,DATE_FORMAT(create_time, '%Y-%m-%d') as tian
@ -34,7 +34,7 @@
group by tian,type
order by tian asc
</if>
<if test="dbType == 'ORACLE'">
<if test="dbType == 'ORACLE' || dbType == 'DM'">
select count(*) as visit
,count(distinct(ip)) as ip
,to_char(create_time, 'yyyy-mm-dd') as tian

View File

@ -2,4 +2,13 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.jeecg.modules.system.mapper.SysThirdAccountMapper">
<!-- 通过 sysUsername 集合批量查询 -->
<select id="selectThirdIdsByUsername" resultType="org.jeecg.modules.system.entity.SysThirdAccount">
SELECT third_user_id FROM sys_third_account
INNER JOIN sys_user ON sys_user.id = sys_third_account.sys_user_id
WHERE third_type = #{thirdType} AND
<!-- TODO in 查询数据量大的时候可能会报错 -->
<foreach collection="sysUsernameArr" item="item" open=" sys_user.username IN (" close=")" separator=",">#{item}</foreach>
</select>
</mapper>

View File

@ -6,4 +6,29 @@
FROM sys_user_depart
WHERE user_id = #{userId, jdbcType=VARCHAR}
</select>
<!-- 查询指定部门下的用户 并且支持用户账号模糊查询 -->
<select id="queryDepartUserList" resultType="org.jeecg.modules.system.entity.SysUser">
select a.* from sys_user a
join sys_user_depart b on b.user_id = a.id
join sys_depart c on b.dep_id = c.id
where a.del_flag = 0 and c.org_code like '${orgCode}%'
<if test="realname!=null and realname!=''">
and a.realname like '%${realname}%'
</if>
</select>
<!-- 根据部门查询部门用户 分页 -->
<select id="queryDepartUserPageList" resultType="org.jeecg.modules.system.entity.SysUser">
select a.*, c.depart_name as org_code_txt from sys_user a
join sys_user_depart b on b.user_id = a.id
join sys_depart c on b.dep_id = c.id
where a.del_flag = 0 and c.org_code like '${orgCode}%'
<if test="username!=null and username!=''">
and a.username like '%${username}%'
</if>
<if test="realname!=null and realname!=''">
and a.realname like '%${realname}%'
</if>
</select>
</mapper>

View File

@ -65,6 +65,8 @@ public class SysDepartTreeModel implements Serializable{
private String delFlag;
private String qywxIdentifier;
private String createBy;
private Date createTime;
@ -100,6 +102,7 @@ public class SysDepartTreeModel implements Serializable{
this.memo = sysDepart.getMemo();
this.status = sysDepart.getStatus();
this.delFlag = sysDepart.getDelFlag();
this.qywxIdentifier = sysDepart.getQywxIdentifier();
this.createBy = sysDepart.getCreateBy();
this.createTime = sysDepart.getCreateTime();
this.updateBy = sysDepart.getUpdateBy();
@ -287,6 +290,14 @@ public class SysDepartTreeModel implements Serializable{
this.delFlag = delFlag;
}
public String getQywxIdentifier() {
return qywxIdentifier;
}
public void setQywxIdentifier(String qywxIdentifier) {
this.qywxIdentifier = qywxIdentifier;
}
public String getCreateBy() {
return createBy;
}
@ -349,6 +360,7 @@ public class SysDepartTreeModel implements Serializable{
Objects.equals(memo, model.memo) &&
Objects.equals(status, model.status) &&
Objects.equals(delFlag, model.delFlag) &&
Objects.equals(qywxIdentifier, model.qywxIdentifier) &&
Objects.equals(createBy, model.createBy) &&
Objects.equals(createTime, model.createTime) &&
Objects.equals(updateBy, model.updateBy) &&
@ -364,7 +376,7 @@ public class SysDepartTreeModel implements Serializable{
return Objects.hash(id, parentId, departName, departNameEn, departNameAbbr,
departOrder, description, orgCategory, orgType, orgCode, mobile, fax, address,
memo, status, delFlag, createBy, createTime, updateBy, updateTime,
memo, status, delFlag, qywxIdentifier, createBy, createTime, updateBy, updateTime,
children);
}

View File

@ -1,13 +1,12 @@
package org.jeecg.modules.system.service;
import java.util.List;
import java.util.Map;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.modules.system.entity.SysCategory;
import org.jeecg.modules.system.model.TreeSelectModel;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
import java.util.Map;
/**
* @Description: 分类字典
@ -59,4 +58,21 @@ public interface ISysCategoryService extends IService<SysCategory> {
*/
void deleteSysCategory(String ids);
/**
* 分类字典控件数据回显[表单页面]
*
* @param ids
* @return
*/
List<String> loadDictItem(String ids);
/**
* 分类字典控件数据回显[表单页面]
*
* @param ids
* @param delNotExist 是否移除不存在的项设为false如果某个key不存在数据库中则直接返回key本身
* @return
*/
List<String> loadDictItem(String ids, boolean delNotExist);
}

View File

@ -1,5 +1,6 @@
package org.jeecg.modules.system.service;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.system.entity.SysDepart;
import org.jeecg.modules.system.model.DepartIdModel;
@ -112,6 +113,20 @@ public interface ISysDepartService extends IService<SysDepart>{
* @return
*/
List<SysDepartTreeModel> queryTreeListByPid(String parentId);
/**
* 获取某个部门的所有父级部门的ID
*
* @param departId 根据departId查
*/
JSONObject queryAllParentIdByDepartId(String departId);
/**
* 获取某个部门的所有父级部门的ID
*
* @param orgCode 根据orgCode查
*/
JSONObject queryAllParentIdByOrgCode(String orgCode);
/**
* 获取公司信息
* @return

View File

@ -1,15 +1,15 @@
package org.jeecg.modules.system.service;
import java.util.List;
import java.util.Map;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.common.system.vo.DictModel;
import org.jeecg.common.system.vo.DictQuery;
import org.jeecg.modules.system.entity.SysDict;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.system.entity.SysDictItem;
import org.jeecg.modules.system.model.TreeSelectModel;
import java.util.List;
import java.util.Map;
/**
* <p>
* 字典表 服务类
@ -37,6 +37,8 @@ public interface ISysDictService extends IService<SysDict> {
@Deprecated
List<String> queryTableDictByKeys(String table, String text, String code, String keys);
@Deprecated
List<String> queryTableDictByKeys(String table, String text, String code, String keys,boolean delNotExist);
/**
* 根据字典类型删除关联表中其对应的数据
@ -82,7 +84,18 @@ public interface ISysDictService extends IService<SysDict> {
* @param keyword
* @return
*/
public List<DictModel> queryLittleTableDictItems(String table, String text, String code,String keyword, int pageSize);
public List<DictModel> queryLittleTableDictItems(String table, String text, String code, String condition, String keyword, int pageSize);
/**
* 查询字典表所有数据
* @param table
* @param text
* @param code
* @param condition
* @param keyword
* @return
*/
public List<DictModel> queryAllTableDictItems(String table, String text, String code, String condition, String keyword);
/**
* 根据表名显示字段名存储字段名 查询树
* @param table
@ -125,4 +138,21 @@ public interface ISysDictService extends IService<SysDict> {
@Deprecated
public List<DictModel> queryDictTablePageList(DictQuery query,int pageSize, int pageNo);
/**
* 获取字典数据
* @param dictCode 字典code
* @param dictCode 表名,文本字段,code字段 | 举例sys_user,realname,id
* @return
*/
List<DictModel> getDictItems(String dictCode);
/**
* JSearchSelectTag下拉搜索组件专用接口
* 大数据量的字典表 走异步加载 即前端输入内容过滤数据
*
* @param dictCode 字典code格式table,text,code
* @return
*/
List<DictModel> loadDict(String dictCode, String keyword, Integer pageSize);
}

View File

@ -11,4 +11,9 @@ import org.jeecg.modules.system.entity.SysPosition;
*/
public interface ISysPositionService extends IService<SysPosition> {
/**
* 通过code查询
*/
SysPosition getByCode(String code);
}

View File

@ -1,9 +1,11 @@
package org.jeecg.modules.system.service;
import org.jeecg.modules.system.entity.SysThirdAccount;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.system.entity.SysThirdAccount;
import org.jeecg.modules.system.entity.SysUser;
import java.util.List;
/**
* @Description: 第三方登录账号表
* @Author: jeecg-boot
@ -16,4 +18,18 @@ public interface ISysThirdAccountService extends IService<SysThirdAccount> {
/**创建第三方用户*/
SysUser createUser(String phone, String thirdUserUuid);
/** 根据本地userId查询数据 */
SysThirdAccount getOneBySysUserId(String sysUserId, String thirdType);
/** 根据第三方userId查询数据 */
SysThirdAccount getOneByThirdUserId(String thirdUserId, String thirdType);
/**
* 通过 sysUsername 集合批量查询
*
* @param sysUsernameArr username集合
* @param thirdType 第三方类型
* @return
*/
List<SysThirdAccount> listThirdUserIdByUsername(String[] sysUsernameArr, String thirdType);
}

View File

@ -3,6 +3,7 @@ package org.jeecg.modules.system.service;
import java.util.List;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.jeecg.modules.system.entity.SysUser;
import org.jeecg.modules.system.entity.SysUserDepart;
import org.jeecg.modules.system.model.DepartIdModel;
@ -37,5 +38,16 @@ public interface ISysUserDepartService extends IService<SysUserDepart> {
/**
* 根据部门code查询当前部门和下级部门的用户信息
*/
public List<SysUser> queryUserByDepCode(String depCode,String realname);
List<SysUser> queryUserByDepCode(String depCode,String realname);
/**
* 用户组件数据查询
* @param departId
* @param username
* @param pageSize
* @param pageNo
* @return
*/
IPage<SysUser> queryDepartUserPageList(String departId, String username, String realname, int pageSize, int pageNo);
}

View File

@ -1,22 +1,21 @@
package org.jeecg.modules.system.service;
import java.util.List;
import java.util.Map;
import java.util.Set;
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 com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.vo.SysUserCacheInfo;
import org.jeecg.modules.system.entity.SysUser;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.system.model.SysUserSysDepartModel;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* <p>
* 用户表 服务类
@ -233,4 +232,24 @@ public interface ISysUserService extends IService<SysUser> {
* @return
*/
List<SysUser> queryByDepIds(List<String> departIds, String username);
/**
* 保存用户
* @param user 用户
* @param selectedRoles 选择的角色id多个以逗号隔开
* @param selectedDeparts 选择的部门id多个以逗号隔开
*/
void saveUser(SysUser user, String selectedRoles, String selectedDeparts);
/**
* 编辑用户
* @param user 用户
* @param roles 选择的角色id多个以逗号隔开
* @param departs 选择的部门id多个以逗号隔开
*/
void editUser(SysUser user, String roles, String departs);
/** userId转为username */
List<String> userIdToUsername(Collection<String> userIdList);
}

View File

@ -0,0 +1,79 @@
package org.jeecg.modules.system.service;
import org.jeecg.common.api.dto.message.MessageDTO;
import org.jeecg.modules.system.vo.thirdapp.SyncInfoVo;
import java.util.List;
/**
* 第三方App对接
*/
public interface IThirdAppService {
String getAccessToken();
/**
* 将本地部门同步到第三方App<br>
* 同步方向本地 --> 第三方APP
* 同步逻辑<br>
* 1. 先判断是否同步过有则修改无则创建<br>
* 2. 本地没有但第三方App里有则删除第三方App里的
*
* @return 成功返回true
*/
boolean syncLocalDepartmentToThirdApp(String ids);
/**
* 将第三方App部门同步到本地<br>
* 同步方向第三方APP --> 本地
* 同步逻辑<br>
* 1. 先判断是否同步过有则修改无则创建<br>
* 2. 本地没有但第三方App里有则删除第三方App里的
*
* @return 成功返回true
*/
SyncInfoVo syncThirdAppDepartmentToLocal(String ids);
/**
* 将本地用户同步到第三方App<br>
* 同步方向本地 --> 第三方APP <br>
* 同步逻辑先判断是否同步过有则修改无则创建<br>
* 注意同步人员的状态比如离职禁用逻辑删除等
* (特殊点1目前逻辑特意做的不删除用户防止企业微信提前上线用户已经存在但是平台无此用户
* 企业微信支持禁用账号钉钉不支持
* 2企业微信里面是手机号激活只能用户自己改不允许通过接口改)
*
* @return 成功返回空数组失败返回错误信息
*/
SyncInfoVo syncLocalUserToThirdApp(String ids);
/**
* 将第三方App用户同步到本地<br>
* 同步方向第三方APP --> 本地 <br>
* 同步逻辑先判断是否同步过有则修改无则创建<br>
* 注意同步人员的状态比如离职禁用逻辑删除等
*
* @return 成功返回空数组失败返回错误信息
*/
SyncInfoVo syncThirdAppUserToLocal();
/**
* 根据本地用户ID删除第三方APP的用户
*
* @param userIdList 本地用户ID列表
* @return 0表示成功其他值表示失败
*/
int removeThirdAppUser(List<String> userIdList);
/**
* 发送消息
*
* @param message
* @param verifyConfig 是否验证配置未启用的APP会拒绝发送
* @return
*/
boolean sendMessage(MessageDTO message, boolean verifyConfig);
boolean sendMessage(MessageDTO message);
}

View File

@ -1,7 +1,4 @@
package org.jeecg.modules.system.service.impl;
import java.util.HashMap;
import java.util.ArrayList;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@ -95,6 +92,11 @@ public class SysBaseApiImpl implements ISysBaseAPI {
@Autowired
private ISysPermissionDataRuleService sysPermissionDataRuleService;
@Autowired
private ThirdAppWechatEnterpriseServiceImpl wechatEnterpriseService;
@Autowired
private ThirdAppDingtalkServiceImpl dingtalkService;
@Override
@Cacheable(cacheNames=CacheConstant.SYS_USERS_CACHE, key="#username")
public LoginUser getUserByName(String username) {
@ -201,6 +203,8 @@ public class SysBaseApiImpl implements ISysBaseAPI {
info.setSysUserCode(user.getUsername());
info.setSysUserName(user.getRealname());
info.setSysOrgCode(user.getOrgCode());
}else{
return null;
}
//多部门支持in查询
List<SysDepart> list = departMapper.queryUserDeparts(user.getId());
@ -267,7 +271,7 @@ public class SysBaseApiImpl implements ISysBaseAPI {
}
@Override
@Cacheable(value = CacheConstant.SYS_DICT_CACHE,key = "#code")
@Cacheable(value = CacheConstant.SYS_DICT_CACHE,key = "#code", unless = "#result == null ")
public List<DictModel> queryDictItemsByCode(String code) {
return sysDictService.queryDictItemsByCode(code);
}
@ -294,6 +298,13 @@ public class SysBaseApiImpl implements ISysBaseAPI {
message.getTitle(),
message.getContent(),
message.getCategory());
try {
// 同步发送第三方APP消息
wechatEnterpriseService.sendMessage(message, true);
dingtalkService.sendMessage(message, true);
} catch (Exception e) {
log.error("同步发送第三方APP消息失败", e);
}
}
@Override
@ -305,6 +316,13 @@ public class SysBaseApiImpl implements ISysBaseAPI {
message.getCategory(),
message.getBusType(),
message.getBusId());
try {
// 同步发送第三方APP消息
wechatEnterpriseService.sendMessage(message, true);
dingtalkService.sendMessage(message, true);
} catch (Exception e) {
log.error("同步发送第三方APP消息失败", e);
}
}
@Override
@ -368,6 +386,13 @@ public class SysBaseApiImpl implements ISysBaseAPI {
webSocket.sendMessage(sysUser.getId(), obj.toJSONString());
}
}
try {
// 同步企业微信钉钉的消息通知
dingtalkService.sendActionCardMessage(announcement, true);
wechatEnterpriseService.sendTextCardMessage(announcement, true);
} catch (Exception e) {
log.error("同步发送第三方APP消息失败", e);
}
}
@ -435,6 +460,14 @@ public class SysBaseApiImpl implements ISysBaseAPI {
webSocket.sendMessage(sysUser.getId(), obj.toJSONString());
}
}
try {
// 同步企业微信钉钉的消息通知
dingtalkService.sendActionCardMessage(announcement, true);
wechatEnterpriseService.sendTextCardMessage(announcement, true);
} catch (Exception e) {
log.error("同步发送第三方APP消息失败", e);
}
}
@Override
@ -492,8 +525,11 @@ public class SysBaseApiImpl implements ISysBaseAPI {
DB_TYPE = DataBaseConstant.DB_TYPE_SQLSERVER;
}else if(dbType.indexOf("postgresql")>=0) {
DB_TYPE = DataBaseConstant.DB_TYPE_POSTGRESQL;
}else if(dbType.indexOf("mariadb")>=0) {
DB_TYPE = DataBaseConstant.DB_TYPE_MARIADB;
}else {
throw new JeecgBootException("数据库类型:["+dbType+"]不识别!");
log.error("数据库类型:[" + dbType + "]不识别!");
//throw new JeecgBootException("数据库类型:["+dbType+"]不识别!");
}
} catch (Exception e) {
log.error(e.getMessage(), e);
@ -848,7 +884,7 @@ public class SysBaseApiImpl implements ISysBaseAPI {
/**
* 36根据多个用户账号(逗号分隔)查询返回多个用户信息
* @param orgCodes
* @param usernames
* @return
*/
@Override
@ -867,7 +903,7 @@ public class SysBaseApiImpl implements ISysBaseAPI {
/**
* 37根据多个部门编码(逗号分隔)查询返回多个部门信息
* @param usernames
* @param orgCodes
* @return
*/
@Override

View File

@ -1,27 +1,27 @@
package org.jeecg.modules.system.service.impl;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.jeecg.common.constant.FillRuleConstant;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.util.FillRuleUtil;
import org.jeecg.common.util.YouBianCodeUtil;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.system.entity.SysCategory;
import org.jeecg.modules.system.mapper.SysCategoryMapper;
import org.jeecg.modules.system.model.TreeSelectModel;
import org.jeecg.modules.system.service.ISysCategoryService;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @Description: 分类字典
* @Author: jeecg-boot
@ -204,4 +204,32 @@ public class SysCategoryServiceImpl extends ServiceImpl<SysCategoryMapper, SysCa
return sb;
}
@Override
public List<String> loadDictItem(String ids) {
return this.loadDictItem(ids, true);
}
@Override
public List<String> loadDictItem(String ids, boolean delNotExist) {
String[] idArray = ids.split(",");
LambdaQueryWrapper<SysCategory> query = new LambdaQueryWrapper<>();
query.in(SysCategory::getId, Arrays.asList(idArray));
// 查询数据
List<SysCategory> list = super.list(query);
// 取出name并返回
List<String> textList;
// update-begin--author:sunjianlei--date:20210514--for新增delNotExist参数设为false不删除数据库里不存在的key ----
if (delNotExist) {
textList = list.stream().map(SysCategory::getName).collect(Collectors.toList());
} else {
textList = new ArrayList<>();
for (String id : idArray) {
List<SysCategory> res = list.stream().filter(i -> id.equals(i.getId())).collect(Collectors.toList());
textList.add(res.size() > 0 ? res.get(0).getName() : id);
}
}
// update-end--author:sunjianlei--date:20210514--for新增delNotExist参数设为false不删除数据库里不存在的key ----
return textList;
}
}

View File

@ -1,8 +1,11 @@
package org.jeecg.modules.system.service.impl;
import java.util.*;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.netty.util.internal.StringUtil;
import org.apache.commons.lang.StringUtils;
import org.jeecg.common.constant.CacheConstant;
import org.jeecg.common.constant.CommonConstant;
@ -21,10 +24,7 @@ import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.netty.util.internal.StringUtil;
import java.util.*;
/**
* <p>
@ -468,7 +468,8 @@ public class SysDepartServiceImpl extends ServiceImpl<SysDepartMapper, SysDepart
SysDepart depart = list.get(i);
SysDepartTreeModel treeModel = new SysDepartTreeModel(depart);
//TODO 异步树加载key拼接__+时间戳,以便于每次展开节点会刷新数据
treeModel.setKey(treeModel.getKey()+"__"+System.currentTimeMillis());
//treeModel.setKey(treeModel.getKey()+"__"+System.currentTimeMillis());
treeModel.setKey(treeModel.getKey());
Integer count=this.baseMapper.queryCountByPid(depart.getId());
if(count>0){
treeModel.setIsLeaf(false);
@ -479,6 +480,59 @@ public class SysDepartServiceImpl extends ServiceImpl<SysDepartMapper, SysDepart
}
return records;
}
@Override
public JSONObject queryAllParentIdByDepartId(String departId) {
JSONObject result = new JSONObject();
for (String id : departId.split(",")) {
JSONObject all = this.queryAllParentId("id", id);
result.put(id, all);
}
return result;
}
@Override
public JSONObject queryAllParentIdByOrgCode(String orgCode) {
JSONObject result = new JSONObject();
for (String code : orgCode.split(",")) {
JSONObject all = this.queryAllParentId("org_code", code);
result.put(code, all);
}
return result;
}
/**
* 查询某个部门的所有父ID信息
*
* @param fieldName 字段名
* @param value
*/
private JSONObject queryAllParentId(String fieldName, String value) {
JSONObject data = new JSONObject();
// 父ID集合有序
data.put("parentIds", new JSONArray());
// 父ID的部门数据key是idvalue是数据
data.put("parentMap", new JSONObject());
this.queryAllParentIdRecursion(fieldName, value, data);
return data;
}
/**
* 递归调用查询父部门接口
*/
private void queryAllParentIdRecursion(String fieldName, String value, JSONObject data) {
QueryWrapper<SysDepart> queryWrapper = new QueryWrapper<>();
queryWrapper.eq(fieldName, value);
SysDepart depart = super.getOne(queryWrapper);
if (depart != null) {
data.getJSONArray("parentIds").add(0, depart.getId());
data.getJSONObject("parentMap").put(depart.getId(), depart);
if (oConvertUtils.isNotEmpty(depart.getParentId())) {
this.queryAllParentIdRecursion("id", depart.getParentId(), data);
}
}
}
@Override
public SysDepart queryCompByOrgCode(String orgCode) {
int length = YouBianCodeUtil.zhanweiLength;

View File

@ -9,6 +9,7 @@ import org.jeecg.common.constant.CacheConstant;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.vo.DictModel;
import org.jeecg.common.system.vo.DictQuery;
import org.jeecg.common.util.SqlInjectionUtil;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.system.entity.SysDict;
import org.jeecg.modules.system.entity.SysDictItem;
@ -50,7 +51,7 @@ public class SysDictServiceImpl extends ServiceImpl<SysDictMapper, SysDict> impl
* @return
*/
@Override
@Cacheable(value = CacheConstant.SYS_DICT_CACHE,key = "#code")
@Cacheable(value = CacheConstant.SYS_DICT_CACHE,key = "#code", unless = "#result == null ")
public List<DictModel> queryDictItemsByCode(String code) {
log.debug("无缓存dictCache的时候调用这里");
return sysDictMapper.queryDictItemsByCode(code);
@ -86,7 +87,7 @@ public class SysDictServiceImpl extends ServiceImpl<SysDictMapper, SysDict> impl
*/
@Override
@Cacheable(value = CacheConstant.SYS_DICT_CACHE,key = "#code+':'+#key")
@Cacheable(value = CacheConstant.SYS_DICT_CACHE,key = "#code+':'+#key", unless = "#result == null ")
public String queryDictTextByKey(String code, String key) {
log.debug("无缓存dictText的时候调用这里");
return sysDictMapper.queryDictTextByKey(code, key);
@ -123,12 +124,17 @@ public class SysDictServiceImpl extends ServiceImpl<SysDictMapper, SysDict> impl
* @return
*/
@Override
@Cacheable(value = CacheConstant.SYS_DICT_TABLE_CACHE)
@Cacheable(value = CacheConstant.SYS_DICT_TABLE_CACHE, unless = "#result == null ")
public String queryTableDictTextByKey(String table,String text,String code, String key) {
log.debug("无缓存dictTable的时候调用这里");
return sysDictMapper.queryTableDictTextByKey(table,text,code,key);
}
@Override
public List<String> queryTableDictByKeys(String table, String text, String code, String keys) {
return this.queryTableDictByKeys(table, text, code, keys, true);
}
/**
* 通过查询指定table的 text code 获取字典包含text和value
* dictTableCache采用redis缓存有效期10分钟
@ -136,28 +142,33 @@ public class SysDictServiceImpl extends ServiceImpl<SysDictMapper, SysDict> impl
* @param text
* @param code
* @param keys (逗号分隔)
* @param delNotExist 是否移除不存在的项默认为true设为false如果某个key不存在数据库中则直接返回key本身
* @return
*/
@Override
//update-begin--Author:lvdandan Date:20201204 forJT-36online树形列表bug修改后还是显示原来值 暂时去掉缓存
//@Cacheable(value = CacheConstant.SYS_DICT_TABLE_BY_KEYS_CACHE)
//update-end--Author:lvdandan Date:20201204 forJT-36online树形列表bug修改后还是显示原来值 暂时去掉缓存
public List<String> queryTableDictByKeys(String table, String text, String code, String keys) {
public List<String> queryTableDictByKeys(String table, String text, String code, String keys, boolean delNotExist) {
if(oConvertUtils.isEmpty(keys)){
return null;
}
String[] keyArray = keys.split(",");
List<DictModel> dicts = sysDictMapper.queryTableDictByKeys(table, text, code, keyArray);
List<String> texts = new ArrayList<>(dicts.size());
// update-begin--author:sunjianlei--date:20210514--for新增delNotExist参数设为false不删除数据库里不存在的key ----
// 查询出来的顺序可能是乱的需要排个序
for (String key : keyArray) {
for (DictModel dict : dicts) {
if (key.equals(dict.getValue())) {
texts.add(dict.getText());
break;
}
List<DictModel> res = dicts.stream().filter(i -> key.equals(i.getValue())).collect(Collectors.toList());
if (res.size() > 0) {
texts.add(res.get(0).getText());
} else if (!delNotExist) {
texts.add(key);
}
}
// update-end--author:sunjianlei--date:20210514--for新增delNotExist参数设为false不删除数据库里不存在的key ----
return texts;
}
@ -205,12 +216,49 @@ public class SysDictServiceImpl extends ServiceImpl<SysDictMapper, SysDict> impl
}
@Override
public List<DictModel> queryLittleTableDictItems(String table, String text, String code, String keyword, int pageSize) {
public List<DictModel> queryLittleTableDictItems(String table, String text, String code, String condition, String keyword, int pageSize) {
Page<DictModel> page = new Page<DictModel>(1, pageSize);
IPage<DictModel> pageList = baseMapper.queryTableDictItems(page, table, text, code, "%"+keyword+"%");
page.setSearchCount(false);
String filterSql = getFilterSql(text, code, condition, keyword);
IPage<DictModel> pageList = baseMapper.queryTableDictWithFilter(page, table, text, code, filterSql);
return pageList.getRecords();
}
/**
* 获取条件语句
* @param text
* @param code
* @param condition
* @param keyword
* @return
*/
private String getFilterSql(String text, String code, String condition, String keyword){
String keywordSql = null, filterSql = "", sql_where = " where ";
if(oConvertUtils.isNotEmpty(keyword)){
// 判断是否是多选
if (keyword.contains(",")) {
String inKeywords = "\"" + keyword.replaceAll(",", "\",\"") + "\"";
keywordSql = "(" + text + " in (" + inKeywords + ") or " + code + " in (" + inKeywords + "))";
} else {
keywordSql = "("+text + " like '%"+keyword+"%' or "+ code + " like '%"+keyword+"%')";
}
}
if(oConvertUtils.isNotEmpty(condition) && oConvertUtils.isNotEmpty(keywordSql)){
filterSql+= sql_where + condition + " and " + keywordSql;
}else if(oConvertUtils.isNotEmpty(condition)){
filterSql+= sql_where + condition;
}else if(oConvertUtils.isNotEmpty(keywordSql)){
filterSql+= sql_where + keywordSql;
}
return filterSql;
}
@Override
public List<DictModel> queryAllTableDictItems(String table, String text, String code, String condition, String keyword) {
String filterSql = getFilterSql(text, code, condition, keyword);
List<DictModel> ls = baseMapper.queryAllTableDictItems(table, text, code, filterSql);
return ls;
}
@Override
public List<TreeSelectModel> queryTreeList(Map<String, String> query,String table, String text, String code, String pidField,String pid,String hasChildField) {
return baseMapper.queryTreeList(query,table, text, code, pidField, pid,hasChildField);
@ -238,4 +286,61 @@ public class SysDictServiceImpl extends ServiceImpl<SysDictMapper, SysDict> impl
Page<DictModel> pageList = baseMapper.queryDictTablePageList(page, query);
return pageList.getRecords();
}
@Override
public List<DictModel> getDictItems(String dictCode) {
List<DictModel> ls;
if (dictCode.contains(",")) {
//关联表字典举例sys_user,realname,id
String[] params = dictCode.split(",");
if (params.length < 3) {
// 字典Code格式不正确
return null;
}
//SQL注入校验只限制非法串改数据库
final String[] sqlInjCheck = {params[0], params[1], params[2]};
SqlInjectionUtil.filterContent(sqlInjCheck);
if (params.length == 4) {
// SQL注入校验查询条件SQL 特殊check此方法仅供此处使用
SqlInjectionUtil.specialFilterContent(params[3]);
ls = this.queryTableDictItemsByCodeAndFilter(params[0], params[1], params[2], params[3]);
} else if (params.length == 3) {
ls = this.queryTableDictItemsByCode(params[0], params[1], params[2]);
} else {
// 字典Code格式不正确
return null;
}
} else {
//字典表
ls = this.queryDictItemsByCode(dictCode);
}
return ls;
}
@Override
public List<DictModel> loadDict(String dictCode, String keyword, Integer pageSize) {
if (dictCode.contains(",")) {
//update-begin-author:taoyan date:20210329 for: 下拉搜索不支持表名后加查询条件
String[] params = dictCode.split(",");
String condition = null;
if (params.length != 3 && params.length != 4) {
// 字典Code格式不正确
return null;
} else if (params.length == 4) {
condition = params[3];
}
List<DictModel> ls;
if (pageSize != null) {
ls = this.queryLittleTableDictItems(params[0], params[1], params[2], condition, keyword, pageSize);
} else {
ls = this.queryAllTableDictItems(params[0], params[1], params[2], condition, keyword);
}
//update-end-author:taoyan date:20210329 for: 下拉搜索不支持表名后加查询条件
return ls;
} else {
// 字典Code格式不正确
return null;
}
}
}

View File

@ -1,5 +1,6 @@
package org.jeecg.modules.system.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.jeecg.modules.system.entity.SysPosition;
import org.jeecg.modules.system.mapper.SysPositionMapper;
@ -15,4 +16,11 @@ import org.springframework.stereotype.Service;
@Service
public class SysPositionServiceImpl extends ServiceImpl<SysPositionMapper, SysPosition> implements ISysPositionService {
@Override
public SysPosition getByCode(String code) {
LambdaQueryWrapper<SysPosition> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysPosition::getCode, code);
return super.getOne(queryWrapper);
}
}

View File

@ -1,7 +1,9 @@
package org.jeecg.modules.system.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.PasswordUtil;
import org.jeecg.common.util.UUIDGenerator;
import org.jeecg.common.util.oConvertUtils;
@ -14,12 +16,10 @@ import org.jeecg.modules.system.mapper.SysThirdAccountMapper;
import org.jeecg.modules.system.mapper.SysUserMapper;
import org.jeecg.modules.system.mapper.SysUserRoleMapper;
import org.jeecg.modules.system.service.ISysThirdAccountService;
import org.jeecg.modules.system.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import java.util.Date;
import java.util.List;
/**
@ -55,6 +55,7 @@ public class SysThirdAccountServiceImpl extends ServiceImpl<SysThirdAccountMappe
thirdQuery.eq(SysThirdAccount::getThirdType,account.getThirdType());
SysThirdAccount sysThirdAccounts = sysThirdAccountMapper.selectOne(thirdQuery);
if(sysThirdAccounts!=null){
sysThirdAccount.setThirdUserId(sysThirdAccounts.getThirdUserId());
sysThirdAccountMapper.deleteById(sysThirdAccounts.getId());
}
//更新用户账户表sys_user_id
@ -67,6 +68,13 @@ public class SysThirdAccountServiceImpl extends ServiceImpl<SysThirdAccountMappe
LambdaQueryWrapper<SysThirdAccount> query = new LambdaQueryWrapper<>();
query.eq(SysThirdAccount::getThirdUserUuid,thirdUserUuid);
SysThirdAccount account = sysThirdAccountMapper.selectOne(query);
//通过用户名查询数据库是否已存在
SysUser userByName = sysUserMapper.getUserByName(thirdUserUuid);
if(null!=userByName){
//如果账号存在的话则自动加上一个时间戳
String format = DateUtils.yyyymmddhhmmss.get().format(new Date());
thirdUserUuid = thirdUserUuid + format;
}
//添加用户
SysUser user = new SysUser();
user.setActivitiSync(CommonConstant.ACT_SYNC_0);
@ -103,4 +111,26 @@ public class SysThirdAccountServiceImpl extends ServiceImpl<SysThirdAccountMappe
sysUserRoleMapper.insert(userRole);
return userid;
}
@Override
public SysThirdAccount getOneBySysUserId(String sysUserId, String thirdType) {
LambdaQueryWrapper<SysThirdAccount> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysThirdAccount::getSysUserId, sysUserId);
queryWrapper.eq(SysThirdAccount::getThirdType, thirdType);
return super.getOne(queryWrapper);
}
@Override
public SysThirdAccount getOneByThirdUserId(String thirdUserId, String thirdType) {
LambdaQueryWrapper<SysThirdAccount> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysThirdAccount::getThirdUserId, thirdUserId);
queryWrapper.eq(SysThirdAccount::getThirdType, thirdType);
return super.getOne(queryWrapper);
}
@Override
public List<SysThirdAccount> listThirdUserIdByUsername(String[] sysUsernameArr, String thirdType) {
return sysThirdAccountMapper.selectThirdIdsByUsername(sysUsernameArr, thirdType);
}
}

View File

@ -1,11 +1,15 @@
package org.jeecg.modules.system.service.impl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Collectors;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.system.entity.SysDepart;
import org.jeecg.modules.system.entity.SysUser;
@ -100,34 +104,58 @@ public class SysUserDepartServiceImpl extends ServiceImpl<SysUserDepartMapper, S
*/
@Override
public List<SysUser> queryUserByDepCode(String depCode,String realname) {
LambdaQueryWrapper<SysDepart> queryByDepCode = new LambdaQueryWrapper<SysDepart>();
queryByDepCode.likeRight(SysDepart::getOrgCode,depCode);
List<SysDepart> sysDepartList = sysDepartService.list(queryByDepCode);
List<String> depIds = sysDepartList.stream().map(SysDepart::getId).collect(Collectors.toList());
LambdaQueryWrapper<SysUserDepart> queryUDep = new LambdaQueryWrapper<SysUserDepart>();
queryUDep.in(SysUserDepart::getDepId, depIds);
List<String> userIdList = new ArrayList<>();
List<SysUserDepart> uDepList = this.list(queryUDep);
if(uDepList != null && uDepList.size() > 0) {
for(SysUserDepart uDep : uDepList) {
userIdList.add(uDep.getUserId());
}
LambdaQueryWrapper<SysUser> queryUser = new LambdaQueryWrapper<SysUser>();
queryUser.in(SysUser::getId,userIdList);
//update-begin-author:taoyan date:20210422 for: 根据部门选择用户接口代码优化
if(oConvertUtils.isNotEmpty(realname)){
queryUser.like(SysUser::getRealname,realname.trim());
realname = realname.trim();
}
List<SysUser> userList = (List<SysUser>) sysUserService.list(queryUser);
//update-begin-author:taoyan date:201905047 for:接口调用查询返回结果不能返回密码相关信息
List<SysUser> userList = this.baseMapper.queryDepartUserList(depCode, realname);
Map<String, SysUser> map = new HashMap<String, SysUser>();
for (SysUser sysUser : userList) {
// 返回的用户数据去掉密码信息
sysUser.setSalt("");
sysUser.setPassword("");
map.put(sysUser.getId(), sysUser);
}
//update-end-author:taoyan date:201905047 for:接口调用查询返回结果不能返回密码相关信息
return userList;
return new ArrayList<SysUser>(map.values());
//update-end-author:taoyan date:20210422 for: 根据部门选择用户接口代码优化
}
return new ArrayList<SysUser>();
@Override
public IPage<SysUser> queryDepartUserPageList(String departId, String username, String realname, int pageSize, int pageNo) {
IPage<SysUser> pageList = null;
// 部门ID不存在 直接查询用户表即可
Page<SysUser> page = new Page<SysUser>(pageNo, pageSize);
if(oConvertUtils.isEmpty(departId)){
LambdaQueryWrapper<SysUser> query = new LambdaQueryWrapper<>();
if(oConvertUtils.isNotEmpty(username)){
query.like(SysUser::getUsername, username);
}
pageList = sysUserService.page(page, query);
}else{
// 有部门ID 需要走自定义sql
SysDepart sysDepart = sysDepartService.getById(departId);
pageList = this.baseMapper.queryDepartUserPageList(page, sysDepart.getOrgCode(), username, realname);
}
List<SysUser> userList = pageList.getRecords();
if(userList!=null && userList.size()>0){
List<String> userIds = userList.stream().map(SysUser::getId).collect(Collectors.toList());
Map<String, SysUser> map = new HashMap<String, SysUser>();
if(userIds!=null && userIds.size()>0){
// 查部门名称
Map<String,String> useDepNames = sysUserService.getDepNamesByUserIds(userIds);
userList.forEach(item->{
//TODO 临时借用这个字段用于页面展示
item.setOrgCodeTxt(useDepNames.get(item.getId()));
item.setSalt("");
item.setPassword("");
// 去重
map.put(item.getId(), item);
});
}
pageList.setRecords(new ArrayList<SysUser>(map.values()));
}
return pageList;
}
}

View File

@ -10,12 +10,12 @@ import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CacheConstant;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.modules.base.service.BaseCommonService;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.system.vo.SysUserCacheInfo;
import org.jeecg.common.util.PasswordUtil;
import org.jeecg.common.util.UUIDGenerator;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.base.service.BaseCommonService;
import org.jeecg.modules.system.entity.*;
import org.jeecg.modules.system.mapper.*;
import org.jeecg.modules.system.model.SysUserSysDepartModel;
@ -62,6 +62,12 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
private SysDepartRoleMapper sysDepartRoleMapper;
@Resource
private BaseCommonService baseCommonService;
@Autowired
private SysThirdAccountMapper sysThirdAccountMapper;
@Autowired
ThirdAppWechatEnterpriseServiceImpl wechatEnterpriseService;
@Autowired
ThirdAppDingtalkServiceImpl dingtalkService;
@Override
@CacheEvict(value = {CacheConstant.SYS_USERS_CACHE}, allEntries = true)
@ -408,6 +414,16 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
line += sysUserDepartMapper.delete(new LambdaQueryWrapper<SysUserDepart>().in(SysUserDepart::getUserId, userIds));
//3. 删除用户角色关系
line += sysUserRoleMapper.delete(new LambdaQueryWrapper<SysUserRole>().in(SysUserRole::getUserId, userIds));
//4.同步删除第三方App的用户
try {
dingtalkService.removeThirdAppUser(userIds);
wechatEnterpriseService.removeThirdAppUser(userIds);
} catch (Exception e) {
log.error("同步删除第三方App的用户失败", e);
}
//5. 删除第三方用户表因为第4步需要用到第三方用户表所以在他之后删
line += sysThirdAccountMapper.delete(new LambdaQueryWrapper<SysThirdAccount>().in(SysThirdAccount::getSysUserId, userIds));
return line != 0;
}
@ -439,4 +455,88 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
return userMapper.queryByDepIds(departIds,username);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void saveUser(SysUser user, String selectedRoles, String selectedDeparts) {
//step.1 保存用户
this.save(user);
//step.2 保存角色
if(oConvertUtils.isNotEmpty(selectedRoles)) {
String[] arr = selectedRoles.split(",");
for (String roleId : arr) {
SysUserRole userRole = new SysUserRole(user.getId(), roleId);
sysUserRoleMapper.insert(userRole);
}
}
//step.3 保存所属部门
if(oConvertUtils.isNotEmpty(selectedDeparts)) {
String[] arr = selectedDeparts.split(",");
for (String deaprtId : arr) {
SysUserDepart userDeaprt = new SysUserDepart(user.getId(), deaprtId);
sysUserDepartMapper.insert(userDeaprt);
}
}
}
@Override
@Transactional(rollbackFor = Exception.class)
@CacheEvict(value={CacheConstant.SYS_USERS_CACHE}, allEntries=true)
public void editUser(SysUser user, String roles, String departs) {
//step.1 修改用户基础信息
this.updateById(user);
//step.2 修改角色
//处理用户角色 先删后加
sysUserRoleMapper.delete(new QueryWrapper<SysUserRole>().lambda().eq(SysUserRole::getUserId, user.getId()));
if(oConvertUtils.isNotEmpty(roles)) {
String[] arr = roles.split(",");
for (String roleId : arr) {
SysUserRole userRole = new SysUserRole(user.getId(), roleId);
sysUserRoleMapper.insert(userRole);
}
}
//step.3 修改部门
String[] arr = {};
if(oConvertUtils.isNotEmpty(departs)){
arr = departs.split(",");
}
//查询已关联部门
List<SysUserDepart> userDepartList = sysUserDepartMapper.selectList(new QueryWrapper<SysUserDepart>().lambda().eq(SysUserDepart::getUserId, user.getId()));
if(userDepartList != null && userDepartList.size()>0){
for(SysUserDepart depart : userDepartList ){
//修改已关联部门删除部门用户角色关系
if(!Arrays.asList(arr).contains(depart.getDepId())){
List<SysDepartRole> sysDepartRoleList = sysDepartRoleMapper.selectList(
new QueryWrapper<SysDepartRole>().lambda().eq(SysDepartRole::getDepartId,depart.getDepId()));
List<String> roleIds = sysDepartRoleList.stream().map(SysDepartRole::getId).collect(Collectors.toList());
if(roleIds != null && roleIds.size()>0){
departRoleUserMapper.delete(new QueryWrapper<SysDepartRoleUser>().lambda().eq(SysDepartRoleUser::getUserId, user.getId())
.in(SysDepartRoleUser::getDroleId,roleIds));
}
}
}
}
//先删后加
sysUserDepartMapper.delete(new QueryWrapper<SysUserDepart>().lambda().eq(SysUserDepart::getUserId, user.getId()));
if(oConvertUtils.isNotEmpty(departs)) {
for (String departId : arr) {
SysUserDepart userDepart = new SysUserDepart(user.getId(), departId);
sysUserDepartMapper.insert(userDepart);
}
}
//step.4 修改手机号和邮箱
// 更新手机号邮箱空字符串为 null
userMapper.updateNullByEmptyString("email");
userMapper.updateNullByEmptyString("phone");
}
@Override
public List<String> userIdToUsername(Collection<String> userIdList) {
LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.in(SysUser::getId, userIdList);
List<SysUser> userList = super.list(queryWrapper);
return userList.stream().map(SysUser::getUsername).collect(Collectors.toList());
}
}

View File

@ -0,0 +1,769 @@
package org.jeecg.modules.system.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.jeecg.dingtalk.api.base.JdtBaseAPI;
import com.jeecg.dingtalk.api.core.response.Response;
import com.jeecg.dingtalk.api.core.vo.AccessToken;
import com.jeecg.dingtalk.api.core.vo.PageResult;
import com.jeecg.dingtalk.api.department.JdtDepartmentAPI;
import com.jeecg.dingtalk.api.department.vo.Department;
import com.jeecg.dingtalk.api.message.JdtMessageAPI;
import com.jeecg.dingtalk.api.message.vo.ActionCardMessage;
import com.jeecg.dingtalk.api.message.vo.Message;
import com.jeecg.dingtalk.api.message.vo.TextMessage;
import com.jeecg.dingtalk.api.user.JdtUserAPI;
import com.jeecg.dingtalk.api.user.body.GetUserListBody;
import com.jeecg.dingtalk.api.user.vo.User;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.jeecg.common.api.dto.message.MessageDTO;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.util.PasswordUtil;
import org.jeecg.common.util.RestUtil;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.config.thirdapp.ThirdAppConfig;
import org.jeecg.modules.system.entity.*;
import org.jeecg.modules.system.mapper.SysAnnouncementSendMapper;
import org.jeecg.modules.system.model.SysDepartTreeModel;
import org.jeecg.modules.system.service.*;
import org.jeecg.modules.system.vo.thirdapp.JdtDepartmentTreeVo;
import org.jeecg.modules.system.vo.thirdapp.SyncInfoVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* 第三方App对接钉钉实现类
*/
@Slf4j
@Service
public class ThirdAppDingtalkServiceImpl implements IThirdAppService {
@Autowired
ThirdAppConfig thirdAppConfig;
@Autowired
private ISysDepartService sysDepartService;
@Autowired
private ISysUserService sysUserService;
@Autowired
private ISysThirdAccountService sysThirdAccountService;
@Autowired
private ISysUserDepartService sysUserDepartService;
@Autowired
private ISysPositionService sysPositionService;
@Autowired
private SysAnnouncementSendMapper sysAnnouncementSendMapper;
@Override
public String getAccessToken() {
String appKey = thirdAppConfig.getDingtalk().getClientId();
String appSecret = thirdAppConfig.getDingtalk().getClientSecret();
AccessToken accessToken = JdtBaseAPI.getAccessToken(appKey, appSecret);
if (accessToken != null) {
return accessToken.getAccessToken();
}
log.warn("获取AccessToken失败");
return null;
}
@Override
public boolean syncLocalDepartmentToThirdApp(String ids) {
String accessToken = this.getAccessToken();
if (accessToken == null) {
return false;
}
// 获取钉钉所有的部门
List<Department> departments = JdtDepartmentAPI.listAll(accessToken);
// 删除钉钉有但本地没有的部门以本地部门数据为主钉钉不能创建同名部门只能先删除
List<SysDepart> sysDepartList = sysDepartService.list();
for1:
for (Department department : departments) {
for (SysDepart depart : sysDepartList) {
// id相同代表已存在不删除
String sourceIdentifier = department.getSource_identifier();
if (sourceIdentifier != null && sourceIdentifier.equals(depart.getId())) {
continue for1;
}
}
// 循环到此说明本地没有删除
int deptId = department.getDept_id();
// 钉钉不允许删除带有用户的部门所以需要判断下将有用户的部门的用户移动至根部门
Response<List<String>> userIdRes = JdtUserAPI.getUserListIdByDeptId(deptId, accessToken);
if (userIdRes.isSuccess() && userIdRes.getResult().size() > 0) {
for (String userId : userIdRes.getResult()) {
User updateUser = new User();
updateUser.setUserid(userId);
updateUser.setDept_id_list(1);
JdtUserAPI.update(updateUser, accessToken);
}
}
JdtDepartmentAPI.delete(deptId, accessToken);
}
// 获取本地所有部门树结构
List<SysDepartTreeModel> sysDepartsTree = sysDepartService.queryTreeList();
// -- 钉钉不能创建新的顶级部门所以新的顶级部门的parentId就为1
Department parent = new Department();
parent.setDept_id(1);
// 递归同步部门
departments = JdtDepartmentAPI.listAll(accessToken);
this.syncDepartmentRecursion(sysDepartsTree, departments, parent, accessToken);
return true;
}
// 递归同步部门到本地
public void syncDepartmentRecursion(List<SysDepartTreeModel> sysDepartsTree, List<Department> departments, Department parent, String accessToken) {
if (sysDepartsTree != null && sysDepartsTree.size() != 0) {
for1:
for (SysDepartTreeModel depart : sysDepartsTree) {
for (Department department : departments) {
// id相同代表已存在执行修改操作
String sourceIdentifier = department.getSource_identifier();
if (sourceIdentifier != null && sourceIdentifier.equals(depart.getId())) {
this.sysDepartToDtDepartment(depart, department, parent.getDept_id());
JdtDepartmentAPI.update(department, accessToken);
// 紧接着同步子级
this.syncDepartmentRecursion(depart.getChildren(), departments, department, accessToken);
// 跳出外部循环
continue for1;
}
}
// 循环到此说明是新部门直接调接口创建
Department newDepartment = this.sysDepartToDtDepartment(depart, parent.getDept_id());
Response<Integer> response = JdtDepartmentAPI.create(newDepartment, accessToken);
// 创建成功将返回的id绑定到本地
if (response.getResult() != null) {
Department newParent = new Department();
newParent.setDept_id(response.getResult());
// 紧接着同步子级
this.syncDepartmentRecursion(depart.getChildren(), departments, newParent, accessToken);
}
// 收集错误信息
// this.syncUserCollectErrInfo(errCode, sysUser, errInfo);
}
}
}
@Override
public SyncInfoVo syncThirdAppDepartmentToLocal(String ids) {
SyncInfoVo syncInfo = new SyncInfoVo();
String accessToken = this.getAccessToken();
if (accessToken == null) {
syncInfo.addFailInfo("accessToken获取失败");
return syncInfo;
}
// 获取钉钉所有的部门
List<Department> departments = JdtDepartmentAPI.listAll(accessToken);
String username = JwtUtil.getUserNameByToken(SpringContextUtils.getHttpServletRequest());
List<JdtDepartmentTreeVo> departmentTreeList = JdtDepartmentTreeVo.listToTree(departments);
// 递归同步部门
this.syncDepartmentToLocalRecursion(departmentTreeList, null, username, syncInfo, accessToken);
return syncInfo;
}
public void syncDepartmentToLocalRecursion(List<JdtDepartmentTreeVo> departmentTreeList, String sysParentId, String username, SyncInfoVo syncInfo, String accessToken) {
if (departmentTreeList != null && departmentTreeList.size() != 0) {
for (JdtDepartmentTreeVo departmentTree : departmentTreeList) {
LambdaQueryWrapper<SysDepart> queryWrapper = new LambdaQueryWrapper<>();
// 根据 source_identifier 字段查询
queryWrapper.eq(SysDepart::getId, departmentTree.getSource_identifier());
SysDepart sysDepart = sysDepartService.getOne(queryWrapper);
if (sysDepart != null) {
// 执行更新操作
SysDepart updateSysDepart = this.dtDepartmentToSysDepart(departmentTree, sysDepart);
if (sysParentId != null) {
updateSysDepart.setParentId(sysParentId);
}
try {
sysDepartService.updateDepartDataById(updateSysDepart, username);
String str = String.format("部门 %s 更新成功!", updateSysDepart.getDepartName());
syncInfo.addSuccessInfo(str);
} catch (Exception e) {
this.syncDepartCollectErrInfo(e, departmentTree, syncInfo);
}
if (departmentTree.hasChildren()) {
// 紧接着同步子级
this.syncDepartmentToLocalRecursion(departmentTree.getChildren(), updateSysDepart.getId(), username, syncInfo, accessToken);
}
} else {
// 执行新增操作
SysDepart newSysDepart = this.dtDepartmentToSysDepart(departmentTree, null);
if (sysParentId != null) {
newSysDepart.setParentId(sysParentId);
}
try {
sysDepartService.saveDepartData(newSysDepart, username);
// 更新钉钉 source_identifier
Department updateDtDepart = new Department();
updateDtDepart.setDept_id(departmentTree.getDept_id());
updateDtDepart.setSource_identifier(newSysDepart.getId());
Response response = JdtDepartmentAPI.update(updateDtDepart, accessToken);
if (!response.isSuccess()) {
throw new RuntimeException(response.getErrmsg());
}
String str = String.format("部门 %s 创建成功!", newSysDepart.getDepartName());
syncInfo.addSuccessInfo(str);
} catch (Exception e) {
this.syncDepartCollectErrInfo(e, departmentTree, syncInfo);
}
// 紧接着同步子级
if (departmentTree.hasChildren()) {
this.syncDepartmentToLocalRecursion(departmentTree.getChildren(), newSysDepart.getId(), username, syncInfo, accessToken);
}
}
}
}
}
private boolean syncDepartCollectErrInfo(Exception e, Department department, SyncInfoVo syncInfo) {
String msg;
if (e instanceof DuplicateKeyException) {
msg = e.getCause().getMessage();
} else {
msg = e.getMessage();
}
String str = String.format("部门 %s(%s) 同步失败!错误信息:%s", department.getName(), department.getDept_id(), msg);
syncInfo.addFailInfo(str);
return false;
}
@Override
public SyncInfoVo syncLocalUserToThirdApp(String ids) {
SyncInfoVo syncInfo = new SyncInfoVo();
String accessToken = this.getAccessToken();
if (accessToken == null) {
syncInfo.addFailInfo("accessToken获取失败");
return syncInfo;
}
List<SysUser> sysUsers;
if (StringUtils.isNotBlank(ids)) {
String[] idList = ids.split(",");
LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.in(SysUser::getId, (Object[]) idList);
// 获取本地指定用户
sysUsers = sysUserService.list(queryWrapper);
} else {
// 获取本地所有用户
sysUsers = sysUserService.list();
}
// 查询钉钉所有的部门用于同步用户和部门的关系
List<Department> allDepartment = JdtDepartmentAPI.listAll(accessToken);
for (SysUser sysUser : sysUsers) {
// 外部模拟登陆临时账号不同步
if ("_reserve_user_external".equals(sysUser.getUsername())) {
continue;
}
// 钉钉用户信息不为null代表已同步过
Response<User> dtUserInfo;
/*
* 判断是否同步过的逻辑
* 1. 查询 sys_third_account第三方账号表是否有数据如果有代表已同步
* 2. 本地表里没有就先用手机号判断不通过再用username判断
*/
SysThirdAccount sysThirdAccount = sysThirdAccountService.getOneBySysUserId(sysUser.getId(), ThirdAppConfig.DINGTALK.toLowerCase());
if (sysThirdAccount != null && oConvertUtils.isNotEmpty(sysThirdAccount.getThirdUserId())) {
// sys_third_account 表匹配成功通过第三方userId查询出第三方userInfo
dtUserInfo = JdtUserAPI.getUserById(sysThirdAccount.getThirdUserId(), accessToken);
} else {
// 手机号匹配
Response<String> thirdUserId = JdtUserAPI.getUseridByMobile(sysUser.getPhone(), accessToken);
// 手机号匹配成功
if (thirdUserId.isSuccess() && oConvertUtils.isNotEmpty(thirdUserId.getResult())) {
// 通过查询到的userId查询用户详情
dtUserInfo = JdtUserAPI.getUserById(thirdUserId.getResult(), accessToken);
} else {
// 手机号匹配失败尝试使用username匹配
dtUserInfo = JdtUserAPI.getUserById(sysUser.getUsername(), accessToken);
}
}
String dtUserId;
// api 接口是否执行成功
boolean apiSuccess;
// 已同步就更新否则就创建
if (dtUserInfo != null && dtUserInfo.isSuccess() && dtUserInfo.getResult() != null) {
User dtUser = dtUserInfo.getResult();
dtUserId = dtUser.getUserid();
User updateQwUser = this.sysUserToDtUser(sysUser, dtUser, allDepartment);
Response<JSONObject> updateRes = JdtUserAPI.update(updateQwUser, accessToken);
// 收集成功/失败信息
apiSuccess = this.syncUserCollectErrInfo(updateRes, sysUser, syncInfo);
} else {
User newQwUser = this.sysUserToDtUser(sysUser, allDepartment);
Response<String> createRes = JdtUserAPI.create(newQwUser, accessToken);
dtUserId = createRes.getResult();
// 收集成功/失败信息
apiSuccess = this.syncUserCollectErrInfo(createRes, sysUser, syncInfo);
}
// api 接口执行成功并且 sys_third_account 表匹配失败就向 sys_third_account 里插入一条数据
if (apiSuccess && (sysThirdAccount == null || oConvertUtils.isEmpty(sysThirdAccount.getThirdUserId()))) {
if (sysThirdAccount == null) {
sysThirdAccount = new SysThirdAccount();
sysThirdAccount.setSysUserId(sysUser.getId());
sysThirdAccount.setStatus(1);
sysThirdAccount.setDelFlag(0);
sysThirdAccount.setThirdType(ThirdAppConfig.DINGTALK.toLowerCase());
}
// 设置第三方app用户ID
sysThirdAccount.setThirdUserId(dtUserId);
sysThirdAccountService.saveOrUpdate(sysThirdAccount);
}
}
return syncInfo;
}
@Override
public SyncInfoVo syncThirdAppUserToLocal() {
SyncInfoVo syncInfo = new SyncInfoVo();
String accessToken = this.getAccessToken();
if (accessToken == null) {
syncInfo.addFailInfo("accessToken获取失败");
return syncInfo;
}
// 获取本地用户
List<SysUser> sysUsersList = sysUserService.list();
// 查询钉钉所有的部门用于同步用户和部门的关系
List<Department> allDepartment = JdtDepartmentAPI.listAll(accessToken);
// 根据钉钉部门查询所有钉钉用户用于反向同步到本地
List<User> ddUserList = this.getDtAllUserByDepartment(allDepartment, accessToken);
for (User dtUserInfo : ddUserList) {
SysThirdAccount sysThirdAccount = sysThirdAccountService.getOneByThirdUserId(dtUserInfo.getUserid(), ThirdAppConfig.DINGTALK.toLowerCase());
List<SysUser> collect = sysUsersList.stream().filter(user -> (dtUserInfo.getMobile().equals(user.getPhone()) || dtUserInfo.getUserid().equals(user.getUsername()))
).collect(Collectors.toList());
if (collect != null && collect.size() > 0) {
SysUser sysUserTemp = collect.get(0);
// 循环到此说明用户匹配成功进行更新操作
SysUser updateSysUser = this.dtUserToSysUser(dtUserInfo, sysUserTemp);
try {
sysUserService.updateById(updateSysUser);
String str = String.format("用户 %s(%s) 更新成功!", updateSysUser.getRealname(), updateSysUser.getUsername());
syncInfo.addSuccessInfo(str);
} catch (Exception e) {
this.syncUserCollectErrInfo(e, dtUserInfo, syncInfo);
}
//第三方账号关系表
this.thirdAccountSaveOrUpdate(sysThirdAccount, updateSysUser.getId(), dtUserInfo.getUserid());
}else{
// 如果没有匹配到用户则走创建逻辑
SysUser newSysUser = this.dtUserToSysUser(dtUserInfo);
try {
sysUserService.save(newSysUser);
String str = String.format("用户 %s(%s) 创建成功!", newSysUser.getRealname(), newSysUser.getUsername());
syncInfo.addSuccessInfo(str);
} catch (Exception e) {
this.syncUserCollectErrInfo(e, dtUserInfo, syncInfo);
}
//第三方账号关系表
this.thirdAccountSaveOrUpdate(null, newSysUser.getId(), dtUserInfo.getUserid());
}
}
return syncInfo;
}
private List<User> getDtAllUserByDepartment(List<Department> allDepartment, String accessToken) {
// 根据钉钉部门查询所有钉钉用户用于反向同步到本地
List<User> userList = new ArrayList<>();
for (Department department : allDepartment) {
this.getUserListByDeptIdRecursion(department.getDept_id(), 0, userList, accessToken);
}
return userList;
}
/**
* 递归查询所有用户
*/
private void getUserListByDeptIdRecursion(int deptId, int cursor, List<User> userList, String accessToken) {
// 根据钉钉部门查询所有钉钉用户用于反向同步到本地
GetUserListBody getUserListBody = new GetUserListBody(deptId, cursor, 100);
Response<PageResult<User>> response = JdtUserAPI.getUserListByDeptId(getUserListBody, accessToken);
if (response.isSuccess()) {
PageResult<User> page = response.getResult();
userList.addAll(page.getList());
if (page.getHas_more()) {
this.getUserListByDeptIdRecursion(deptId, page.getNext_cursor(), userList, accessToken);
}
}
}
/**
* 保存或修改第三方登录表
*
* @param sysThirdAccount 第三方账户表对象为null就新增数据否则就修改
* @param sysUserId 本地系统用户ID
* @param dtUserId 钉钉用户ID
*/
private void thirdAccountSaveOrUpdate(SysThirdAccount sysThirdAccount, String sysUserId, String dtUserId) {
if (sysThirdAccount == null) {
sysThirdAccount = new SysThirdAccount();
sysThirdAccount.setSysUserId(sysUserId);
sysThirdAccount.setStatus(1);
sysThirdAccount.setDelFlag(0);
sysThirdAccount.setThirdType(ThirdAppConfig.DINGTALK.toLowerCase());
}
sysThirdAccount.setThirdUserId(dtUserId);
sysThirdAccountService.saveOrUpdate(sysThirdAccount);
}
/**
* 同步用户收集同步过程中的错误信息
*/
private boolean syncUserCollectErrInfo(Response<?> response, SysUser sysUser, SyncInfoVo syncInfo) {
if (!response.isSuccess()) {
String str = String.format("用户 %s(%s) 同步失败!错误码:%s——%s", sysUser.getUsername(), sysUser.getRealname(), response.getErrcode(), response.getErrmsg());
syncInfo.addFailInfo(str);
return false;
} else {
String str = String.format("用户 %s(%s) 同步成功!", sysUser.getUsername(), sysUser.getRealname());
syncInfo.addSuccessInfo(str);
return true;
}
}
/**
* 同步用户收集同步过程中的错误信息
*/
private boolean syncUserCollectErrInfo(Exception e, User dtUser, SyncInfoVo syncInfo) {
String msg;
if (e instanceof DuplicateKeyException) {
msg = e.getCause().getMessage();
} else {
msg = e.getMessage();
}
String str = String.format("用户 %s(%s) 同步失败!错误信息:%s", dtUser.getUserid(), dtUser.getName(), msg);
syncInfo.addFailInfo(str);
return false;
}
/**
* 同步用户将SysUser转为钉钉的User对象创建新用户
*/
private User sysUserToDtUser(SysUser sysUser, List<Department> allDepartment) {
User user = new User();
// 通过 username 来关联
user.setUserid(sysUser.getUsername());
return this.sysUserToDtUser(sysUser, user, allDepartment);
}
/**
* 同步用户将SysUser转为钉钉的User对象更新旧用户
*/
private User sysUserToDtUser(SysUser sysUser, User user, List<Department> allDepartment) {
user.setName(sysUser.getRealname());
user.setMobile(sysUser.getPhone());
user.setTelephone(sysUser.getTelephone());
user.setJob_number(sysUser.getWorkNo());
// 职务翻译
if (oConvertUtils.isNotEmpty(sysUser.getPost())) {
SysPosition position = sysPositionService.getByCode(sysUser.getPost());
if (position != null) {
user.setTitle(position.getName());
}
}
user.setEmail(sysUser.getEmail());
// 查询并同步用户部门关系
List<SysDepart> departList = this.getUserDepart(sysUser);
if (departList != null) {
List<Integer> departmentIdList = new ArrayList<>();
for (SysDepart sysDepart : departList) {
// 企业微信的部门id
Department department = this.getDepartmentByDepartId(sysDepart.getId(), allDepartment);
if (department != null) {
departmentIdList.add(department.getDept_id());
}
}
user.setDept_id_list(departmentIdList.toArray(new Integer[]{}));
user.setDept_order_list(null);
}
if (oConvertUtils.isEmpty(user.getDept_id_list())) {
// 没有找到匹配部门同步到根部门下
user.setDept_id_list(1);
user.setDept_order_list(null);
}
// --- 钉钉没有逻辑删除功能
// sysUser.getDelFlag()
// --- 钉钉没有冻结启用禁用功能
// sysUser.getStatus()
return user;
}
/**
* 同步用户钉钉的User对象转为SysUser创建新用户
*/
private SysUser dtUserToSysUser(User dtUser) {
SysUser sysUser = new SysUser();
sysUser.setDelFlag(0);
// 通过 username 来关联
sysUser.setUsername(dtUser.getUserid());
// 密码默认为 123456随机加盐
String password = "123456", salt = oConvertUtils.randomGen(8);
String passwordEncode = PasswordUtil.encrypt(sysUser.getUsername(), password, salt);
sysUser.setSalt(salt);
sysUser.setPassword(passwordEncode);
return this.dtUserToSysUser(dtUser, sysUser);
}
/**
* 同步用户钉钉的User对象转为SysUser更新旧用户
*/
private SysUser dtUserToSysUser(User dtUser, SysUser oldSysUser) {
SysUser sysUser = new SysUser();
BeanUtils.copyProperties(oldSysUser, sysUser);
sysUser.setRealname(dtUser.getName());
sysUser.setPhone(dtUser.getMobile());
sysUser.setTelephone(dtUser.getTelephone());
// 因为唯一键约束的原因如果原数据和旧数据相同就不更新
if (oConvertUtils.isNotEmpty(dtUser.getEmail()) && !dtUser.getEmail().equals(sysUser.getEmail())) {
sysUser.setEmail(dtUser.getEmail());
} else {
sysUser.setEmail(null);
}
// 因为唯一键约束的原因如果原数据和旧数据相同就不更新
if (oConvertUtils.isNotEmpty(dtUser.getMobile()) && !dtUser.getMobile().equals(sysUser.getPhone())) {
sysUser.setPhone(dtUser.getMobile());
} else {
sysUser.setPhone(null);
}
sysUser.setWorkNo(null);
// --- 钉钉没有逻辑删除功能
// sysUser.getDelFlag()
// --- 钉钉没有冻结启用禁用功能
// sysUser.getStatus()
return sysUser;
}
/**
* 查询用户和部门的关系
*/
private List<SysDepart> getUserDepart(SysUser sysUser) {
// 根据用户部门关系表查询出用户的部门
LambdaQueryWrapper<SysUserDepart> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysUserDepart::getUserId, sysUser.getId());
List<SysUserDepart> sysUserDepartList = sysUserDepartService.list(queryWrapper);
if (sysUserDepartList.size() == 0) {
return null;
}
// 根据用户部门
LambdaQueryWrapper<SysDepart> departQueryWrapper = new LambdaQueryWrapper<>();
List<String> departIdList = sysUserDepartList.stream().map(SysUserDepart::getDepId).collect(Collectors.toList());
departQueryWrapper.in(SysDepart::getId, departIdList);
List<SysDepart> departList = sysDepartService.list(departQueryWrapper);
return departList.size() == 0 ? null : departList;
}
/**
* 根据sysDepartId查询钉钉的部门
*/
private Department getDepartmentByDepartId(String departId, List<Department> allDepartment) {
for (Department department : allDepartment) {
if (departId.equals(department.getSource_identifier())) {
return department;
}
}
return null;
}
/**
* 同步部门将SysDepartTreeModel转为钉钉的Department对象创建新部门
*/
private Department sysDepartToDtDepartment(SysDepartTreeModel departTree, Integer parentId) {
Department department = new Department();
department.setSource_identifier(departTree.getId());
return this.sysDepartToDtDepartment(departTree, department, parentId);
}
/**
* 同步部门将SysDepartTreeModel转为钉钉的Department对象
*/
private Department sysDepartToDtDepartment(SysDepartTreeModel departTree, Department department, Integer parentId) {
department.setName(departTree.getDepartName());
department.setParent_id(parentId);
department.setOrder(departTree.getDepartOrder());
return department;
}
/**
* 同步部门钉钉的Department对象转为SysDepartTreeModel
*/
private SysDepart dtDepartmentToSysDepart(Department department, SysDepart departTree) {
SysDepart sysDepart = new SysDepart();
if (departTree != null) {
BeanUtils.copyProperties(departTree, sysDepart);
}
sysDepart.setDepartName(department.getName());
sysDepart.setDepartOrder(department.getOrder());
return sysDepart;
}
@Override
public int removeThirdAppUser(List<String> userIdList) {
// 判断启用状态
if (!thirdAppConfig.isDingtalkEnabled()) {
return -1;
}
int count = 0;
if (userIdList != null && userIdList.size() > 0) {
String accessToken = this.getAccessToken();
if (accessToken == null) {
return count;
}
LambdaQueryWrapper<SysThirdAccount> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysThirdAccount::getThirdType, ThirdAppConfig.DINGTALK.toLowerCase());
queryWrapper.in(SysThirdAccount::getSysUserId, userIdList);
// 根据userId获取第三方用户的id
List<SysThirdAccount> thirdAccountList = sysThirdAccountService.list(queryWrapper);
List<String> thirdUserIdList = thirdAccountList.stream().map(SysThirdAccount::getThirdUserId).collect(Collectors.toList());
for (String thirdUserId : thirdUserIdList) {
if (oConvertUtils.isNotEmpty(thirdUserId)) {
// 没有批量删除的接口
Response<JSONObject> response = JdtUserAPI.delete(thirdUserId, accessToken);
if (response.getErrcode() == 0) {
count++;
}
}
}
}
return count;
}
@Override
public boolean sendMessage(MessageDTO message) {
return this.sendMessage(message, false);
}
/**
* 发送消息
*
* @param message
* @param verifyConfig
* @return
*/
public boolean sendMessage(MessageDTO message, boolean verifyConfig) {
Response<String> response = this.sendMessageResponse(message, verifyConfig);
if (response != null) {
return response.isSuccess();
}
return false;
}
public Response<String> sendMessageResponse(MessageDTO message, boolean verifyConfig) {
if (verifyConfig && !thirdAppConfig.isDingtalkEnabled()) {
return null;
}
String accessToken = this.getAccessToken();
if (accessToken == null) {
return null;
}
// 封装钉钉消息
String content = message.getContent();
int agentId = thirdAppConfig.getDingtalk().getAgentIdInt();
Message<TextMessage> textMessage = new Message<>(agentId, new TextMessage(content));
if (message.isToAll()) {
textMessage.setTo_all_user(true);
} else {
String[] toUsers = message.getToUser().split(",");
// 通过第三方账号表查询出第三方userId
List<SysThirdAccount> thirdAccountList = sysThirdAccountService.listThirdUserIdByUsername(toUsers, ThirdAppConfig.DINGTALK.toLowerCase());
List<String> dtUserIds = thirdAccountList.stream().map(SysThirdAccount::getThirdUserId).collect(Collectors.toList());
textMessage.setUserid_list(dtUserIds);
}
return JdtMessageAPI.sendTextMessage(textMessage, accessToken);
}
public boolean recallMessage(String msg_task_id) {
Response<JSONObject> response = this.recallMessageResponse(msg_task_id);
if (response == null) {
return false;
}
return response.isSuccess();
}
/**
* 撤回消息
*
* @param msg_task_id
* @return
*/
public Response<JSONObject> recallMessageResponse(String msg_task_id) {
String accessToken = this.getAccessToken();
if (accessToken == null) {
return null;
}
int agentId = thirdAppConfig.getDingtalk().getAgentIdInt();
return JdtMessageAPI.recallMessage(agentId, msg_task_id, getAccessToken());
}
/**
* 发送卡片消息SysAnnouncement定制
*
* @param announcement
* @param verifyConfig 是否验证配置未启用的APP会拒绝发送
* @return
*/
public Response<String> sendActionCardMessage(SysAnnouncement announcement, boolean verifyConfig) {
if (verifyConfig && !thirdAppConfig.isDingtalkEnabled()) {
return null;
}
String accessToken = this.getAccessToken();
if (accessToken == null) {
return null;
}
int agentId = thirdAppConfig.getDingtalk().getAgentIdInt();
String markdown = "### " + announcement.getTitile() + "\n" + oConvertUtils.getString(announcement.getMsgAbstract(),"");
ActionCardMessage actionCard = new ActionCardMessage(markdown);
actionCard.setTitle(announcement.getTitile());
actionCard.setSingle_title("详情");
actionCard.setSingle_url(RestUtil.getBaseUrl() + "/sys/annountCement/show/" + announcement.getId());
Message<ActionCardMessage> actionCardMessage = new Message<>(agentId, actionCard);
if (CommonConstant.MSG_TYPE_ALL.equals(announcement.getMsgType())) {
actionCardMessage.setTo_all_user(true);
return JdtMessageAPI.sendActionCardMessage(actionCardMessage, accessToken);
} else {
// 将userId转为username
String[] userIds = null;
String userId = announcement.getUserIds();
if(oConvertUtils.isNotEmpty(userId)){
userIds = userId.substring(0, (userId.length() - 1)).split(",");
}else{
LambdaQueryWrapper<SysAnnouncementSend> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysAnnouncementSend::getAnntId, announcement.getId());
SysAnnouncementSend sysAnnouncementSend = sysAnnouncementSendMapper.selectOne(queryWrapper);
userIds = new String[] {sysAnnouncementSend.getUserId()};
}
if(userIds!=null){
String[] usernameList = sysUserService.userIdToUsername(Arrays.asList(userIds)).toArray(new String[]{});
// 通过第三方账号表查询出第三方userId
List<SysThirdAccount> thirdAccountList = sysThirdAccountService.listThirdUserIdByUsername(usernameList, ThirdAppConfig.DINGTALK.toLowerCase());
List<String> dtUserIds = thirdAccountList.stream().map(SysThirdAccount::getThirdUserId).collect(Collectors.toList());
actionCardMessage.setUserid_list(dtUserIds);
return JdtMessageAPI.sendActionCardMessage(actionCardMessage, accessToken);
}
}
return null;
}
}

View File

@ -0,0 +1,791 @@
package org.jeecg.modules.system.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.jeecg.qywx.api.base.JwAccessTokenAPI;
import com.jeecg.qywx.api.core.common.AccessToken;
import com.jeecg.qywx.api.department.JwDepartmentAPI;
import com.jeecg.qywx.api.department.vo.DepartMsgResponse;
import com.jeecg.qywx.api.department.vo.Department;
import com.jeecg.qywx.api.message.JwMessageAPI;
import com.jeecg.qywx.api.message.vo.Text;
import com.jeecg.qywx.api.message.vo.TextCard;
import com.jeecg.qywx.api.message.vo.TextCardEntity;
import com.jeecg.qywx.api.message.vo.TextEntity;
import com.jeecg.qywx.api.user.JwUserAPI;
import com.jeecg.qywx.api.user.vo.User;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.jeecg.common.api.dto.message.MessageDTO;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.util.PasswordUtil;
import org.jeecg.common.util.RestUtil;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.config.thirdapp.ThirdAppConfig;
import org.jeecg.modules.system.entity.*;
import org.jeecg.modules.system.mapper.SysAnnouncementSendMapper;
import org.jeecg.modules.system.model.SysDepartTreeModel;
import org.jeecg.modules.system.service.*;
import org.jeecg.modules.system.vo.thirdapp.JwDepartmentTreeVo;
import org.jeecg.modules.system.vo.thirdapp.SyncInfoVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* 第三方App对接企业微信实现类
*/
@Slf4j
@Service
public class ThirdAppWechatEnterpriseServiceImpl implements IThirdAppService {
@Autowired
ThirdAppConfig thirdAppConfig;
@Autowired
private ISysDepartService sysDepartService;
@Autowired
private ISysUserService sysUserService;
@Autowired
private ISysThirdAccountService sysThirdAccountService;
@Autowired
private ISysUserDepartService sysUserDepartService;
@Autowired
private ISysPositionService sysPositionService;
@Autowired
private SysAnnouncementSendMapper sysAnnouncementSendMapper;
@Override
public String getAccessToken() {
String CORP_ID = thirdAppConfig.getWechatEnterprise().getClientId();
String SECRET = thirdAppConfig.getWechatEnterprise().getClientSecret();
AccessToken accessToken = JwAccessTokenAPI.getAccessToken(CORP_ID, SECRET);
if (accessToken != null) {
return accessToken.getAccesstoken();
}
log.warn("获取AccessToken失败");
return null;
}
/** 获取APPToken新版企业微信的秘钥是分开的 */
public String getAppAccessToken() {
String CORP_ID = thirdAppConfig.getWechatEnterprise().getClientId();
String SECRET = thirdAppConfig.getWechatEnterprise().getAgentAppSecret();
// 如果没有配置APP秘钥就说明是老企业可以通用秘钥
if (oConvertUtils.isEmpty(SECRET)) {
SECRET = thirdAppConfig.getWechatEnterprise().getClientSecret();
}
AccessToken accessToken = JwAccessTokenAPI.getAccessToken(CORP_ID, SECRET);
if (accessToken != null) {
return accessToken.getAccesstoken();
}
log.warn("获取AccessToken失败");
return null;
}
@Override
public boolean syncLocalDepartmentToThirdApp(String ids) {
String accessToken = this.getAccessToken();
if (accessToken == null) {
return false;
}
// 获取企业微信所有的部门
List<Department> departments = JwDepartmentAPI.getAllDepartment(accessToken);
if (departments == null) {
return false;
}
// 删除企业微信有但本地没有的部门以本地部门数据为主(以为企业微信不能创建同名部门所以只能先删除
List<JwDepartmentTreeVo> departmentTreeList = JwDepartmentTreeVo.listToTree(departments);
this.deleteDepartRecursion(departmentTreeList, accessToken, true);
// 获取本地所有部门树结构
List<SysDepartTreeModel> sysDepartsTree = sysDepartService.queryTreeList();
// -- 企业微信不能创建新的顶级部门所以新的顶级部门的parentId就为1
Department parent = new Department();
parent.setId("1");
// 递归同步部门
departments = JwDepartmentAPI.getAllDepartment(accessToken);
this.syncDepartmentRecursion(sysDepartsTree, departments, parent, accessToken);
return true;
}
// 递归删除部门以及子部门由于企业微信不允许删除带有成员和子部门的部门所以需要递归删除下子部门然后把部门成员移动端根部门下
private void deleteDepartRecursion(List<JwDepartmentTreeVo> children, String accessToken, boolean ifLocal) {
for (JwDepartmentTreeVo departmentTree : children) {
String depId = departmentTree.getId();
// 过滤根部门
if (!"1".equals(depId)) {
// 判断本地是否有该部门
if (ifLocal) {
LambdaQueryWrapper<SysDepart> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysDepart::getQywxIdentifier, depId);
SysDepart sysDepart = sysDepartService.getOne(queryWrapper);
// 本地有该部门不删除
if (sysDepart != null) {
if (departmentTree.hasChildren()) {
this.deleteDepartRecursion(departmentTree.getChildren(), accessToken, true);
}
continue;
}
}
// 判断是否有成员有就移动到根部门
List<User> departUserList = JwUserAPI.getUsersByDepartid(depId, "1", null, accessToken);
if (departUserList != null && departUserList.size() > 0) {
for (User user : departUserList) {
User updateUser = new User();
updateUser.setUserid(user.getUserid());
updateUser.setDepartment(new Integer[]{1});
JwUserAPI.updateUser(updateUser, accessToken);
}
}
// 有子部门优先删除子部门
if (departmentTree.hasChildren()) {
this.deleteDepartRecursion(departmentTree.getChildren(), accessToken, false);
}
// 执行删除操作
JwDepartmentAPI.deleteDepart(depId, accessToken);
}
}
}
// 递归同步部门到第三方APP
private void syncDepartmentRecursion(List<SysDepartTreeModel> sysDepartsTree, List<Department> departments, Department parent, String accessToken) {
if (sysDepartsTree != null && sysDepartsTree.size() != 0) {
for1:
for (SysDepartTreeModel depart : sysDepartsTree) {
for (Department department : departments) {
// id相同代表已存在执行修改操作
if (department.getId().equals(depart.getQywxIdentifier())) {
this.sysDepartToQwDepartment(depart, department, parent.getId());
JwDepartmentAPI.updateDepart(department, accessToken);
// 紧接着同步子级
this.syncDepartmentRecursion(depart.getChildren(), departments, department, accessToken);
// 跳出外部循环
continue for1;
}
}
// 循环到此说明是新部门直接调接口创建
Department newDepartment = this.sysDepartToQwDepartment(depart, parent.getId());
DepartMsgResponse response = JwDepartmentAPI.createDepartment(newDepartment, accessToken);
// 创建成功将返回的id绑定到本地
if (response != null && response.getId() != null) {
SysDepart sysDepart = new SysDepart();
sysDepart.setId(depart.getId());
sysDepart.setQywxIdentifier(response.getId().toString());
sysDepartService.updateById(sysDepart);
Department newParent = new Department();
newParent.setId(response.getId().toString());
// 紧接着同步子级
this.syncDepartmentRecursion(depart.getChildren(), departments, newParent, accessToken);
}
// 收集错误信息
// this.syncUserCollectErrInfo(errCode, sysUser, errInfo);
}
}
}
@Override
public SyncInfoVo syncThirdAppDepartmentToLocal(String ids) {
SyncInfoVo syncInfo = new SyncInfoVo();
String accessToken = this.getAccessToken();
if (accessToken == null) {
syncInfo.addFailInfo("accessToken获取失败");
return syncInfo;
}
// 获取企业微信所有的部门
List<Department> departments = JwDepartmentAPI.getAllDepartment(accessToken);
if (departments == null) {
syncInfo.addFailInfo("企业微信部门信息获取失败!");
return syncInfo;
}
String username = JwtUtil.getUserNameByToken(SpringContextUtils.getHttpServletRequest());
// 将list转为tree
List<JwDepartmentTreeVo> departmentTreeList = JwDepartmentTreeVo.listToTree(departments);
// 递归同步部门
this.syncDepartmentToLocalRecursion(departmentTreeList, null, username, syncInfo);
return syncInfo;
}
/**
* 递归同步部门到本地
*/
private void syncDepartmentToLocalRecursion(List<JwDepartmentTreeVo> departmentTreeList, String sysParentId, String username, SyncInfoVo syncInfo) {
if (departmentTreeList != null && departmentTreeList.size() != 0) {
for (JwDepartmentTreeVo departmentTree : departmentTreeList) {
String depId = departmentTree.getId();
LambdaQueryWrapper<SysDepart> queryWrapper = new LambdaQueryWrapper<>();
// 根据 qywxIdentifier 字段查询
queryWrapper.eq(SysDepart::getQywxIdentifier, depId);
SysDepart sysDepart = sysDepartService.getOne(queryWrapper);
if (sysDepart != null) {
// 执行更新操作
SysDepart updateSysDepart = this.qwDepartmentToSysDepart(departmentTree, sysDepart);
if (sysParentId != null) {
updateSysDepart.setParentId(sysParentId);
}
try {
sysDepartService.updateDepartDataById(updateSysDepart, username);
String str = String.format("部门 %s 更新成功!", updateSysDepart.getDepartName());
syncInfo.addSuccessInfo(str);
} catch (Exception e) {
this.syncDepartCollectErrInfo(e, departmentTree, syncInfo);
}
if (departmentTree.hasChildren()) {
// 紧接着同步子级
this.syncDepartmentToLocalRecursion(departmentTree.getChildren(), updateSysDepart.getId(), username, syncInfo);
}
} else {
// 执行新增操作
SysDepart newSysDepart = this.qwDepartmentToSysDepart(departmentTree, null);
if (sysParentId != null) {
newSysDepart.setParentId(sysParentId);
}
try {
sysDepartService.saveDepartData(newSysDepart, username);
String str = String.format("部门 %s 创建成功!", newSysDepart.getDepartName());
syncInfo.addSuccessInfo(str);
} catch (Exception e) {
this.syncDepartCollectErrInfo(e, departmentTree, syncInfo);
}
// 紧接着同步子级
if (departmentTree.hasChildren()) {
this.syncDepartmentToLocalRecursion(departmentTree.getChildren(), newSysDepart.getId(), username, syncInfo);
}
}
}
}
}
@Override
public SyncInfoVo syncLocalUserToThirdApp(String ids) {
SyncInfoVo syncInfo = new SyncInfoVo();
String accessToken = this.getAccessToken();
if (accessToken == null) {
syncInfo.addFailInfo("accessToken获取失败");
return syncInfo;
}
// 获取企业微信所有的用户
List<User> qwUsers = JwUserAPI.getDetailUsersByDepartid("1", null, null, accessToken);
if (qwUsers == null) {
syncInfo.addFailInfo("企业微信用户列表查询失败!");
return syncInfo;
}
List<SysUser> sysUsers;
if (StringUtils.isNotBlank(ids)) {
String[] idList = ids.split(",");
LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.in(SysUser::getId, (Object[]) idList);
// 获取本地指定用户
sysUsers = sysUserService.list(queryWrapper);
} else {
// 获取本地所有用户
sysUsers = sysUserService.list();
}
// 循环判断新用户和需要更新的用户
for1:
for (SysUser sysUser : sysUsers) {
// 外部模拟登陆临时账号不同步
if ("_reserve_user_external".equals(sysUser.getUsername())) {
continue;
}
/*
* 判断是否同步过的逻辑
* 1. 查询 sys_third_account第三方账号表是否有数据如果有代表已同步
* 2. 本地表里没有就先用手机号判断不通过再用username判断
*/
User qwUser;
SysThirdAccount sysThirdAccount = sysThirdAccountService.getOneBySysUserId(sysUser.getId(), ThirdAppConfig.WECHAT_ENTERPRISE.toLowerCase());
for (User qwUserTemp : qwUsers) {
if (sysThirdAccount == null || oConvertUtils.isEmpty(sysThirdAccount.getThirdUserId()) || !sysThirdAccount.getThirdUserId().equals(qwUserTemp.getUserid())) {
// sys_third_account 表匹配失败尝试用手机号匹配
String phone = sysUser.getPhone();
if (!(oConvertUtils.isEmpty(phone) || phone.equals(qwUserTemp.getMobile()))) {
// 手机号匹配失败再尝试用username匹配
String username = sysUser.getUsername();
if (!(oConvertUtils.isEmpty(username) || username.equals(qwUserTemp.getUserid()))) {
// username 匹配失败直接跳到下一次循环继续
continue;
}
}
}
// 循环到此说明用户匹配成功进行更新操作
qwUser = this.sysUserToQwUser(sysUser, qwUserTemp);
int errCode = JwUserAPI.updateUser(qwUser, accessToken);
// 收集错误信息
this.syncUserCollectErrInfo(errCode, sysUser, syncInfo);
this.thirdAccountSaveOrUpdate(sysThirdAccount, sysUser.getId(), qwUser.getUserid());
// 更新完成直接跳到下一次外部循环继续
continue for1;
}
// 循环到此说明是新用户直接调接口创建
qwUser = this.sysUserToQwUser(sysUser);
int errCode = JwUserAPI.createUser(qwUser, accessToken);
// 收集错误信息
boolean apiSuccess = this.syncUserCollectErrInfo(errCode, sysUser, syncInfo);
if (apiSuccess) {
this.thirdAccountSaveOrUpdate(sysThirdAccount, sysUser.getId(), qwUser.getUserid());
}
}
return syncInfo;
}
@Override
public SyncInfoVo syncThirdAppUserToLocal() {
SyncInfoVo syncInfo = new SyncInfoVo();
String accessToken = this.getAccessToken();
if (accessToken == null) {
syncInfo.addFailInfo("accessToken获取失败");
return syncInfo;
}
// 获取企业微信所有的用户
List<User> qwUsersList = JwUserAPI.getDetailUsersByDepartid("1", null, null, accessToken);
if (qwUsersList == null) {
syncInfo.addFailInfo("企业微信用户列表查询失败!");
return syncInfo;
}
//查询本地用户
List<SysUser> sysUsersList = sysUserService.list();
// 循环判断新用户和需要更新的用户
for (User qwUser : qwUsersList) {
/*
* 判断是否同步过的逻辑
* 1. 查询 sys_third_account第三方账号表是否有数据如果有代表已同步
* 2. 本地表里没有就先用手机号判断不通过再用username判断
*/
SysThirdAccount sysThirdAccount = sysThirdAccountService.getOneByThirdUserId(qwUser.getUserid(), ThirdAppConfig.WECHAT_ENTERPRISE.toLowerCase());
List<SysUser> collect = sysUsersList.stream().filter(user -> (qwUser.getMobile().equals(user.getPhone()) || qwUser.getUserid().equals(user.getUsername()))
).collect(Collectors.toList());
if (collect != null && collect.size() > 0) {
SysUser sysUserTemp = collect.get(0);
// 循环到此说明用户匹配成功进行更新操作
SysUser updateSysUser = this.qwUserToSysUser(qwUser, sysUserTemp);
try {
sysUserService.updateById(updateSysUser);
String str = String.format("用户 %s(%s) 更新成功!", updateSysUser.getRealname(), updateSysUser.getUsername());
syncInfo.addSuccessInfo(str);
} catch (Exception e) {
this.syncUserCollectErrInfo(e, qwUser, syncInfo);
}
this.thirdAccountSaveOrUpdate(sysThirdAccount, updateSysUser.getId(), qwUser.getUserid());
// 更新完成直接跳到下一次外部循环继续
}else{
// 没匹配到用户则走新增逻辑
SysUser newSysUser = this.qwUserToSysUser(qwUser);
try {
sysUserService.save(newSysUser);
String str = String.format("用户 %s(%s) 创建成功!", newSysUser.getRealname(), newSysUser.getUsername());
syncInfo.addSuccessInfo(str);
} catch (Exception e) {
this.syncUserCollectErrInfo(e, qwUser, syncInfo);
}
this.thirdAccountSaveOrUpdate(sysThirdAccount, newSysUser.getId(), qwUser.getUserid());
}
}
return syncInfo;
}
/**
* 保存或修改第三方登录表
*
* @param sysThirdAccount 第三方账户表对象为null就新增数据否则就修改
* @param sysUserId 本地系统用户ID
* @param qwUserId 企业微信用户ID
*/
private void thirdAccountSaveOrUpdate(SysThirdAccount sysThirdAccount, String sysUserId, String qwUserId) {
if (sysThirdAccount == null) {
sysThirdAccount = new SysThirdAccount();
sysThirdAccount.setSysUserId(sysUserId);
sysThirdAccount.setStatus(1);
sysThirdAccount.setDelFlag(0);
sysThirdAccount.setThirdType(ThirdAppConfig.WECHAT_ENTERPRISE.toLowerCase());
}
sysThirdAccount.setThirdUserId(qwUserId);
sysThirdAccountService.saveOrUpdate(sysThirdAccount);
}
/**
* 同步用户收集同步过程中的错误信息
*/
private boolean syncUserCollectErrInfo(int errCode, SysUser sysUser, SyncInfoVo syncInfo) {
if (errCode != 0) {
String msg = "";
// https://open.work.weixin.qq.com/api/doc/90000/90139/90313
switch (errCode) {
case 40003:
msg = "无效的UserID";
break;
case 60129:
msg = "手机和邮箱不能都为空";
break;
case 60102:
msg = "UserID已存在";
break;
case 60103:
msg = "手机号码不合法";
break;
case 60104:
msg = "手机号码已存在";
break;
}
String str = String.format("用户 %s(%s) 同步失败!错误码:%s——%s", sysUser.getUsername(), sysUser.getRealname(), errCode, msg);
syncInfo.addFailInfo(str);
return false;
} else {
String str = String.format("用户 %s(%s) 同步成功!", sysUser.getUsername(), sysUser.getRealname());
syncInfo.addSuccessInfo(str);
return true;
}
}
private boolean syncUserCollectErrInfo(Exception e, User qwUser, SyncInfoVo syncInfo) {
String msg;
if (e instanceof DuplicateKeyException) {
msg = e.getCause().getMessage();
} else {
msg = e.getMessage();
}
String str = String.format("用户 %s(%s) 同步失败!错误信息:%s", qwUser.getUserid(), qwUser.getName(), msg);
syncInfo.addFailInfo(str);
return false;
}
private boolean syncDepartCollectErrInfo(Exception e, Department department, SyncInfoVo syncInfo) {
String msg;
if (e instanceof DuplicateKeyException) {
msg = e.getCause().getMessage();
} else {
msg = e.getMessage();
}
String str = String.format("部门 %s(%s) 同步失败!错误信息:%s", department.getName(), department.getId(), msg);
syncInfo.addFailInfo(str);
return false;
}
/**
* 同步用户将SysUser转为企业微信的User对象创建新用户
*/
private User sysUserToQwUser(SysUser sysUser) {
User user = new User();
// 通过 username 来关联
user.setUserid(sysUser.getUsername());
return this.sysUserToQwUser(sysUser, user);
}
/**
* 同步用户将SysUser转为企业微信的User对象更新旧用户
*/
private User sysUserToQwUser(SysUser sysUser, User user) {
user.setName(sysUser.getRealname());
user.setMobile(sysUser.getPhone());
// 查询并同步用户部门关系
List<SysDepart> departList = this.getUserDepart(sysUser);
if (departList != null) {
List<Integer> departmentIdList = new ArrayList<>();
// 企业微信 1表示为上级0表示非上级
List<Integer> isLeaderInDept = new ArrayList<>();
// 当前用户管理的部门
List<String> manageDepartIdList = new ArrayList<>();
if (oConvertUtils.isNotEmpty(sysUser.getDepartIds())) {
manageDepartIdList = Arrays.asList(sysUser.getDepartIds().split(","));
}
for (SysDepart sysDepart : departList) {
// 企业微信的部门id
if (oConvertUtils.isNotEmpty(sysDepart.getQywxIdentifier())) {
try {
departmentIdList.add(Integer.parseInt(sysDepart.getQywxIdentifier()));
} catch (NumberFormatException ignored) {
continue;
}
// 判断用户身份是否为上级
if (CommonConstant.USER_IDENTITY_2.equals(sysUser.getUserIdentity())) {
// 判断当前部门是否为该用户管理的部门
isLeaderInDept.add(manageDepartIdList.contains(sysDepart.getId()) ? 1 : 0);
} else {
isLeaderInDept.add(0);
}
}
}
user.setDepartment(departmentIdList.toArray(new Integer[]{}));
// 个数必须和参数department的个数一致表示在所在的部门内是否为上级1表示为上级0表示非上级在审批等应用里可以用来标识上级审批人
user.setIs_leader_in_dept(isLeaderInDept.toArray(new Integer[]{}));
}
if (user.getDepartment() == null || user.getDepartment().length == 0) {
// 没有找到匹配部门同步到根部门下
user.setDepartment(new Integer[]{1});
user.setIs_leader_in_dept(new Integer[]{0});
}
// 职务翻译
if (oConvertUtils.isNotEmpty(sysUser.getPost())) {
SysPosition position = sysPositionService.getByCode(sysUser.getPost());
if (position != null) {
user.setPosition(position.getName());
}
}
if (sysUser.getSex() != null) {
user.setGender(sysUser.getSex().toString());
}
user.setEmail(sysUser.getEmail());
// 启用/禁用成员状态规则不同需要转换
// 企业微信规则1表示启用成员0表示禁用成员
// JEECG规则1正常2冻结
if (sysUser.getStatus() != null) {
if (sysUser.getStatus() == 1 || sysUser.getStatus() == 2) {
user.setEnable(sysUser.getStatus() == 1 ? 1 : 0);
} else {
user.setEnable(1);
}
}
user.setTelephone(sysUser.getTelephone());// 座机号
// --- 企业微信没有逻辑删除的功能
// update-begin--Author:sunjianlei Date:20210520 for本地逻辑删除的用户在企业微信里禁用 -----
if (CommonConstant.DEL_FLAG_1.equals(sysUser.getDelFlag())) {
user.setEnable(0);
}
// update-end--Author:sunjianlei Date:20210520 for本地逻辑删除的用户在企业微信里冻结 -----
return user;
}
/**
* 查询用户和部门的关系
*/
private List<SysDepart> getUserDepart(SysUser sysUser) {
// 根据用户部门关系表查询出用户的部门
LambdaQueryWrapper<SysUserDepart> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysUserDepart::getUserId, sysUser.getId());
List<SysUserDepart> sysUserDepartList = sysUserDepartService.list(queryWrapper);
if (sysUserDepartList.size() == 0) {
return null;
}
// 根据用户部门
LambdaQueryWrapper<SysDepart> departQueryWrapper = new LambdaQueryWrapper<>();
List<String> departIdList = sysUserDepartList.stream().map(SysUserDepart::getDepId).collect(Collectors.toList());
departQueryWrapper.in(SysDepart::getId, departIdList);
List<SysDepart> departList = sysDepartService.list(departQueryWrapper);
return departList.size() == 0 ? null : departList;
}
/**
* 同步用户将企业微信的User对象转为SysUser创建新用户
*/
private SysUser qwUserToSysUser(User user) {
SysUser sysUser = new SysUser();
sysUser.setDelFlag(0);
// 通过 username 来关联
sysUser.setUsername(user.getUserid());
// 密码默认为 123456随机加盐
String password = "123456", salt = oConvertUtils.randomGen(8);
String passwordEncode = PasswordUtil.encrypt(sysUser.getUsername(), password, salt);
sysUser.setSalt(salt);
sysUser.setPassword(passwordEncode);
return this.qwUserToSysUser(user, sysUser);
}
/**
* 同步用户将企业微信的User对象转为SysUser更新旧用户
*/
private SysUser qwUserToSysUser(User qwUser, SysUser oldSysUser) {
SysUser sysUser = new SysUser();
BeanUtils.copyProperties(oldSysUser, sysUser);
sysUser.setRealname(qwUser.getName());
sysUser.setPost(qwUser.getPosition());
try {
sysUser.setSex(Integer.parseInt(qwUser.getGender()));
} catch (NumberFormatException ignored) {
}
// 因为唯一键约束的原因如果原数据和旧数据相同就不更新
if (oConvertUtils.isNotEmpty(qwUser.getEmail()) && !qwUser.getEmail().equals(sysUser.getEmail())) {
sysUser.setEmail(qwUser.getEmail());
} else {
sysUser.setEmail(null);
}
// 因为唯一键约束的原因如果原数据和旧数据相同就不更新
if (oConvertUtils.isNotEmpty(qwUser.getMobile()) && !qwUser.getMobile().equals(sysUser.getPhone())) {
sysUser.setPhone(qwUser.getMobile());
} else {
sysUser.setPhone(null);
}
// 启用/禁用成员状态规则不同需要转换
// 企业微信规则1表示启用成员0表示禁用成员
// JEECG规则1正常2冻结
if (qwUser.getEnable() != null) {
sysUser.setStatus(qwUser.getEnable() == 1 ? 1 : 2);
}
sysUser.setTelephone(qwUser.getTelephone());// 座机号
// --- 企业微信没有逻辑删除的功能
// sysUser.setDelFlag()
return sysUser;
}
/**
* 同步部门将SysDepartTreeModel转为企业微信的Department对象创建新部门
*/
private Department sysDepartToQwDepartment(SysDepartTreeModel departTree, String parentId) {
Department department = new Department();
return this.sysDepartToQwDepartment(departTree, department, parentId);
}
/**
* 同步部门将SysDepartTreeModel转为企业微信的Department对象
*/
private Department sysDepartToQwDepartment(SysDepartTreeModel departTree, Department department, String parentId) {
department.setName(departTree.getDepartName());
department.setParentid(parentId);
if (departTree.getDepartOrder() != null) {
department.setOrder(departTree.getDepartOrder().toString());
}
return department;
}
/**
* 同步部门将企业微信的Department对象转为SysDepart
*/
private SysDepart qwDepartmentToSysDepart(Department department, SysDepart oldSysDepart) {
SysDepart sysDepart = new SysDepart();
if (oldSysDepart != null) {
BeanUtils.copyProperties(oldSysDepart, sysDepart);
}
sysDepart.setQywxIdentifier(department.getId());
sysDepart.setDepartName(department.getName());
try {
sysDepart.setDepartOrder(Integer.parseInt(department.getOrder()));
} catch (NumberFormatException ignored) {
}
return sysDepart;
}
@Override
public int removeThirdAppUser(List<String> userIdList) {
// 判断启用状态
if (!thirdAppConfig.isWechatEnterpriseEnabled()) {
return -1;
}
int count = 0;
if (userIdList != null && userIdList.size() > 0) {
String accessToken = this.getAccessToken();
if (accessToken == null) {
return count;
}
LambdaQueryWrapper<SysThirdAccount> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysThirdAccount::getThirdType, ThirdAppConfig.WECHAT_ENTERPRISE.toLowerCase());
queryWrapper.in(SysThirdAccount::getSysUserId, userIdList);
// 根据userId获取第三方用户的id
List<SysThirdAccount> thirdAccountList = sysThirdAccountService.list(queryWrapper);
List<String> thirdUserIdList = thirdAccountList.stream().map(SysThirdAccount::getThirdUserId).collect(Collectors.toList());
for (String thirdUserId : thirdUserIdList) {
if (oConvertUtils.isNotEmpty(thirdUserId)) {
// 没有批量删除的接口
int err = JwUserAPI.deleteUser(thirdUserId, accessToken);
if (err == 0) {
count++;
}
}
}
}
return count;
}
@Override
public boolean sendMessage(MessageDTO message) {
return this.sendMessage(message, false);
}
@Override
public boolean sendMessage(MessageDTO message, boolean verifyConfig) {
JSONObject response = this.sendMessageResponse(message, verifyConfig);
if (response != null) {
return response.getIntValue("errcode") == 0;
}
return false;
}
public JSONObject sendMessageResponse(MessageDTO message, boolean verifyConfig) {
if (verifyConfig && !thirdAppConfig.isWechatEnterpriseEnabled()) {
return null;
}
String accessToken = this.getAppAccessToken();
if (accessToken == null) {
return null;
}
Text text = new Text();
text.setMsgtype("text");
text.setTouser(this.getTouser(message.getToUser(), message.isToAll()));
TextEntity entity = new TextEntity();
entity.setContent(message.getContent());
text.setText(entity);
text.setAgentid(thirdAppConfig.getWechatEnterprise().getAgentIdInt());
return JwMessageAPI.sendTextMessage(text, accessToken);
}
/**
* 发送文本卡片消息SysAnnouncement定制
*
* @param announcement
* @param verifyConfig 是否验证配置未启用的APP会拒绝发送
* @return
*/
public JSONObject sendTextCardMessage(SysAnnouncement announcement, boolean verifyConfig) {
if (verifyConfig && !thirdAppConfig.isWechatEnterpriseEnabled()) {
return null;
}
String accessToken = this.getAppAccessToken();
if (accessToken == null) {
return null;
}
TextCard textCard = new TextCard();
textCard.setAgentid(thirdAppConfig.getWechatEnterprise().getAgentIdInt());
boolean isToAll = CommonConstant.MSG_TYPE_ALL.equals(announcement.getMsgType());
String usernameString = "";
if (!isToAll) {
// 将userId转为username
String userId = announcement.getUserIds();
String[] userIds = null;
if(oConvertUtils.isNotEmpty(userId)){
userIds = userId.substring(0, (userId.length() - 1)).split(",");
}else{
LambdaQueryWrapper<SysAnnouncementSend> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysAnnouncementSend::getAnntId, announcement.getId());
SysAnnouncementSend sysAnnouncementSend = sysAnnouncementSendMapper.selectOne(queryWrapper);
userIds = new String[] {sysAnnouncementSend.getUserId()};
}
List<String> usernameList = sysUserService.userIdToUsername(Arrays.asList(userIds));
usernameString = String.join(",", usernameList);
}
textCard.setTouser(this.getTouser(usernameString, isToAll));
TextCardEntity entity = new TextCardEntity();
entity.setTitle(announcement.getTitile());
entity.setDescription(oConvertUtils.getString(announcement.getMsgAbstract(),""));
entity.setUrl(RestUtil.getBaseUrl() + "/sys/annountCement/show/" + announcement.getId());
textCard.setTextcard(entity);
return JwMessageAPI.sendTextCardMessage(textCard, accessToken);
}
private String getTouser(String origin, boolean toAll) {
if (toAll) {
return "@all";
} else {
String[] toUsers = origin.split(",");
// 通过第三方账号表查询出第三方userId
List<SysThirdAccount> thirdAccountList = sysThirdAccountService.listThirdUserIdByUsername(toUsers, ThirdAppConfig.WECHAT_ENTERPRISE.toLowerCase());
List<String> toUserList = thirdAccountList.stream().map(SysThirdAccount::getThirdUserId).collect(Collectors.toList());
// 多个接收者用|分隔
return String.join("|", toUserList);
}
}
}

View File

@ -0,0 +1,52 @@
package org.jeecg.modules.system.service.impl.desform;
import org.jeecg.common.api.desform.ISysTranslateAPI;
import org.jeecg.common.system.vo.DictModel;
import org.jeecg.modules.system.service.ISysCategoryService;
import org.jeecg.modules.system.service.ISysDictService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 表单设计器翻译API接口system实现类
*
* @author sunjianlei
*/
@Component
public class SysTranslateAPIImpl implements ISysTranslateAPI {
@Autowired
ISysCategoryService sysCategoryService;
@Autowired
ISysDictService sysDictService;
@Override
public List<String> categoryLoadDictItem(String ids) {
return sysCategoryService.loadDictItem(ids, false);
}
@Override
public List<String> dictLoadDictItem(String dictCode, String keys) {
String[] params = dictCode.split(",");
return sysDictService.queryTableDictByKeys(params[0], params[1], params[2], keys, false);
}
@Override
public List<DictModel> dictGetDictItems(String dictCode) {
List<DictModel> ls = sysDictService.getDictItems(dictCode);
if (ls == null) {
ls = new ArrayList<>();
}
return ls;
}
@Override
public List<DictModel> dictLoadDict(String dictCode, String keyword, Integer pageSize) {
return sysDictService.loadDict(dictCode, keyword, pageSize);
}
}

View File

@ -1,25 +1,25 @@
package org.jeecg.modules.system.util;
import lombok.extern.slf4j.Slf4j;
/**
* 多租户 tenant_id存储器
*/
@Slf4j
public class TenantContext {
private static ThreadLocal<String> currentTenant = new ThreadLocal<>();
public static void setTenant(String tenant) {
log.debug(" setting tenant to " + tenant);
currentTenant.set(tenant);
}
public static String getTenant() {
return currentTenant.get();
}
public static void clear(){
currentTenant.remove();
}
}
//package org.jeecg.modules.system.util;
//
//import lombok.extern.slf4j.Slf4j;
//
///**
// * 多租户 tenant_id存储器
// */
//@Slf4j
//public class TenantContext {
//
// private static ThreadLocal<String> currentTenant = new ThreadLocal<>();
//
// public static void setTenant(String tenant) {
// log.debug(" setting tenant to " + tenant);
// currentTenant.set(tenant);
// }
//
// public static String getTenant() {
// return currentTenant.get();
// }
//
// public static void clear(){
// currentTenant.remove();
// }
//}

View File

@ -0,0 +1,76 @@
package org.jeecg.modules.system.vo.thirdapp;
import com.jeecg.dingtalk.api.department.vo.Department;
import org.springframework.beans.BeanUtils;
import java.util.ArrayList;
import java.util.List;
/**
* 钉钉树结构的部门
*
* @author sunjianlei
*/
public class JdtDepartmentTreeVo extends Department {
private List<JdtDepartmentTreeVo> children;
public List<JdtDepartmentTreeVo> getChildren() {
return children;
}
public JdtDepartmentTreeVo setChildren(List<JdtDepartmentTreeVo> children) {
this.children = children;
return this;
}
public JdtDepartmentTreeVo(Department department) {
BeanUtils.copyProperties(department, this);
}
/**
* 是否有子项
*/
public boolean hasChildren() {
return children != null && children.size() > 0;
}
@Override
public String toString() {
return "JwDepartmentTree{" +
"children=" + children +
"} " + super.toString();
}
/**
* 静态辅助方法将list转为tree结构
*/
public static List<JdtDepartmentTreeVo> listToTree(List<Department> allDepartment) {
// 先找出所有的父级
List<JdtDepartmentTreeVo> treeList = getByParentId(1, allDepartment);
getChildrenRecursion(treeList, allDepartment);
return treeList;
}
private static List<JdtDepartmentTreeVo> getByParentId(Integer parentId, List<Department> allDepartment) {
List<JdtDepartmentTreeVo> list = new ArrayList<>();
for (Department department : allDepartment) {
if (parentId.equals(department.getParent_id())) {
list.add(new JdtDepartmentTreeVo(department));
}
}
return list;
}
private static void getChildrenRecursion(List<JdtDepartmentTreeVo> treeList, List<Department> allDepartment) {
for (JdtDepartmentTreeVo departmentTree : treeList) {
// 递归寻找子级
List<JdtDepartmentTreeVo> children = getByParentId(departmentTree.getDept_id(), allDepartment);
if (children.size() > 0) {
departmentTree.setChildren(children);
getChildrenRecursion(children, allDepartment);
}
}
}
}

View File

@ -0,0 +1,76 @@
package org.jeecg.modules.system.vo.thirdapp;
import com.jeecg.qywx.api.department.vo.Department;
import org.springframework.beans.BeanUtils;
import java.util.ArrayList;
import java.util.List;
/**
* 企业微信树结构的部门
*
* @author sunjianlei
*/
public class JwDepartmentTreeVo extends Department {
private List<JwDepartmentTreeVo> children;
public List<JwDepartmentTreeVo> getChildren() {
return children;
}
public JwDepartmentTreeVo setChildren(List<JwDepartmentTreeVo> children) {
this.children = children;
return this;
}
public JwDepartmentTreeVo(Department department) {
BeanUtils.copyProperties(department, this);
}
/**
* 是否有子项
*/
public boolean hasChildren() {
return children != null && children.size() > 0;
}
@Override
public String toString() {
return "JwDepartmentTree{" +
"children=" + children +
"} " + super.toString();
}
/**
* 静态辅助方法将list转为tree结构
*/
public static List<JwDepartmentTreeVo> listToTree(List<Department> allDepartment) {
// 先找出所有的父级
List<JwDepartmentTreeVo> treeList = getByParentId("1", allDepartment);
getChildrenRecursion(treeList, allDepartment);
return treeList;
}
private static List<JwDepartmentTreeVo> getByParentId(String parentId, List<Department> allDepartment) {
List<JwDepartmentTreeVo> list = new ArrayList<>();
for (Department department : allDepartment) {
if (parentId.equals(department.getParentid())) {
list.add(new JwDepartmentTreeVo(department));
}
}
return list;
}
private static void getChildrenRecursion(List<JwDepartmentTreeVo> treeList, List<Department> allDepartment) {
for (JwDepartmentTreeVo departmentTree : treeList) {
// 递归寻找子级
List<JwDepartmentTreeVo> children = getByParentId(departmentTree.getId(), allDepartment);
if (children.size() > 0) {
departmentTree.setChildren(children);
getChildrenRecursion(children, allDepartment);
}
}
}
}

View File

@ -0,0 +1,44 @@
package org.jeecg.modules.system.vo.thirdapp;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* 同步结果信息包含成功的信息和失败的信息
*
* @author sunjianlei
*/
@Data
public class SyncInfoVo {
/**
* 成功的信息
*/
private List<String> successInfo;
/**
* 失败的信息
*/
private List<String> failInfo;
public SyncInfoVo() {
this.successInfo = new ArrayList<>();
this.failInfo = new ArrayList<>();
}
public SyncInfoVo(List<String> successInfo, List<String> failInfo) {
this.successInfo = successInfo;
this.failInfo = failInfo;
}
public SyncInfoVo addSuccessInfo(String info) {
this.successInfo.add(info);
return this;
}
public SyncInfoVo addFailInfo(String info) {
this.failInfo.add(info);
return this;
}
}

View File

@ -131,7 +131,7 @@ spring:
connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
datasource:
master:
url: jdbc:mysql://127.0.0.1:3306/jeecg-boot?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
url: jdbc:mysql://127.0.0.1:3306/jeecg-boot-os-re?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
@ -147,7 +147,7 @@ spring:
host: 127.0.0.1
lettuce:
pool:
max-active: 8 #最大连接数据库连接数,设 0 为没有限制
max-active: 8 #最大连接数据库连接数,设 -1 为没有限制
max-idle: 8 #最大等待连接中的数量,设 0 为没有限制
max-wait: -1ms #最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。
min-idle: 0 #最小等待连接中的数量,设 0 为没有限制
@ -170,29 +170,27 @@ mybatis-plus:
#log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 返回类型为Map,显示null对应的字段
call-setters-on-nulls: true
#minidao 设
#jeecg专用配
minidao :
base-package: org.jeecg.modules.jmreport.*
#DB类型mysql | postgresql | oracle | sqlserver
#DB类型mysql | postgresql | oracle | sqlserver| other
db-type: mysql
#jeecg专用配置
jeecg :
# 本地local\Miniominio\阿里云alioss
uploadType: local
path :
#文件上传根目录 设置
upload: D://opt//upFiles
upload: /opt/upFiles
#webapp文件路径
webapp: D://opt//webapp
webapp: /opt/webapp
shiro:
excludeUrls: /test/jeecgDemo/demo3,/test/jeecgDemo/redisDemo/**,/category/**,/visual/**,/map/**,/jmreport/bigscreen2/**
#阿里云oss存储配置
#阿里云oss存储和大鱼短信秘钥配置
oss:
endpoint: oss-cn-beijing.aliyuncs.com
accessKey: ??
secretKey: ??
endpoint: oss-cn-beijing.aliyuncs.com
bucketName: ??
staticDomain: ??
# ElasticSearch 6设置
elasticsearch:
cluster-name: jeecg-ES
@ -204,6 +202,9 @@ jeecg :
theme-color: "#1890ff"
# 文件、图片上传方式可选项qiniu七牛云、system跟随系统配置
upload-type: system
map:
# 配置百度地图的AK申请地址https://lbs.baidu.com/apiconsole/key?application=key#/home
baidu: ??
# 在线预览文件服务器地址配置
file-view-domain: 127.0.0.1:8012
# minio文件上传
@ -218,7 +219,7 @@ jeecg :
#数据字典是否进行saas数据隔离自己看自己的字典
saas: false
#是否需要校验token
is_verify_token: false
is_verify_token: true
#必须校验方法
verify_methods: remove,delete,save,add,update
#Wps在线文档
@ -260,7 +261,7 @@ logging:
knife4j:
production: false
basic:
enable: false
enable: true
username: jeecg
password: jeecg1314
#第三方登录
@ -275,7 +276,7 @@ justauth:
client-id: ??
client-secret: ??
redirect-uri: http://sso.test.com:8080/jeecg-boot/sys/thirdLogin/wechat_enterprise/callback
agent-id: 1000002
agent-id: ??
DINGTALK:
client-id: ??
client-secret: ??
@ -288,3 +289,26 @@ justauth:
type: default
prefix: 'demo::'
timeout: 1h
#第三方APP对接
third-app:
enabled: false
type:
#企业微信
WECHAT_ENTERPRISE:
enabled: false
#CORP_ID
client-id: ??
#SECRET
client-secret: ??
#自建应用id
agent-id: ??
#自建应用秘钥(新版企微需要配置)
# agent-app-secret: ??
#钉钉
DINGTALK:
enabled: false
# appKey
client-id: ??
# appSecret
client-secret: ??
agent-id: ??

View File

@ -147,7 +147,7 @@ spring:
host: 127.0.0.1
lettuce:
pool:
max-active: 8 #最大连接数据库连接数,设 0 为没有限制
max-active: 8 #最大连接数据库连接数,设 -1 为没有限制
max-idle: 8 #最大等待连接中的数量,设 0 为没有限制
max-wait: -1ms #最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。
min-idle: 0 #最小等待连接中的数量,设 0 为没有限制
@ -170,12 +170,11 @@ mybatis-plus:
#log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 返回类型为Map,显示null对应的字段
call-setters-on-nulls: true
#minidao 设
#jeecg专用配
minidao :
base-package: org.jeecg.modules.jmreport.*
#DB类型mysql | postgresql | oracle | sqlserver
#DB类型mysql | postgresql | oracle | sqlserver| other
db-type: mysql
#jeecg专用配置
jeecg :
# 本地local\Miniominio\阿里云alioss
uploadType: alioss
@ -185,18 +184,18 @@ jeecg :
#webapp文件路径
webapp: /opt/jeecg-boot/webapp
shiro:
excludeUrls: /test/jeecgDemo/demo3,/test/jeecgDemo/redisDemo/**,/category/**,/visual/**,/map/**,/jmreport/bigscreen2/**
#阿里云oss存储配置
excludeUrls: /test/jeecgDemo/demo3,/test/jeecgDemo/redisDemo/**,/category/**,/visual/**,/map/**,/jmreport/bigscreen2/**,/api/getUserInfo
#阿里云oss存储和大鱼短信秘钥配置
oss:
endpoint: oss-cn-beijing.aliyuncs.com
accessKey: ??
secretKey: ??
endpoint: oss-cn-beijing.aliyuncs.com
bucketName: ??
staticDomain: https://static.jeecg.com
# ElasticSearch 设置
elasticsearch:
cluster-name: jeecg-ES
cluster-nodes: 111.225.222.176:9200
cluster-nodes: ??
check-enabled: true
# 表单设计器配置
desform:
@ -204,6 +203,9 @@ jeecg :
theme-color: "#1890ff"
# 文件、图片上传方式可选项qiniu七牛云、system跟随系统配置
upload-type: system
map:
# 配置百度地图的AK申请地址https://lbs.baidu.com/apiconsole/key?application=key#/home
baidu: ??
# 在线预览文件服务器地址配置
file-view-domain: http://fileview.jeecg.com
# minio文件上传
@ -211,7 +213,7 @@ jeecg :
minio_url: http://minio.jeecg.com
minio_name: ??
minio_pass: ??
bucketName: otatest
bucketName: ??
#大屏报表参数设置
jmreport:
mode: prod
@ -275,7 +277,7 @@ justauth:
client-id: ??
client-secret: ??
redirect-uri: http://sso.test.com:8080/jeecg-boot/sys/thirdLogin/wechat_enterprise/callback
agent-id: 1000002
agent-id: ??
DINGTALK:
client-id: ??
client-secret: ??
@ -288,3 +290,26 @@ justauth:
type: default
prefix: 'demo::'
timeout: 1h
#第三方APP对接
third-app:
enabled: false
type:
#企业微信
WECHAT_ENTERPRISE:
enabled: false
#CORP_ID
client-id: ??
#SECRET
client-secret: ??
#自建应用id
agent-id: ??
#自建应用秘钥(新版企微需要配置)
# agent-app-secret: ??
#钉钉
DINGTALK:
enabled: false
# appKey
client-id: ??
# appSecret
client-secret: ??
agent-id: ??

View File

@ -147,7 +147,7 @@ spring:
host: 192.168.1.199
lettuce:
pool:
max-active: 8 #最大连接数据库连接数,设 0 为没有限制
max-active: 8 #最大连接数据库连接数,设 -1 为没有限制
max-idle: 8 #最大等待连接中的数量,设 0 为没有限制
max-wait: -1ms #最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。
min-idle: 0 #最小等待连接中的数量,设 0 为没有限制
@ -170,12 +170,11 @@ mybatis-plus:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 返回类型为Map,显示null对应的字段
call-setters-on-nulls: true
#minidao 设
#jeecg专用配
minidao :
base-package: org.jeecg.modules.jmreport.*
#DB类型mysql | postgresql | oracle | sqlserver
#DB类型mysql | postgresql | oracle | sqlserver| other
db-type: mysql
#jeecg专用配置
jeecg :
# 本地local\Miniominio\阿里云alioss
uploadType: local
@ -186,17 +185,17 @@ jeecg :
webapp: D://opt//webapp
shiro:
excludeUrls: /test/jeecgDemo/demo3,/test/jeecgDemo/redisDemo/**,/category/**,/visual/**,/map/**,/jmreport/bigscreen2/**
#阿里云oss存储配置
#阿里云oss存储和大鱼短信秘钥配置
oss:
endpoint: oss-cn-beijing.aliyuncs.com
accessKey: ??
secretKey: ??
endpoint: oss-cn-beijing.aliyuncs.com
bucketName: ??
staticDomain: https://static.jeecg.com
# ElasticSearch 设置
elasticsearch:
cluster-name: jeecg-ES
cluster-nodes: http://fileview.jeecg.com
cluster-nodes: ??
check-enabled: false
# 表单设计器配置
desform:
@ -204,6 +203,9 @@ jeecg :
theme-color: "#1890ff"
# 文件、图片上传方式可选项qiniu七牛云、system跟随系统配置
upload-type: system
map:
# 配置百度地图的AK申请地址https://lbs.baidu.com/apiconsole/key?application=key#/home
baidu: ??
# 在线预览文件服务器地址配置
file-view-domain: http://127.0.0.1:8012
# minio文件上传
@ -211,7 +213,7 @@ jeecg :
minio_url: http://minio.jeecg.com
minio_name: ??
minio_pass: ??
bucketName: otatest
bucketName: ??
#大屏报表参数设置
jmreport:
mode: prod
@ -260,7 +262,7 @@ cas:
knife4j:
production: false
basic:
enable: false
enable: true
username: jeecg
password: jeecg1314
#第三方登录
@ -275,7 +277,7 @@ justauth:
client-id: ??
client-secret: ??
redirect-uri: http://sso.test.com:8080/jeecg-boot/sys/thirdLogin/wechat_enterprise/callback
agent-id: 1000002
agent-id: ??
DINGTALK:
client-id: ??
client-secret: ??
@ -288,3 +290,26 @@ justauth:
type: default
prefix: 'demo::'
timeout: 1h
#第三方APP对接
third-app:
enabled: false
type:
#企业微信
WECHAT_ENTERPRISE:
enabled: false
#CORP_ID
client-id: ??
#SECRET
client-secret: ??
#自建应用id
agent-id: ??
#自建应用秘钥(新版企微需要配置)
# agent-app-secret: ??
#钉钉
DINGTALK:
enabled: false
# appKey
client-id: ??
# appSecret
client-secret: ??
agent-id: ??

View File

@ -9,6 +9,6 @@ ${AnsiColor.BRIGHT_BLUE}
${AnsiColor.BRIGHT_GREEN}
Jeecg Boot Version: 2.4.3
Jeecg Boot Version: 2.4.5
Spring Boot Version: ${spring-boot.version}${spring-boot.formatted-version}
${AnsiColor.BLACK}

View File

@ -45,6 +45,8 @@ public class ${entityName} implements Serializable {
<#elseif po.dictField?default("")?trim?length gt 1>
<#assign list_field_dictCode=', dicCode = "${po.dictField}"'>
</#if>
<#elseif po.classType=='sel_tree'>
<#assign list_field_dictCode=', dictTable = "${po.dictTable}", dicText = "${po.dictText?split(",")[2]}", dicCode = "${po.dictText?split(",")[0]}"'>
</#if>
/**${po.filedComment}*/
<#if po.fieldName == primaryKeyField>

View File

@ -50,7 +50,7 @@
<#elseif po.classType=='pca'>
<#if query_field_no gt 1> </#if><j-area-linkage type="cascader" v-model="queryParam.${po.fieldName}" placeholder="请选择省市区"/>
<#elseif po.classType=='popup'>
<#if query_field_no gt 1> </#if><j-popup placeholder="请选择${po.filedComment}" v-model="queryParam.${po.fieldName}" code="${po.dictTable}" org-fields="${po.dictField}" dest-fields="${po.dictText}" :field="getPopupField('${po.dictText}')"/>
<#if query_field_no gt 1> </#if><j-popup placeholder="请选择${po.filedComment}" v-model="queryParam.${po.fieldName}" code="${po.dictTable}" org-fields="${po.dictField}" dest-fields="${po.dictText}" :field="getPopupField('${po.dictText}')" :multi="${po.extendParams.popupMulti?c}"/>
<#elseif po.classType=='list' || po.classType=='radio' || po.classType=='checkbox'>
<#-- ---------------------------下拉或是单选 判断数据字典是表字典还是普通字典------------------------------- -->
<#if po.dictTable?default("")?trim?length gt 1>
@ -289,7 +289,7 @@
<#elseif po.classType=='switch'>
dataIndex: '${po.fieldName}',
customRender: (text) => (text ? filterMultiDictText(this.dictOptions['${po.fieldName}'], text) : ''),
<#elseif po.classType=='list' || po.classType=='list_multi' || po.classType=='sel_search' || po.classType=='radio' || po.classType=='checkbox' || po.classType=='sel_depart' || po.classType=='sel_user'>
<#elseif po.classType == 'sel_tree' || po.classType=='list' || po.classType=='list_multi' || po.classType=='sel_search' || po.classType=='radio' || po.classType=='checkbox' || po.classType=='sel_depart' || po.classType=='sel_user'>
dataIndex: '${po.fieldName}_dictText'
<#elseif po.classType=='cat_tree'>
<#if list_need_category>

View File

@ -44,6 +44,7 @@
org-fields="${po.dictField}"
dest-fields="${Format.underlineToHump(po.dictText)}"
code="${po.dictTable}"
:multi="${po.extendParams.popupMulti?c}"
@input="popupCallback"
<#if po.readonly=='Y'>disabled</#if>/>
<#elseif po.classType =='sel_depart'>

View File

@ -39,6 +39,8 @@ public class ${entityName} implements Serializable {
<#assign list_field_dictCode=', dictTable = "${po.dictTable}", dicText = "${po.dictText}", dicCode = "${po.dictField}"'>
<#elseif po.dictField?default("")?trim?length gt 1>
<#assign list_field_dictCode=', dicCode = "${po.dictField}"'>
<#elseif po.classType=='sel_tree'>
<#assign list_field_dictCode=', dictTable = "${po.dictTable}", dicText = "${po.dictText?split(",")[2]}", dicCode = "${po.dictText?split(",")[0]}"'>
</#if>
</#if>
/**${po.filedComment}*/

View File

@ -40,6 +40,8 @@ public class ${subTab.entityName} implements Serializable {
<#assign list_field_dictCode=', dictTable = "${po.dictTable}", dicText = "${po.dictText}", dicCode = "${po.dictField}"'>
<#elseif po.dictField?default("")?trim?length gt 1>
<#assign list_field_dictCode=', dicCode = "${po.dictField}"'>
<#elseif po.classType=='sel_tree'>
<#assign list_field_dictCode=', dictTable = "${po.dictTable}", dicText = "${po.dictText?split(",")[2]}", dicCode = "${po.dictText?split(",")[0]}"'>
</#if>
</#if>
/**${po.filedComment}*/

View File

@ -47,7 +47,7 @@
<#elseif po.classType=='pca'>
<#if query_field_no gt 1> </#if><j-area-linkage type="cascader" v-model="queryParam.${po.fieldName}" placeholder="请选择省市区"/>
<#elseif po.classType=='popup'>
<#if query_field_no gt 1> </#if><j-popup placeholder="请选择${po.filedComment}" v-model="queryParam.${po.fieldName}" code="${po.dictTable}" org-fields="${po.dictField}" dest-fields="${po.dictText}" :field="getPopupField('${po.dictText}')"/>
<#if query_field_no gt 1> </#if><j-popup placeholder="请选择${po.filedComment}" v-model="queryParam.${po.fieldName}" code="${po.dictTable}" org-fields="${po.dictField}" dest-fields="${po.dictText}" :field="getPopupField('${po.dictText}')" :multi="${po.extendParams.popupMulti?c}"/>
<#elseif po.classType=='list' || po.classType=='radio' || po.classType=='checkbox'>
<#-- ---------------------------下拉或是单选 判断数据字典是表字典还是普通字典------------------------------- -->
<#if po.dictTable?default("")?trim?length gt 1>
@ -277,7 +277,7 @@
<#elseif po.classType=='image'>
dataIndex: '${po.fieldName}',
scopedSlots: {customRender: 'imgSlot'}
<#elseif po.classType=='list' || po.classType=='list_multi' || po.classType=='sel_search' || po.classType=='radio' || po.classType=='checkbox' || po.classType=='sel_depart' || po.classType=='sel_user'>
<#elseif po.classType == 'sel_tree' || po.classType=='list' || po.classType=='list_multi' || po.classType=='sel_search' || po.classType=='radio' || po.classType=='checkbox' || po.classType=='sel_depart' || po.classType=='sel_user'>
dataIndex: '${po.fieldName}_dictText'
<#elseif po.classType=='cat_tree'>
<#if list_need_category>

View File

@ -49,6 +49,7 @@
org-fields="${po.dictField}"
dest-fields="${Format.underlineToHump(po.dictText)}"
code="${po.dictTable}"
:multi="${po.extendParams.popupMulti?c}"
@input="popupCallback"
<#if po.readonly=='Y'>disabled</#if>/>
<#elseif po.classType =='sel_depart'>

View File

@ -46,6 +46,7 @@
org-fields="${po.dictField}"
dest-fields="${Format.underlineToHump(po.dictText)}"
code="${po.dictTable}"
:multi="${po.extendParams.popupMulti?c}"
@input="popupCallback"/>
<#elseif po.classType =='sel_depart'>
<j-select-depart v-model="model.${po.fieldName}" multi/>

View File

@ -40,6 +40,8 @@ public class ${entityName} implements Serializable {
<#assign list_field_dictCode=', dictTable = "${po.dictTable}", dicText = "${po.dictText}", dicCode = "${po.dictField}"'>
<#elseif po.dictField?default("")?trim?length gt 1>
<#assign list_field_dictCode=', dicCode = "${po.dictField}"'>
<#elseif po.classType=='sel_tree'>
<#assign list_field_dictCode=', dictTable = "${po.dictTable}", dicText = "${po.dictText?split(",")[2]}", dicCode = "${po.dictText?split(",")[0]}"'>
</#if>
</#if>
/**${po.filedComment}*/

View File

@ -35,6 +35,8 @@ public class ${entityName}ServiceImpl extends ServiceImpl<${entityName}Mapper, $
@Override
public void add${entityName}(${entityName} ${entityName?uncap_first}) {
//新增时设置hasChild为0
${entityName?uncap_first}.set${hasChildrenField?cap_first}(I${entityName}Service.NOCHILD);
if(oConvertUtils.isEmpty(${entityName?uncap_first}.get${pidFieldName?cap_first}())){
${entityName?uncap_first}.set${pidFieldName?cap_first}(I${entityName}Service.ROOT_PID_VALUE);
}else{

View File

@ -59,7 +59,7 @@
<#elseif po.classType=='pca'>
<#if query_field_no gt 1> </#if><j-area-linkage type="cascader" v-model="queryParam.${po.fieldName}" placeholder="请选择省市区"/>
<#elseif po.classType=='popup'>
<#if query_field_no gt 1> </#if><j-popup placeholder="请选择${po.filedComment}" v-model="queryParam.${po.fieldName}" code="${po.dictTable}" org-fields="${po.dictField}" dest-fields="${po.dictText}" :field="getPopupField('${po.dictText}')"/>
<#if query_field_no gt 1> </#if><j-popup placeholder="请选择${po.filedComment}" v-model="queryParam.${po.fieldName}" code="${po.dictTable}" org-fields="${po.dictField}" dest-fields="${po.dictText}" :field="getPopupField('${po.dictText}')" :multi="${po.extendParams.popupMulti?c}"/>
<#elseif po.classType=='list' || po.classType=='radio' || po.classType=='checkbox'>
<#-- ---------------------------下拉或是单选 判断数据字典是表字典还是普通字典------------------------------- -->
<#if po.dictTable?default("")?trim?length gt 1>
@ -265,7 +265,7 @@
<#elseif po.classType=='image'>
dataIndex: '${po.fieldName}',
scopedSlots: {customRender: 'imgSlot'}
<#elseif po.classType=='list' || po.classType=='list_multi' || po.classType=='sel_search' || po.classType=='radio' || po.classType=='checkbox' || po.classType=='sel_depart' || po.classType=='sel_user'>
<#elseif po.classType == 'sel_tree' || po.classType=='list' || po.classType=='list_multi' || po.classType=='sel_search' || po.classType=='radio' || po.classType=='checkbox' || po.classType=='sel_depart' || po.classType=='sel_user'>
dataIndex: '${po.fieldName}_dictText'
<#elseif po.classType=='switch'>
dataIndex: '${po.fieldName}',

View File

@ -52,6 +52,7 @@
org-fields="${po.dictField}"
dest-fields="${Format.underlineToHump(po.dictText)}"
code="${po.dictTable}"
:multi="${po.extendParams.popupMulti?c}"
@input="popupCallback"
<#if po.readonly=='Y'>disabled</#if>/>
<#elseif po.classType =='sel_depart'>

View File

@ -39,6 +39,8 @@ public class ${entityName} implements Serializable {
<#assign list_field_dictCode=', dictTable = "${po.dictTable}", dicText = "${po.dictText}", dicCode = "${po.dictField}"'>
<#elseif po.dictField?default("")?trim?length gt 1>
<#assign list_field_dictCode=', dicCode = "${po.dictField}"'>
<#elseif po.classType=='sel_tree'>
<#assign list_field_dictCode=', dictTable = "${po.dictTable}", dicText = "${po.dictText?split(",")[2]}", dicCode = "${po.dictText?split(",")[0]}"'>
</#if>
</#if>
/**${po.filedComment}*/

View File

@ -42,7 +42,7 @@
<#elseif po.classType=='pca'>
<#if query_field_no gt 1> </#if><j-area-linkage type="cascader" v-model="queryParam.${po.fieldName}" placeholder="请选择省市区"/>
<#elseif po.classType=='popup'>
<#if query_field_no gt 1> </#if><j-popup placeholder="请选择${po.filedComment}" v-model="queryParam.${po.fieldName}" code="${po.dictTable}" org-fields="${po.dictField}" dest-fields="${po.dictText}" :field="getPopupField('${po.dictText}')"/>
<#if query_field_no gt 1> </#if><j-popup placeholder="请选择${po.filedComment}" v-model="queryParam.${po.fieldName}" code="${po.dictTable}" org-fields="${po.dictField}" dest-fields="${po.dictText}" :field="getPopupField('${po.dictText}')" :multi="${po.extendParams.popupMulti?c}"/>
<#elseif po.classType=='list' || po.classType=='radio' || po.classType=='checkbox'>
<#-- ---------------------------下拉或是单选 判断数据字典是表字典还是普通字典------------------------------- -->
<#if po.dictTable?default("")?trim?length gt 1>
@ -263,7 +263,7 @@
<#elseif po.classType=='image'>
dataIndex: '${po.fieldName}',
scopedSlots: {customRender: 'imgSlot'}
<#elseif po.classType=='sel_search' || po.classType=='list_multi' || po.classType=='list' || po.classType=='radio' || po.classType=='checkbox' || po.classType=='sel_depart'>
<#elseif po.classType=='sel_tree' || po.classType=='sel_search' || po.classType=='list_multi' || po.classType=='list' || po.classType=='radio' || po.classType=='checkbox' || po.classType=='sel_depart'>
dataIndex: '${po.fieldName}_dictText',
<#elseif po.classType=='switch'>
dataIndex: '${po.fieldName}',

View File

@ -40,6 +40,7 @@
org-fields="${po.dictField}"
dest-fields="${Format.underlineToHump(po.dictText)}"
code="${po.dictTable}"
:multi="${po.extendParams.popupMulti?c}"
@input="popupCallback"
<#if po.readonly=='Y'>disabled</#if>/>
<#elseif po.classType =='sel_depart'>

View File

@ -42,6 +42,7 @@
org-fields="${po.dictField}"
dest-fields="${Format.underlineToHump(po.dictText)}"
code="${po.dictTable}"
:multi="${po.extendParams.popupMulti?c}"
@input="popupCallback"
<#if po.readonly=='Y'>disabled</#if>/>
<#elseif po.classType =='sel_depart'>

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