23 changed files with 1601 additions and 1 deletions
Binary file not shown.
@ -1,3 +1,6 @@
|
||||
# open-JSD-6952 |
||||
|
||||
JSD-6952 开源任务代码 |
||||
JSD-6952 开源任务代码\ |
||||
免责说明:该源码为第三方爱好者提供,不保证源码和方案的可靠性,也不提供任何形式的源码教学指导和协助!\ |
||||
仅作为开发者学习参考使用!禁止用于任何商业用途!\ |
||||
为保护开发者隐私,开发者信息已隐去!若原开发者希望公开自己的信息,可联系hugh处理。 |
Binary file not shown.
Binary file not shown.
@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<plugin> |
||||
<id>com.fr.plugin.mqh.dingtalksyn.job</id> |
||||
<name><![CDATA[钉钉数据同步]]></name> |
||||
<active>yes</active> |
||||
<version>1.0</version> |
||||
<env-version>10.0</env-version> |
||||
<jartime>2018-07-31</jartime> |
||||
<vendor>mqh</vendor> |
||||
<description><![CDATA[钉钉数据同步]]></description> |
||||
<change-notes><![CDATA[ |
||||
版本1.0主要功能:<br/> |
||||
1.用户信息全量同步<br/> |
||||
2.用户单点登陆功能<br/> |
||||
]]></change-notes> |
||||
<main-package>com.fr.plugin.mqh.dingtalksyn</main-package> |
||||
<prefer-packages> |
||||
<prefer-package>com.fanruan.api</prefer-package> |
||||
</prefer-packages> |
||||
<lifecycle-monitor class="com.fr.plugin.mqh.dingtalksyn.provider.LifeCycleMonitorImpl"/> |
||||
<extra-core> |
||||
<LocaleFinder class="com.fr.plugin.mqh.dingtalksyn.provider.LocaleFinder"/> |
||||
</extra-core> |
||||
<extra-decision> |
||||
<GlobalRequestFilterProvider class="com.fr.plugin.mqh.dingtalksyn.request.GlobalRequestFilterBridge"/> |
||||
</extra-decision> |
||||
<function-recorder class="com.fr.plugin.mqh.dingtalksyn.provider.LocaleFinder"/> |
||||
</plugin> |
@ -0,0 +1,158 @@
|
||||
/* |
||||
* Copyright (C), 2018-2021 |
||||
* Project: starter |
||||
* FileName: DataResponse |
||||
* Author: Louis |
||||
* Date: 2021/3/19 11:46 |
||||
*/ |
||||
package com.fr.plugin.mqh.dingtalksyn.bean; |
||||
|
||||
import com.fanruan.api.util.StringKit; |
||||
import com.fr.decision.webservice.Response; |
||||
import com.fr.third.fasterxml.jackson.annotation.JsonInclude; |
||||
import com.fr.third.fasterxml.jackson.annotation.JsonProperty; |
||||
|
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* <Function Description><br> |
||||
* <DataResponse> |
||||
* |
||||
* @author Louis |
||||
* @since 1.0.0 |
||||
*/ |
||||
@JsonInclude(JsonInclude.Include.NON_DEFAULT) |
||||
public class DataResponse extends Response { |
||||
private static final long serialVersionUID = -6470353731188369521L; |
||||
@JsonProperty("msg_signature") |
||||
private String msgSignature; |
||||
private String timeStamp; |
||||
private String nonce; |
||||
private String encrypt; |
||||
|
||||
private String code; |
||||
private String message; |
||||
|
||||
public DataResponse() { |
||||
} |
||||
|
||||
private static DataResponse create() { |
||||
return new DataResponse(); |
||||
} |
||||
|
||||
/** |
||||
* 相应success结果 |
||||
* |
||||
* @return |
||||
*/ |
||||
public static DataResponse success(Map<String, String> successMap) { |
||||
return create().msgSignature(successMap.get("msg_signature")) |
||||
.timeStamp(successMap.get("timeStamp")) |
||||
.nonce(successMap.get("nonce")) |
||||
.encrypt(successMap.get("encrypt")); |
||||
} |
||||
|
||||
/** |
||||
* 操作结果 |
||||
* |
||||
* @param data |
||||
* @return |
||||
*/ |
||||
public static DataResponse operation(String data) { |
||||
return create().code("200").message("success").data(data); |
||||
} |
||||
|
||||
/** |
||||
* 报错结果 |
||||
* |
||||
* @param code |
||||
* @param message |
||||
* @return |
||||
*/ |
||||
public static DataResponse error(String code, String message) { |
||||
return create().code(code).message(message).data(StringKit.EMPTY); |
||||
} |
||||
|
||||
public DataResponse msgSignature(String msgSignature) { |
||||
this.msgSignature = msgSignature; |
||||
return this; |
||||
} |
||||
|
||||
public DataResponse timeStamp(String timeStamp) { |
||||
this.timeStamp = timeStamp; |
||||
return this; |
||||
} |
||||
|
||||
public DataResponse nonce(String nonce) { |
||||
this.nonce = nonce; |
||||
return this; |
||||
} |
||||
|
||||
public DataResponse encrypt(String encrypt) { |
||||
this.encrypt = encrypt; |
||||
return this; |
||||
} |
||||
|
||||
public DataResponse code(String code) { |
||||
this.code = code; |
||||
return this; |
||||
} |
||||
|
||||
public DataResponse message(String message) { |
||||
this.message = message; |
||||
return this; |
||||
} |
||||
|
||||
public DataResponse data(Object data) { |
||||
this.setData(data); |
||||
return this; |
||||
} |
||||
|
||||
public String getMsgSignature() { |
||||
return msgSignature; |
||||
} |
||||
|
||||
public void setMsgSignature(String msgSignature) { |
||||
this.msgSignature = msgSignature; |
||||
} |
||||
|
||||
public String getTimeStamp() { |
||||
return timeStamp; |
||||
} |
||||
|
||||
public void setTimeStamp(String timeStamp) { |
||||
this.timeStamp = timeStamp; |
||||
} |
||||
|
||||
public String getNonce() { |
||||
return nonce; |
||||
} |
||||
|
||||
public void setNonce(String nonce) { |
||||
this.nonce = nonce; |
||||
} |
||||
|
||||
public String getEncrypt() { |
||||
return encrypt; |
||||
} |
||||
|
||||
public void setEncrypt(String encrypt) { |
||||
this.encrypt = encrypt; |
||||
} |
||||
|
||||
public String getCode() { |
||||
return code; |
||||
} |
||||
|
||||
public void setCode(String code) { |
||||
this.code = code; |
||||
} |
||||
|
||||
public String getMessage() { |
||||
return message; |
||||
} |
||||
|
||||
public void setMessage(String message) { |
||||
this.message = message; |
||||
} |
||||
} |
@ -0,0 +1,107 @@
|
||||
/* |
||||
* Copyright (C), 2018-2021 |
||||
* Project: starter |
||||
* FileName: DingTalkJobConstructor |
||||
* Author: Louis |
||||
* Date: 2021/4/21 15:58 |
||||
*/ |
||||
package com.fr.plugin.mqh.dingtalksyn.bean; |
||||
|
||||
import com.fr.scheduler.job.FineScheduleJob; |
||||
|
||||
/** |
||||
* <Function Description><br> |
||||
* <DingTalkJobConstructor> |
||||
* |
||||
* @author Louis |
||||
* @since 1.0.0 |
||||
*/ |
||||
public class DingTalkJobConstructor { |
||||
private String cron; |
||||
private String jobName; |
||||
private String jobGroup; |
||||
private String triggerName; |
||||
private String triggerGroup; |
||||
private Class<? extends FineScheduleJob> jobClazz; |
||||
|
||||
public DingTalkJobConstructor() { |
||||
} |
||||
|
||||
public DingTalkJobConstructor cron(String var1) { |
||||
this.setCron(var1); |
||||
return this; |
||||
} |
||||
|
||||
public String getCron() { |
||||
return this.cron; |
||||
} |
||||
|
||||
public void setCron(String var1) { |
||||
this.cron = var1; |
||||
} |
||||
|
||||
public DingTalkJobConstructor jobName(String var1) { |
||||
this.setJobName(var1); |
||||
return this; |
||||
} |
||||
|
||||
public String getJobName() { |
||||
return this.jobName; |
||||
} |
||||
|
||||
public void setJobName(String var1) { |
||||
this.jobName = var1; |
||||
} |
||||
|
||||
public DingTalkJobConstructor jobGroup(String var1) { |
||||
this.setJobGroup(var1); |
||||
return this; |
||||
} |
||||
|
||||
public String getJobGroup() { |
||||
return this.jobGroup; |
||||
} |
||||
|
||||
public void setJobGroup(String var1) { |
||||
this.jobGroup = var1; |
||||
} |
||||
|
||||
public DingTalkJobConstructor triggerName(String var1) { |
||||
this.setTriggerName(var1); |
||||
return this; |
||||
} |
||||
|
||||
public String getTriggerName() { |
||||
return this.triggerName; |
||||
} |
||||
|
||||
public void setTriggerName(String var1) { |
||||
this.triggerName = var1; |
||||
} |
||||
|
||||
public DingTalkJobConstructor triggerGroup(String var1) { |
||||
this.setTriggerGroup(var1); |
||||
return this; |
||||
} |
||||
|
||||
public String getTriggerGroup() { |
||||
return this.triggerGroup; |
||||
} |
||||
|
||||
public void setTriggerGroup(String var1) { |
||||
this.triggerGroup = var1; |
||||
} |
||||
|
||||
public DingTalkJobConstructor jobClazz(Class<? extends FineScheduleJob> var1) { |
||||
this.setJobClazz(var1); |
||||
return this; |
||||
} |
||||
|
||||
public Class<? extends FineScheduleJob> getJobClazz() { |
||||
return this.jobClazz; |
||||
} |
||||
|
||||
public void setJobClazz(Class<? extends FineScheduleJob> var1) { |
||||
this.jobClazz = var1; |
||||
} |
||||
} |
@ -0,0 +1,107 @@
|
||||
/* |
||||
* Copyright (C), 2018-2021 |
||||
* Project: starter |
||||
* FileName: DingSynConfig |
||||
* Author: Louis |
||||
* Date: 2021/3/30 9:38 |
||||
*/ |
||||
package com.fr.plugin.mqh.dingtalksyn.config; |
||||
|
||||
import com.fanruan.api.util.StringKit; |
||||
import com.fr.config.*; |
||||
import com.fr.config.holder.Conf; |
||||
import com.fr.config.holder.factory.Holders; |
||||
|
||||
/** |
||||
* <Function Description><br> |
||||
* <DingSynConfig> |
||||
* |
||||
* @author Louis |
||||
* @since 1.0.0 |
||||
*/ |
||||
@Visualization(category = "Plugin-dingtalksyn_Group") |
||||
public class DingSynConfig extends DefaultConfiguration { |
||||
|
||||
// 每天中午十二点触发
|
||||
public static final String CRON_CONDITION = "0 0 12 * * ?"; |
||||
public static final long ROOT_DEP_ID = 1; |
||||
private static volatile DingSynConfig config = null; |
||||
|
||||
public static DingSynConfig getInstance() { |
||||
if (config == null) { |
||||
config = ConfigContext.getConfigInstance(DingSynConfig.class); |
||||
} |
||||
return config; |
||||
} |
||||
|
||||
@Identifier(value = "cronCondition", name = "Plugin-dingtalksyn_Config_CronCondition", description = "Plugin-dingtalksyn_Config_CronCondition_Description", status = Status.SHOW) |
||||
private Conf<String> cronCondition = Holders.simple(CRON_CONDITION); |
||||
@Identifier(value = "appKey", name = "Plugin-dingtalksyn_Config_appKey", description = "Plugin-dingtalksyn_Config_appKey_Description", status = Status.SHOW) |
||||
private Conf<String> appKey = Holders.simple(StringKit.EMPTY); |
||||
@Identifier(value = "appSecret", name = "Plugin-dingtalksyn_Config_appSecret", description = "Plugin-dingtalksyn_Config_appSecret_Description", status = Status.SHOW) |
||||
private Conf<String> appSecret = Holders.simple(StringKit.EMPTY); |
||||
@Identifier(value = "corpId", name = "Plugin-dingtalksyn_Config_corpId", description = "Plugin-dingtalksyn_Config_corpId_Description", status = Status.SHOW) |
||||
private Conf<String> corpId = Holders.simple(StringKit.EMPTY); |
||||
@Identifier(value = "rootDepId", name = "Plugin-dingtalksyn_Config_rootDepId", description = "Plugin-dingtalksyn_Config_rootDepId_Description", status = Status.SHOW) |
||||
private Conf<Long> rootDepId = Holders.simple(ROOT_DEP_ID); |
||||
// @Identifier(value = "token", name = "Plugin-dingtalksyn_Config_Token", description = "Plugin-dingtalksyn_Config_Token_Description", status = Status.SHOW)
|
||||
// private Conf<String> token = Holders.simple(StringKit.EMPTY);
|
||||
// @Identifier(value = "aesKey", name = "Plugin-dingtalksyn_Config_AesKey", description = "Plugin-dingtalksyn_Config_AesKey_Description", status = Status.SHOW)
|
||||
// private Conf<String> aesKey = Holders.simple(StringKit.EMPTY);
|
||||
|
||||
public String getCronCondition() { |
||||
return cronCondition.get(); |
||||
} |
||||
|
||||
public void setCronCondition(String cronCondition) { |
||||
this.cronCondition.set(cronCondition); |
||||
} |
||||
|
||||
public String getAppKey() { |
||||
return appKey.get(); |
||||
} |
||||
|
||||
public void setAppKey(String appKey) { |
||||
this.appKey.set(appKey); |
||||
} |
||||
|
||||
public String getAppSecret() { |
||||
return appSecret.get(); |
||||
} |
||||
|
||||
public void setAppSecret(String appSecret) { |
||||
this.appSecret.set(appSecret); |
||||
} |
||||
|
||||
public String getCorpId() { |
||||
return corpId.get(); |
||||
} |
||||
|
||||
public void setCorpId(String corpId) { |
||||
this.corpId.set(corpId); |
||||
} |
||||
|
||||
public long getRootDepId() { |
||||
return rootDepId.get(); |
||||
} |
||||
|
||||
public void setRootDepId(long rootDepId) { |
||||
this.rootDepId.set(rootDepId); |
||||
} |
||||
|
||||
// public String getToken() {
|
||||
// return token.get();
|
||||
// }
|
||||
//
|
||||
// public void setToken(String token) {
|
||||
// this.token.set(token);
|
||||
// }
|
||||
//
|
||||
// public String getAesKey() {
|
||||
// return aesKey.get();
|
||||
// }
|
||||
//
|
||||
// public void setAesKey(String aesKey) {
|
||||
// this.aesKey.set(aesKey);
|
||||
// }
|
||||
} |
@ -0,0 +1,77 @@
|
||||
/* |
||||
* Copyright (C), 2018-2021 |
||||
* Project: starter |
||||
* FileName: DingTalkScheduleHelper |
||||
* Author: Louis |
||||
* Date: 2021/4/21 15:52 |
||||
*/ |
||||
package com.fr.plugin.mqh.dingtalksyn.helper; |
||||
|
||||
import com.fr.plugin.mqh.dingtalksyn.bean.DingTalkJobConstructor; |
||||
import com.fr.plugin.mqh.dingtalksyn.job.DingTalkSyncMemberJob; |
||||
import com.fr.scheduler.ScheduleJobManager; |
||||
import com.fr.third.v2.org.quartz.CronScheduleBuilder; |
||||
import com.fr.third.v2.org.quartz.TriggerBuilder; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.HashMap; |
||||
import java.util.TimeZone; |
||||
|
||||
/** |
||||
* <Function Description><br> |
||||
* <DingTalkScheduleHelper> |
||||
* |
||||
* @author Louis |
||||
* @since 1.0.0 |
||||
*/ |
||||
public class DingTalkScheduleHelper { |
||||
public static final String DINGTALK_SCHEDULE_SYN_MEMBER_JOB_NAME = "MqhDingTalkSynDepMemberJob"; |
||||
public static final String DINGTALK_SCHEDULE_SYN_MEMBER_TRIGGER_NAME = "MqhDingTalkSynDepMemberTrigger"; |
||||
public static final String DINGTALK_SCHEDULE_SYN_MEMBER_GROUP = "MqhDingTalkSynDepMemberGroup"; |
||||
public static final String DINGTALK_SCHEDULE_SYN_MEMBER_TRIGGER_GROUP = "MqhDingTalkSynDepMemberTriggerGroup"; |
||||
|
||||
private DingTalkScheduleHelper() { |
||||
} |
||||
|
||||
public static DingTalkScheduleHelper getInstance() { |
||||
return DingTalkScheduleHelper.HOLDER.INSTANCE; |
||||
} |
||||
|
||||
public void startSynMemberSchedule(String cronCondition) throws Exception { |
||||
DingTalkJobConstructor jobConstructor = (new DingTalkJobConstructor()) |
||||
.cron(cronCondition).jobName(DINGTALK_SCHEDULE_SYN_MEMBER_JOB_NAME) |
||||
.jobGroup(DINGTALK_SCHEDULE_SYN_MEMBER_GROUP).triggerName(DINGTALK_SCHEDULE_SYN_MEMBER_TRIGGER_NAME) |
||||
.triggerGroup(DINGTALK_SCHEDULE_SYN_MEMBER_TRIGGER_GROUP).jobClazz(DingTalkSyncMemberJob.class); |
||||
this.startSchedule(jobConstructor); |
||||
} |
||||
|
||||
public void startSchedule(DingTalkJobConstructor var1) throws Exception { |
||||
if (var1 != null) { |
||||
String var2 = var1.getCron(); |
||||
String var3 = var1.getTriggerName(); |
||||
String var4 = var1.getTriggerGroup(); |
||||
String var5 = var1.getJobName(); |
||||
String var6 = var1.getJobGroup(); |
||||
Class var7 = var1.getJobClazz(); |
||||
TriggerBuilder var8 = TriggerBuilder.newTrigger(); |
||||
var8.withIdentity(var3, var4); |
||||
var8.withSchedule(CronScheduleBuilder.cronSchedule(var2).withMisfireHandlingInstructionFireAndProceed().inTimeZone(TimeZone.getTimeZone(TimeZone.getDefault().getID()))).startNow(); |
||||
var8.forJob(var5, var6); |
||||
ArrayList var9 = new ArrayList(); |
||||
var9.add(var8.build()); |
||||
ScheduleJobManager.getInstance().removeJob(var5, var6); |
||||
ScheduleJobManager.getInstance().addJob(var5, var6, "jobDescription", var7, var9, new HashMap()); |
||||
} |
||||
} |
||||
|
||||
public void stopSchedule(String var1, String var2) { |
||||
ScheduleJobManager.getInstance().removeJob(var1, var2); |
||||
} |
||||
|
||||
public static class HOLDER { |
||||
private static final DingTalkScheduleHelper INSTANCE = new DingTalkScheduleHelper(); |
||||
|
||||
public HOLDER() { |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,34 @@
|
||||
/* |
||||
* Copyright (C), 2018-2021 |
||||
* Project: starter |
||||
* FileName: DingTalkSyncMemberJob |
||||
* Author: Louis |
||||
* Date: 2021/4/21 16:02 |
||||
*/ |
||||
package com.fr.plugin.mqh.dingtalksyn.job; |
||||
|
||||
import com.fanruan.api.log.LogKit; |
||||
import com.fr.cluster.core.ClusterNode; |
||||
import com.fr.plugin.mqh.dingtalksyn.user.DingTalkUserManager; |
||||
import com.fr.scheduler.job.FineScheduleJob; |
||||
import com.fr.third.v2.org.quartz.JobExecutionContext; |
||||
|
||||
/** |
||||
* <Function Description><br> |
||||
* <DingTalkSyncMemberJob> |
||||
* |
||||
* @author Louis |
||||
* @since 1.0.0 |
||||
*/ |
||||
public class DingTalkSyncMemberJob extends FineScheduleJob { |
||||
public DingTalkSyncMemberJob() { |
||||
} |
||||
|
||||
public void run(JobExecutionContext jobExecutionContext, ClusterNode clusterNode) { |
||||
try { |
||||
DingTalkUserManager.getInstance().synDingTalkUsers(); |
||||
} catch (Exception e) { |
||||
LogKit.error(e.getMessage(), e); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,65 @@
|
||||
/* |
||||
* Copyright (C), 2018-2021 |
||||
* Project: starter |
||||
* FileName: CustomRoleServiceKit |
||||
* Author: Louis |
||||
* Date: 2021/5/14 10:31 |
||||
*/ |
||||
package com.fr.plugin.mqh.dingtalksyn.kit; |
||||
|
||||
import com.fr.decision.authority.AuthorityContext; |
||||
import com.fr.decision.authority.base.constant.SoftRoleType; |
||||
import com.fr.decision.authority.base.constant.type.operation.ManualOperationType; |
||||
import com.fr.decision.authority.data.CustomRole; |
||||
import com.fr.decision.record.OperateMessage; |
||||
import com.fr.decision.webservice.bean.user.RoleBean; |
||||
import com.fr.decision.webservice.exception.general.DuplicatedNameException; |
||||
import com.fr.decision.webservice.utils.ControllerFactory; |
||||
import com.fr.decision.webservice.v10.user.CustomRoleService; |
||||
import com.fr.intelli.record.MetricRegistry; |
||||
import com.fr.stable.query.QueryFactory; |
||||
import com.fr.stable.query.condition.QueryCondition; |
||||
import com.fr.stable.query.restriction.Restriction; |
||||
import com.fr.stable.query.restriction.RestrictionFactory; |
||||
|
||||
/** |
||||
* <Function Description><br> |
||||
* <CustomRoleServiceKit> |
||||
* |
||||
* @author Louis |
||||
* @since 1.0.0 |
||||
*/ |
||||
public class CustomRoleServiceKit extends CustomRoleService { |
||||
private static volatile CustomRoleServiceKit customRoleServiceKit = null; |
||||
|
||||
public CustomRoleServiceKit() { |
||||
} |
||||
|
||||
public static CustomRoleServiceKit getInstance() { |
||||
if (customRoleServiceKit == null) { |
||||
customRoleServiceKit = new CustomRoleServiceKit(); |
||||
} |
||||
return customRoleServiceKit; |
||||
} |
||||
|
||||
public void addCustomRole(String username, RoleBean roleBean) throws Exception { |
||||
this.checkDuplicatedCustomRole(roleBean.getText()); |
||||
CustomRole customRole = (new CustomRole()).id(roleBean.getId()).name(roleBean.getText()).description(roleBean.getDescription()).creationType(ManualOperationType.KEY).lastOperationType(ManualOperationType.KEY).enable(true); |
||||
ControllerFactory.getInstance().getCustomRoleController(username).addCustomRole(username, customRole); |
||||
this.deleteSoftData(customRole.getName()); |
||||
MetricRegistry.getMetric().submit(OperateMessage.build("Dec-Module-User_Manager", "Dec-Role", roleBean.getText(), "Dec-Log_Add")); |
||||
} |
||||
|
||||
private void checkDuplicatedCustomRole(String roleName) throws Exception { |
||||
QueryCondition queryCondition = QueryFactory.create().addRestriction(RestrictionFactory.eq("name", roleName)); |
||||
CustomRole customRole = (CustomRole) AuthorityContext.getInstance().getCustomRoleController().findOne(queryCondition); |
||||
if (customRole != null) { |
||||
throw new DuplicatedNameException(); |
||||
} |
||||
} |
||||
|
||||
private void deleteSoftData(String roleName) throws Exception { |
||||
QueryCondition queryCondition = QueryFactory.create().addRestriction(RestrictionFactory.and(new Restriction[]{RestrictionFactory.eq("deletedName", roleName), RestrictionFactory.eq("type", SoftRoleType.CUSTOM)})); |
||||
AuthorityContext.getInstance().getSoftDataController().remove(queryCondition); |
||||
} |
||||
} |
@ -0,0 +1,95 @@
|
||||
/* |
||||
* Copyright (C), 2018-2021 |
||||
* Project: starter |
||||
* FileName: DepartmentServiceKit |
||||
* Author: Louis |
||||
* Date: 2021/5/14 9:38 |
||||
*/ |
||||
package com.fr.plugin.mqh.dingtalksyn.kit; |
||||
|
||||
import com.fanruan.api.i18n.I18nKit; |
||||
import com.fanruan.api.util.StringKit; |
||||
import com.fr.decision.authority.AuthorityContext; |
||||
import com.fr.decision.authority.base.constant.type.operation.ManualOperationType; |
||||
import com.fr.decision.authority.data.Department; |
||||
import com.fr.decision.record.OperateMessage; |
||||
import com.fr.decision.webservice.exception.general.DuplicatedNameException; |
||||
import com.fr.decision.webservice.v10.user.DepartmentService; |
||||
import com.fr.general.ComparatorUtils; |
||||
import com.fr.intelli.record.MetricRegistry; |
||||
import com.fr.stable.StableUtils; |
||||
import com.fr.stable.query.QueryFactory; |
||||
import com.fr.stable.query.condition.QueryCondition; |
||||
import com.fr.stable.query.restriction.Restriction; |
||||
import com.fr.stable.query.restriction.RestrictionFactory; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* <Function Description><br> |
||||
* <DepartmentServiceKit> |
||||
* |
||||
* @author Louis |
||||
* @since 1.0.0 |
||||
*/ |
||||
public class DepartmentServiceKit extends DepartmentService { |
||||
public static final String DECISION_DEP_ROOT = "decision-dep-root"; |
||||
private static volatile DepartmentServiceKit departmentServiceKit = null; |
||||
|
||||
public DepartmentServiceKit() { |
||||
} |
||||
|
||||
public static DepartmentServiceKit getInstance() { |
||||
if (departmentServiceKit == null) { |
||||
departmentServiceKit = new DepartmentServiceKit(); |
||||
} |
||||
return departmentServiceKit; |
||||
} |
||||
|
||||
public void addDepartment(String id, String pId, String depName) throws Exception { |
||||
if (StringKit.equals(pId, DECISION_DEP_ROOT)) { |
||||
pId = null; |
||||
} |
||||
this.checkDuplicatedDepartmentName(pId, depName); |
||||
Department department = (new Department()).id(id).name(depName).parentId(pId).creationType(ManualOperationType.KEY).lastOperationType(ManualOperationType.KEY).enable(true); |
||||
AuthorityContext.getInstance().getDepartmentController().add(department); |
||||
MetricRegistry.getMetric().submit(OperateMessage.build("Dec-Module-User_Manager", "Dec-Department", this.getDepartmentFullPath(pId, depName, "/"), "Dec-Log_Add")); |
||||
} |
||||
|
||||
private void checkDuplicatedDepartmentName(String parentId, String depName) throws Exception { |
||||
QueryCondition condition = QueryFactory.create().addRestriction(RestrictionFactory.and(new Restriction[]{RestrictionFactory.eq("name", depName), RestrictionFactory.eq("parentId", parentId)})); |
||||
Department sameNameDep = AuthorityContext.getInstance().getDepartmentController().findOne(condition); |
||||
if (sameNameDep != null) { |
||||
throw new DuplicatedNameException(); |
||||
} |
||||
} |
||||
|
||||
private String getDepartmentFullPath(String pId, String depName, String splitter) throws Exception { |
||||
List<String> paths = new ArrayList<>(); |
||||
paths.add(depName); |
||||
while (!ComparatorUtils.equals(pId, DECISION_DEP_ROOT) && pId != null) { |
||||
Department parentDepartment = AuthorityContext.getInstance().getDepartmentController().getById(pId); |
||||
paths.add(parentDepartment.getName()); |
||||
pId = parentDepartment.getParentId(); |
||||
} |
||||
Collections.reverse(paths); |
||||
return StableUtils.join(paths.toArray(new String[0]), splitter); |
||||
} |
||||
|
||||
public void editDepartment(String departmentId, String depName, String pId) throws Exception { |
||||
if (StringKit.equals(pId, DECISION_DEP_ROOT)) { |
||||
pId = null; |
||||
} |
||||
Department department = AuthorityContext.getInstance().getDepartmentController().getById(departmentId); |
||||
String departmentFullPath = DepartmentService.getInstance().getDepartmentFullPath(departmentId); |
||||
if (!ComparatorUtils.equals(department.getName(), depName)) { |
||||
this.checkDuplicatedDepartmentName(department.getParentId(), depName); |
||||
department.setName(depName); |
||||
department.setParentId(pId); |
||||
AuthorityContext.getInstance().getDepartmentController().update(department); |
||||
} |
||||
MetricRegistry.getMetric().submit(OperateMessage.build("Dec-Module-User_Manager", "Dec-Department", DepartmentService.getInstance().getDepartmentFullPath(departmentId), "Dec-Log_Update", I18nKit.getLocText("Fine-Dec_Department") + ":" + departmentFullPath)); |
||||
} |
||||
} |
@ -0,0 +1,186 @@
|
||||
/* |
||||
* Copyright (C), 2018-2021 |
||||
* Project: starter |
||||
* FileName: UserServiceKit |
||||
* Author: Louis |
||||
* Date: 2021/5/14 8:28 |
||||
*/ |
||||
package com.fr.plugin.mqh.dingtalksyn.kit; |
||||
|
||||
import com.dingtalk.api.response.OapiV2UserGetResponse; |
||||
import com.fanruan.api.log.LogKit; |
||||
import com.fanruan.api.util.StringKit; |
||||
import com.fr.decision.authority.AuthorityContext; |
||||
import com.fr.decision.authority.data.Post; |
||||
import com.fr.decision.authority.data.User; |
||||
import com.fr.decision.authority.data.personnel.DepRole; |
||||
import com.fr.decision.privilege.TransmissionTool; |
||||
import com.fr.decision.webservice.bean.user.DepartmentPostBean; |
||||
import com.fr.decision.webservice.bean.user.UserBean; |
||||
import com.fr.decision.webservice.utils.UserSourceFactory; |
||||
import com.fr.decision.webservice.utils.WebServiceUtils; |
||||
import com.fr.decision.webservice.v10.user.PositionService; |
||||
import com.fr.decision.webservice.v10.user.UserService; |
||||
import com.fr.stable.StringUtils; |
||||
import com.fr.stable.collections.CollectionUtils; |
||||
import com.fr.stable.query.QueryFactory; |
||||
import com.fr.stable.query.restriction.RestrictionFactory; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* <Function Description><br> |
||||
* <UserServiceKit> |
||||
* |
||||
* @author Louis |
||||
* @since 1.0.0 |
||||
*/ |
||||
public class UserServiceKit extends UserService { |
||||
private static volatile UserServiceKit userServiceKit = null; |
||||
|
||||
public UserServiceKit() { |
||||
} |
||||
|
||||
public static UserServiceKit getInstance() { |
||||
if (userServiceKit == null) { |
||||
userServiceKit = new UserServiceKit(); |
||||
} |
||||
return userServiceKit; |
||||
} |
||||
|
||||
public UserBean createUserBean(OapiV2UserGetResponse.UserGetResponse userGetResponse) throws Exception { |
||||
UserBean userBean = new UserBean(); |
||||
userBean.setUsername(userGetResponse.getUserid()); |
||||
userBean.setRealName(userGetResponse.getName()); |
||||
userBean.setEmail(userGetResponse.getEmail()); |
||||
userBean.setMobile(userGetResponse.getMobile()); |
||||
userBean.setPassword(TransmissionTool.defaultEncrypt(userGetResponse.getUserid() + "123456")); |
||||
if (!CollectionUtils.isEmpty(userGetResponse.getRoleList())) { |
||||
userBean.setRoleIds(oapiUserRoles2Ids(userGetResponse.getRoleList())); |
||||
} |
||||
if (!CollectionUtils.isEmpty(userGetResponse.getDeptIdList())) { |
||||
List<String> departmentPostIds = createDepartmentPostIds(userGetResponse.getDeptIdList(), userGetResponse.getTitle()); |
||||
userBean.setDepartmentPostIds(departmentPostIds); |
||||
} |
||||
return userBean; |
||||
} |
||||
|
||||
/** |
||||
* 钉钉部门list转为部门职务组合list |
||||
* |
||||
* @param deptIdList |
||||
* @param title |
||||
* @return |
||||
* @throws Exception |
||||
*/ |
||||
private List<String> createDepartmentPostIds(List<Long> deptIdList, String title) throws Exception { |
||||
List<String> departmentPostIds = new ArrayList<>(); |
||||
String departmentPostId; |
||||
for (long currentDepId : deptIdList) { |
||||
departmentPostId = String.valueOf(currentDepId); |
||||
if (StringKit.isBlank(departmentPostId) || StringKit.equals(departmentPostId, "null")) { |
||||
continue; |
||||
} |
||||
// 职务处理
|
||||
String positionId = positionSynOperation(title, departmentPostId); |
||||
if (StringKit.isNotBlank(positionId)) { |
||||
departmentPostId = departmentPostId + "@@@" + positionId; |
||||
} |
||||
departmentPostIds.add(departmentPostId); |
||||
} |
||||
return departmentPostIds; |
||||
} |
||||
|
||||
/** |
||||
* 职务同步操作 |
||||
* |
||||
* @param title |
||||
* @return |
||||
* @throws Exception |
||||
*/ |
||||
private String positionSynOperation(String title, String departmentId) throws Exception { |
||||
String position = StringKit.isNotBlank(title) ? title : "职员"; |
||||
Post post = AuthorityContext.getInstance().getPostController().findOne(QueryFactory.create().addRestriction(RestrictionFactory.eq("name", position))); |
||||
String positionId; |
||||
if (post == null) { |
||||
positionId = PositionService.getInstance().addPosition(position, position); |
||||
} else { |
||||
positionId = post.getId(); |
||||
} |
||||
List<DepartmentPostBean> departmentPostBeanList = PositionService.getInstance().getPositionsUnderParentDepartment(getAdminUserId(), departmentId, position); |
||||
if (departmentPostBeanList == null || departmentPostBeanList.isEmpty()) { |
||||
try { |
||||
AuthorityContext.getInstance().getPostController().addPostToDepartment(positionId, departmentId); |
||||
} catch (Exception e) { |
||||
LogKit.info("dingtalksyn-UserServiceKit-positionSynOperation-addPostToDepartmentFailed-position:{}, departmentId:{}", positionId + position, departmentId); |
||||
LogKit.error(e.getMessage(), e); |
||||
} |
||||
} |
||||
return positionId; |
||||
} |
||||
|
||||
/** |
||||
* 获取管理员id |
||||
* |
||||
* @return |
||||
* @throws Exception |
||||
*/ |
||||
public String getAdminUserId() throws Exception { |
||||
List<String> adminUserIdList = UserService.getInstance().getAdminUserIdList(); |
||||
if (adminUserIdList.isEmpty()) { |
||||
return "admin"; |
||||
} |
||||
return StringKit.isNotBlank(adminUserIdList.get(0)) ? adminUserIdList.get(0) : "admin"; |
||||
} |
||||
|
||||
public UserBean updateUserBean(OapiV2UserGetResponse.UserGetResponse userGetResponse) throws Exception { |
||||
User user = UserService.getInstance().getUserByUserName(userGetResponse.getUserid()); |
||||
if (user == null) { |
||||
return null; |
||||
} |
||||
UserBean userBean = new UserBean(); |
||||
userBean.setId(user.getId()); |
||||
userBean.setUsername(user.getUserName()); |
||||
userBean.setRealName(userGetResponse.getName()); |
||||
userBean.setEmail(userGetResponse.getEmail()); |
||||
userBean.setMobile(userGetResponse.getMobile()); |
||||
if (!CollectionUtils.isEmpty(userGetResponse.getRoleList())) { |
||||
userBean.setRoleIds(oapiUserRoles2Ids(userGetResponse.getRoleList())); |
||||
} |
||||
if (!CollectionUtils.isEmpty(userGetResponse.getDeptIdList())) { |
||||
List<String> departmentPostIds = createDepartmentPostIds(userGetResponse.getDeptIdList(), userGetResponse.getTitle()); |
||||
userBean.setDepartmentPostIds(departmentPostIds); |
||||
} |
||||
return userBean; |
||||
} |
||||
|
||||
/** |
||||
* 增加用户部门关联 |
||||
* |
||||
* @param userBean |
||||
* @throws Exception |
||||
*/ |
||||
public void addUserDepartment(UserBean userBean) throws Exception { |
||||
if (CollectionUtils.isEmpty(userBean.getDepartmentPostIds())) { |
||||
return; |
||||
} |
||||
for (String departmentPostId : userBean.getDepartmentPostIds()) { |
||||
if (StringUtils.isEmpty(departmentPostId)) { |
||||
continue; |
||||
} |
||||
User user = UserService.getInstance().getUserByUserName(userBean.getUsername()); |
||||
DepRole depRole = WebServiceUtils.parseUniqueDepartmentPostId(departmentPostId); |
||||
UserSourceFactory.getInstance().checkSource(user, AuthorityContext.getInstance().getDepartmentController().getById(depRole.getDepartmentId())); |
||||
AuthorityContext.getInstance().getUserController().addUserToDepartmentAndPost(user.getId(), depRole.getDepartmentId(), depRole.getPostId()); |
||||
} |
||||
} |
||||
|
||||
private String[] oapiUserRoles2Ids(List<OapiV2UserGetResponse.UserRole> oapiUserRoleList) { |
||||
String[] roleIds = new String[oapiUserRoleList.size()]; |
||||
for (int i = 0; i < oapiUserRoleList.size(); i++) { |
||||
roleIds[i] = String.valueOf(oapiUserRoleList.get(i).getId()); |
||||
} |
||||
return roleIds; |
||||
} |
||||
} |
@ -0,0 +1,49 @@
|
||||
/* |
||||
* Copyright (C), 2018-2021 |
||||
* Project: starter |
||||
* FileName: LifeCycleMonitorImpl |
||||
* Author: Louis |
||||
* Date: 2021/3/30 15:10 |
||||
*/ |
||||
package com.fr.plugin.mqh.dingtalksyn.provider; |
||||
|
||||
import com.fanruan.api.log.LogKit; |
||||
import com.fr.plugin.context.PluginContext; |
||||
import com.fr.plugin.mqh.dingtalksyn.helper.DingTalkScheduleHelper; |
||||
import com.fr.plugin.observer.inner.AbstractPluginLifecycleMonitor; |
||||
import com.fr.plugin.mqh.dingtalksyn.config.DingSynConfig; |
||||
|
||||
import static com.fr.plugin.mqh.dingtalksyn.helper.DingTalkScheduleHelper.DINGTALK_SCHEDULE_SYN_MEMBER_GROUP; |
||||
import static com.fr.plugin.mqh.dingtalksyn.helper.DingTalkScheduleHelper.DINGTALK_SCHEDULE_SYN_MEMBER_JOB_NAME; |
||||
|
||||
/** |
||||
* <Function Description><br> |
||||
* <LifeCycleMonitorImpl> |
||||
* |
||||
* @author Louis |
||||
* @since 1.0.0 |
||||
*/ |
||||
public class LifeCycleMonitorImpl extends AbstractPluginLifecycleMonitor { |
||||
public LifeCycleMonitorImpl() { |
||||
} |
||||
|
||||
@Override |
||||
public void afterRun(PluginContext pluginContext) { |
||||
DingSynConfig.getInstance(); |
||||
this.reStartSchedule(); |
||||
} |
||||
|
||||
@Override |
||||
public void beforeStop(PluginContext pluginContext) { |
||||
DingTalkScheduleHelper.getInstance().stopSchedule(DINGTALK_SCHEDULE_SYN_MEMBER_JOB_NAME, DINGTALK_SCHEDULE_SYN_MEMBER_GROUP); |
||||
} |
||||
|
||||
private void reStartSchedule() { |
||||
try { |
||||
String cronCondition = DingSynConfig.getInstance().getCronCondition(); |
||||
DingTalkScheduleHelper.getInstance().startSynMemberSchedule(cronCondition); |
||||
} catch (Exception e) { |
||||
LogKit.error(e.getMessage(), e); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,36 @@
|
||||
/* |
||||
* Copyright (C), 2018-2020 |
||||
* Project: starter |
||||
* FileName: LocaleFinder |
||||
* Author: Louis |
||||
* Date: 2020/8/31 22:19 |
||||
*/ |
||||
package com.fr.plugin.mqh.dingtalksyn.provider; |
||||
|
||||
import com.fr.intelli.record.Focus; |
||||
import com.fr.intelli.record.Original; |
||||
import com.fr.record.analyzer.EnableMetrics; |
||||
import com.fr.stable.fun.impl.AbstractLocaleFinder; |
||||
|
||||
/** |
||||
* <Function Description><br> |
||||
* <LocaleFinder> |
||||
* |
||||
* @author Louis |
||||
* @since 1.0.0 |
||||
*/ |
||||
@EnableMetrics |
||||
public class LocaleFinder extends AbstractLocaleFinder { |
||||
public static final String PLUGIN_ID = "com.fr.plugin.mqh.dingtalksyn.job"; |
||||
|
||||
@Override |
||||
@Focus(id = PLUGIN_ID, text = "Plugin-dingtalksyn", source = Original.PLUGIN) |
||||
public String find() { |
||||
return "com/fr/plugin/mqh/dingtalksyn/locale/lang"; |
||||
} |
||||
|
||||
@Override |
||||
public int currentAPILevel() { |
||||
return CURRENT_LEVEL; |
||||
} |
||||
} |
@ -0,0 +1,198 @@
|
||||
/* |
||||
* Copyright (C), 2018-2021 |
||||
* Project: starter |
||||
* FileName: GlobalRequestFilterBridge |
||||
* Author: Louis |
||||
* Date: 2021/3/30 22:09 |
||||
*/ |
||||
package com.fr.plugin.mqh.dingtalksyn.request; |
||||
|
||||
import com.fanruan.api.decision.login.LoginKit; |
||||
import com.fanruan.api.log.LogKit; |
||||
import com.fanruan.api.net.NetworkKit; |
||||
import com.fanruan.api.net.http.HttpKit; |
||||
import com.fanruan.api.util.IOKit; |
||||
import com.fanruan.api.util.StringKit; |
||||
import com.fr.base.TemplateUtils; |
||||
import com.fr.decision.fun.impl.AbstractGlobalRequestFilterProvider; |
||||
import com.fr.decision.webservice.utils.DecisionServiceConstants; |
||||
import com.fr.json.JSONObject; |
||||
import com.fr.plugin.mqh.dingtalksyn.config.DingSynConfig; |
||||
import com.fr.web.utils.WebUtils; |
||||
|
||||
import javax.servlet.FilterChain; |
||||
import javax.servlet.FilterConfig; |
||||
import javax.servlet.http.HttpServletRequest; |
||||
import javax.servlet.http.HttpServletResponse; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
import static com.fr.plugin.mqh.dingtalksyn.utils.DingAPI.GETTOKEN; |
||||
import static com.fr.plugin.mqh.dingtalksyn.utils.DingAPI.GET_USER_INFO; |
||||
|
||||
/** |
||||
* <Function Description><br> |
||||
* <GlobalRequestFilterBridge> |
||||
* |
||||
* @author Louis |
||||
* @since 1.0.0 |
||||
*/ |
||||
public class GlobalRequestFilterBridge extends AbstractGlobalRequestFilterProvider { |
||||
public static final String TPL_PATH = "/com/fr/plugin/mqh/dingtalksyn/web/codePage.html"; |
||||
public static final String DINGTALK_OPEN_JS = "/com/fr/plugin/mqh/dingtalksyn/web/dingtalk.open.js"; |
||||
public static final String CODE = "code"; |
||||
public static final String DING_TALK_LOGIN = "dt"; |
||||
|
||||
private DingSynConfig config; |
||||
|
||||
/** |
||||
* 过滤器名称 |
||||
* |
||||
* @return |
||||
*/ |
||||
@Override |
||||
public String filterName() { |
||||
return "DingTalkSynFilter"; |
||||
} |
||||
|
||||
/** |
||||
* 过滤规则 |
||||
* |
||||
* @return |
||||
*/ |
||||
@Override |
||||
public String[] urlPatterns() { |
||||
return new String[]{"/decision/*"}; |
||||
} |
||||
|
||||
/** |
||||
* 过滤器初始化 |
||||
* |
||||
* @param filterConfig |
||||
*/ |
||||
@Override |
||||
public void init(FilterConfig filterConfig) { |
||||
this.config = DingSynConfig.getInstance(); |
||||
super.init(filterConfig); |
||||
} |
||||
|
||||
/** |
||||
* 过滤器处理 |
||||
* |
||||
* @param request |
||||
* @param response |
||||
* @param filterChain |
||||
*/ |
||||
@Override |
||||
public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) { |
||||
try { |
||||
if (operation(request, response)) { |
||||
filterChain.doFilter(request, response); |
||||
} |
||||
} catch (Exception e) { |
||||
LogKit.error(e.getMessage(), e); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 用户验证登陆操作 |
||||
* |
||||
* @param req |
||||
* @param res |
||||
* @throws Exception |
||||
*/ |
||||
private boolean operation(HttpServletRequest req, HttpServletResponse res) throws Exception { |
||||
String pathInfo = (req.getPathInfo() != null) ? req.getPathInfo() : StringKit.EMPTY; |
||||
if (StringKit.equals(DINGTALK_OPEN_JS, pathInfo)) { |
||||
WebUtils.printAsString(res, IOKit.readResourceAsString(DINGTALK_OPEN_JS)); |
||||
return false; |
||||
} |
||||
//DingTalk登陆参数
|
||||
String dt = NetworkKit.getHTTPRequestParameter(req, DING_TALK_LOGIN); |
||||
if (StringKit.isBlank(dt)) { |
||||
return true; |
||||
} |
||||
String code = NetworkKit.getHTTPRequestParameter(req, CODE); |
||||
LogKit.info("dingtalksyn-GlobalRequestFilterBridge-operation-code:{}", code); |
||||
if (StringKit.isEmpty(code)) { |
||||
loginPage(req, res); |
||||
return false; |
||||
} |
||||
String accessToken = getAccessToken(code); |
||||
String username = getUsername(code, accessToken); |
||||
LogKit.info("dingtalksyn-GlobalRequestFilterBridge-operation-username:{}", username); |
||||
if (StringKit.isEmpty(username)) { |
||||
return true; |
||||
} |
||||
String tokenFR = LoginKit.login(req, res, username); |
||||
req.setAttribute(DecisionServiceConstants.FINE_AUTH_TOKEN_NAME, tokenFR); |
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* 钉钉获取code页面 |
||||
* |
||||
* @param req |
||||
* @param res |
||||
* @return |
||||
* @throws Exception |
||||
*/ |
||||
private void loginPage(HttpServletRequest req, HttpServletResponse res) throws Exception { |
||||
Map<String, String> parameterMap = new HashMap<>(); |
||||
parameterMap.put("corpId", this.config.getCorpId()); |
||||
parameterMap.put("openJs", WebUtils.createServletURL(req) + DINGTALK_OPEN_JS); |
||||
parameterMap.put("remoteServletURL", getRemoteServletURL(WebUtils.getOriginalURL(req))); |
||||
String codePage = TemplateUtils.renderTemplate(TPL_PATH, parameterMap); |
||||
WebUtils.printAsString(res, codePage); |
||||
} |
||||
|
||||
/** |
||||
* 处理请求url加入code参数 |
||||
* |
||||
* @param url |
||||
* @return |
||||
*/ |
||||
private String getRemoteServletURL(String url) { |
||||
if (url.contains("?")) { |
||||
return url + "&" + CODE + "="; |
||||
} |
||||
return url + "?" + CODE + "="; |
||||
} |
||||
|
||||
/** |
||||
* 获取access_token |
||||
* |
||||
* @param code |
||||
* @return |
||||
* @throws Exception |
||||
*/ |
||||
private String getAccessToken(String code) throws Exception { |
||||
Map<String, String> tokenParams = new HashMap<>(); |
||||
tokenParams.put("appkey", this.config.getAppKey()); |
||||
tokenParams.put("appsecret", this.config.getAppSecret()); |
||||
tokenParams.put("code", code); |
||||
LogKit.info("dingtalksyn-GlobalRequestFilterBridge-getAccessToken-params:{}", tokenParams); |
||||
String res = HttpKit.get(GETTOKEN, tokenParams); |
||||
LogKit.info("dingtalksyn-GlobalRequestFilterBridge-getAccessToken-res:{}", res); |
||||
if (StringKit.isEmpty(res)) { |
||||
return StringKit.EMPTY; |
||||
} |
||||
return new JSONObject(res).getString("access_token"); |
||||
} |
||||
|
||||
/** |
||||
* 通过凭证获得username |
||||
* |
||||
* @param code |
||||
* @param accessToken |
||||
* @return |
||||
*/ |
||||
private String getUsername(String code, String accessToken) throws Exception { |
||||
Map<String, String> userInfoParams = new HashMap<>(); |
||||
userInfoParams.put("access_token", accessToken); |
||||
userInfoParams.put("code", code); |
||||
String userRes = HttpKit.get(GET_USER_INFO, userInfoParams); |
||||
LogKit.info("dingtalksyn-GlobalRequestFilterBridge-getUsername-userRes:{}", userRes); |
||||
return new JSONObject(userRes).getString("userid"); |
||||
} |
||||
} |
@ -0,0 +1,179 @@
|
||||
/* |
||||
* Copyright (C), 2018-2021 |
||||
* Project: starter |
||||
* FileName: DingTalkUserManager |
||||
* Author: Louis |
||||
* Date: 2021/4/21 16:18 |
||||
*/ |
||||
package com.fr.plugin.mqh.dingtalksyn.user; |
||||
|
||||
import com.dingtalk.api.response.OapiRoleListResponse; |
||||
import com.dingtalk.api.response.OapiV2DepartmentListsubResponse; |
||||
import com.dingtalk.api.response.OapiV2UserGetResponse; |
||||
import com.fanruan.api.decision.user.UserKit; |
||||
import com.fanruan.api.log.LogKit; |
||||
import com.fanruan.api.util.StringKit; |
||||
import com.fr.decision.authority.AuthorityContext; |
||||
import com.fr.decision.authority.base.constant.type.operation.ManualOperationType; |
||||
import com.fr.decision.authority.data.Department; |
||||
import com.fr.decision.webservice.bean.user.RoleBean; |
||||
import com.fr.decision.webservice.bean.user.UserBean; |
||||
import com.fr.decision.webservice.v10.user.CustomRoleService; |
||||
import com.fr.decision.webservice.v10.user.UserService; |
||||
import com.fr.plugin.mqh.dingtalksyn.config.DingSynConfig; |
||||
import com.fr.plugin.mqh.dingtalksyn.kit.CustomRoleServiceKit; |
||||
import com.fr.plugin.mqh.dingtalksyn.kit.DepartmentServiceKit; |
||||
import com.fr.plugin.mqh.dingtalksyn.kit.UserServiceKit; |
||||
import com.taobao.api.ApiException; |
||||
|
||||
import java.util.List; |
||||
|
||||
import static com.fr.plugin.mqh.dingtalksyn.kit.DepartmentServiceKit.DECISION_DEP_ROOT; |
||||
import static com.fr.plugin.mqh.dingtalksyn.utils.DingAPI.*; |
||||
|
||||
/** |
||||
* <Function Description><br> |
||||
* <DingTalkUserManager> |
||||
* |
||||
* @author Louis |
||||
* @since 1.0.0 |
||||
*/ |
||||
public final class DingTalkUserManager { |
||||
private DingSynConfig config; |
||||
|
||||
public DingTalkUserManager() { |
||||
this.config = DingSynConfig.getInstance(); |
||||
} |
||||
|
||||
private static class HOLDER { |
||||
private static final DingTalkUserManager INSTANCE = new DingTalkUserManager(); |
||||
} |
||||
public static DingTalkUserManager getInstance() { |
||||
return HOLDER.INSTANCE; |
||||
} |
||||
|
||||
/** |
||||
* 同步更新的字段包括:用户、邮箱、手机、部门、职位、角色。 |
||||
* 用户唯一字段用户名,更新时也是基于用户名。 |
||||
* |
||||
* @throws Exception |
||||
*/ |
||||
public synchronized void synDingTalkUsers() throws Exception { |
||||
LogKit.info("dingtalksyn-DingTalkUserManager-synDingTalkUsers-start"); |
||||
// 同步用户角色
|
||||
List<OapiRoleListResponse.OpenRoleGroup> oapiRoleGroupList = getOapiRoleList(); |
||||
for (OapiRoleListResponse.OpenRoleGroup openRoleGroup : oapiRoleGroupList) { |
||||
List<OapiRoleListResponse.OpenRole> openRoleList = openRoleGroup.getRoles(); |
||||
for (OapiRoleListResponse.OpenRole openRole : openRoleList) { |
||||
roleSynOperation(openRoleGroup.getName(), openRole); |
||||
} |
||||
} |
||||
// 同步部门和用户信息
|
||||
departmentSynLoop(this.config.getRootDepId()); |
||||
LogKit.info("dingtalksyn-DingTalkUserManager-synDingTalkUsers-end"); |
||||
} |
||||
|
||||
/** |
||||
* 按部门遍历子部门并同步人员信息 |
||||
* |
||||
* @param deptId |
||||
* @throws Exception |
||||
*/ |
||||
private void departmentSynLoop(long deptId) throws ApiException { |
||||
List<OapiV2DepartmentListsubResponse.DeptBaseResponse> departmentList = getDepartmentList(deptId); |
||||
if (departmentList == null || departmentList.isEmpty()) { |
||||
return; |
||||
} |
||||
// 同步部门信息
|
||||
for (OapiV2DepartmentListsubResponse.DeptBaseResponse oapiDepartment : departmentList) { |
||||
try { |
||||
departmentSynOperation(oapiDepartment); |
||||
} catch (Exception e) { |
||||
LogKit.error(e.getMessage(), e); |
||||
} |
||||
// 同步当前部门的用户信息
|
||||
List<String> deptMemberList = getDeptMember(oapiDepartment.getDeptId()); |
||||
for (String userId : deptMemberList) { |
||||
try { |
||||
OapiV2UserGetResponse.UserGetResponse userGetResponse = getUserResponse(userId); |
||||
if (userGetResponse == null) { |
||||
continue; |
||||
} |
||||
userSynOperation(userGetResponse); |
||||
} catch (Exception e) { |
||||
LogKit.error(e.getMessage(), e); |
||||
} |
||||
} |
||||
// 操作子部门遍历
|
||||
departmentSynLoop(oapiDepartment.getDeptId()); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 角色的新增更新操作 |
||||
* |
||||
* @param openRole |
||||
* @throws Exception |
||||
*/ |
||||
private void roleSynOperation(String groupName, OapiRoleListResponse.OpenRole openRole) throws Exception { |
||||
if (openRole == null) { |
||||
return; |
||||
} |
||||
RoleBean roleBean = CustomRoleService.getInstance().getCustomRole(String.valueOf(openRole.getId())); |
||||
String roleName = StringKit.isBlank(groupName) ? openRole.getName() : groupName + "-" + openRole.getName(); |
||||
if (roleBean == null) { |
||||
roleBean = new RoleBean(roleName, String.valueOf(openRole.getId()), roleName, ManualOperationType.KEY.toInteger()); |
||||
CustomRoleServiceKit.getInstance().addCustomRole(UserServiceKit.getInstance().getAdminUserId(), roleBean); |
||||
} else if(!StringKit.equals(roleBean.getText(), roleName)) { |
||||
roleBean.setText(roleName); |
||||
roleBean.setDescription(roleName); |
||||
CustomRoleService.getInstance().editCustomRole(roleBean.getId(), roleBean); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 部门组织的新增更新操作 |
||||
* |
||||
* @param oapiDepartment |
||||
* @throws Exception |
||||
*/ |
||||
private void departmentSynOperation(OapiV2DepartmentListsubResponse.DeptBaseResponse oapiDepartment) throws Exception { |
||||
String departmentId = String.valueOf(oapiDepartment.getDeptId()); |
||||
String parentId = String.valueOf(oapiDepartment.getParentId()); |
||||
if (StringKit.equals(parentId, String.valueOf(this.config.getRootDepId()))) { parentId = DECISION_DEP_ROOT; } |
||||
String depName = oapiDepartment.getName(); |
||||
Department department = AuthorityContext.getInstance().getDepartmentController().getById(departmentId); |
||||
if (department == null) { |
||||
DepartmentServiceKit.getInstance().addDepartment(departmentId, parentId, depName); |
||||
} else { |
||||
DepartmentServiceKit.getInstance().editDepartment(department.getId(), depName, parentId); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 用户新增更新操作 |
||||
* |
||||
* @param userGetResponse |
||||
* @throws Exception |
||||
*/ |
||||
private void userSynOperation(OapiV2UserGetResponse.UserGetResponse userGetResponse) throws Exception { |
||||
UserBean userBean; |
||||
if (UserKit.existUsername(userGetResponse.getUserid())) { |
||||
userBean = UserServiceKit.getInstance().updateUserBean(userGetResponse); |
||||
if (userBean == null) { |
||||
return; |
||||
} |
||||
UserService.getInstance().editUser(userBean); |
||||
UserServiceKit.getInstance().addUserDepartment(userBean); |
||||
} else { |
||||
userBean = UserServiceKit.getInstance().createUserBean(userGetResponse); |
||||
try { |
||||
UserService.getInstance().addUser(userBean); |
||||
} catch (Exception e) { |
||||
LogKit.error("dingtalksyn-DingTalkUserManager-userSynOperation-Username:{}, RealName:{}, Mobile:{}, Email:{}", |
||||
userBean.getUsername(), userBean.getRealName(), userBean.getMobile(), userBean.getEmail()); |
||||
LogKit.error(e.getMessage(), e); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,150 @@
|
||||
/* |
||||
* Copyright (C), 2018-2021 |
||||
* Project: starter |
||||
* FileName: DingAPI |
||||
* Author: Louis |
||||
* Date: 2021/5/13 16:16 |
||||
*/ |
||||
package com.fr.plugin.mqh.dingtalksyn.utils; |
||||
|
||||
import com.dingtalk.api.DefaultDingTalkClient; |
||||
import com.dingtalk.api.DingTalkClient; |
||||
import com.dingtalk.api.request.*; |
||||
import com.dingtalk.api.response.*; |
||||
import com.fr.plugin.mqh.dingtalksyn.config.DingSynConfig; |
||||
import com.taobao.api.ApiException; |
||||
|
||||
import java.util.List; |
||||
|
||||
/** |
||||
* <Function Description><br> |
||||
* <DingAPI> |
||||
* |
||||
* @author Louis |
||||
* @since 1.0.0 |
||||
*/ |
||||
public class DingAPI { |
||||
public static final int TOKEN_EXPIRE_TIME = 3600000; |
||||
/** |
||||
* 获取企业内部应用的access_token |
||||
*/ |
||||
public static final String GETTOKEN = "https://oapi.dingtalk.com/gettoken"; |
||||
/** |
||||
* 通过免登码获取用户信息 |
||||
*/ |
||||
public static final String GET_USER_INFO = "https://oapi.dingtalk.com/user/getuserinfo"; |
||||
public static final String USER_GET = "https://oapi.dingtalk.com/topapi/v2/user/get"; |
||||
public static final String DEPARTMENT_LIST = "https://oapi.dingtalk.com/topapi/v2/department/listsub"; |
||||
public static final String GET_DEPT_MEMBER = "https://oapi.dingtalk.com/topapi/user/listid"; |
||||
public static final String GET_DEPT = "https://oapi.dingtalk.com/topapi/v2/department/get"; |
||||
public static final String ROLE_LIST = "https://oapi.dingtalk.com/topapi/role/list"; |
||||
public static final String GETROLE = "https://oapi.dingtalk.com/topapi/role/getrole"; |
||||
|
||||
/** |
||||
* 根据deptId获取用户详情 |
||||
* |
||||
* @param deptId |
||||
* @return |
||||
* @throws ApiException |
||||
*/ |
||||
public static OapiV2DepartmentGetResponse.DeptGetResponse getDeptResponse(long deptId) throws ApiException { |
||||
DingTalkClient client = new DefaultDingTalkClient(GET_DEPT); |
||||
OapiV2DepartmentGetRequest req = new OapiV2DepartmentGetRequest(); |
||||
req.setDeptId(deptId); |
||||
req.setLanguage("zh_CN"); |
||||
OapiV2DepartmentGetResponse rsp = client.execute(req, DingTokenUtils.getAccessToken()); |
||||
return rsp.getResult(); |
||||
} |
||||
|
||||
/** |
||||
* 获取部门用户userid列表 |
||||
* |
||||
* @param deptId |
||||
* @return |
||||
* @throws ApiException |
||||
*/ |
||||
public static List<String> getDeptMember(long deptId) throws ApiException { |
||||
DingTalkClient client = new DefaultDingTalkClient(GET_DEPT_MEMBER); |
||||
OapiUserListidRequest req = new OapiUserListidRequest(); |
||||
req.setDeptId(deptId); |
||||
OapiUserListidResponse rsp = client.execute(req, DingTokenUtils.getAccessToken()); |
||||
return rsp.getResult().getUseridList(); |
||||
} |
||||
|
||||
/** |
||||
* 获取部门列表 |
||||
* |
||||
* @return |
||||
* @throws ApiException |
||||
*/ |
||||
public static List<OapiV2DepartmentListsubResponse.DeptBaseResponse> getDepartmentList(long deptId) throws ApiException { |
||||
DingTalkClient client = new DefaultDingTalkClient(DEPARTMENT_LIST); |
||||
OapiV2DepartmentListsubRequest req = new OapiV2DepartmentListsubRequest(); |
||||
req.setDeptId(deptId); |
||||
req.setLanguage("zh_CN"); |
||||
OapiV2DepartmentListsubResponse rsp = client.execute(req, DingTokenUtils.getAccessToken()); |
||||
return rsp.getResult(); |
||||
} |
||||
|
||||
/** |
||||
* 根据roleId获取角色详情 |
||||
* |
||||
* @param roleId |
||||
* @return |
||||
* @throws ApiException |
||||
*/ |
||||
public static OapiRoleGetroleResponse.OpenRole getRoleResponse(long roleId) throws ApiException { |
||||
DingTalkClient client = new DefaultDingTalkClient(GETROLE); |
||||
OapiRoleGetroleRequest req = new OapiRoleGetroleRequest(); |
||||
req.setRoleId(roleId); |
||||
OapiRoleGetroleResponse rsp = client.execute(req, DingTokenUtils.getAccessToken()); |
||||
return rsp.getRole(); |
||||
} |
||||
|
||||
/** |
||||
* 获取角色列表 |
||||
* |
||||
* @return |
||||
* @throws ApiException |
||||
*/ |
||||
public static List<OapiRoleListResponse.OpenRoleGroup> getOapiRoleList() throws ApiException { |
||||
DingTalkClient client = new DefaultDingTalkClient(ROLE_LIST); |
||||
OapiRoleListRequest req = new OapiRoleListRequest(); |
||||
req.setSize(200L); |
||||
req.setOffset(0L); |
||||
OapiRoleListResponse rsp = client.execute(req, DingTokenUtils.getAccessToken()); |
||||
return rsp.getResult().getList(); |
||||
} |
||||
|
||||
/** |
||||
* 根据userid获取用户详情 |
||||
* |
||||
* @param userId |
||||
* @return |
||||
* @throws ApiException |
||||
*/ |
||||
public static OapiV2UserGetResponse.UserGetResponse getUserResponse(String userId) throws ApiException { |
||||
DingTalkClient client = new DefaultDingTalkClient(USER_GET); |
||||
OapiV2UserGetRequest req = new OapiV2UserGetRequest(); |
||||
req.setUserid(userId); |
||||
req.setLanguage("zh_CN"); |
||||
OapiV2UserGetResponse rsp = client.execute(req, DingTokenUtils.getAccessToken()); |
||||
return rsp.getResult(); |
||||
} |
||||
|
||||
/** |
||||
* 获取企业内部应用的access_token |
||||
* |
||||
* @return |
||||
* @throws ApiException |
||||
*/ |
||||
public static String getAccessToken() throws ApiException { |
||||
DingTalkClient client = new DefaultDingTalkClient(GETTOKEN); |
||||
OapiGettokenRequest request = new OapiGettokenRequest(); |
||||
request.setAppkey(DingSynConfig.getInstance().getAppKey()); |
||||
request.setAppsecret(DingSynConfig.getInstance().getAppSecret()); |
||||
request.setHttpMethod("GET"); |
||||
OapiGettokenResponse response = client.execute(request); |
||||
return response.getAccessToken(); |
||||
} |
||||
} |
@ -0,0 +1,67 @@
|
||||
/* |
||||
* Copyright (C), 2018-2021 |
||||
* Project: starter |
||||
* FileName: DingTokenUtils |
||||
* Author: Louis |
||||
* Date: 2021/5/14 14:23 |
||||
*/ |
||||
package com.fr.plugin.mqh.dingtalksyn.utils; |
||||
|
||||
import com.fanruan.api.log.LogKit; |
||||
import com.fanruan.api.util.StringKit; |
||||
import com.fr.plugin.mqh.dingtalksyn.config.DingSynConfig; |
||||
import com.fr.store.StateHubManager; |
||||
import com.fr.store.StateHubService; |
||||
|
||||
import static com.fr.plugin.mqh.dingtalksyn.utils.DingAPI.TOKEN_EXPIRE_TIME; |
||||
|
||||
/** |
||||
* <Function Description><br> |
||||
* <DingTokenUtils> |
||||
* |
||||
* @author Louis |
||||
* @since 1.0.0 |
||||
*/ |
||||
public class DingTokenUtils { |
||||
|
||||
public static final String DINGTALKSYN_ACCESS_TOKEN = "dingtalksyn_access_token"; |
||||
public static final String DINGTALKSYN_SUITE_TICKET = "dingtalksyn_suite_ticket"; |
||||
|
||||
private static final StateHubService tokenService = StateHubManager.applyForService(DINGTALKSYN_ACCESS_TOKEN); |
||||
private static final StateHubService ticketService = StateHubManager.applyForService(DINGTALKSYN_SUITE_TICKET); |
||||
|
||||
public DingTokenUtils() { |
||||
} |
||||
|
||||
/** |
||||
* 缓存钉钉端AccessToken |
||||
* |
||||
* @return |
||||
*/ |
||||
public static String getAccessToken() { |
||||
String appKey = DingSynConfig.getInstance().getAppKey(); |
||||
try { |
||||
if (StringKit.isBlank(tokenService.get(appKey))) { |
||||
tokenService.put(appKey, DingAPI.getAccessToken(), TOKEN_EXPIRE_TIME); |
||||
} |
||||
return tokenService.get(appKey); |
||||
} catch (Exception e) { |
||||
LogKit.error(e.getMessage(), e); |
||||
return StringKit.EMPTY; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 缓存钉钉端SuiteTicket |
||||
* |
||||
* @return |
||||
*/ |
||||
public static void setSuiteTicket(String suiteTicket) { |
||||
String appKey = DingSynConfig.getInstance().getAppKey(); |
||||
try { |
||||
ticketService.put(appKey, suiteTicket, TOKEN_EXPIRE_TIME); |
||||
} catch (Exception e) { |
||||
LogKit.error(e.getMessage(), e); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,18 @@
|
||||
Plugin-dingtalksyn=DingTalk syn Plugin |
||||
Plugin-dingtalksyn_Group=DingTalk syn Plugin |
||||
Plugin-dingtalksyn_Config_CronCondition=Cron Condition |
||||
Plugin-dingtalksyn_Config_CronCondition_Description=Cron Condition |
||||
Plugin-dingtalksyn_Config_appKey=APPKEY |
||||
Plugin-dingtalksyn_Config_appKey_Description=APPKEY |
||||
Plugin-dingtalksyn_Config_appSecret=appSecret |
||||
Plugin-dingtalksyn_Config_appSecret_Description=appSecret |
||||
Plugin-dingtalksyn_Config_corpId=Corp Id |
||||
Plugin-dingtalksyn_Config_corpId_Description=Corp Id |
||||
Plugin-dingtalksyn_Config_rootDepId=Root Department ID |
||||
Plugin-dingtalksyn_Config_rootDepId_Description=Root Department ID |
||||
Plugin-dingtalksyn_Config_Token=Token |
||||
Plugin-dingtalksyn_Config_Token_Description=Token |
||||
Plugin-dingtalksyn_Config_AesKey=AesKey |
||||
Plugin-dingtalksyn_Config_AesKey_Description=AesKey |
||||
Plugin-dingtalksyn_Error_500=error |
||||
Plugin-dingtalksyn_Error_501=DingTalkSyn Plugin License Expired! |
@ -0,0 +1,18 @@
|
||||
Plugin-dingtalksyn=\u9489\u9489\u6570\u636E\u540C\u6B65\u63D2\u4EF6 |
||||
Plugin-dingtalksyn_Group=\u9489\u9489\u6570\u636E\u540C\u6B65\u63D2\u4EF6 |
||||
Plugin-dingtalksyn_Config_CronCondition=Cron\u8868\u8FBE\u5F0F |
||||
Plugin-dingtalksyn_Config_CronCondition_Description=Cron\u8868\u8FBE\u5F0F |
||||
Plugin-dingtalksyn_Config_appKey=\u5E94\u7528\u7CFB\u7EDF\u7684APPKEY |
||||
Plugin-dingtalksyn_Config_appKey_Description=\u5E94\u7528\u7CFB\u7EDF\u7684APPKEY |
||||
Plugin-dingtalksyn_Config_appSecret=\u5E94\u7528\u7CFB\u7EDF\u7684appSecret |
||||
Plugin-dingtalksyn_Config_appSecret_Description=\u5E94\u7528\u7CFB\u7EDF\u7684appSecret |
||||
Plugin-dingtalksyn_Config_corpId=Corp Id |
||||
Plugin-dingtalksyn_Config_corpId_Description=Corp Id |
||||
Plugin-dingtalksyn_Config_rootDepId=\u6839\u90E8\u95E8ID |
||||
Plugin-dingtalksyn_Config_rootDepId_Description=\u6839\u90E8\u95E8ID |
||||
Plugin-dingtalksyn_Config_Token=\u7B7E\u540DToken |
||||
Plugin-dingtalksyn_Config_Token_Description=\u7B7E\u540DToken |
||||
Plugin-dingtalksyn_Config_AesKey=\u52A0\u5BC6aes_key |
||||
Plugin-dingtalksyn_Config_AesKey_Description=\u52A0\u5BC6aes_key |
||||
Plugin-dingtalksyn_Error_500=error |
||||
Plugin-dingtalksyn_Error_501=DingTalkSyn\u63D2\u4EF6\u8BB8\u53EF\u5931\u6548! |
@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html> |
||||
<html lang="en"> |
||||
<head> |
||||
<meta charset="utf-8"> |
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> |
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0"> |
||||
<title>DingTalk</title> |
||||
<script src="${openJs}"></script> |
||||
</head> |
||||
<body> |
||||
<script> |
||||
dd.ready(function () { |
||||
dd.runtime.permission.requestAuthCode({ |
||||
corpId: "${corpId}", |
||||
onSuccess: function (result) { |
||||
window.open("${remoteServletURL}" + result.code); |
||||
}, |
||||
onFail: function (err) { |
||||
} |
||||
}); |
||||
}); |
||||
</script> |
||||
</body> |
||||
</html> |
File diff suppressed because one or more lines are too long
Loading…
Reference in new issue