diff --git a/JSD-6952-配置使用文档.docx b/JSD-6952-配置使用文档.docx
new file mode 100644
index 0000000..2e3f778
Binary files /dev/null and b/JSD-6952-配置使用文档.docx differ
diff --git a/README.md b/README.md
index 379c149..655253d 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,6 @@
# open-JSD-6952
-JSD-6952 开源任务代码
\ No newline at end of file
+JSD-6952 开源任务代码\
+免责说明:该源码为第三方爱好者提供,不保证源码和方案的可靠性,也不提供任何形式的源码教学指导和协助!\
+仅作为开发者学习参考使用!禁止用于任何商业用途!\
+为保护开发者隐私,开发者信息已隐去!若原开发者希望公开自己的信息,可联系hugh处理。
\ No newline at end of file
diff --git a/jsd-6952-需求确认书v1.docx b/jsd-6952-需求确认书v1.docx
new file mode 100644
index 0000000..39b6fd4
Binary files /dev/null and b/jsd-6952-需求确认书v1.docx differ
diff --git a/lib/finekit-10.0.jar b/lib/finekit-10.0.jar
new file mode 100644
index 0000000..f4482fc
Binary files /dev/null and b/lib/finekit-10.0.jar differ
diff --git a/plugin.xml b/plugin.xml
new file mode 100644
index 0000000..86a32fc
--- /dev/null
+++ b/plugin.xml
@@ -0,0 +1,28 @@
+
+
+ com.fr.plugin.mqh.dingtalksyn.job
+
+ yes
+ 1.0
+ 10.0
+ 2018-07-31
+ mqh
+
+
+ 1.用户信息全量同步
+ 2.用户单点登陆功能
+ ]]>
+ com.fr.plugin.mqh.dingtalksyn
+
+ com.fanruan.api
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/mqh/dingtalksyn/bean/DataResponse.java b/src/main/java/com/fr/plugin/mqh/dingtalksyn/bean/DataResponse.java
new file mode 100644
index 0000000..6306735
--- /dev/null
+++ b/src/main/java/com/fr/plugin/mqh/dingtalksyn/bean/DataResponse.java
@@ -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;
+
+ /**
+ *
+ *
+ *
+ * @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 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;
+ }
+ }
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/mqh/dingtalksyn/bean/DingTalkJobConstructor.java b/src/main/java/com/fr/plugin/mqh/dingtalksyn/bean/DingTalkJobConstructor.java
new file mode 100644
index 0000000..bbcd3b9
--- /dev/null
+++ b/src/main/java/com/fr/plugin/mqh/dingtalksyn/bean/DingTalkJobConstructor.java
@@ -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;
+
+ /**
+ *
+ *
+ *
+ * @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;
+ }
+ }
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/mqh/dingtalksyn/config/DingSynConfig.java b/src/main/java/com/fr/plugin/mqh/dingtalksyn/config/DingSynConfig.java
new file mode 100644
index 0000000..565c752
--- /dev/null
+++ b/src/main/java/com/fr/plugin/mqh/dingtalksyn/config/DingSynConfig.java
@@ -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;
+
+ /**
+ *
+ *
+ *
+ * @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 cronCondition = Holders.simple(CRON_CONDITION);
+ @Identifier(value = "appKey", name = "Plugin-dingtalksyn_Config_appKey", description = "Plugin-dingtalksyn_Config_appKey_Description", status = Status.SHOW)
+ private Conf appKey = Holders.simple(StringKit.EMPTY);
+ @Identifier(value = "appSecret", name = "Plugin-dingtalksyn_Config_appSecret", description = "Plugin-dingtalksyn_Config_appSecret_Description", status = Status.SHOW)
+ private Conf appSecret = Holders.simple(StringKit.EMPTY);
+ @Identifier(value = "corpId", name = "Plugin-dingtalksyn_Config_corpId", description = "Plugin-dingtalksyn_Config_corpId_Description", status = Status.SHOW)
+ private Conf corpId = Holders.simple(StringKit.EMPTY);
+ @Identifier(value = "rootDepId", name = "Plugin-dingtalksyn_Config_rootDepId", description = "Plugin-dingtalksyn_Config_rootDepId_Description", status = Status.SHOW)
+ private Conf 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 token = Holders.simple(StringKit.EMPTY);
+// @Identifier(value = "aesKey", name = "Plugin-dingtalksyn_Config_AesKey", description = "Plugin-dingtalksyn_Config_AesKey_Description", status = Status.SHOW)
+// private Conf 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);
+// }
+ }
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/mqh/dingtalksyn/helper/DingTalkScheduleHelper.java b/src/main/java/com/fr/plugin/mqh/dingtalksyn/helper/DingTalkScheduleHelper.java
new file mode 100644
index 0000000..ad14e30
--- /dev/null
+++ b/src/main/java/com/fr/plugin/mqh/dingtalksyn/helper/DingTalkScheduleHelper.java
@@ -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;
+
+ /**
+ *
+ *
+ *
+ * @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() {
+ }
+ }
+ }
diff --git a/src/main/java/com/fr/plugin/mqh/dingtalksyn/job/DingTalkSyncMemberJob.java b/src/main/java/com/fr/plugin/mqh/dingtalksyn/job/DingTalkSyncMemberJob.java
new file mode 100644
index 0000000..385b97b
--- /dev/null
+++ b/src/main/java/com/fr/plugin/mqh/dingtalksyn/job/DingTalkSyncMemberJob.java
@@ -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;
+
+ /**
+ *
+ *
+ *
+ * @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);
+ }
+ }
+ }
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/mqh/dingtalksyn/kit/CustomRoleServiceKit.java b/src/main/java/com/fr/plugin/mqh/dingtalksyn/kit/CustomRoleServiceKit.java
new file mode 100644
index 0000000..782eca3
--- /dev/null
+++ b/src/main/java/com/fr/plugin/mqh/dingtalksyn/kit/CustomRoleServiceKit.java
@@ -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;
+
+ /**
+ *
+ *
+ *
+ * @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);
+ }
+ }
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/mqh/dingtalksyn/kit/DepartmentServiceKit.java b/src/main/java/com/fr/plugin/mqh/dingtalksyn/kit/DepartmentServiceKit.java
new file mode 100644
index 0000000..d401c33
--- /dev/null
+++ b/src/main/java/com/fr/plugin/mqh/dingtalksyn/kit/DepartmentServiceKit.java
@@ -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;
+
+ /**
+ *
+ *
+ *
+ * @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 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));
+ }
+ }
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/mqh/dingtalksyn/kit/UserServiceKit.java b/src/main/java/com/fr/plugin/mqh/dingtalksyn/kit/UserServiceKit.java
new file mode 100644
index 0000000..7e9f096
--- /dev/null
+++ b/src/main/java/com/fr/plugin/mqh/dingtalksyn/kit/UserServiceKit.java
@@ -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;
+
+ /**
+ *
+ *
+ *
+ * @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 departmentPostIds = createDepartmentPostIds(userGetResponse.getDeptIdList(), userGetResponse.getTitle());
+ userBean.setDepartmentPostIds(departmentPostIds);
+ }
+ return userBean;
+ }
+
+ /**
+ * 钉钉部门list转为部门职务组合list
+ *
+ * @param deptIdList
+ * @param title
+ * @return
+ * @throws Exception
+ */
+ private List createDepartmentPostIds(List deptIdList, String title) throws Exception {
+ List 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 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 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 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 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;
+ }
+ }
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/mqh/dingtalksyn/provider/LifeCycleMonitorImpl.java b/src/main/java/com/fr/plugin/mqh/dingtalksyn/provider/LifeCycleMonitorImpl.java
new file mode 100644
index 0000000..3b318aa
--- /dev/null
+++ b/src/main/java/com/fr/plugin/mqh/dingtalksyn/provider/LifeCycleMonitorImpl.java
@@ -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;
+
+ /**
+ *
+ *
+ *
+ * @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);
+ }
+ }
+ }
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/mqh/dingtalksyn/provider/LocaleFinder.java b/src/main/java/com/fr/plugin/mqh/dingtalksyn/provider/LocaleFinder.java
new file mode 100644
index 0000000..f7a9816
--- /dev/null
+++ b/src/main/java/com/fr/plugin/mqh/dingtalksyn/provider/LocaleFinder.java
@@ -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;
+
+ /**
+ *
+ *
+ *
+ * @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;
+ }
+ }
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/mqh/dingtalksyn/request/GlobalRequestFilterBridge.java b/src/main/java/com/fr/plugin/mqh/dingtalksyn/request/GlobalRequestFilterBridge.java
new file mode 100644
index 0000000..839dc8f
--- /dev/null
+++ b/src/main/java/com/fr/plugin/mqh/dingtalksyn/request/GlobalRequestFilterBridge.java
@@ -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;
+
+ /**
+ *
+ *
+ *
+ * @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 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 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 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");
+ }
+ }
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/mqh/dingtalksyn/user/DingTalkUserManager.java b/src/main/java/com/fr/plugin/mqh/dingtalksyn/user/DingTalkUserManager.java
new file mode 100644
index 0000000..b0d7cef
--- /dev/null
+++ b/src/main/java/com/fr/plugin/mqh/dingtalksyn/user/DingTalkUserManager.java
@@ -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.*;
+
+ /**
+ *
+ *
+ *
+ * @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 oapiRoleGroupList = getOapiRoleList();
+ for (OapiRoleListResponse.OpenRoleGroup openRoleGroup : oapiRoleGroupList) {
+ List 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 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 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);
+ }
+ }
+ }
+ }
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/mqh/dingtalksyn/utils/DingAPI.java b/src/main/java/com/fr/plugin/mqh/dingtalksyn/utils/DingAPI.java
new file mode 100644
index 0000000..b3051b2
--- /dev/null
+++ b/src/main/java/com/fr/plugin/mqh/dingtalksyn/utils/DingAPI.java
@@ -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;
+
+ /**
+ *
+ *
+ *
+ * @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 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 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 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();
+ }
+ }
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/mqh/dingtalksyn/utils/DingTokenUtils.java b/src/main/java/com/fr/plugin/mqh/dingtalksyn/utils/DingTokenUtils.java
new file mode 100644
index 0000000..e4b165d
--- /dev/null
+++ b/src/main/java/com/fr/plugin/mqh/dingtalksyn/utils/DingTokenUtils.java
@@ -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;
+
+ /**
+ *
+ *
+ *
+ * @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);
+ }
+ }
+ }
\ No newline at end of file
diff --git a/src/main/resources/com/fr/plugin/mqh/dingtalksyn/locale/lang.properties b/src/main/resources/com/fr/plugin/mqh/dingtalksyn/locale/lang.properties
new file mode 100644
index 0000000..4b80ff4
--- /dev/null
+++ b/src/main/resources/com/fr/plugin/mqh/dingtalksyn/locale/lang.properties
@@ -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!
\ No newline at end of file
diff --git a/src/main/resources/com/fr/plugin/mqh/dingtalksyn/locale/lang_zh_CN.properties b/src/main/resources/com/fr/plugin/mqh/dingtalksyn/locale/lang_zh_CN.properties
new file mode 100644
index 0000000..b91492b
--- /dev/null
+++ b/src/main/resources/com/fr/plugin/mqh/dingtalksyn/locale/lang_zh_CN.properties
@@ -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!
\ No newline at end of file
diff --git a/src/main/resources/com/fr/plugin/mqh/dingtalksyn/web/codePage.html b/src/main/resources/com/fr/plugin/mqh/dingtalksyn/web/codePage.html
new file mode 100644
index 0000000..064ed88
--- /dev/null
+++ b/src/main/resources/com/fr/plugin/mqh/dingtalksyn/web/codePage.html
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+ DingTalk
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/com/fr/plugin/mqh/dingtalksyn/web/dingtalk.open.js b/src/main/resources/com/fr/plugin/mqh/dingtalksyn/web/dingtalk.open.js
new file mode 100644
index 0000000..3d1ed4d
--- /dev/null
+++ b/src/main/resources/com/fr/plugin/mqh/dingtalksyn/web/dingtalk.open.js
@@ -0,0 +1 @@
+(function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.dd=t():e.dd=t()})(this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};return t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=707)}([function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(2),o=n(180),i=n(2);t.ENV_ENUM=i.ENV_ENUM,t.ENV_ENUM_SUB=i.ENV_ENUM_SUB;var a=n(3);n(186),t.ddSdk=new a.Sdk(r.getENV(),o.log)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.addWatchParamsDeal=function(e){var t=Object.assign({},e);return t.watch=!0,t},t.addDefaultCorpIdParamsDeal=function(e){var t=Object.assign({},e);return t.corpId="corpId",t},t.genDefaultParamsDealFn=function(e){var t=Object.assign({},e);return function(e){return Object.assign({},t,e)}},t.forceChangeParamsDealFn=function(e){var t=Object.assign({},e);return function(e){return Object.assign(e,t)}},t.genBoolResultDealFn=function(e){return function(t){var n=Object.assign({},t);return e.forEach(function(e){void 0!==n[e]&&(n[e]=!!n[e])}),n}},t.genBizStoreParamsDealFn=function(e){var t=Object.assign({},e);return"string"!=typeof t.params?(t.params=JSON.stringify(t),t):t}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(3),o=n(3);t.ENV_ENUM=o.ENV_ENUM,t.APP_TYPE=o.APP_TYPE,t.ENV_ENUM_SUB=o.ENV_ENUM_SUB;var i,a=n(183);(function(e){e.singlePage="singlePage",e.miniApp="miniApp",e.miniWidget="miniWidget"})(i||(i={})),t.getUA=function(){var e="";try{"undefined"!=typeof navigator&&(e=navigator&&(navigator.userAgent||navigator.swuserAgent)||"")}catch(t){e=""}return e},t.getENV=function(){var e=t.getUA(),n=/iPhone|iPad|iPod|iOS/i.test(e),o=/Android/i.test(e),s=/Nebula/i.test(e),d=/DingTalk/i.test(e),u=/dd-web/i.test(e),c="object"==typeof nuva,l="object"==typeof dd&&"function"==typeof dd.dtBridge,f=l&&n||c&&n,v=d||a.default.isDingTalk,p=n&&v||a.default.isWeexiOS||f,_=o&&v||a.default.isWeexAndroid,E=s&&v||l,N=u,P=r.APP_TYPE.WEB;if(N)P=r.APP_TYPE.WEBVIEW_IN_MINIAPP;else if(E)P=r.APP_TYPE.MINI_APP;else if(a.default.isWeexiOS||a.default.isWeexAndroid)try{var M=weex.config.ddWeexEnv;P=M===i.miniWidget?r.APP_TYPE.WEEX_WIDGET:r.APP_TYPE.WEEX}catch(e){P=r.APP_TYPE.WEEX}var h,m="*",k=e.match(/AliApp\(\w+\/([a-zA-Z0-9.-]+)\)/);null===k&&(k=e.match(/DingTalk\/([a-zA-Z0-9.-]+)/));var b;k&&k[1]&&(b=k[1]);var g="";if("undefined"!=typeof name&&(g=name),g)try{var I=JSON.parse(g);I.hostVersion&&(b=I.hostVersion),m=I.language||navigator.language||"*",h=I.containerId}catch(e){}var y=!!h;y&&!b&&(k=e.match(/DingTalk\(([a-zA-Z0-9\.-]+)\)/))&&k[1]&&(b=k[1]);var A,S=r.ENV_ENUM_SUB.noSub;if(p)A=r.ENV_ENUM.ios;else if(_)A=r.ENV_ENUM.android;else if(y){var V=e.indexOf("Macintosh; Intel Mac OS")>-1;S=V?r.ENV_ENUM_SUB.mac:r.ENV_ENUM_SUB.win,A=r.ENV_ENUM.pc}else A=r.ENV_ENUM.notInDingTalk;return{platform:A,platformSub:S,version:b,appType:P,language:m}}},function(e,t,n){"use strict";function r(e,t){var n=e&&e.vs;return"object"==typeof n&&(n=n[t.platformSub]),n}Object.defineProperty(t,"__esModule",{value:!0});var o=n(189);t.APP_TYPE=o.APP_TYPE,t.LogLevel=o.LogLevel,t.isFunction=o.isFunction,t.compareVersion=o.compareVersion,t.ENV_ENUM=o.ENV_ENUM,t.ENV_ENUM_SUB=o.ENV_ENUM_SUB;var i=function(){function e(e,t){var n=this;this.configJsApiList=[],this.hadConfig=!1,this.p={},this.config$=new Promise(function(e,t){n.p.reject=t,n.p.resolve=e}),this.logQueue=[],this.devConfig={debug:!1},this.platformConfigMap={},this.invokeAPIConfigMapByMethod={},this.isBridgeDrity=!0,this.getExportSdk=function(){return n.exportSdk},this.setAPI=function(e,t){n.invokeAPIConfigMapByMethod[e]=t},this.setPlatform=function(e){n.isBridgeDrity=!0,n.platformConfigMap[e.platform]=e,e.platform===n.env.platform&&e.bridgeInit().catch(function(e){n.customLog(o.LogLevel.WARNING,["auto bridgeInit error",e||""])})},this.getPlatformConfigMap=function(){return n.platformConfigMap},this.deleteApiConfig=function(e,t){var r=n.invokeAPIConfigMapByMethod[e];r&&delete r[t]},this.invokeAPI=function(e,t,i){void 0===t&&(t={}),void 0===i&&(i=!0),n.customLog(o.LogLevel.INFO,['==> "'+e+'" params: ',t]);var a=+new Date,s=a+"_"+Math.floor(1e3*Math.random());if(n.devConfig.onBeforeInvokeAPI)try{n.devConfig.onBeforeInvokeAPI({invokeId:s,method:e,params:t,startTime:a})}catch(e){n.customLog(o.LogLevel.ERROR,["call Hook:onBeforeInvokeAPI failed, reason:",e])}return!1===n.devConfig.isAuthApi&&(i=!1),n.bridgeInitFn().then(function(d){var u=n.invokeAPIConfigMapByMethod[e],c=n.devConfig.forceEnableDealApiFnMap&&n.devConfig.forceEnableDealApiFnMap[e]&&!0===n.devConfig.forceEnableDealApiFnMap[e](t),l=!c&&(!0===n.devConfig.isDisableDeal||n.devConfig.disbaleDealApiWhiteList&&-1!==n.devConfig.disbaleDealApiWhiteList.indexOf(e));if(u||!i){var f;if(u&&(f=u[n.env.platform]),f||!i){var v={};v=!l&&f&&f.paramsDeal&&o.isFunction(f.paramsDeal)?f.paramsDeal(t):Object.assign({},t);var p=function(e){return!l&&f&&f.resultDeal&&o.isFunction(f.resultDeal)?f.resultDeal(e):e};if(o.isFunction(v.onSuccess)){var _=v.onSuccess;v.onSuccess=function(e){_(p(e))}}return d(e,v).then(p,function(t){var a=n.hadConfig&&void 0===n.isReady&&-1!==n.configJsApiList.indexOf(e),s="object"==typeof t&&"string"==typeof t.errorCode&&t.errorCode===o.ERROR_CODE.no_permission,u="object"==typeof t&&"string"==typeof t.errorCode&&t.errorCode===o.ERROR_CODE.cancel,c=r(f,n.env),l=c&&n.env.version&&o.compareVersion(n.env.version,c),_=(n.env.platform===o.ENV_ENUM.ios||n.env.platform===o.ENV_ENUM.android)&&a&&s,E=n.env.platform===o.ENV_ENUM.pc&&a&&(l&&!u&&i||s);return _||E?n.config$.then(function(){return d(e,v).then(p)}):Promise.reject(t)}).then(function(r){if(n.devConfig.onAfterInvokeAPI)try{n.devConfig.onAfterInvokeAPI({invokeId:s,method:e,params:t,payload:r,isSuccess:!0,startTime:a,duration:+new Date-a})}catch(e){n.customLog(o.LogLevel.ERROR,["call Hook:onAfterInvokeAPI failed, reason:",e])}return n.customLog(o.LogLevel.INFO,['<== "'+e+'" success result: ',r]),r},function(r){if(n.devConfig.onAfterInvokeAPI)try{n.devConfig.onAfterInvokeAPI({invokeId:s,method:e,params:t,payload:r,startTime:a,duration:+new Date-a,isSuccess:!1})}catch(r){n.customLog(o.LogLevel.ERROR,["call Hook:onAfterInvokeAPI failed, reason:",r])}return n.customLog(o.LogLevel.WARNING,['<== "'+e+'" fail result: ',r]),Promise.reject(r)})}var E='"'+e+'" do not support the current platform ('+n.env.platform+")";return n.customLog(o.LogLevel.ERROR,[E]),Promise.reject({errorCode:o.ERROR_CODE.jsapi_internal_error,errorMessage:E})}var E="This API method is not configured for the platform ("+n.env.platform+")";return n.customLog(o.LogLevel.ERROR,[E]),Promise.reject({errorCode:o.ERROR_CODE.jsapi_internal_error,errorMessage:E})})},this.customLog=function(e,t){var r={level:e,text:t,time:new Date};if(!0===n.devConfig.debug)n.customLogInstance(r);else{n.logQueue.push(r);n.logQueue.length>10&&(n.logQueue=n.logQueue.slice(n.logQueue.length-10))}},this.clearLogQueue=function(){n.logQueue.forEach(function(e){n.customLogInstance(e)}),n.logQueue=[]},this.customLogInstance=t,this.env=e,this.bridgeInitFn=function(){if(n.bridgeInitFnPromise&&!n.isBridgeDrity)return n.bridgeInitFnPromise;n.isBridgeDrity=!1;var t=n.platformConfigMap[e.platform];if(t)n.bridgeInitFnPromise=t.bridgeInit().catch(function(e){return n.customLog(o.LogLevel.ERROR,["\b\b\b\b\bJsBridge initialization fails, jsapi will not work"]),Promise.reject(e)});else{var r="Do not support the current environment:"+e.platform;n.customLog(o.LogLevel.WARNING,[r]),n.bridgeInitFnPromise=Promise.reject(new Error(r))}return n.bridgeInitFnPromise};var i=function(e){void 0===e&&(e={}),n.devConfig=Object.assign(n.devConfig,e),!0===e.debug&&n.clearLogQueue(),e.extraPlatform&&n.setPlatform(e.extraPlatform)};this.exportSdk={config:function(t){void 0===t&&(t={});var r=!0;Object.keys(t).forEach(function(e){-1===["debug","usePromise"].indexOf(e)&&(r=!1)}),r?(n.customLog(o.LogLevel.WARNING,["This is a deprecated feature, recommend use dd.devConfig"]),i(t)):n.hadConfig?n.customLog(o.LogLevel.WARNING,["Config has been executed"]):(t.jsApiList&&(n.configJsApiList=t.jsApiList),n.hadConfig=!0,n.bridgeInitFn().then(function(r){var o=n.platformConfigMap[e.platform],i=t;o.authParamsDeal&&(i=o.authParamsDeal(i)),r(o.authMethod,i).then(function(e){n.isReady=!0,n.p.resolve(e)}).catch(function(e){n.isReady=!1,n.p.reject(e)})},function(){n.customLog(o.LogLevel.ERROR,['\b\b\b\b\bJsBridge initialization failed and "dd.config" failed to call'])}))},devConfig:i,ready:function(e){!1===n.hadConfig?(n.customLog(o.LogLevel.WARNING,["You don 't use a dd.config, so you don't need to wrap dd.ready, recommend remove dd.ready"]),n.bridgeInitFn().then(function(){e()})):n.config$.then(function(t){e()})},error:function(e){n.config$.catch(function(t){e(t)})},on:function(t,r){n.bridgeInitFn().then(function(){n.platformConfigMap[e.platform].event.on(t,r)})},off:function(t,r){n.bridgeInitFn().then(function(){n.platformConfigMap[e.platform].event.off(t,r)})},env:e,checkJsApi:function(t){void 0===t&&(t={});var i={};return t.jsApiList&&t.jsApiList.forEach(function(t){var a=n.invokeAPIConfigMapByMethod[t];if(a){var s=a[e.platform],d=r(s,e);d&&e.version&&o.compareVersion(e.version,d)&&(i[t]=!0)}i[t]||(i[t]=!1)}),Promise.resolve(i)},_invoke:function(e,t){return void 0===t&&(t={}),n.invokeAPI(e,t,!1)}}}return e}();t.Sdk=i},function(e,t,n){(function(t,n){e.exports=n()})(0,function(){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};return t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=579)}({179:function(e,t,n){"use strict";var r=n(181);e.exports=r},181:function(e,t,n){"use strict";var r=n(183),o=n(184),i=n(182),a=n(185),s=new i,d=!1,u="",c=null,l={},f=/{.*}/;try{var v=window.name.match(f);if(v&&v[0])var l=JSON.parse(v[0])}catch(e){l={}}l.hostOrigin&&".dingtalk.com"===l.hostOrigin.split(":")[1].slice(0-".dingtalk.com".length)&&l.containerId&&(d=!0,u=l.hostOrigin,c=l.containerId);var p={},_=new Promise(function(e,t){p._resolve=e,p._reject=t}),E={},N=null;window.top!==window&&(N=window.top,p._resolve()),E[a.SYS_INIT]=function(e){N=e.frameWindow,p._resolve(),e.respond({})},window.addEventListener("message",function(e){var t=e.data,n=e.origin;if(n===u)if("response"===t.type&&t.msgId){var r=t.msgId,i=s.getMsyById(r);i&&i.receiveResponse(t.body,!t.success)}else if("event"===t.type&&t.msgId){var r=t.msgId,i=s.getMsyById(r);i&&i.receiveEvent(t.eventName,t.body)}else if("request"===t.type&&t.msgId){var i=new o(e.source,n,t);E[i.methodName]&&E[i.methodName](i)}}),t.invokeAPI=function(e,t){var n=new r(c,e,t);return d&&_.then(function(){N&&N.postMessage(n.getPayload(),u),s.addPending(n)}),n};var P=null;t.addEventListener=function(e,n){P||(P=t.invokeAPI(a.SYS_EVENT,{})),P.addEventListener(e,n)},t.removeEventListener=function(e,t){P&&P.removeEventListener(e,t)}},182:function(e,t,n){"use strict";var r=function(){this.pendingMsgs={}};r.prototype.addPending=function(e){this.pendingMsgs[e.id]=e;var t=function(){delete this.pendingMsgs[e.id],e.removeEventListener("_finish",t)}.bind(this);e.addEventListener("_finish",t)},r.prototype.getMsyById=function(e){return this.pendingMsgs[e]},e.exports=r},183:function(e,t,n){"use strict";var r=n(574),o=n(573),i=0,a=Math.floor(1e3*Math.random()),s=function(){return 1e3*(1e3*a+Math.floor(1e3*Math.random()))+ ++i%1e3},d={code:408,reason:"timeout"},u={TIMEOUT:"_timeout",FINISH:"_finish"},c={timeout:-1},l=function(e,t,n,r){this.id=s(),this.methodName=t,this.containerId=e,this.option=o({},c,r);var n=n||{};this._p={},this.result=new Promise(function(e,t){this._p._resolve=e,this._p._reject=t}.bind(this)),this.callbacks={},this.plainMsg=this._handleMsg(n),this._eventsHandle={},this._timeoutTimer=null,this._initTimeout(),this.isFinish=!1};l.prototype._initTimeout=function(){this._clearTimeout(),this.option.timeout>0&&(this._timeoutTimer=setTimeout(function(){this.receiveEvent(u.TIMEOUT),this.receiveResponse(d,!0)}.bind(this),this.option.timeout))},l.prototype._clearTimeout=function(){clearTimeout(this._timeoutTimer)},l.prototype._handleMsg=function(e){var t={};return Object.keys(e).forEach(function(n){var o=e[n];"function"==typeof o&&"on"===n.slice(0,2)?this.callbacks[n]=o:t[n]=r(o)}.bind(this)),t},l.prototype.getPayload=function(){return{msgId:this.id,containerId:this.containerId,methodName:this.methodName,body:this.plainMsg,type:"request"}},l.prototype.receiveEvent=function(e,t){if(this.isFinish&&e!==u.FINISH)return!1;e!==u.FINISH&&e!==u.TIMEOUT&&this._initTimeout(),Array.isArray(this._eventsHandle[e])&&this._eventsHandle[e].forEach(function(e){try{e(t)}catch(e){console.error(t)}});var n="on"+e.charAt(0).toUpperCase()+e.slice(1);return this.callbacks[n]&&this.callbacks[n](t),!0},l.prototype.addEventListener=function(e,t){if(!e||"function"!=typeof t)throw"eventName is null or handle is not a function, addEventListener fail";Array.isArray(this._eventsHandle[e])||(this._eventsHandle[e]=[]),this._eventsHandle[e].push(t)},l.prototype.removeEventListener=function(e,t){if(!e||!t)throw"eventName is null or handle is null, invoke removeEventListener fail";if(Array.isArray(this._eventsHandle[e])){var n=this._eventsHandle[e].indexOf(t);-1!==n&&this._eventsHandle[e].splice(n,1)}},l.prototype.receiveResponse=function(e,t){if(!0===this.isFinish)return!1;this._clearTimeout();var t=!!t;return t?this._p._reject(e):this._p._resolve(e),setTimeout(function(){this.receiveEvent(u.FINISH)}.bind(this),0),this.isFinish=!0,!0},e.exports=l},184:function(e,t,n){"use strict";var r=function(e,t,n){if(this._msgId=n.msgId,this.frameWindow=e,this.methodName=n.methodName,this.clientOrigin=t,this.containerId=n.containerId,this.params=n.body,!this._msgId)throw"msgId not exist";if(!this.frameWindow)throw"frameWindow not exist";if(!this.methodName)throw"methodName not exits";if(!this.clientOrigin)throw"clientOrigin not exist";this.hasResponded=!1};r.prototype.respond=function(e,t){var t=!!t;if(!0!==this.hasResponded){var n={type:"response",success:!t,body:e,msgId:this._msgId};this.frameWindow.postMessage(n,this.clientOrigin),this.hasResponded=!0}},r.prototype.emit=function(e,t){var n={type:"event",eventName:e,body:t,msgId:this._msgId};this.frameWindow.postMessage(n,this.clientOrigin)},e.exports=r},185:function(e,t,n){"use strict";e.exports={SYS_EVENT:"SYS_openAPIContainerInitEvent",SYS_INIT:"SYS_openAPIContainerInit"}},4:function(e,t){var n;n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(e){"object"==typeof window&&(n=window)}e.exports=n},572:function(e,t,n){(function(e,n){function r(e,t){return e.set(t[0],t[1]),e}function o(e,t){return e.add(t),e}function i(e,t){for(var n=-1,r=e.length;++n-1}function g(e,t){var n=this.__data__,r=W(n,e);return r<0?n.push([e,t]):n[r][1]=t,this}function I(e){var t=-1,n=e?e.length:0;for(this.clear();++t-1&&e%1==0&&e-1&&e%1==0&&e<=Ve}function ke(e){var t=typeof e;return!!e&&("object"==t||"function"==t)}function be(e){return!!e&&"object"==typeof e}function ge(e){return!!ke(e)&&(he(e)||c(e)?Pt:tt).test(_e(e))}function Ie(e){return"string"==typeof e||!Lt(e)&&be(e)&&Nt.call(e)==xe}function ye(e){var t=pe(e);if(!t&&!Pe(e))return z(e);var n=le(e),r=!!n,o=n||[],i=o.length;for(var a in e)!B(e,a)||r&&("length"==a||fe(a,i))||t&&"constructor"==a||o.push(a);return o}var Ae=200,Se="__lodash_hash_undefined__",Ve=9007199254740991,Ue="[object Arguments]",Oe="[object Boolean]",je="[object Date]",$e="[object Function]",we="[object GeneratorFunction]",De="[object Map]",Ce="[object Number]",Te="[object Object]",We="[object RegExp]",Re="[object Set]",xe="[object String]",Fe="[object Symbol]",Le="[object ArrayBuffer]",Be="[object DataView]",ze="[object Float32Array]",qe="[object Float64Array]",Ye="[object Int8Array]",Je="[object Int16Array]",Ge="[object Int32Array]",He="[object Uint8Array]",Xe="[object Uint8ClampedArray]",Ke="[object Uint16Array]",Qe="[object Uint32Array]",Ze=/[\\^$.*+?()[\]{}|]/g,et=/\w*$/,tt=/^\[object .+?Constructor\]$/,nt=/^(?:0|[1-9]\d*)$/,rt={};rt[Ue]=rt["[object Array]"]=rt[Le]=rt[Be]=rt[Oe]=rt[je]=rt[ze]=rt[qe]=rt[Ye]=rt[Je]=rt[Ge]=rt[De]=rt[Ce]=rt[Te]=rt[We]=rt[Re]=rt[xe]=rt[Fe]=rt[He]=rt[Xe]=rt[Ke]=rt[Qe]=!0,rt["[object Error]"]=rt[$e]=rt["[object WeakMap]"]=!1;var ot={function:!0,object:!0},it=ot[typeof t]&&t&&!t.nodeType?t:void 0,at=ot[typeof e]&&e&&!e.nodeType?e:void 0,st=at&&at.exports===it?it:void 0,dt=u(it&&at&&"object"==typeof n&&n),ut=u(ot[typeof self]&&self),ct=u(ot[typeof window]&&window),lt=u(ot[typeof this]&&this),ft=dt||ct!==(lt&<.window)&&ct||ut||lt||Function("return this")(),vt=Array.prototype,pt=Object.prototype,_t=Function.prototype.toString,Et=pt.hasOwnProperty,Nt=pt.toString,Pt=RegExp("^"+_t.call(Et).replace(Ze,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),Mt=st?ft.Buffer:void 0,ht=ft.Symbol,mt=ft.Uint8Array,kt=Object.getOwnPropertySymbols,bt=Object.create,gt=pt.propertyIsEnumerable,It=vt.splice,yt=Object.getPrototypeOf,At=Object.keys,St=oe(ft,"DataView"),Vt=oe(ft,"Map"),Ut=oe(ft,"Promise"),Ot=oe(ft,"Set"),jt=oe(ft,"WeakMap"),$t=oe(Object,"create"),wt=_e(St),Dt=_e(Vt),Ct=_e(Ut),Tt=_e(Ot),Wt=_e(jt),Rt=ht?ht.prototype:void 0,xt=Rt?Rt.valueOf:void 0;v.prototype.clear=p,v.prototype.delete=_,v.prototype.get=E,v.prototype.has=N,v.prototype.set=P,M.prototype.clear=h,M.prototype.delete=m,M.prototype.get=k,M.prototype.has=b,M.prototype.set=g,I.prototype.clear=y,I.prototype.delete=A,I.prototype.get=S,I.prototype.has=V,I.prototype.set=U,O.prototype.clear=j,O.prototype.delete=$,O.prototype.get=w,O.prototype.has=D,O.prototype.set=C;var Ft=function(e){return function(e){return null==e?void 0:e.length}}();kt||(ae=function(){return[]}),(St&&se(new St(new ArrayBuffer(1)))!=Be||Vt&&se(new Vt)!=De||Ut&&"[object Promise]"!=se(Ut.resolve())||Ot&&se(new Ot)!=Re||jt&&"[object WeakMap]"!=se(new jt))&&(se=function(e){var t=Nt.call(e),n=t==Te?e.constructor:void 0,r=n?_e(n):void 0;if(r)switch(r){case wt:return Be;case Dt:return De;case Ct:return"[object Promise]";case Tt:return Re;case Wt:return"[object WeakMap]"}return t});var Lt=Array.isArray,Bt=Mt?function(e){return e instanceof Mt}:function(e){return function(){return!1}}();e.exports=x}).call(t,n(577)(e),n(4))},573:function(e,t,n){function r(e,t,n){var r=e[t];h.call(e,t)&&d(r,n)&&(void 0!==n||t in e)||(e[t]=n)}function o(e,t,n,o){n||(n={});for(var i=-1,a=t.length;++i-1&&e%1==0&&e-1&&e%1==0&&e<=_}function f(e){var t=typeof e;return!!e&&("object"==t||"function"==t)}var v=n(575),p=n(576),_=9007199254740991,E="[object Function]",N="[object GeneratorFunction]",P=/^(?:0|[1-9]\d*)$/,M=Object.prototype,h=M.hasOwnProperty,m=M.toString,k=M.propertyIsEnumerable,b=!k.call({valueOf:1},"valueOf"),g=function(e){return function(e){return null==e?void 0:e.length}}(),I=function(e){return p(function(t,n){var r=-1,o=n.length,i=o>1?n[o-1]:void 0,s=o>2?n[2]:void 0;for(i=e.length>3&&"function"==typeof i?(o--,i):void 0,s&&a(n[0],n[1],s)&&(i=o<3?void 0:i,o=1),t=Object(t);++r-1&&e%1==0&&e-1&&e%1==0&&e<=_}function f(e){var t=typeof e;return!!e&&("object"==t||"function"==t)}function v(e){return!!e&&"object"==typeof e}function p(e){return d(e)?r(e):o(e)}var _=9007199254740991,E="[object Arguments]",N="[object Function]",P="[object GeneratorFunction]",M=/^(?:0|[1-9]\d*)$/,h=Object.prototype,m=h.hasOwnProperty,k=h.toString,b=h.propertyIsEnumerable,g=function(e,t){return function(n){return e(t(n))}}(Object.keys,Object),I=Array.isArray;e.exports=p},576:function(e,t){function n(e,t,n){switch(n.length){case 0:return e.call(t);case 1:return e.call(t,n[0]);case 2:return e.call(t,n[0],n[1]);case 3:return e.call(t,n[0],n[1],n[2])}return e.apply(t,n)}function r(e,t){return t=b(void 0===t?e.length-1:t,0),function(){for(var r=arguments,o=-1,i=b(r.length-t,0),a=Array(i);++o0;)o=a.shift(),i=s.shift();return n?(i||0)>=(o||0):(i||0)>(o||0)},t.language=o.language,t.version=o.version},function(e,t,n){"use strict";function r(e,t,n){var r="Web"===n.platform,i="iOS"===n.platform,a="android"===n.platform,s=a||i,d=function(){return r?window.navigator.userAgent.toLowerCase():""}(),u=function(){var e={};if(r){var t=window.name;try{var n=JSON.parse(t);e.containerId=n.containerId,e.version=n.hostVersion,e.language=n.language||"*"}catch(e){}}return e}(),c=function(){return s?"DingTalk"===n.appName||"com.alibaba.android.rimet"===n.appName:d.indexOf("dingtalk")>-1||!!u.containerId}(),l=function(){if(r){if(u.version)return u.version;var e=d.match(/aliapp\(\w+\/([a-zA-Z0-9.-]+)\)/);null===e&&(e=d.match(/dingtalk\/([a-zA-Z0-9.-]+)/));return e&&e[1]||"Unknown"}return n.appVersion}(),f=!!u.containerId,v=/iphone|ipod|ios/.test(d),p=/ipad/.test(d),_=d.indexOf("android")>-1,E=d.indexOf("mac")>-1&&f,N=d.indexOf("win")>-1&&f,P=!E&&!N&&f,M=f,h="";return h=c?v||i?o.PLATFORM.IOS:_||a?o.PLATFORM.ANDROID:p?o.PLATFORM.IPAD:E?o.PLATFORM.MAC:N?o.PLATFORM.WINDOWS:P?o.PLATFORM.BROWSER:o.PLATFORM.UNKNOWN:o.PLATFORM.UNKNOWN,{isDingTalk:c,isWebiOS:v,isWebAndroid:_,isWeexiOS:i,isWeexAndroid:a,isDingTalkPCMac:E,isDingTalkPCWeb:P,isDingTalkPCWindows:N,isDingTalkPC:M,runtime:e,framework:t,platform:h,version:l,isWeex:s}}Object.defineProperty(t,"__esModule",{value:!0});var o=n(11);t.default=r},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(184),o=n(182),i=n(11),a=r.default().split("."),s=a[0],d=a[1],u=function(){var e={};switch(d){case i.FRAMEWORK.VUE:var t=weex.config,n=t.env;e.platform=n.platform,i.RUNTIME.WEEX===s&&(e.appVersion=n.appVersion,e.appName=n.appName);break;case i.FRAMEWORK.RAX:i.RUNTIME.WEEX===s&&(e.platform=navigator.platform,e.appName=navigator.appName,e.appVersion=navigator.appVersion);break;case i.FRAMEWORK.UNKNOWN:i.RUNTIME.WEB===s&&(e.platform=i.RUNTIME.WEB),i.RUNTIME.UNKNOWN===s&&(e.platform=i.RUNTIME.UNKNOWN)}return e}(),c=o.default(s,d,u);t.default=c},function(e,t,n){"use strict";function r(e,t){for(var n=e.length,r=0,o=!0;ro[i])return!0}return!0}Object.defineProperty(t,"__esModule",{value:!0}),t.isFunction=r,t.compareVersion=o;(function(e){e.cancel="-1",e.not_exist="1",e.no_permission="7",e.jsapi_internal_error="22"})(t.ERROR_CODE||(t.ERROR_CODE={}));(function(e){e.pc="pc",e.android="android",e.ios="ios",e.notInDingTalk="notInDingTalk"})(t.ENV_ENUM||(t.ENV_ENUM={}));(function(e){e.mac="mac",e.win="win",e.noSub="noSub"})(t.ENV_ENUM_SUB||(t.ENV_ENUM_SUB={}));(function(e){e.WEB="WEB",e.MINI_APP="MINI_APP",e.WEEX="WEEX",e.WEBVIEW_IN_MINIAPP="WEBVIEW_IN_MINIAPP",e.WEEX_WIDGET="WEEX_WIDGET"})(t.APP_TYPE||(t.APP_TYPE={}));(function(e){e[e.INFO=1]="INFO",e[e.WARNING=2]="WARNING",e[e.ERROR=3]="ERROR"})(t.LogLevel||(t.LogLevel={}))},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(0),o=n(2),i=n(3),a=n(5),s=n(9),d=n(176),u=n(6),c=n(8),l=n(10);r.ddSdk.setPlatform({platform:o.ENV_ENUM.android,bridgeInit:function(){var e=o.getENV();return e.appType===i.APP_TYPE.MINI_APP?Promise.resolve(a.default):e.appType===i.APP_TYPE.WEBVIEW_IN_MINIAPP?Promise.resolve(s.default):e.appType===i.APP_TYPE.WEEX?u.androidWeexBridge():d.h5AndroidbridgeInit().then(function(){return d.default})},authMethod:"runtime.permission.requestJsApis",event:{on:function(e,t){var n=o.getENV();switch(n.appType){case i.APP_TYPE.WEB:case i.APP_TYPE.WEBVIEW_IN_MINIAPP:c.on(e,t);break;case i.APP_TYPE.WEEX:l.on(e,t);break;default:throw new Error("Not support global event in the platfrom: "+n.appType)}},off:function(e,t){var n=o.getENV();switch(n.appType){case i.APP_TYPE.WEB:case i.APP_TYPE.WEBVIEW_IN_MINIAPP:c.off(e,t);break;case i.APP_TYPE.WEEX:l.off(e,t);break;default:throw new Error("Not support global event in the platfrom: "+n.appType)}}}})},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),n(193),n(190),n(192)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(0),o=n(2),i=n(3),a=n(5),s=n(9),d=n(177),u=n(6),c=n(8),l=n(10);r.ddSdk.setPlatform({platform:o.ENV_ENUM.ios,bridgeInit:function(){var e=o.getENV();return e.appType===i.APP_TYPE.MINI_APP?Promise.resolve(a.default):e.appType===i.APP_TYPE.WEBVIEW_IN_MINIAPP?Promise.resolve(s.default):e.appType===i.APP_TYPE.WEEX?u.iosWeexBridge():d.h5IosBridgeInit().then(function(){return d.default})},authMethod:"runtime.permission.requestJsApis",event:{on:function(e,t){var n=o.getENV();switch(n.appType){case i.APP_TYPE.WEB:case i.APP_TYPE.WEBVIEW_IN_MINIAPP:c.on(e,t);break;case i.APP_TYPE.WEEX:l.on(e,t);break;default:throw new Error("Not support global event in the platfrom: "+n.appType)}},off:function(e,t){var n=o.getENV();switch(n.appType){case i.APP_TYPE.WEB:case i.APP_TYPE.WEBVIEW_IN_MINIAPP:c.off(e,t);break;case i.APP_TYPE.WEEX:l.off(e,t);break;default:throw new Error("Not support global event in the platfrom: "+n.appType)}}}})},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(0),o=n(2),i=n(178),a=n(5),s=n(3),d=n(179);r.ddSdk.setPlatform({platform:o.ENV_ENUM.pc,bridgeInit:function(){switch(o.getENV().appType){case s.APP_TYPE.MINI_APP:return Promise.resolve(a.default);default:return i.h5PcBridgeInit().then(function(){return i.default})}},authMethod:"config",authParamsDeal:function(e){var t=Object.assign({},e);return t.url=window.location.href.split("#")[0],t},event:{on:function(e,t){if(o.getENV().appType===s.APP_TYPE.WEB)return d.on(e,t)},off:function(e,t){if(o.getENV().appType===s.APP_TYPE.WEB)return d.off(e,t)}}})},function(e,t){function n(){throw new Error("setTimeout has not been defined")}function r(){throw new Error("clearTimeout has not been defined")}function o(e){if(c===setTimeout)return setTimeout(e,0);if((c===n||!c)&&setTimeout)return c=setTimeout,setTimeout(e,0);try{return c(e,0)}catch(t){try{return c.call(null,e,0)}catch(t){return c.call(this,e,0)}}}function i(e){if(l===clearTimeout)return clearTimeout(e);if((l===r||!l)&&clearTimeout)return l=clearTimeout,clearTimeout(e);try{return l(e)}catch(t){try{return l.call(null,e)}catch(t){return l.call(this,e)}}}function a(){_&&v&&(_=!1,v.length?p=v.concat(p):E=-1,p.length&&s())}function s(){if(!_){var e=o(a);_=!0;for(var t=p.length;t;){for(v=p,p=[];++E1)for(var n=1;n=0&&(e._idleTimeoutId=setTimeout(function(){e._onTimeout&&e._onTimeout()},t))},n(196),t.setImmediate="undefined"!=typeof self&&self.setImmediate||void 0!==e&&e.setImmediate||this&&this.setImmediate,t.clearImmediate="undefined"!=typeof self&&self.clearImmediate||void 0!==e&&e.clearImmediate||this&&this.clearImmediate}).call(t,n(7))},,function(e,t,n){"use strict";var r=n(175),o=n(665),i=Object.assign(r,o.apiObj);e.exports=i},,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(12),o=n(13),i=n(14),a=n(15),s=n(16),d=n(17),u=n(18),c=n(19),l=n(20),f=n(21),v=n(22),p=n(23),_=n(24),E=n(25),N=n(26),P=n(27),M=n(28),h=n(29),m=n(30),k=n(31),b=n(32),g=n(33),I=n(34),y=n(35),A=n(36),S=n(37),V=n(38),U=n(39),O=n(40),j=n(41),$=n(42),w=n(43),D=n(44),C=n(45),T=n(46),W=n(47),R=n(48),x=n(49),F=n(50),L=n(51),B=n(52),z=n(53),q=n(54),Y=n(55),J=n(56),G=n(57),H=n(58),X=n(59),K=n(60),Q=n(61),Z=n(62),ee=n(63),te=n(64),ne=n(65),re=n(66),oe=n(67),ie=n(68),ae=n(69),se=n(70),de=n(71),ue=n(72),ce=n(73),le=n(74),fe=n(75),ve=n(76),pe=n(77),_e=n(78),Ee=n(79),Ne=n(80),Pe=n(81),Me=n(82),he=n(83),me=n(84),ke=n(85),be=n(86),ge=n(87),Ie=n(88),ye=n(89),Ae=n(90),Se=n(91),Ve=n(92),Ue=n(93),Oe=n(94),je=n(95),$e=n(96),we=n(97),De=n(98),Ce=n(99),Te=n(100),We=n(101),Re=n(102),xe=n(103),Fe=n(104),Le=n(105),Be=n(106),ze=n(107),qe=n(108),Ye=n(109),Je=n(110),Ge=n(111),He=n(112),Xe=n(113),Ke=n(114),Qe=n(115),Ze=n(116),et=n(117),tt=n(118),nt=n(119),rt=n(120),ot=n(121),it=n(122),at=n(123),st=n(124),dt=n(125),ut=n(126),ct=n(127),lt=n(128),ft=n(129),vt=n(130),pt=n(131),_t=n(132),Et=n(133),Nt=n(134),Pt=n(135),Mt=n(136),ht=n(137),mt=n(138),kt=n(139),bt=n(140),gt=n(141),It=n(142),yt=n(143),At=n(144),St=n(145),Vt=n(146),Ut=n(147),Ot=n(148),jt=n(149),$t=n(150),wt=n(151),Dt=n(152),Ct=n(153),Tt=n(154),Wt=n(155),Rt=n(156),xt=n(157),Ft=n(158),Lt=n(159),Bt=n(160),zt=n(161),qt=n(162),Yt=n(163),Jt=n(164),Gt=n(165),Ht=n(166),Xt=n(167),Kt=n(168),Qt=n(169),Zt=n(170),en=n(171),tn=n(172),nn=n(173);t.apiObj={biz:{ATMBle:{beaconPicker:r.beaconPicker$,faceManager:o.faceManager$,punchModePicker:i.punchModePicker$},alipay:{pay:a.pay$},calendar:{chooseDateTime:s.chooseDateTime$,chooseHalfDay:d.chooseHalfDay$,chooseInterval:u.chooseInterval$,chooseOneDay:c.chooseOneDay$},chat:{chooseConversationByCorpId:l.chooseConversationByCorpId$,collectSticker:f.collectSticker$,createSceneGroup:v.createSceneGroup$,getRealmCid:p.getRealmCid$,locationChatMessage:_.locationChatMessage$,openSingleChat:E.openSingleChat$,pickConversation:N.pickConversation$,sendEmotion:P.sendEmotion$,toConversation:M.toConversation$},clipboardData:{setData:h.setData$},conference:{videoConfCall:m.videoConfCall$},contact:{choose:k.choose$,chooseMobileContacts:b.chooseMobileContacts$,complexPicker:g.complexPicker$,createGroup:I.createGroup$,departmentsPicker:y.departmentsPicker$,externalComplexPicker:A.externalComplexPicker$,externalEditForm:S.externalEditForm$,setRule:V.setRule$},cspace:{chooseSpaceDir:U.chooseSpaceDir$,delete:O.delete$,preview:j.preview$,saveFile:$.saveFile$},customContact:{choose:w.choose$,multipleChoose:D.multipleChoose$},ding:{create:C.create$,post:T.post$},event:{notifyWeex:W.notifyWeex$},intent:{fetchData:R.fetchData$},iot:{bind:x.bind$,bindMeetingRoom:F.bindMeetingRoom$,getDeviceProperties:L.getDeviceProperties$,invokeThingService:B.invokeThingService$,queryMeetingRoomList:z.queryMeetingRoomList$,setDeviceProperties:q.setDeviceProperties$,unbind:Y.unbind$},map:{locate:J.locate$,search:G.search$,view:H.view$},media:{compressVideo:X.compressVideo$},microApp:{openApp:K.openApp$},navigation:{close:Q.close$,goBack:Z.goBack$,hideBar:ee.hideBar$,quit:te.quit$,replace:ne.replace$,setIcon:re.setIcon$,setLeft:oe.setLeft$,setMenu:ie.setMenu$,setRight:ae.setRight$,setTitle:se.setTitle$},realm:{subscribe:de.subscribe$,unsubscribe:ue.unsubscribe$},shortCut:{addShortCut:ce.addShortCut$},store:{closeUnpayOrder:le.closeUnpayOrder$,createOrder:fe.createOrder$,getPayUrl:ve.getPayUrl$,inquiry:pe.inquiry$},telephone:{call:_e.call$,checkBizCall:Ee.checkBizCall$,quickCallList:Ne.quickCallList$,showCallMenu:Pe.showCallMenu$},user:{checkPassword:Me.checkPassword$,get:he.get$},util:{chosen:me.chosen$,datepicker:ke.datepicker$,datetimepicker:be.datetimepicker$,decrypt:ge.decrypt$,downloadFile:Ie.downloadFile$,encrypt:ye.encrypt$,isLocalFileExist:Ae.isLocalFileExist$,multiSelect:Se.multiSelect$,open:Ve.open$,openLink:Ue.openLink$,openLocalFile:Oe.openLocalFile$,openModal:je.openModal$,openSlidePanel:$e.openSlidePanel$,presentWindow:we.presentWindow$,previewImage:De.previewImage$,previewVideo:Ce.previewVideo$,scan:Te.scan$,scanCard:We.scanCard$,setScreenBrightnessAndKeepOn:Re.setScreenBrightnessAndKeepOn$,share:xe.share$,startDocSign:Fe.startDocSign$,systemShare:Le.systemShare$,timepicker:Be.timepicker$,uploadAttachment:ze.uploadAttachment$,uploadImage:qe.uploadImage$,uploadImageFromCamera:Ye.uploadImageFromCamera$,ut:Je.ut$},verify:{openBindIDCard:Ge.openBindIDCard$,startAuth:He.startAuth$}},channel:{permission:{requestAuthCode:Xe.requestAuthCode$}},device:{accelerometer:{clearShake:Ke.clearShake$,watchShake:Qe.watchShake$},audio:{download:Ze.download$,onPlayEnd:et.onPlayEnd$,onRecordEnd:tt.onRecordEnd$,pause:nt.pause$,play:rt.play$,resume:ot.resume$,startRecord:it.startRecord$,stop:at.stop$,stopRecord:st.stopRecord$,translateVoice:dt.translateVoice$},base:{getInterface:ut.getInterface$,getPhoneInfo:ct.getPhoneInfo$,getUUID:lt.getUUID$,getWifiStatus:ft.getWifiStatus$},connection:{getNetworkType:vt.getNetworkType$},geolocation:{checkPermission:pt.checkPermission$,get:_t.get$,start:Et.start$,status:Nt.status$,stop:Pt.stop$},launcher:{checkInstalledApps:Mt.checkInstalledApps$,launchApp:ht.launchApp$},nfc:{nfcRead:mt.nfcRead$,nfcStop:kt.nfcStop$,nfcWrite:bt.nfcWrite$},notification:{actionSheet:gt.actionSheet$,alert:It.alert$,confirm:yt.confirm$,extendModal:At.extendModal$,hidePreloader:St.hidePreloader$,modal:Vt.modal$,prompt:Ut.prompt$,showPreloader:Ot.showPreloader$,toast:jt.toast$,vibrate:$t.vibrate$},screen:{insetAdjust:wt.insetAdjust$,resetView:Dt.resetView$,rotateView:Ct.rotateView$}},net:{bjGovApn:{loginGovNet:Tt.loginGovNet$}},runtime:{message:{fetch:Wt.fetch$,post:Rt.post$},permission:{requestAuthCode:xt.requestAuthCode$,requestOperateAuthCode:Ft.requestOperateAuthCode$}},ui:{input:{plain:Lt.plain$},nav:{close:Bt.close$,getCurrentId:zt.getCurrentId$,go:qt.go$,preload:Yt.preload$,recycle:Jt.recycle$},progressBar:{setColors:Gt.setColors$},pullToRefresh:{disable:Ht.disable$,enable:Xt.enable$,stop:Kt.stop$},webViewBounce:{disable:Qt.disable$,enable:Zt.enable$}},util:{domainStorage:{getItem:en.getItem$,removeItem:tn.removeItem$,setItem:nn.setItem$}}}},,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,function(e,t,n){e.exports=n(199)}])});
\ No newline at end of file