diff --git a/README.md b/README.md
index 87f4b36..9c06552 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,10 @@
# com.fr.plugin.decision.integration
-登录集成插件
\ No newline at end of file
+登录集成插件
+
+# 问题记录
+Refused to execute script from ‘XXXX’ because its MIME type (‘text/html’) is not executable, and strict MIME type checking is enabled. 响应头中增加了X-Content-Type-Options:nosniff字段,导致跨域接口异常。
+解决方案:响应头中新增 content-type: application/javascript; charset=utf-8 (https://www.freesion.com/article/64191377003/)
+
+# 插件说明
+https://www.showdoc.com.cn/1435897295186406/7012059782236943
\ No newline at end of file
diff --git a/plugin.xml b/plugin.xml
new file mode 100644
index 0000000..1bfd661
--- /dev/null
+++ b/plugin.xml
@@ -0,0 +1,41 @@
+
+
+ com.fr.plugin.decision.integration
+
+ yes
+ no
+ 1.0.20
+ 10.0~
+ 2019-1-31
+ JianYe.Wang
+
+
+ 1.0.0:调整插件结构
+ [2021-06-08]
1.0.1:优化插件配置项
+ [2021-07-07]
1.0.2:跨域登录接口新增响应头content-type规避nosniff的限制
+ [2021-08-03]
1.0.3:创建用户时部门为空
+ [2021-08-18]
1.0.4:去除角色id的传递,统一通过角色名来匹配
+ [2021-08-24]
1.0.5:角色添加逻辑异常修复
+ [2021-10-15]
1.0.8:新增URL参数单点登录校验
+ [2021-11-15]
1.0.12:去除管理员账号的权限处理
+ [2021-12-06]
1.0.13/15:添加用户类型配置参数
+ [2021-12-10]
1.0.14:移动端单点场景时重定向至携带fine_auth_token的地址
+ [2022-01-04]
1.0.16:移动端跨域登录与RequestSsoFilter移动端重定向的冲突/部门创建支持名称拼接
+ [2022-02-22]
1.0.17:将启动设计器按钮添加平台中/代码接口优化
+ [2022-03-23]
1.0.18:新增接口,单点场景可直接访问固定地址打开URL来唤醒设计器;key默认值设置为uuid
+ [2022-04-01]
1.0.19:调整WebResourceProvider接口;新增配置远程权限控制是否开启;调整用户平台类型添加逻辑
+ [2022-04-11]
1.0.20:去除自定义登录页配置选项
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/decision/integration/ControllerBridgeImpl.java b/src/main/java/com/fr/plugin/decision/integration/ControllerBridgeImpl.java
new file mode 100644
index 0000000..5ab25f6
--- /dev/null
+++ b/src/main/java/com/fr/plugin/decision/integration/ControllerBridgeImpl.java
@@ -0,0 +1,23 @@
+package com.fr.plugin.decision.integration;
+
+import com.fr.decision.fun.impl.AbstractControllerRegisterProvider;
+import com.fr.plugin.context.PluginContexts;
+import com.fr.plugin.decision.integration.controller.IntegratedController;
+import com.fr.plugin.decision.integration.service.IntegrateAuthService;
+import com.fr.plugin.transform.ExecuteFunctionRecord;
+import com.fr.plugin.transform.FunctionRecorder;
+import com.fr.stable.fun.Authorize;
+
+@Authorize(callSignKey = "com.fr.plugin.decision.integration")
+@FunctionRecorder
+public class ControllerBridgeImpl extends AbstractControllerRegisterProvider {
+ @ExecuteFunctionRecord
+ @Override
+ public Class>[] getControllers() {
+ if (PluginContexts.currentContext().isAvailable()) {
+ return new Class[]{IntegratedController.class};
+ } else {
+ return new Class[0];
+ }
+ }
+}
diff --git a/src/main/java/com/fr/plugin/decision/integration/LifecycleMonitorImpl.java b/src/main/java/com/fr/plugin/decision/integration/LifecycleMonitorImpl.java
new file mode 100644
index 0000000..4991bcd
--- /dev/null
+++ b/src/main/java/com/fr/plugin/decision/integration/LifecycleMonitorImpl.java
@@ -0,0 +1,24 @@
+package com.fr.plugin.decision.integration;
+
+import com.fr.plugin.context.PluginContext;
+import com.fr.plugin.decision.integration.config.IntegrateConf;
+import com.fr.plugin.observer.inner.AbstractPluginLifecycleMonitor;
+
+/**
+ * @Author JianYe.Wang
+ * @Data 2022/2/22 15:54
+ * @Description TODO
+ * @Version 10.0
+ **/
+public class LifecycleMonitorImpl extends AbstractPluginLifecycleMonitor {
+
+ @Override
+ public void afterRun(PluginContext pluginContext) {
+ IntegrateConf.getInstance();
+ }
+
+ @Override
+ public void beforeStop(PluginContext pluginContext) {
+
+ }
+}
diff --git a/src/main/java/com/fr/plugin/decision/integration/bean/TplInfoBean.java b/src/main/java/com/fr/plugin/decision/integration/bean/TplInfoBean.java
new file mode 100644
index 0000000..5ba3da7
--- /dev/null
+++ b/src/main/java/com/fr/plugin/decision/integration/bean/TplInfoBean.java
@@ -0,0 +1,54 @@
+package com.fr.plugin.decision.integration.bean;
+
+import com.fr.decision.webservice.bean.BaseBean;
+
+@Deprecated
+public class TplInfoBean extends BaseBean {
+
+ private String menuId;
+ private String title;
+ private String url;
+ private String pmenuId;
+
+ public TplInfoBean() {
+ }
+
+ public TplInfoBean(String menuId, String title, String url, String pmenuId) {
+ this.menuId = menuId;
+ this.title = title;
+ this.url = url;
+ this.pmenuId = pmenuId;
+ }
+
+ public String getMenuId() {
+ return menuId;
+ }
+
+ public void setMenuId(String menuId) {
+ this.menuId = menuId;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public String getPmenuId() {
+ return pmenuId;
+ }
+
+ public void setPmenuId(String pmenuId) {
+ this.pmenuId = pmenuId;
+ }
+}
diff --git a/src/main/java/com/fr/plugin/decision/integration/bean/user/NamedBean.java b/src/main/java/com/fr/plugin/decision/integration/bean/user/NamedBean.java
new file mode 100644
index 0000000..1f7681d
--- /dev/null
+++ b/src/main/java/com/fr/plugin/decision/integration/bean/user/NamedBean.java
@@ -0,0 +1,25 @@
+package com.fr.plugin.decision.integration.bean.user;
+
+import com.fr.decision.webservice.bean.BaseBean;
+
+public class NamedBean extends BaseBean {
+
+ private String name;
+ private String id;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+}
diff --git a/src/main/java/com/fr/plugin/decision/integration/bean/user/UserInfoBean.java b/src/main/java/com/fr/plugin/decision/integration/bean/user/UserInfoBean.java
new file mode 100644
index 0000000..ec5e54d
--- /dev/null
+++ b/src/main/java/com/fr/plugin/decision/integration/bean/user/UserInfoBean.java
@@ -0,0 +1,45 @@
+package com.fr.plugin.decision.integration.bean.user;
+
+import com.fr.decision.webservice.bean.BaseBean;
+
+import java.util.List;
+
+public class UserInfoBean extends BaseBean {
+
+ private String username;
+ private NamedBean department;
+ private List roles;
+ private List platformType;
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public NamedBean getDepartment() {
+ return department;
+ }
+
+ public void setDepartment(NamedBean department) {
+ this.department = department;
+ }
+
+ public List getRoles() {
+ return roles;
+ }
+
+ public void setRoles(List roles) {
+ this.roles = roles;
+ }
+
+ public List getPlatformType() {
+ return platformType;
+ }
+
+ public void setPlatformType(List platformType) {
+ this.platformType = platformType;
+ }
+}
diff --git a/src/main/java/com/fr/plugin/decision/integration/config/IntegrateConf.java b/src/main/java/com/fr/plugin/decision/integration/config/IntegrateConf.java
new file mode 100644
index 0000000..27f7d6a
--- /dev/null
+++ b/src/main/java/com/fr/plugin/decision/integration/config/IntegrateConf.java
@@ -0,0 +1,273 @@
+package com.fr.plugin.decision.integration.config;
+
+import com.fr.cert.token.Jwts;
+import com.fr.cert.token.SignatureAlgorithm;
+import com.fr.config.ConfigContext;
+import com.fr.config.DefaultConfiguration;
+import com.fr.config.Identifier;
+import com.fr.config.holder.Conf;
+import com.fr.config.holder.factory.Holders;
+import com.fr.config.utils.ValueReader;
+import com.fr.config.utils.ValueWriter;
+import com.fr.decision.base.util.UUIDUtil;
+import com.fr.json.JSONObject;
+import com.fr.plugin.decision.integration.config.strategy.CreateDepartmentStrategy;
+import com.fr.plugin.decision.integration.config.strategy.CreatePwdStrategy;
+import com.fr.plugin.decision.integration.config.strategy.user.impl.ContainDepRoleStrategy;
+import com.fr.plugin.decision.integration.config.strategy.user.impl.OnlyUserStrategy;
+import com.fr.plugin.decision.integration.config.strategy.user.CreateUserStrategy;
+import com.fr.plugin.decision.integration.utils.AESUtils;
+import com.fr.plugin.decision.integration.utils.Constants;
+import com.fr.stable.StringUtils;
+import com.fr.third.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
+import java.util.Base64;
+import java.util.Date;
+import java.util.HashMap;
+
+@JsonIgnoreProperties({"id", "data", "classInfo", "nameSpace"})
+public class IntegrateConf extends DefaultConfiguration {
+ private static volatile IntegrateConf configuration = null;
+
+ public static IntegrateConf getInstance() {
+ if (null == configuration) {
+ configuration = ConfigContext.getConfigInstance(IntegrateConf.class);
+ }
+ return configuration;
+ }
+
+ @Identifier("jwtKey")
+ private Conf jwtKey = Holders.simple(UUIDUtil.generate());
+
+ @Identifier("createUserTurnOn")
+ private Conf createUserTurnOn = Holders.simple(false);
+
+ @Identifier("userStrategy")
+ private Conf userStrategy = Holders.obj(new OnlyUserStrategy(), CreateUserStrategy.class);
+
+ @Identifier("paraCheckTurnOn")
+ private Conf paraCheckTurnOn = Holders.simple(false);
+
+ @Identifier("paraCheckUrl")
+ private Conf paraCheckUrl = Holders.simple(StringUtils.EMPTY);
+
+ @Identifier("remoteTokenAuth")
+ private Conf remoteTokenAuth = Holders.simple(true);
+
+ @Identifier("aesEncryptSubject")
+ private Conf aesEncryptSubject = Holders.simple(false);
+
+ @Identifier("aesEncryptKey")
+ private Conf aesEncryptKey = Holders.simple(StringUtils.EMPTY);
+
+ @Identifier("portalURLTurnOn")
+ private Conf portalURLTurnOn = Holders.simple(false);
+
+ @Identifier("portalURL")
+ private Conf portalURL = Holders.simple(StringUtils.EMPTY);
+
+ @Identifier("crossDomain")
+ private Conf crossDomain = Holders.simple(false);
+
+ @Identifier("webStartDesigner")
+ private Conf webStartDesigner = Holders.simple(false);
+
+ @Identifier("downloadUrl32")
+ private Conf downloadUrl32 = Holders.simple("");
+
+ @Identifier("downloadUrl64")
+ private Conf downloadUrl64 = Holders.simple("");
+
+ @Identifier("decisionUrl")
+ private Conf decisionUrl = Holders.simple(StringUtils.EMPTY);
+
+ @Identifier("remoteFolder")
+ private Conf remoteFolder = Holders.simple(false);
+
+ public String getJwtKey() {
+ return jwtKey.get();
+ }
+
+ public void setJwtKey(String jwtKey) {
+ this.jwtKey.set(jwtKey);
+ }
+
+ public Boolean getCreateUserTurnOn() {
+ return createUserTurnOn.get();
+ }
+
+ public void setCreateUserTurnOn(Boolean createUserTurnOn) {
+ this.createUserTurnOn.set(createUserTurnOn);
+ }
+
+ public CreateUserStrategy getUserStrategy() {
+ return userStrategy.get();
+ }
+
+ public void setUserStrategy(CreateUserStrategy userStrategy) {
+ this.userStrategy.set(userStrategy);
+ }
+
+ public Boolean getParaCheckTurnOn() {
+ return paraCheckTurnOn.get();
+ }
+
+ public void setParaCheckTurnOn(Boolean paraCheckTurnOn) {
+ this.paraCheckTurnOn.set(paraCheckTurnOn);
+ }
+
+ public String getParaCheckUrl() {
+ return paraCheckUrl.get();
+ }
+
+ public void setParaCheckUrl(String paraCheckUrl) {
+ this.paraCheckUrl.set(paraCheckUrl);
+ }
+
+ public Boolean getRemoteTokenAuth() {
+ return remoteTokenAuth.get();
+ }
+
+ public void setRemoteTokenAuth(Boolean remoteTokenAuth) {
+ this.remoteTokenAuth.set(remoteTokenAuth);
+ }
+
+ public Boolean getAesEncryptSubject() {
+ return aesEncryptSubject.get();
+ }
+
+ public void setAesEncryptSubject(Boolean aesEncryptSubject) {
+ this.aesEncryptSubject.set(aesEncryptSubject);
+ }
+
+ public String getAesEncryptKey() {
+ return aesEncryptKey.get();
+ }
+
+ public void setAesEncryptKey(String aesEncryptKey) {
+ this.aesEncryptKey.set(aesEncryptKey);
+ }
+
+ public Boolean getPortalURLTurnOn() {
+ return portalURLTurnOn.get();
+ }
+
+ public void setPortalURLTurnOn(Boolean portalURLTurnOn) {
+ this.portalURLTurnOn.set(portalURLTurnOn);
+ }
+
+ public String getPortalURL() {
+ return portalURL.get();
+ }
+
+ public void setPortalURL(String portalURL) {
+ this.portalURL.set(portalURL);
+ }
+
+ public Boolean getCrossDomain() {
+ return crossDomain.get();
+ }
+
+ public void setCrossDomain(Boolean crossDomain) {
+ this.crossDomain.set(crossDomain);
+ }
+
+ public Boolean getWebStartDesigner() {
+ return webStartDesigner.get();
+ }
+
+ public void setWebStartDesigner(Boolean webStartDesigner) {
+ this.webStartDesigner.set(webStartDesigner);
+ }
+
+ public String getDownloadUrl32() {
+ return downloadUrl32.get();
+ }
+
+ public void setDownloadUrl32(String downloadUrl32) {
+ this.downloadUrl32.set(downloadUrl32);
+ }
+
+ public String getDownloadUrl64() {
+ return downloadUrl64.get();
+ }
+
+ public void setDownloadUrl64(String downloadUrl64) {
+ this.downloadUrl64.set(downloadUrl64);
+ }
+
+ public String getDecisionUrl() {
+ return decisionUrl.get();
+ }
+
+ public void setDecisionUrl(String decisionUrl) {
+ this.decisionUrl.set(decisionUrl);
+ }
+
+ public Boolean getRemoteFolder() {
+ return remoteFolder.get();
+ }
+
+ public void setRemoteFolder(Boolean remoteFolder) {
+ this.remoteFolder.set(remoteFolder);
+ }
+
+ @Override
+ protected void initialize() {
+ ValueWriter.registerWriter(CreateDepartmentStrategy.class, new ValueWriter() {
+ @Override
+ public String writeObject(CreateDepartmentStrategy createDepEnum) {
+ return String.valueOf(createDepEnum.toInteger());
+ }
+ });
+ ValueReader.registerReader(CreateDepartmentStrategy.class, new ValueReader() {
+ public CreateDepartmentStrategy covert(String value) {
+ return CreateDepartmentStrategy.parse(Integer.parseInt(value));
+ }
+ });
+ ValueWriter.registerWriter(CreatePwdStrategy.class, new ValueWriter() {
+ @Override
+ public String writeObject(CreatePwdStrategy createDepEnum) {
+ return String.valueOf(createDepEnum.toInteger());
+ }
+ });
+ ValueReader.registerReader(CreatePwdStrategy.class, new ValueReader() {
+ public CreatePwdStrategy covert(String value) {
+ return CreatePwdStrategy.parse(Integer.parseInt(value));
+ }
+ });
+ }
+
+ public HashMap getWebStartedInfo(String username) throws Exception {
+ HashMap info = new HashMap<>(3);
+ info.put("url", "fanruan://" + getDecisionUrl() + "?GWToken?" + getTokenByUserName(username));
+ info.put("download64", getDownloadUrl64());
+ info.put("download32", getDownloadUrl32());
+ return info;
+ }
+
+ public String getWebStartURL(String username, String path) throws Exception {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("");
+ return buffer.toString();
+ }
+
+ private String getTokenByUserName(String username) throws Exception {
+ String subject = username;
+ if (getUserStrategy() instanceof ContainDepRoleStrategy) {
+ subject = JSONObject.create().put("username", username).toString();
+ }
+ if (getAesEncryptSubject()) {
+ subject = Base64.getEncoder().encodeToString(AESUtils.aesEncryptToBytes(subject, getAesEncryptKey()));
+ }
+ Date date = new Date();
+ return Jwts.builder()
+ .setSubject(subject)
+ .setExpiration(new Date(date.getTime() + Constants.EXPIRED_TIME))
+ .signWith(SignatureAlgorithm.HS256, getJwtKey())
+ .compact();
+ }
+}
diff --git a/src/main/java/com/fr/plugin/decision/integration/config/strategy/CreateDepartmentStrategy.java b/src/main/java/com/fr/plugin/decision/integration/config/strategy/CreateDepartmentStrategy.java
new file mode 100644
index 0000000..de848d8
--- /dev/null
+++ b/src/main/java/com/fr/plugin/decision/integration/config/strategy/CreateDepartmentStrategy.java
@@ -0,0 +1,50 @@
+package com.fr.plugin.decision.integration.config.strategy;
+
+import com.fr.general.ComparatorUtils;
+import com.fr.third.fasterxml.jackson.annotation.JsonCreator;
+import com.fr.third.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * @Author JianYe.Wang
+ * @Data 10:14
+ * @Description TODO
+ * @Version 10.0
+ **/
+
+/**
+ * "department": {
+ * "name": "beijing",
+ * "id": "02"
+ * }
+ */
+public enum CreateDepartmentStrategy {
+ /**
+ * 根据id长度, 01 011 012
+ */
+ DEP_ID_LENGTH(0),
+ /**
+ * 根据name拼接 中国|北京市|朝阳区
+ */
+ DEP_NAME_GROUP(1);
+
+ private int type;
+
+ CreateDepartmentStrategy(int type) {
+ this.type = type;
+ }
+
+ @JsonValue
+ public int toInteger() {
+ return this.type;
+ }
+
+ @JsonCreator
+ public static CreateDepartmentStrategy parse(int value) {
+ for (CreateDepartmentStrategy type : CreateDepartmentStrategy.values()) {
+ if (ComparatorUtils.equals(value, type.toInteger())) {
+ return type;
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/com/fr/plugin/decision/integration/config/strategy/CreatePwdStrategy.java b/src/main/java/com/fr/plugin/decision/integration/config/strategy/CreatePwdStrategy.java
new file mode 100644
index 0000000..9ee3aa4
--- /dev/null
+++ b/src/main/java/com/fr/plugin/decision/integration/config/strategy/CreatePwdStrategy.java
@@ -0,0 +1,37 @@
+package com.fr.plugin.decision.integration.config.strategy;
+
+import com.fr.general.ComparatorUtils;
+import com.fr.third.fasterxml.jackson.annotation.JsonCreator;
+import com.fr.third.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * @Author JianYe.Wang
+ * @Data 21-06-07 10:07
+ * @Description TODO
+ * @Version 10.0
+ **/
+public enum CreatePwdStrategy {
+ SAME_USER(0), RANDOM(1);
+
+ private int type;
+
+ CreatePwdStrategy(int type) {
+ this.type = type;
+ }
+
+ @JsonValue
+ public int toInteger() {
+ return this.type;
+ }
+
+ @JsonCreator
+ public static CreatePwdStrategy parse(int value) {
+ for (CreatePwdStrategy type : CreatePwdStrategy.values()) {
+ if (ComparatorUtils.equals(value, type.toInteger())) {
+ return type;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/main/java/com/fr/plugin/decision/integration/config/strategy/user/CreateUserStrategy.java b/src/main/java/com/fr/plugin/decision/integration/config/strategy/user/CreateUserStrategy.java
new file mode 100644
index 0000000..3b21798
--- /dev/null
+++ b/src/main/java/com/fr/plugin/decision/integration/config/strategy/user/CreateUserStrategy.java
@@ -0,0 +1,38 @@
+package com.fr.plugin.decision.integration.config.strategy.user;
+
+import com.fr.config.Identifier;
+import com.fr.config.holder.Conf;
+import com.fr.config.holder.factory.Holders;
+import com.fr.config.utils.UniqueKey;
+import com.fr.plugin.decision.integration.config.strategy.CreatePwdStrategy;
+import com.fr.plugin.decision.integration.config.strategy.user.impl.ContainDepRoleStrategy;
+import com.fr.plugin.decision.integration.config.strategy.user.impl.OnlyUserStrategy;
+import com.fr.third.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fr.third.fasterxml.jackson.annotation.JsonSubTypes;
+import com.fr.third.fasterxml.jackson.annotation.JsonTypeInfo;
+
+/**
+ * @Author JianYe.Wang
+ * @Data 21-6-7 10:01
+ * @Description TODO
+ * @Version 10.0
+ **/
+@JsonIgnoreProperties({"id", "data", "classInfo", "nameSpace"})
+@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "userType")
+@JsonSubTypes({
+ @JsonSubTypes.Type(value = OnlyUserStrategy.class, name = "onlyUser"),
+ @JsonSubTypes.Type(value = ContainDepRoleStrategy.class, name = "containDepRole")
+})
+public abstract class CreateUserStrategy extends UniqueKey {
+
+ @Identifier("pwdStrategy")
+ private Conf pwdStrategy = Holders.simple(CreatePwdStrategy.SAME_USER);
+
+ public CreatePwdStrategy getPwdStrategy() {
+ return pwdStrategy.get();
+ }
+
+ public void setPwdStrategy(CreatePwdStrategy pwdStrategy) {
+ this.pwdStrategy.set(pwdStrategy);
+ }
+}
diff --git a/src/main/java/com/fr/plugin/decision/integration/config/strategy/user/impl/ContainDepRoleStrategy.java b/src/main/java/com/fr/plugin/decision/integration/config/strategy/user/impl/ContainDepRoleStrategy.java
new file mode 100644
index 0000000..915d82f
--- /dev/null
+++ b/src/main/java/com/fr/plugin/decision/integration/config/strategy/user/impl/ContainDepRoleStrategy.java
@@ -0,0 +1,53 @@
+package com.fr.plugin.decision.integration.config.strategy.user.impl;
+
+import com.fr.config.Identifier;
+import com.fr.config.holder.Conf;
+import com.fr.config.holder.factory.Holders;
+import com.fr.plugin.decision.integration.config.strategy.CreateDepartmentStrategy;
+import com.fr.plugin.decision.integration.config.strategy.user.CreateUserStrategy;
+import com.fr.stable.StringUtils;
+import com.fr.third.fasterxml.jackson.annotation.JsonTypeName;
+
+/**
+ * @Author JianYe.Wang
+ * @Data 10:13
+ * @Description TODO
+ * @Version 10.0
+ **/
+@JsonTypeName("containDepRole")
+public class ContainDepRoleStrategy extends CreateUserStrategy {
+
+ @Identifier("depStrategy")
+ private Conf depStrategy = Holders.simple(CreateDepartmentStrategy.DEP_ID_LENGTH);
+
+ @Identifier("specialRootId")
+ private Conf specialRootId = Holders.simple(StringUtils.EMPTY);
+
+ // 分隔符, CreateDepEnum.DEP_NAME_GROUP 会用到
+ @Identifier("separator")
+ private Conf separator = Holders.simple(StringUtils.EMPTY);
+
+ public CreateDepartmentStrategy getDepStrategy() {
+ return depStrategy.get();
+ }
+
+ public void setDepStrategy(CreateDepartmentStrategy depStrategy) {
+ this.depStrategy.set(depStrategy);
+ }
+
+ public String getSpecialRootId() {
+ return specialRootId.get();
+ }
+
+ public void setSpecialRootId(String specialRootId) {
+ this.specialRootId.set(specialRootId);
+ }
+
+ public String getSeparator() {
+ return separator.get();
+ }
+
+ public void setSeparator(String separator) {
+ this.separator.set(separator);
+ }
+}
diff --git a/src/main/java/com/fr/plugin/decision/integration/config/strategy/user/impl/OnlyUserStrategy.java b/src/main/java/com/fr/plugin/decision/integration/config/strategy/user/impl/OnlyUserStrategy.java
new file mode 100644
index 0000000..a5b35b3
--- /dev/null
+++ b/src/main/java/com/fr/plugin/decision/integration/config/strategy/user/impl/OnlyUserStrategy.java
@@ -0,0 +1,15 @@
+package com.fr.plugin.decision.integration.config.strategy.user.impl;
+
+import com.fr.plugin.decision.integration.config.strategy.user.CreateUserStrategy;
+import com.fr.third.fasterxml.jackson.annotation.JsonTypeName;
+
+/**
+ * @Author JianYe.Wang
+ * @Data 21-6-7 10:12
+ * @Description TODO
+ * @Version 10.0
+ **/
+@JsonTypeName("onlyUser")
+public class OnlyUserStrategy extends CreateUserStrategy {
+
+}
diff --git a/src/main/java/com/fr/plugin/decision/integration/controller/IntegratedController.java b/src/main/java/com/fr/plugin/decision/integration/controller/IntegratedController.java
new file mode 100644
index 0000000..726fd81
--- /dev/null
+++ b/src/main/java/com/fr/plugin/decision/integration/controller/IntegratedController.java
@@ -0,0 +1,224 @@
+package com.fr.plugin.decision.integration.controller;
+
+import com.fr.base.ServerConfig;
+import com.fr.decision.authority.data.User;
+import com.fr.decision.webservice.CrossDomainResponse;
+import com.fr.decision.webservice.Response;
+import com.fr.decision.webservice.annotation.LoginStatusChecker;
+import com.fr.decision.webservice.v10.login.LoginService;
+import com.fr.decision.webservice.v10.login.TokenResource;
+import com.fr.decision.webservice.v10.user.UserService;
+import com.fr.io.utils.ResourceIOUtils;
+import com.fr.log.FineLoggerFactory;
+import com.fr.plugin.decision.integration.config.IntegrateConf;
+import com.fr.plugin.decision.integration.service.IntegrateAuthService;
+import com.fr.plugin.decision.integration.service.IntegrateCustomService;
+import com.fr.plugin.decision.integration.utils.CommonUtils;
+import com.fr.plugin.decision.integration.utils.Constants;
+import com.fr.stable.StableUtils;
+import com.fr.stable.StringUtils;
+import com.fr.third.fasterxml.jackson.databind.ObjectMapper;
+import com.fr.third.springframework.stereotype.Controller;
+import com.fr.third.springframework.web.bind.annotation.*;
+import com.fr.web.utils.WebUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Map;
+
+@Controller
+@RequestMapping({"/third/auth"})
+@LoginStatusChecker(required = false)
+public class IntegratedController {
+
+ private ObjectMapper mapper = new ObjectMapper();
+
+ /**
+ * 建议用于后台换取 fine_auth_token
+ * @param request
+ * @param response
+ * @param body 请求体
+ * @param third_token 请求参数
+ * @return fine_auth_token
+ * @throws Exception
+ */
+ @RequestMapping("/login")
+ @ResponseBody
+ public Response loginByToken(HttpServletRequest request, HttpServletResponse response, @RequestBody(required = false) Map body, @RequestParam(required = false, value = Constants.TOKEN_NAME) String third_token) throws Exception {
+ String token = third_token;
+ if (StringUtils.isBlank(third_token)) {
+ token = (String) body.get(Constants.TOKEN_NAME);
+ }
+ String username = IntegrateAuthService.getInstance().getUserNameFromToken(token);
+ return Response.ok(LoginService.getInstance().login(request, response, username));
+ }
+
+ /**
+ * 用于前台跨域请求
+ * @param request
+ * @param response
+ * @param third_token
+ * @param callback
+ * @return
+ * @throws Exception
+ */
+ @RequestMapping(
+ value = {"/cross/login"},
+ method = {RequestMethod.GET},
+ produces = "application/javascript; charset=utf-8"
+ )
+ @ResponseBody
+ public String crossLoginByToken(HttpServletRequest request,
+ HttpServletResponse response,
+ @RequestParam(Constants.TOKEN_NAME) String third_token,
+ @RequestParam(value = "callback", required = false, defaultValue = "callback") String callback
+ ) throws Exception {
+ String username = IntegrateAuthService.getInstance().getUserNameFromToken(third_token);
+ return CrossDomainResponse.create().callbackFuncName(callback)
+ .parameter("accessToken", LoginService.getInstance().login(request, response, username))
+ .parameter("url", IntegrateCustomService.getInstance().generateDefaultHomePageUrl(request))
+ .parameter("status", "success")
+ .createCrossDomainResponse();
+ }
+
+ /**
+ * URL设计器启动需要用的接口
+ */
+
+ /**
+ * 解析获取 token 对应的用户名
+ * @param third_token
+ * @return
+ */
+ @RequestMapping(
+ value = {"/username"}
+ )
+ @ResponseBody
+ public String getUsernameFromToken(@RequestParam(Constants.TOKEN_NAME) String third_token) throws Exception {
+ return IntegrateAuthService.getInstance().getUserNameFromToken(third_token);
+ }
+
+ /**
+ * 获取用户对应的token,用于web唤醒设计器传入token
+ */
+ @RequestMapping(
+ value = {"/username/token"}
+ )
+ @ResponseBody
+ @LoginStatusChecker
+ public Response getThirdTokenByUsername(HttpServletRequest request) throws Exception {
+ String username = LoginService.getInstance().getCurrentUserNameFromRequest(request);
+ return Response.ok(IntegrateConf.getInstance().getWebStartedInfo(username));
+ }
+
+ /**
+ * 登录状态下
+ */
+ @RequestMapping(
+ value = {"/designer/start"}
+ )
+ @ResponseBody
+ //@LoginStatusChecker(tokenResource = TokenResource.COOKIE)
+ public String openDesignerStartUrl(HttpServletRequest request,
+ HttpServletResponse response,
+ @RequestParam(value = "path", required = false) String path) throws Exception {
+ String username = LoginService.getInstance().getCurrentUserNameFromRequestCookie(request);
+ if (StringUtils.isEmpty(username)) return StringUtils.EMPTY;
+ response.setContentType("text/html; charset=GB2312");
+ return IntegrateConf.getInstance().getWebStartURL(username, path);
+ }
+
+ /**
+ * 插件配置读取、保存
+ */
+ @RequestMapping(
+ value = {"/conf"},
+ method = RequestMethod.GET
+ )
+ @ResponseBody
+ @LoginStatusChecker
+ public Response getIntegrateConfig(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ String userId = UserService.getInstance().getCurrentUserIdFromCookie(request);
+ if (UserService.getInstance().isAdmin(userId)) {
+ response.setContentType("application/json;charset=" + ServerConfig.getInstance().getServerCharset());
+ mapper.writeValue(response.getOutputStream(), IntegrateConf.getInstance());
+ }
+
+ return Response.error("getIntegrateConf", "仅管理员用户可见");
+ }
+
+ @RequestMapping(
+ value = {"/conf"},
+ method = RequestMethod.POST
+ )
+ @ResponseBody
+ @LoginStatusChecker
+ public Response updateIntegrateConfig(HttpServletRequest request) throws IOException {
+ IntegrateConf conf = mapper.readValue(request.getInputStream(), IntegrateConf.class);
+ IntegrateAuthService.getInstance().updateIntegrateConf(conf);
+ return Response.success();
+ }
+
+
+ /**
+ * 插件历史接口,做兼容使用
+ */
+
+ @RequestMapping(
+ value = {"/tpl/paste"}
+ )
+ @ResponseBody
+ @Deprecated
+ public Response pasteTemplate(@RequestParam(Constants.TOKEN_NAME) String third_token, @RequestParam("tplPath") String tplPath) throws Exception {
+ String username = IntegrateAuthService.getInstance().getUserNameFromToken(third_token);
+ if (StringUtils.isBlank(username))
+ return Response.error("pasteTemplate", "TOKEN解析异常");
+
+ if (StringUtils.isBlank(tplPath) || !ResourceIOUtils.exist(StableUtils.pathJoin("reportlets", tplPath)))
+ return Response.error("pasteTemplate", "模板文件不存在");
+
+ ResourceIOUtils.copy(
+ StableUtils.pathJoin("reportlets", tplPath),
+ StableUtils.pathJoin("reportlets", username, tplPath.substring(tplPath.lastIndexOf("/")))
+ );
+ return Response.success();
+ }
+
+ @RequestMapping(
+ value = {"/tpl/remote/list"}
+ )
+ @ResponseBody
+ @Deprecated
+ public Response getUserRemoteTplList(HttpServletRequest request, @RequestParam(Constants.TOKEN_NAME) String third_token) throws Exception {
+ String username = IntegrateAuthService.getInstance().getUserNameFromToken(third_token);
+ if (StringUtils.isBlank(username))
+ return Response.error("UserRemoteTplList", "TOKEN解析异常");
+
+ User user = UserService.getInstance().getUserByUserName(username);
+ return Response.ok(IntegrateCustomService.getInstance().getRemoteTplListByUserId(user.getId(), WebUtils.createServletURL(request)));
+ }
+
+ /**
+ * token 换取帆软token,主要用于后台请求
+ */
+ @RequestMapping({"/token"})
+ @ResponseBody
+ @Deprecated
+ public String tokenExchangeFineAuthToken(HttpServletRequest request, HttpServletResponse response, @RequestBody Map body) throws Exception {
+ String username = IntegrateAuthService.getInstance().getUserNameFromToken((String) body.get(Constants.TOKEN_NAME));
+ return LoginService.getInstance().login(request, response, username);
+ }
+
+ /**
+ * 处理下异常返回值统一格式返回
+ * @param ex
+ * @return
+ */
+ @ExceptionHandler(Exception.class)
+ @ResponseBody
+ public Response exceptionRespond(Exception ex) {
+ FineLoggerFactory.getLogger().error(ex.getMessage(), ex);
+ return CommonUtils.formatException(ex);
+ }
+}
diff --git a/src/main/java/com/fr/plugin/decision/integration/exception/JwtKeyNullException.java b/src/main/java/com/fr/plugin/decision/integration/exception/JwtKeyNullException.java
new file mode 100644
index 0000000..e446c88
--- /dev/null
+++ b/src/main/java/com/fr/plugin/decision/integration/exception/JwtKeyNullException.java
@@ -0,0 +1,7 @@
+package com.fr.plugin.decision.integration.exception;
+
+public class JwtKeyNullException extends ThirdAuthException {
+ public JwtKeyNullException() {
+ super("The value of jwt key is null!");
+ }
+}
diff --git a/src/main/java/com/fr/plugin/decision/integration/exception/JwtSubjectFormatException.java b/src/main/java/com/fr/plugin/decision/integration/exception/JwtSubjectFormatException.java
new file mode 100644
index 0000000..8e5eed3
--- /dev/null
+++ b/src/main/java/com/fr/plugin/decision/integration/exception/JwtSubjectFormatException.java
@@ -0,0 +1,14 @@
+package com.fr.plugin.decision.integration.exception;
+
+/**
+ * @Author JianYe.Wang
+ * @Data 2021/7/7 14:22
+ * @Description TODO
+ * @Version 10.0
+ **/
+public class JwtSubjectFormatException extends ThirdAuthException {
+
+ public JwtSubjectFormatException() {
+ super("Jwt key subject format error!");
+ }
+}
diff --git a/src/main/java/com/fr/plugin/decision/integration/exception/ThirdAuthException.java b/src/main/java/com/fr/plugin/decision/integration/exception/ThirdAuthException.java
new file mode 100644
index 0000000..f7cbf63
--- /dev/null
+++ b/src/main/java/com/fr/plugin/decision/integration/exception/ThirdAuthException.java
@@ -0,0 +1,14 @@
+package com.fr.plugin.decision.integration.exception;
+
+public class ThirdAuthException extends RuntimeException {
+
+ public static final String KEY = "FR decision integration exception: ";
+
+ public ThirdAuthException(String message) {
+ super(KEY + message);
+ }
+
+ public ThirdAuthException(String message, Throwable cause) {
+ super(KEY + message, cause);
+ }
+}
diff --git a/src/main/java/com/fr/plugin/decision/integration/exception/TokenDecodeExpiredException.java b/src/main/java/com/fr/plugin/decision/integration/exception/TokenDecodeExpiredException.java
new file mode 100644
index 0000000..eea4b7b
--- /dev/null
+++ b/src/main/java/com/fr/plugin/decision/integration/exception/TokenDecodeExpiredException.java
@@ -0,0 +1,7 @@
+package com.fr.plugin.decision.integration.exception;
+
+public class TokenDecodeExpiredException extends ThirdAuthException {
+ public TokenDecodeExpiredException() {
+ super("Jwt token decode result is expired!");
+ }
+}
diff --git a/src/main/java/com/fr/plugin/decision/integration/exception/TokenNullException.java b/src/main/java/com/fr/plugin/decision/integration/exception/TokenNullException.java
new file mode 100644
index 0000000..11e1806
--- /dev/null
+++ b/src/main/java/com/fr/plugin/decision/integration/exception/TokenNullException.java
@@ -0,0 +1,14 @@
+package com.fr.plugin.decision.integration.exception;
+
+/**
+ * @Author JianYe.Wang
+ * @Data 2021/11/1 9:41
+ * @Description TODO
+ * @Version 10.0
+ **/
+public class TokenNullException extends ThirdAuthException {
+
+ public TokenNullException() {
+ super("Jwt token is null!");
+ }
+}
diff --git a/src/main/java/com/fr/plugin/decision/integration/filter/RemoteFilter.java b/src/main/java/com/fr/plugin/decision/integration/filter/RemoteFilter.java
new file mode 100644
index 0000000..fb3bf7c
--- /dev/null
+++ b/src/main/java/com/fr/plugin/decision/integration/filter/RemoteFilter.java
@@ -0,0 +1,57 @@
+package com.fr.plugin.decision.integration.filter;
+
+import com.fr.data.NetworkHelper;
+import com.fr.decision.fun.impl.AbstractEmbedRequestFilterProvider;
+import com.fr.decision.webservice.Response;
+import com.fr.decision.webservice.v10.remote.RemoteDesignStatusService;
+import com.fr.plugin.decision.integration.config.IntegrateConf;
+import com.fr.plugin.decision.integration.service.IntegrateAuthService;
+import com.fr.plugin.decision.integration.utils.LogUtils;
+import com.fr.security.JwtUtils;
+import com.fr.security.SecurityToolbox;
+import com.fr.security.encryption.transmission.TransmissionEncryptors;
+import com.fr.stable.StringUtils;
+import com.fr.third.fasterxml.jackson.databind.ObjectMapper;
+import com.fr.web.utils.WebUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * 拦截设计器远程请求, 支持Token认证
+ */
+public class RemoteFilter extends AbstractEmbedRequestFilterProvider {
+
+ private static final String PATH_TOKEN = "/remote/design/token";
+ private static final String PATH_VERIFY = "/remote/design/verify";
+
+ @Override
+ public void filter(HttpServletRequest request, HttpServletResponse response) {
+ if (!IntegrateConf.getInstance().getRemoteTokenAuth()) return;
+ String username = NetworkHelper.getHTTPRequestParameter(request, "username");
+ String password;
+ if (request.getRequestURI().endsWith(PATH_TOKEN)) {
+ if (StringUtils.equalsIgnoreCase("get", request.getMethod())) {
+ password = NetworkHelper.getHTTPRequestParameter(request, "password");
+ } else {
+ password = SecurityToolbox.defaultDecrypt(NetworkHelper.getHTTPRequestParameter(request, "password"));
+ }
+ } else if (request.getRequestURI().endsWith(PATH_VERIFY)) {
+ password = TransmissionEncryptors.getInstance().decrypt(NetworkHelper.getHTTPRequestParameter(request, "password"));
+ } else {
+ return;
+ }
+
+ LogUtils.info("RemoteFilter token auth success: {}", username);
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ if (StringUtils.isNotBlank(IntegrateAuthService.getInstance().getUserNameFromToken(password))) {
+ String token = JwtUtils.createDefaultJWT(username);
+ RemoteDesignStatusService.loginStatusService().put(token, username, 1209600000);
+ WebUtils.printAsString(response, mapper.writeValueAsString(Response.ok(token)));
+ }
+ } catch (Exception e) {
+ LogUtils.error(e.getMessage(), e);
+ }
+ }
+}
diff --git a/src/main/java/com/fr/plugin/decision/integration/filter/RequestSsoFilter.java b/src/main/java/com/fr/plugin/decision/integration/filter/RequestSsoFilter.java
new file mode 100644
index 0000000..6b731e3
--- /dev/null
+++ b/src/main/java/com/fr/plugin/decision/integration/filter/RequestSsoFilter.java
@@ -0,0 +1,78 @@
+package com.fr.plugin.decision.integration.filter;
+
+import com.fr.decision.fun.impl.AbstractEmbedRequestFilterProvider;
+import com.fr.decision.webservice.v10.login.LoginService;
+import com.fr.log.FineLoggerFactory;
+import com.fr.plugin.decision.integration.config.IntegrateConf;
+import com.fr.plugin.decision.integration.service.IntegrateAuthService;
+import com.fr.plugin.decision.integration.utils.CommonUtils;
+import com.fr.stable.StringUtils;
+import com.fr.web.utils.WebUtils;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+import static org.apache.catalina.filters.CorsFilter.*;
+
+/**
+ * @Author JianYe.Wang
+ * @Data 2021/10/15 14:44
+ * @Description TODO
+ * @Version 10.0
+ **/
+public class RequestSsoFilter extends AbstractEmbedRequestFilterProvider {
+
+ @Override
+ public void filter(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
+ try {
+ // url 携带 token 单点
+ String token = WebUtils.getHTTPRequestParameter(request, "third_token");
+ if (StringUtils.isNotEmpty(token)) {
+ String userName = IntegrateAuthService.getInstance().getUserNameFromToken(token);
+ if (CommonUtils.isMobile(request) && !request.getRequestURI().endsWith("/third/auth/cross/login")) {
+ String frToken = doLoginFR(request, response, userName);
+ String originalURL = WebUtils.getOriginalURL(request).replaceAll("&?third_token=[^&]*", "");
+ response.sendRedirect(originalURL + "&fine_auth_token=" + frToken);
+ } else {
+ String curUserName = LoginService.getInstance().getCurrentUserNameFromRequestCookie(request);
+ if (!LoginService.getInstance().isLogged(request) || !StringUtils.equals(userName, curUserName)) {
+ doLoginFR(request, response, userName);
+ }
+ }
+ }
+
+ // 自定义登录页判断,/login 还保留
+ if (IntegrateConf.getInstance().getPortalURLTurnOn() && request.getRequestURI().endsWith(WebUtils.createServletURL(request)) && !LoginService.getInstance().isLogged(request)) {
+ response.sendRedirect(IntegrateConf.getInstance().getPortalURL());
+ return;
+ }
+
+ // 跨域判断
+ if (IntegrateConf.getInstance().getCrossDomain()) {
+ String origin = request.getHeader("Origin");
+ if (StringUtils.isNotEmpty(origin)) {
+ response.addHeader(RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, origin);
+ String headers = request.getHeader(REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS);
+ if (StringUtils.isNotEmpty(headers))
+ response.addHeader(RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_HEADERS, headers);
+ response.addHeader(RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_METHODS, "*");
+ response.addHeader(RESPONSE_HEADER_ACCESS_CONTROL_MAX_AGE, "3600");
+
+ }
+ /*if (request.getMethod().equals("OPTIONS")) {
+ response.setStatus(HttpServletResponse.SC_OK);
+ }*/
+ }
+ } catch (Exception e) {
+ FineLoggerFactory.getLogger().error(e.getMessage(), e);
+ }
+ }
+
+ private String doLoginFR(HttpServletRequest request, HttpServletResponse response, String username) throws Exception {
+ String frToken = LoginService.getInstance().login(request, response, username);
+ request.setAttribute("fine_auth_token", frToken);
+ return frToken;
+ }
+}
diff --git a/src/main/java/com/fr/plugin/decision/integration/filter/TplParaCheckFilter.java b/src/main/java/com/fr/plugin/decision/integration/filter/TplParaCheckFilter.java
new file mode 100644
index 0000000..189d9ac
--- /dev/null
+++ b/src/main/java/com/fr/plugin/decision/integration/filter/TplParaCheckFilter.java
@@ -0,0 +1,62 @@
+package com.fr.plugin.decision.integration.filter;
+
+import com.fr.decision.fun.impl.AbstractEmbedRequestFilterProvider;
+import com.fr.decision.webservice.v10.login.LoginService;
+import com.fr.general.http.HttpRequest;
+import com.fr.general.http.HttpToolbox;
+import com.fr.json.JSONObject;
+import com.fr.plugin.decision.integration.config.IntegrateConf;
+import com.fr.plugin.decision.integration.utils.CommonUtils;
+import com.fr.stable.ArrayUtils;
+import com.fr.stable.StringUtils;
+import com.fr.third.fasterxml.jackson.databind.ObjectMapper;
+import com.fr.third.org.apache.http.HttpEntity;
+import com.fr.third.org.apache.http.entity.StringEntity;
+import com.fr.web.utils.WebUtils;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class TplParaCheckFilter extends AbstractEmbedRequestFilterProvider {
+
+ private String[] reportParameterNames = new String[]{"viewlet", "viewlets", "reportlet", "reportlets"};
+
+ @Override
+ public void filter(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
+ if (!needCheckParameter(request)) return;
+
+ HashMap requestMap = new HashMap<>();
+ for (String parameter : request.getParameterMap().keySet()) {
+ if (!ArrayUtils.contains(reportParameterNames, parameter))
+ requestMap.put(parameter, request.getParameter(parameter));
+ }
+ requestMap.put("userId", LoginService.getInstance().getCurrentUserNameFromRequestCookie(request));
+
+ try {
+ if (doCheckTplParameter(requestMap)) return;
+ } catch (Exception e) {
+ }
+ CommonUtils.showErrorPage(response, "请登录系统访问报表", "");
+ }
+
+ /**
+ * 请求是否需要校验预览参数
+ **/
+ private boolean needCheckParameter(HttpServletRequest request) {
+ return IntegrateConf.getInstance().getParaCheckTurnOn() &&
+ //LoginService.getInstance().isLogged(request) &&
+ StringUtils.isNotBlank(WebUtils.getReportTitleFromRequest(request));
+ }
+
+ private boolean doCheckTplParameter(Map requestMap) throws IOException {
+ HashMap headerMap = new HashMap();
+ headerMap.put("Content-Type", "application/json");
+ HttpEntity entity = new StringEntity(new ObjectMapper().writeValueAsString(requestMap), "utf-8");
+ String result = HttpToolbox.executeAndParse(HttpRequest.custom().url(IntegrateConf.getInstance().getParaCheckUrl()).post(entity).headers(headerMap).build());
+ return new JSONObject(result).getBoolean("data");
+ }
+}
diff --git a/src/main/java/com/fr/plugin/decision/integration/resource/PluginConfigWebResourceImpl.java b/src/main/java/com/fr/plugin/decision/integration/resource/PluginConfigWebResourceImpl.java
new file mode 100644
index 0000000..6e50e2e
--- /dev/null
+++ b/src/main/java/com/fr/plugin/decision/integration/resource/PluginConfigWebResourceImpl.java
@@ -0,0 +1,27 @@
+package com.fr.plugin.decision.integration.resource;
+
+import com.fr.decision.fun.impl.AbstractWebResourceProvider;
+import com.fr.decision.web.MainComponent;
+import com.fr.plugin.decision.integration.config.IntegrateConf;
+import com.fr.web.struct.Atom;
+import com.fr.web.struct.Component;
+import com.fr.web.struct.Filter;
+import com.fr.web.struct.category.ScriptPath;
+
+public class PluginConfigWebResourceImpl extends AbstractWebResourceProvider {
+
+ @Override
+ public Atom attach() {
+ return MainComponent.KEY;
+ }
+
+ @Override
+ public Atom client() {
+ return new Component() {
+ @Override
+ public ScriptPath script() {
+ return ScriptPath.build("com/fr/plugin/decision/integration/web/js/plugin.js");
+ }
+ };
+ }
+}
diff --git a/src/main/java/com/fr/plugin/decision/integration/resource/WebStartWebResourceImpl.java b/src/main/java/com/fr/plugin/decision/integration/resource/WebStartWebResourceImpl.java
new file mode 100644
index 0000000..a25c46b
--- /dev/null
+++ b/src/main/java/com/fr/plugin/decision/integration/resource/WebStartWebResourceImpl.java
@@ -0,0 +1,43 @@
+package com.fr.plugin.decision.integration.resource;
+
+import com.fr.decision.fun.impl.AbstractWebResourceProvider;
+import com.fr.decision.web.MainComponent;
+import com.fr.plugin.decision.integration.config.IntegrateConf;
+import com.fr.web.struct.Atom;
+import com.fr.web.struct.Component;
+import com.fr.web.struct.Filter;
+import com.fr.web.struct.category.ScriptPath;
+
+/**
+ * @Author JianYe.Wang
+ * @Data 2022/4/1 15:22
+ * @Description TODO
+ * @Version 10.0
+ **/
+public class WebStartWebResourceImpl extends AbstractWebResourceProvider {
+
+ @Override
+ public Atom attach() {
+ return MainComponent.KEY;
+ }
+
+ @Override
+ public Atom client() {
+ return new Component() {
+ @Override
+ public ScriptPath script() {
+ return ScriptPath.build("com/fr/plugin/decision/integration/web/js/web_start.js");
+ }
+
+ @Override
+ public Filter filter() {
+ return new Filter() {
+ @Override
+ public boolean accept() {
+ return IntegrateConf.getInstance().getWebStartDesigner();
+ }
+ };
+ }
+ };
+ }
+}
diff --git a/src/main/java/com/fr/plugin/decision/integration/service/IntegrateAuthService.java b/src/main/java/com/fr/plugin/decision/integration/service/IntegrateAuthService.java
new file mode 100644
index 0000000..987d485
--- /dev/null
+++ b/src/main/java/com/fr/plugin/decision/integration/service/IntegrateAuthService.java
@@ -0,0 +1,254 @@
+package com.fr.plugin.decision.integration.service;
+
+
+import com.fr.cert.token.Claims;
+import com.fr.decision.base.util.UUIDUtil;
+import com.fr.decision.privilege.TransmissionTool;
+import com.fr.decision.webservice.bean.user.UserBean;
+import com.fr.decision.webservice.v10.user.UserService;
+import com.fr.io.utils.ResourceIOUtils;
+import com.fr.plugin.decision.integration.config.strategy.CreatePwdStrategy;
+import com.fr.plugin.decision.integration.bean.user.UserInfoBean;
+import com.fr.plugin.decision.integration.config.IntegrateConf;
+import com.fr.plugin.decision.integration.config.strategy.user.impl.OnlyUserStrategy;
+import com.fr.plugin.decision.integration.exception.JwtKeyNullException;
+import com.fr.plugin.decision.integration.exception.JwtSubjectFormatException;
+import com.fr.plugin.decision.integration.exception.TokenDecodeExpiredException;
+import com.fr.plugin.decision.integration.exception.TokenNullException;
+import com.fr.plugin.decision.integration.utils.*;
+import com.fr.report.ReportContext;
+import com.fr.report.constant.RoleType;
+import com.fr.report.data.RemoteDesignAuthority;
+import com.fr.report.util.RemoteDesignAuthHelper;
+import com.fr.report.util.RemoteUserInfo;
+import com.fr.security.JwtUtils;
+import com.fr.stable.StableUtils;
+import com.fr.stable.StringUtils;
+import com.fr.third.fasterxml.jackson.databind.ObjectMapper;
+import com.fr.transaction.Configurations;
+import com.fr.transaction.WorkerAdaptor;
+import com.fr.web.service.RemoteDesignAuthorityDataService;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.*;
+
+public class IntegrateAuthService {
+
+ private volatile static IntegrateAuthService service;
+ private static Properties properties = new Properties();
+ private IntegrateConf config = IntegrateConf.getInstance();
+ private ObjectMapper mapper = new ObjectMapper();
+
+ private IntegrateAuthService() {
+ }
+
+ public static IntegrateAuthService getInstance() {
+ if (service == null) {
+ synchronized (IntegrateAuthService.class) {
+ if (service == null) {
+ service = new IntegrateAuthService();
+ }
+ }
+ }
+ return service;
+ }
+
+ public String getUserNameFromToken(String thirdToken) throws Exception {
+ if (StringUtils.isEmpty(thirdToken)) {
+ throw new TokenNullException();
+ }
+ String userInfo = decodeJwtToken(thirdToken, loadJwtKey());
+ if (config.getAesEncryptSubject()) {
+ userInfo = AESUtils.aesDecryptByBytes(Base64.getDecoder().decode(userInfo), config.getAesEncryptKey());
+ }
+
+ return addOrUpdateUserInfo(userInfo);
+ }
+
+ private String loadJwtKey() {
+ String key = config.getJwtKey();
+ if (StringUtils.isBlank(key)) {
+ key = properties.getProperty("key");
+ }
+
+ if (StringUtils.isBlank(key)) {
+ throw new JwtKeyNullException();
+ }
+ return key;
+ }
+
+ private String decodeJwtToken(String thirdToken, String key) {
+ Claims claims = JwtUtils.parseJWT(thirdToken, key);
+ Date date = claims.getExpiration();
+ if (date != null && date.after(new Date())) {
+ return claims.getSubject();
+ }
+ throw new TokenDecodeExpiredException();
+ }
+
+
+ /**
+ * 校验更新/创建用户信息
+ *
+ * @param userInfoStr
+ * @return username
+ * @throws Exception
+ */
+ private String addOrUpdateUserInfo(String userInfoStr) throws Exception {
+ // 未开启创建用户时,直接返回用户名不需要创建用户信息
+ if (!config.getCreateUserTurnOn()) {
+ if (CommonUtils.isJsonFormatStr(userInfoStr)) {
+ return mapper.readValue(userInfoStr, UserInfoBean.class).getUsername();
+ };
+ return userInfoStr;
+ }
+
+ // 仅用户名
+ if (config.getUserStrategy() instanceof OnlyUserStrategy) {
+ if (CommonUtils.isJsonFormatStr(userInfoStr)) {
+ throw new JwtSubjectFormatException();
+ }
+ // 用户不存在时基于用户名创建下用户
+ if (!UserUtils.existUsername(userInfoStr)) {
+ UserService.getInstance().addUser(buildBaseUserBean(userInfoStr));
+ checkAndCreateRemoteDesignAuth(userInfoStr);
+ }
+ return userInfoStr;
+ }
+
+ // 含部门职位
+ UserInfoBean userInfo = mapper.readValue(userInfoStr, UserInfoBean.class);
+ // 管理员账号不进行处理
+ if (UserUtils.isAdmin(userInfo.getUsername())) {
+ return userInfo.getUsername();
+ }
+ UserBean userBean;
+ if (!UserUtils.existUsername(userInfo.getUsername())) {
+ UserService.getInstance().addUser(buildBaseUserBean(userInfo.getUsername()));
+ }
+ userBean = UserService.getInstance().getUserAccount(userInfo.getUsername());
+ // 更新部门信息
+ if (null != userInfo.getDepartment()) {
+ userBean.setDepartmentPostIds(UserUtils.updateDepTreeAndPost(
+ userInfo.getDepartment().getName(),
+ userInfo.getDepartment().getId())
+ );
+ UserUtils.updateUserDepartmentPost(UserUtils.getAdminUserId(), userBean);
+ }
+
+ // 更新角色信息
+ if (null != userInfo.getRoles()) {
+ ArrayList roles = new ArrayList<>();
+ for (String roleName : userInfo.getRoles()) {
+ roles.add(UserUtils.checkAndAddRole(roleName));
+ }
+ userBean.setRoleIds(roles.toArray(new String[0]));
+ UserService.getInstance().updateUserRoles(UserUtils.getAdminUserId(), userBean);
+ }
+
+ // 用户平台类型
+ if (null != userInfo.getPlatformType()) {
+ UserUtils.updateUserPlatformType(userBean, userInfo.getPlatformType());
+ }
+
+ // 更新远程设计权限和目录信息
+ checkAndCreateRemoteDesignAuth(userInfo.getUsername());
+ return userInfo.getUsername();
+ }
+
+
+ private UserBean buildBaseUserBean(String username) {
+ UserBean userBean = new UserBean(UUIDUtil.generate(), username, username, true);
+ String pwd;
+ if (config.getUserStrategy().getPwdStrategy() == CreatePwdStrategy.SAME_USER) {
+ pwd = username;
+ } else {
+ pwd = UserUtils.getRandomPassword(8);
+ }
+ userBean.setPassword(TransmissionTool.defaultEncrypt(pwd));
+ return userBean;
+ }
+
+ /**
+ * 校验如果用户没有自己同名模板跟目录文件夹的远程设计权限,则新建添加一下
+ *
+ * @param username
+ */
+ private void checkAndCreateRemoteDesignAuth(String username) throws Exception {
+ if (!config.getRemoteFolder()) return;
+ RemoteUserInfo remoteUserInfo = RemoteDesignAuthHelper.getUserInfo(username);
+ if (remoteUserInfo == null) {
+ return;
+ }
+ String userRemoteFileName = formatUsername(username);
+ List remoteDesignAuthorities = RemoteDesignAuthorityDataService.getInstance().getAuthorities(remoteUserInfo.getUserID());
+ for (RemoteDesignAuthority remoteDesignAuthority : remoteDesignAuthorities) {
+ if (remoteDesignAuthority.getPathType() && StringUtils.equals(userRemoteFileName, remoteDesignAuthority.getPath())) {
+ return;
+ }
+ }
+ String path = StableUtils.pathJoin("reportlets", userRemoteFileName);
+ if (!ResourceIOUtils.isDirectoryExist(path)) {
+ ResourceIOUtils.createDirectory(path);
+ }
+
+ RemoteDesignAuthority authority = new RemoteDesignAuthority();
+ authority.userName(username).userId(remoteUserInfo.getUserID()).path(userRemoteFileName).pathType(true).roleType(RoleType.USER);
+ ReportContext.getInstance().getRemoteDesignAuthorityController().add(authority);
+ }
+
+ private String formatUsername(String username) {
+ return username.replaceAll("[\\\\/:*?\"<>|]", StringUtils.EMPTY);
+ }
+
+ public void updateIntegrateConf(IntegrateConf conf) {
+ Configurations.update(new WorkerAdaptor(IntegrateConf.class) {
+ @Override
+ public void run() {
+ IntegrateConf instance = IntegrateConf.getInstance();
+ instance.setDecisionUrl(conf.getDecisionUrl());
+ instance.setJwtKey(conf.getJwtKey());
+ instance.setRemoteTokenAuth(conf.getRemoteTokenAuth());
+ instance.setAesEncryptSubject(conf.getAesEncryptSubject());
+ instance.setAesEncryptKey(conf.getAesEncryptKey());
+ instance.setPortalURLTurnOn(conf.getPortalURLTurnOn());
+ instance.setPortalURL(conf.getPortalURL());
+ instance.setCrossDomain(conf.getCrossDomain());
+ instance.setRemoteFolder(conf.getRemoteFolder());
+ if (conf.getCreateUserTurnOn()) {
+ instance.setUserStrategy(conf.getUserStrategy());
+ instance.setCreateUserTurnOn(true);
+ } else {
+ instance.setCreateUserTurnOn(false);
+ }
+
+ if (conf.getParaCheckTurnOn()) {
+ instance.setParaCheckUrl(conf.getParaCheckUrl());
+ instance.setParaCheckTurnOn(true);
+ } else {
+ instance.setParaCheckTurnOn(false);
+ }
+
+ if (conf.getWebStartDesigner()) {
+ instance.setDownloadUrl32(conf.getDownloadUrl32());
+ instance.setDownloadUrl64(conf.getDownloadUrl64());
+ instance.setWebStartDesigner(true);
+ } else {
+ instance.setWebStartDesigner(false);
+ }
+ }
+ });
+ }
+
+ static {
+ // 历史参数获取方式,兼容使用
+ InputStream inputStream = ResourceIOUtils.read("/resources/third_auth.properties");
+ if (null != inputStream) {
+ try {
+ properties.load(inputStream);
+ } catch (IOException ignore) {}
+ }
+ }
+
+}
diff --git a/src/main/java/com/fr/plugin/decision/integration/service/IntegrateCustomService.java b/src/main/java/com/fr/plugin/decision/integration/service/IntegrateCustomService.java
new file mode 100644
index 0000000..89b80ac
--- /dev/null
+++ b/src/main/java/com/fr/plugin/decision/integration/service/IntegrateCustomService.java
@@ -0,0 +1,98 @@
+package com.fr.plugin.decision.integration.service;
+
+import com.fr.base.FRContext;
+import com.fr.base.TemplateUtils;
+import com.fr.data.NetworkHelper;
+import com.fr.decision.webservice.v10.user.UserService;
+import com.fr.file.filetree.FileNode;
+import com.fr.file.filetree.FileNodes;
+import com.fr.json.JSONException;
+import com.fr.plugin.decision.integration.bean.TplInfoBean;
+import com.fr.report.DesignAuthority;
+import com.fr.workspace.WorkContext;
+import com.fr.workspace.Workspace;
+import com.fr.workspace.server.authority.AuthorityOperator;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+
+@Deprecated
+public class IntegrateCustomService {
+
+ private static final String FRM_PATH = "/view/form?viewlet=";
+ private static final String CPT_PATH = "/view/report?viewlet=";
+
+ private volatile static IntegrateCustomService service = null;
+
+ public IntegrateCustomService() {
+ }
+
+ public static IntegrateCustomService getInstance() {
+ if (service == null) {
+ synchronized (IntegrateAuthService.class) {
+ if (service == null) {
+ service = new IntegrateCustomService();
+ }
+ }
+ }
+ return service;
+ }
+
+ public String generateDefaultHomePageUrl(HttpServletRequest request) throws Exception {
+ String originalURL = NetworkHelper.getOriginalURL(request);
+ String requestURI = request.getRequestURI();
+ return TemplateUtils.render(originalURL.replace(requestURI, "${fineServletURL}"));
+ }
+
+ public ArrayList getRemoteTplListByUserId(String userId, String servletName) {
+ Workspace workspace = WorkContext.getCurrent();
+ FileNodes root = FRContext.getFileNodes();
+ ArrayList fileNodes = new ArrayList();
+
+ fileNodes.add(getNodeItem("reportlets", "", true, servletName));
+ if (UserService.getInstance().isAdmin(userId)) {
+ getFileNode(fileNodes, root, "reportlets", servletName);
+ } else {
+ DesignAuthority auth = (workspace.get(AuthorityOperator.class)).getAuthorities(userId)[0];
+ for (DesignAuthority.Item item : auth.getItems()) {
+ String path = item.getPath();
+ boolean type = item.getType();
+ fileNodes.add(getNodeItem("reportlets/" + path, "reportlets", type, servletName));
+ if (type) {
+ getFileNode(fileNodes, root, "reportlets/" + path, servletName);
+ }
+ }
+ }
+ return fileNodes;
+ }
+
+ private void getFileNode(ArrayList nodeItem, FileNodes root, String path, String servletName) throws JSONException {
+ FileNode[] fNodes = root.list(path, false);
+ for (int i = 0; i < fNodes.length; i++) {
+ FileNode fileNode = fNodes[i];
+ String fileNodePath = fileNode.getEnvPath();
+ nodeItem.add(getNodeItem(fileNodePath, path, fileNode.isDirectory(), servletName));
+ if (fileNode.isDirectory()) {
+ getFileNode(nodeItem, root, fileNodePath, servletName);
+ }
+ }
+ }
+
+ private TplInfoBean getNodeItem(String path, String pId, boolean isDirectory, String servletName) {
+ boolean isCpt = false;
+ String menuId;
+ if (path.contains(".cpt")) {
+ isCpt = true;
+ menuId = path.replace(".cpt", "");
+ } else {
+ menuId = path.replace(".frm", "");
+ }
+ if (!isDirectory) {
+ menuId = new StringBuffer().append(menuId).append("_f").toString();
+ }
+ path = path.replace("reportlets/", "");
+ String nodeName = menuId.replaceAll(pId + "/", "");
+
+ return new TplInfoBean(menuId, nodeName, isDirectory ? "" : isCpt ? servletName + CPT_PATH + path : servletName + FRM_PATH + path, pId);
+ }
+}
diff --git a/src/main/java/com/fr/plugin/decision/integration/utils/AESUtils.java b/src/main/java/com/fr/plugin/decision/integration/utils/AESUtils.java
new file mode 100644
index 0000000..037f812
--- /dev/null
+++ b/src/main/java/com/fr/plugin/decision/integration/utils/AESUtils.java
@@ -0,0 +1,68 @@
+package com.fr.plugin.decision.integration.utils;
+
+import com.fr.base.Base64;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * @Author JianYe.Wang
+ * @Data 2021/8/16 15:06
+ * @Description TODO
+ * @Version 10.0
+ **/
+public class AESUtils {
+
+ private static final String AES_ECB_PKCS_5_PADDING = "AES/ECB/PKCS5Padding";
+ private static final String HMAC_SHA_256 = "HmacSHA256";
+
+ /**
+ * AES加密
+ *
+ * @param content 待加密的内容
+ * @param encryptKey 加密密钥
+ * @return 加密后的byte[]
+ */
+ public static byte[] aesEncryptToBytes(String content, String encryptKey) throws Exception {
+ KeyGenerator kgen = KeyGenerator.getInstance("AES");
+ kgen.init(128);
+ Cipher cipher = Cipher.getInstance(AES_ECB_PKCS_5_PADDING);
+ cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), "AES"));
+ return cipher.doFinal(content.getBytes("utf-8"));
+ }
+
+ /**
+ * AES解密
+ *
+ * @param encryptBytes 待解密的byte[]
+ * @param decryptKey 解密密钥
+ * @return 解密后的String
+ */
+ public static String aesDecryptByBytes(byte[] encryptBytes, String decryptKey) throws Exception {
+ KeyGenerator kgen = KeyGenerator.getInstance("AES");
+ kgen.init(128);
+
+ Cipher cipher = Cipher.getInstance(AES_ECB_PKCS_5_PADDING);
+ cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptKey.getBytes(), "AES"));
+ byte[] decryptBytes = cipher.doFinal(encryptBytes);
+
+ return new String(decryptBytes, "utf-8");
+ }
+
+ /**
+ * HmacSHA256 签名
+ *
+ * @param content
+ * @param signKey
+ * @return base64 encode
+ * @throws Exception
+ */
+ public static String hmacSHA256Sign(String content, String signKey) throws Exception {
+ Mac sha256Hmac = Mac.getInstance(HMAC_SHA_256);
+ SecretKeySpec secret_key = new SecretKeySpec(signKey.getBytes(), HMAC_SHA_256);
+ sha256Hmac.init(secret_key);
+ return Base64.encode(sha256Hmac.doFinal(content.getBytes()));
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/decision/integration/utils/CommonUtils.java b/src/main/java/com/fr/plugin/decision/integration/utils/CommonUtils.java
new file mode 100644
index 0000000..4ba7382
--- /dev/null
+++ b/src/main/java/com/fr/plugin/decision/integration/utils/CommonUtils.java
@@ -0,0 +1,90 @@
+package com.fr.plugin.decision.integration.utils;
+
+import com.fr.data.NetworkHelper;
+import com.fr.decision.webservice.Response;
+import com.fr.decision.webservice.utils.WebServiceUtils;
+import com.fr.intelligence.IntelligenceException;
+import com.fr.json.JSONObject;
+import com.fr.log.FineLoggerFactory;
+import com.fr.stable.StringUtils;
+import com.fr.web.utils.WebUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+public class CommonUtils {
+
+ public static boolean isJsonFormatStr(String str) {
+ if (StringUtils.isNotBlank(str)) {
+ str = str.trim();
+ try {
+ new JSONObject(str);
+ return true;
+ } catch (Exception e) {
+ }
+ }
+ return false;
+ }
+
+ public static void showErrorPage(HttpServletResponse response, String result, String solution) {
+ try {
+ PrintWriter printWriter = WebUtils.createPrintWriter(response);
+ Map map = new HashMap<>();
+ map.put("result", result);
+ map.put("solution", solution);
+ String page = WebServiceUtils.parseWebPageResourceSafe("com/fr/web/controller/decision/entrance/resources/unavailable.html", map);
+ printWriter.write(page);
+ printWriter.flush();
+ printWriter.close();
+ } catch (Exception e) {
+ LogUtils.error(e.getLocalizedMessage(), e);
+ }
+ }
+
+ public static boolean isMobile(HttpServletRequest req) {
+ String[] mobileArray = {"iPhone", "iPad", "android", "windows phone", "xiaomi"};
+ String userAgent = req.getHeader("user-agent");
+ // if (userAgent != null && userAgent.toUpperCase().contains("MOBILE")) {
+ // 华为PAD的chrome浏览器userAgent中不包含mobile
+ if (userAgent != null) {
+ for (String mobile : mobileArray) {
+ if (userAgent.toLowerCase().contains(mobile.toLowerCase())) {
+ return true;
+ }
+ }
+ }
+ //pc端企业微信
+ //if (userAgent.contains("wxwork")) return true;
+ FineLoggerFactory.getLogger().debug("isMobile userAgent:{} is:{}", userAgent, NetworkHelper.getDevice(req).isMobile());
+ return NetworkHelper.getDevice(req).isMobile();
+ }
+
+ public static Response formatException(Exception ex) {
+ String msg = ex.getMessage();
+ if (StringUtils.isBlank(msg)) {
+ msg = getStackTraceInfo(ex);
+ }
+
+ if (ex instanceof IntelligenceException) {
+ return Response.error(((IntelligenceException) ex).errorCode(), msg);
+ }
+
+ return Response.error("", msg);
+ }
+
+ //从异常中获取具体的堆栈信息
+ private static String getStackTraceInfo(Exception ex) {
+ StackTraceElement[] stackElements = ex.getStackTrace();
+ StringBuilder stringBuilder = new StringBuilder("\r\n");
+ if (stackElements != null) {
+ for (StackTraceElement stackElement : stackElements) {
+ stringBuilder.append(stackElement.getMethodName()).append("\r\n");
+ }
+ return stringBuilder.toString();
+ }
+ return ex.toString();
+ }
+}
diff --git a/src/main/java/com/fr/plugin/decision/integration/utils/Constants.java b/src/main/java/com/fr/plugin/decision/integration/utils/Constants.java
new file mode 100644
index 0000000..35de271
--- /dev/null
+++ b/src/main/java/com/fr/plugin/decision/integration/utils/Constants.java
@@ -0,0 +1,18 @@
+package com.fr.plugin.decision.integration.utils;
+
+/**
+ * @Author JianYe.Wang
+ * @Data 2021/12/3 14:05
+ * @Description TODO
+ * @Version 10.0
+ **/
+public class Constants {
+
+ public static final int HOLD = -1;
+
+ // third token名称
+ public static final String TOKEN_NAME = "third_token";
+ // token的默认超期时间
+ public static final long EXPIRED_TIME = 1000 * 60 * 10;
+
+}
diff --git a/src/main/java/com/fr/plugin/decision/integration/utils/LogUtils.java b/src/main/java/com/fr/plugin/decision/integration/utils/LogUtils.java
new file mode 100644
index 0000000..d33d79f
--- /dev/null
+++ b/src/main/java/com/fr/plugin/decision/integration/utils/LogUtils.java
@@ -0,0 +1,96 @@
+package com.fr.plugin.decision.integration.utils;
+
+import com.fr.log.FineLoggerFactory;
+import com.fr.log.FineLoggerProvider;
+import com.fr.plugin.context.PluginContexts;
+import com.fr.stable.StringUtils;
+
+public final class LogUtils {
+ private static String LOG_PREFIX = "[平台集成] ";
+ private static final String PLUGIN_VERSION;
+ private static final FineLoggerProvider LOGGER = FineLoggerFactory.getLogger();
+
+ static {
+ // 判断 null
+ String version = PluginContexts.currentContext().getMarker().getVersion();
+ if (StringUtils.isNotBlank(version)) {
+ PLUGIN_VERSION = "[v" + version + "] ";
+ } else {
+ PLUGIN_VERSION = "[unknown version] ";
+ }
+
+ LOG_PREFIX = LOG_PREFIX + PLUGIN_VERSION;
+ }
+
+ public static void setPrefix(String prefix) {
+ if (prefix != null) {
+ LOG_PREFIX = prefix;
+ }
+ }
+
+ public static boolean isDebugEnabled() {
+ return LOGGER.isDebugEnabled();
+ }
+
+ public static void debug(String s) {
+ if (isDebugEnabled()) {
+ LOGGER.debug(LOG_PREFIX + s);
+ }
+ }
+
+ public static void debug(String s, Object... objects) {
+ if (isDebugEnabled()) {
+ LOGGER.debug(LOG_PREFIX + s, objects);
+ }
+ }
+
+ public static void debug(String s, Throwable throwable) {
+ if (isDebugEnabled()) {
+ LOGGER.debug(LOG_PREFIX + s, throwable);
+ }
+ }
+
+ public static boolean isInfoEnabled() {
+ return LOGGER.isInfoEnabled();
+ }
+
+ public static void info(String s) {
+ LOGGER.info(LOG_PREFIX + s);
+ }
+
+ public static void info(String s, Object... objects) {
+ LOGGER.info(LOG_PREFIX + s, objects);
+ }
+
+ public static void warn(String s) {
+ LOGGER.warn(LOG_PREFIX + s);
+ }
+
+ public static void warn(String s, Object... objects) {
+ LOGGER.warn(LOG_PREFIX + s, objects);
+ }
+
+ public static void warn(String s, Throwable throwable) {
+ LOGGER.warn(LOG_PREFIX + s, throwable);
+ }
+
+ public static void warn(Throwable throwable, String s, Object... objects) {
+ LOGGER.warn(throwable, LOG_PREFIX + s, objects);
+ }
+
+ public static void error(String s) {
+ LOGGER.error(LOG_PREFIX + s);
+ }
+
+ public static void error(String s, Object... objects) {
+ LOGGER.error(LOG_PREFIX + s, objects);
+ }
+
+ public static void error(String s, Throwable throwable) {
+ LOGGER.error(LOG_PREFIX + s, throwable);
+ }
+
+ public static void error(Throwable throwable, String s, Object... objects) {
+ LOGGER.error(throwable, LOG_PREFIX + s, objects);
+ }
+}
diff --git a/src/main/java/com/fr/plugin/decision/integration/utils/UserUtils.java b/src/main/java/com/fr/plugin/decision/integration/utils/UserUtils.java
new file mode 100644
index 0000000..dfdab11
--- /dev/null
+++ b/src/main/java/com/fr/plugin/decision/integration/utils/UserUtils.java
@@ -0,0 +1,417 @@
+package com.fr.plugin.decision.integration.utils;
+
+import com.finebi.cbb.utils.StringEscapeUtils;
+import com.fr.decision.authority.AuthorityContext;
+import com.fr.decision.authority.base.constant.type.operation.ManualOperationType;
+import com.fr.decision.authority.controller.UserController;
+import com.fr.decision.authority.data.*;
+import com.fr.decision.authority.data.extra.user.type.PlatformUserKey;
+import com.fr.decision.authority.data.extra.user.type.UserProductTypeKey;
+import com.fr.decision.authority.data.personnel.DepRole;
+import com.fr.decision.authority.entity.CustomRoleEntity;
+import com.fr.decision.base.util.CollectionUtil;
+import com.fr.decision.base.util.UUIDUtil;
+import com.fr.decision.webservice.annotation.RoleCheckerType;
+import com.fr.decision.webservice.bean.user.DepartmentPostBean;
+import com.fr.decision.webservice.bean.user.UserBean;
+import com.fr.decision.webservice.bean.user.UserUpdateBean;
+import com.fr.decision.webservice.exception.user.AddUserProductTypeException;
+import com.fr.decision.webservice.impl.user.type.UserProductType;
+import com.fr.decision.webservice.utils.ControllerFactory;
+import com.fr.decision.webservice.utils.UserSourceFactory;
+import com.fr.decision.webservice.utils.WebServiceUtils;
+import com.fr.decision.webservice.v10.user.DepartmentService;
+import com.fr.decision.webservice.v10.user.PositionService;
+import com.fr.decision.webservice.v10.user.UserService;
+import com.fr.general.ComparatorUtils;
+import com.fr.plugin.decision.integration.config.IntegrateConf;
+import com.fr.plugin.decision.integration.config.strategy.user.impl.ContainDepRoleStrategy;
+import com.fr.stable.ArrayUtils;
+import com.fr.stable.StringUtils;
+import com.fr.stable.db.data.BaseDataRecord;
+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.*;
+
+public class UserUtils {
+
+ public static final String POST_DEFAULT_NAME = "职员";
+ public static final String DEP_ROOT_NAME = null;
+
+ public static boolean existUsername(String username) throws Exception {
+ return UserService.getInstance().getUserByUserName(username) != null;
+ }
+
+ public static String getAdminUserId() throws Exception {
+ return UserService.getInstance().getAdminUserIdList().get(0);
+ }
+
+ public static boolean isAdmin(String username) throws Exception {
+ return UserService.getInstance().getAdminUserNameList().contains(username);
+ }
+
+ public static List updateDepTreeAndPost(String depName, String depId) throws Exception {
+ ArrayList result = new ArrayList<>();
+ ContainDepRoleStrategy depStrategy = (ContainDepRoleStrategy) IntegrateConf.getInstance().getUserStrategy();
+ switch (depStrategy.getDepStrategy()) {
+ case DEP_ID_LENGTH:
+ generateDepByIdLength(depName, depId);
+ break;
+ case DEP_NAME_GROUP:
+ depId = generateDepByName(depName, depStrategy.getSeparator());
+ break;
+ }
+ String postId = generateDefaultPosition(depId);
+ result.add(WebServiceUtils.createUniquePostId(depId, postId));
+ return result;
+ }
+
+ /**
+ * 添加部门前先计算出最匹配的父节点,添加完之后在更新父节点下子节点的父信息
+ *
+ * @param depName
+ * @param depId
+ * @return
+ * @throws Exception
+ */
+ private static String generateDepByIdLength(String depName, String depId) throws Exception {
+ Department department = AuthorityContext.getInstance().getDepartmentController().getById(depId);
+ if (department == null) {
+ String depPId = getMostSimilarDepId(depId);
+ if (checkDuplicatedDepartmentName(depPId, depName)) {
+ depName = depName + "_" + depId;
+ }
+ addDepartment(depName, depId, depPId);
+ // 更新depPId下子节点的父信息
+ generateUnderParentDepartment(depPId, depId);
+ }
+ return depId;
+ }
+
+ /**
+ * 根据部门名称拼接来更新部门
+ *
+ * @param depNameGroup
+ * @return 部门id
+ */
+ private static String generateDepByName(String depNameGroup, String separator) throws Exception {
+ String[] depArr = depNameGroup.split(StringEscapeUtils.escapeRegexSpecialWord(separator));
+ String pId = DEP_ROOT_NAME;
+ for (String depName : depArr) {
+ Department department = getDepartmentByNameAndPid(depName, pId);
+ if (department != null) {
+ pId = department.getId();
+ } else {
+ String depId = UUIDUtil.generate();
+ addDepartment(depName, depId, pId);
+ pId = depId;
+ }
+ }
+ return pId;
+ }
+
+ private static boolean existDepId(String depId) throws Exception {
+ return AuthorityContext.getInstance().getDepartmentController().getById(depId) != null;
+ }
+
+ private static Department getDepartmentByNameAndPid(String depName, String pId) throws Exception {
+ return AuthorityContext.getInstance().getDepartmentController().findOne(
+ QueryFactory.create().addRestriction(RestrictionFactory.eq(Department.COLUMN_NAME, depName))
+ .addRestriction(RestrictionFactory.eq(Department.COLUMN_PARENT_ID, pId))
+ );
+ }
+
+ private static boolean 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);
+ return sameNameDep != null;
+ }
+
+ /**
+ * 添加部门
+ *
+ * @param depName
+ * @param depId
+ * @param depPId
+ * @throws Exception
+ */
+ private static void addDepartment(String depName, String depId, String depPId) throws Exception {
+ Department record = (new Department()).id(depId).name(depName).parentId(depPId).creationType(ManualOperationType.KEY).lastOperationType(ManualOperationType.KEY).enable(true);
+ AuthorityContext.getInstance().getDepartmentController().add(record);
+ }
+
+ private static void generateUnderParentDepartment(String depPId, String depId) throws Exception {
+ for (DepartmentPostBean departmentPostBean : DepartmentService.getInstance().getDepartmentsUnderParentDepartment(getAdminUserId(), depPId)) {
+ if (ComparatorUtils.equals(depId, departmentPostBean.getId())) continue;
+ String mostSimilarDepId = getMostSimilarDepId(departmentPostBean.getId());
+ if (!ComparatorUtils.equals(mostSimilarDepId, depPId)) {
+ Department record = AuthorityContext.getInstance().getDepartmentController().getById(departmentPostBean.getId());
+ record.setParentId(mostSimilarDepId);
+ AuthorityContext.getInstance().getDepartmentController().update(record);
+ }
+ }
+ }
+
+ private static List getAllDepartment() throws Exception {
+ return AuthorityContext.getInstance().getDepartmentController().find(QueryFactory.create());
+ }
+
+ /**
+ * 校验部门下是否有默认 POST_DEFAULT_NAME 职位
+ *
+ * @param departmentId
+ * @return POST_DEFAULT_NAME 职位的 id
+ * @throws Exception
+ */
+ private static String generateDefaultPosition(String departmentId) throws Exception {
+ String adminId = UserService.getInstance().getAdminUserIdList().get(0);
+ Post[] posts = ControllerFactory.getInstance().getPostController(adminId).getPositionsUnderParentDepartment(adminId, departmentId, null);
+ for (Post post : posts) {
+ if (ComparatorUtils.equals(post.getName(), POST_DEFAULT_NAME)) {
+ return post.getId();
+ }
+ }
+ String positionId = getDefaultPostId();
+ AuthorityContext.getInstance().getPostController().addPostToDepartment(positionId, departmentId);
+ return positionId;
+ }
+
+ /**
+ * 校验是否有 POST_DEFAULT_NAME 职位,没有的话新建个
+ *
+ * @return POST_DEFAULT_NAME 职位的 id
+ * @throws Exception
+ */
+ private static String getDefaultPostId() throws Exception {
+ String adminId = UserService.getInstance().getAdminUserIdList().get(0);
+ Post[] posts = ControllerFactory.getInstance().getPostController(adminId).getPositions(adminId, null);
+ for (Post post : posts) {
+ if (ComparatorUtils.equals(post.getName(), POST_DEFAULT_NAME)) {
+ return post.getId();
+ }
+ }
+
+ return PositionService.getInstance().addPosition(POST_DEFAULT_NAME).getId();
+ }
+
+ /**
+ * 计算出与 depId 最相似的父节点
+ *
+ * @param depId
+ * @return
+ * @throws Exception
+ */
+ private static String getMostSimilarDepId(String depId) throws Exception {
+ int similarLevel = 0;
+ String mostSimilarDepId = DEP_ROOT_NAME;
+ String specialDepRootId = ((ContainDepRoleStrategy) IntegrateConf.getInstance().getUserStrategy()).getSpecialRootId();
+ if (StringUtils.isNotBlank(specialDepRootId)) {
+ if (ComparatorUtils.equals(depId, specialDepRootId)) {
+ return mostSimilarDepId;
+ } else if (existDepId(specialDepRootId)) {
+ mostSimilarDepId = specialDepRootId;
+ }
+ }
+ for (Department department : getAllDepartment()) {
+ String currentDepId = department.getId();
+ // 部门id长度大于当前id长度的直接跳过,因为部门的树结构时根据id其实是否一致判断的
+ if (currentDepId.length() > depId.length() || ComparatorUtils.equals(currentDepId, depId)) continue;
+ int currentLevel = getDepIdSimilarityDegree(currentDepId, depId);
+ if (currentLevel > similarLevel) {
+ similarLevel = currentLevel;
+ mostSimilarDepId = department.getId();
+ }
+ }
+ return mostSimilarDepId;
+ }
+
+ private static int getDepIdSimilarityDegree(String depId1, String depId2) {
+ String shortId, longId;
+ if (depId1.length() > depId2.length()) {
+ shortId = depId2;
+ longId = depId1;
+ } else {
+ shortId = depId1;
+ longId = depId2;
+ }
+
+ if (longId.startsWith(shortId)) {
+ return shortId.length();
+ }
+ return 0;
+ }
+
+ public static String checkAndAddRole(String roleName) throws Exception {
+ CustomRole customRole = AuthorityContext.getInstance().getCustomRoleController().findOne(
+ QueryFactory.create().addRestriction(
+ RestrictionFactory.eq(CustomRoleEntity.COLUMN_NAME, roleName))
+ );
+ if (null != customRole) return customRole.getId();
+ String roleId = UUIDUtil.generate();
+ addRole(roleName, roleId);
+ return roleId;
+ }
+
+ public static void addRole(String roleName, String roleId) throws Exception {
+ String adminId = UserService.getInstance().getAdminUserIdList().get(0);
+ CustomRole record = (new CustomRole()).id(roleId).name(roleName);
+ ControllerFactory.getInstance().getCustomRoleController(adminId).addCustomRole(adminId, record);
+ }
+
+ /**
+ * 更新用户的部门信息
+ *
+ * @param currentUserId
+ * @param userBean
+ * @throws Exception
+ */
+ public static void updateUserDepartmentPost(String currentUserId, UserBean userBean) throws Exception {
+ if (userBean.getDepartmentPostIds() != null) {
+ List newDepRoleList = new LinkedList();
+ Iterator depRoleIt = userBean.getDepartmentPostIds().iterator();
+
+ while (depRoleIt.hasNext()) {
+ String depPostId = (String) depRoleIt.next();
+ DepRole depRole = WebServiceUtils.parseUniqueDepartmentPostId(depPostId);
+ newDepRoleList.add(AuthorityContext.getInstance().getDepartmentController().getDepRole(depRole.getDepartmentId(), depRole.getPostId()));
+ }
+
+ List oldDepRoleList = AuthorityContext.getInstance().getDepartmentController().findDepRoleByUser(userBean.getId());
+ Map newIdDepRoleMap = CollectionUtil.convertToMap(newDepRoleList, BaseDataRecord::getId);
+ Map oldIdDepRoleMap = CollectionUtil.convertToMap(oldDepRoleList, BaseDataRecord::getId);
+ List newDepRoleIdList = new LinkedList(newIdDepRoleMap.keySet());
+ List oldDepRoleIdList = new LinkedList(oldIdDepRoleMap.keySet());
+ List addDepRoleIdList = new LinkedList(newDepRoleIdList);
+ List removeDepRoleIdList = new LinkedList(oldDepRoleIdList);
+ addDepRoleIdList.removeAll(oldDepRoleIdList);
+ removeDepRoleIdList.removeAll(newDepRoleIdList);
+ User targetUser = UserService.getInstance().getUserByUserId(userBean.getId());
+ Iterator addDepRoleIt = addDepRoleIdList.iterator();
+
+ String removeDepRoleId;
+ DepRole depRole;
+ while (addDepRoleIt.hasNext()) {
+ removeDepRoleId = (String) addDepRoleIt.next();
+ depRole = newIdDepRoleMap.get(removeDepRoleId);
+ if (RoleCheckerType.DEPARTMENT.checkAuthority(currentUserId, depRole.getDepartmentId())) {
+ Department department = AuthorityContext.getInstance().getDepartmentController().getById(depRole.getDepartmentId());
+ Post post = AuthorityContext.getInstance().getPostController().getById(depRole.getPostId());
+ UserSourceFactory.getInstance().checkSource(targetUser, department);
+ UserSourceFactory.getInstance().checkSource(targetUser, post);
+ AuthorityContext.getInstance().getUserController().addUserToDepartmentAndPost(targetUser.getId(), depRole.getDepartmentId(), depRole.getPostId());
+ }
+ }
+
+ addDepRoleIt = removeDepRoleIdList.iterator();
+
+ while (addDepRoleIt.hasNext()) {
+ removeDepRoleId = (String) addDepRoleIt.next();
+ depRole = oldIdDepRoleMap.get(removeDepRoleId);
+ if (RoleCheckerType.DEPARTMENT.checkAuthority(currentUserId, depRole.getDepartmentId())) {
+ AuthorityContext.getInstance().getUserController().removeUserFromDepartmentAndPost(targetUser.getId(), depRole.getDepartmentId(), depRole.getPostId());
+ }
+ }
+ }
+ }
+
+
+ /**
+ * 更新用户平台的类型
+ *
+ * @param userBean
+ * @param platformTypeList
+ * @throws Exception
+ */
+ public static void updateUserPlatformType(UserBean userBean, List platformTypeList) throws Exception {
+ UserController userController = AuthorityContext.getInstance().getUserController();
+ // 查询用户的平台用户类型
+ List extraProperties = userController.findUserExtraProperty(QueryFactory.create().addRestriction(
+ RestrictionFactory.eq(ExtraProperty.COLUMN_RELEATED_ID, userBean.getId())
+ ).addRestriction(
+ RestrictionFactory.startWith(ExtraProperty.COLUMN_NAME, UserProductTypeKey.KEY.getKey())
+ ));
+ List newIdList = new LinkedList(platformTypeList);
+ List oldIdList = new LinkedList();
+ for (ExtraProperty property : extraProperties) {
+ UserProductTypeKey userProductTypeKey = (UserProductTypeKey) PlatformUserKey.fromKey(property.getName());
+ oldIdList.add(UserProductType.fromKey(userProductTypeKey).toInteger());
+ }
+
+ // 校验新的用户平台类型是否冲突
+ for (int i=0; i addIdList = new LinkedList(newIdList);
+ List removeIdList = new LinkedList(oldIdList);
+ addIdList.removeAll(oldIdList);
+ removeIdList.removeAll(newIdList);
+
+ // 删除用户原有平台类型
+ for (Integer i : removeIdList) {
+ userController.removeUserProductType(userBean.getId(), UserProductType.fromInteger(i).transProductKey());
+ }
+
+ // 添加新增部分的平台类型
+ out:
+ for (Integer platformType : addIdList) {
+ UserProductType userProductType = UserProductType.fromInteger(platformType);
+ if (!userProductType.isNoLimitEditOpen()) {
+ continue out;
+ }
+
+ if (userProductType.allowMax() > 0 && 1 > userProductType.leftRegisterTotal()) {
+ throw new AddUserProductTypeException();
+ }
+ userController.addUserProductType(userBean.getId(), userProductType.transProductKey());
+ }
+ }
+
+ /**
+ * 随机生成强密码
+ *
+ * @param len
+ * @return
+ */
+ public static String getRandomPassword(int len) {
+ // 密码字典
+ char[] str = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '~', '!', '@', '#', '$', '%', '^', '-', '+'
+ };
+ StringBuffer pwd = null;
+ boolean flag = false;
+ while (!flag) {
+ int a = 0, b = 0, c = 0, d = 0;
+ pwd = new StringBuffer();
+ for (int i = 0; i < len; i++) {
+ int rand = (int) (Math.random() * str.length);
+ pwd.append(str[rand]);
+ if (rand < 26) {
+ a++;
+ } else if (rand < 52) {
+ b++;
+ } else if (rand < 62) {
+ c++;
+ } else {
+ d++;
+ }
+ }
+ flag = (a * b * c * d != 0);
+ }
+ return pwd.toString();
+ }
+}
diff --git a/src/main/resources/com/fr/plugin/decision/integration/web/js/button_group.item.js b/src/main/resources/com/fr/plugin/decision/integration/web/js/button_group.item.js
new file mode 100644
index 0000000..5d7f867
--- /dev/null
+++ b/src/main/resources/com/fr/plugin/decision/integration/web/js/button_group.item.js
@@ -0,0 +1,42 @@
+!(function () {
+ var Item = BI.inherit(BI.Widget, {
+ props: {
+ value: "",
+ items: [],
+ changeAction: BI.emptyFn()
+ },
+
+ render: function () {
+ var self = this, o = this.options;
+ return {
+ type: "bi.button_group",
+ value: o.value + "",
+ layouts: [{
+ type: "bi.vertical_adapt"
+ }],
+ items: BI.createItems(o.items, {
+ type: "bi.single_select_radio_item",
+ height: 16,
+ logic: {
+ dynamic: true
+ },
+ cls: "bi-list-item-none"
+ }),
+ listeners: [{
+ eventName: BI.ButtonGroup.EVENT_CHANGE,
+ action: function (e) {
+ o.changeAction && o.changeAction(e);
+ }
+ }],
+ ref: function (_ref) {
+ self.group = _ref;
+ }
+ }
+ },
+
+ getValue: function () {
+ return this.group.getValue()[0];
+ }
+ });
+ BI.shortcut("dec.plugin.system.integration.group", Item);
+})();
\ No newline at end of file
diff --git a/src/main/resources/com/fr/plugin/decision/integration/web/js/constant.js b/src/main/resources/com/fr/plugin/decision/integration/web/js/constant.js
new file mode 100644
index 0000000..2b94198
--- /dev/null
+++ b/src/main/resources/com/fr/plugin/decision/integration/web/js/constant.js
@@ -0,0 +1,17 @@
+!(function () {
+ BI.constant("plugin.integration.userCreate", [{
+ value: "0", text: "仅用户名", userType: "onlyUser"
+ }, {
+ value: "1", text: "包含部门职位", userType: "containDepRole"
+ }]);
+ BI.constant("plugin.integration.pwd", [{
+ value: "0", text: "同用户名"
+ }, {
+ value: "1", text: "随机密码"
+ }]);
+ BI.constant("plugin.integration.dep", [{
+ value: "0", text: "部门ID长度构建部门树"
+ }, {
+ value: "1", text: "部门名称拼接构建部门树"
+ }]);
+})();
\ No newline at end of file
diff --git a/src/main/resources/com/fr/plugin/decision/integration/web/js/model.pane.js b/src/main/resources/com/fr/plugin/decision/integration/web/js/model.pane.js
new file mode 100644
index 0000000..7126684
--- /dev/null
+++ b/src/main/resources/com/fr/plugin/decision/integration/web/js/model.pane.js
@@ -0,0 +1,44 @@
+!(function () {
+ var user_only = BI.Constants.getConstant("plugin.integration.userCreate")[0],
+ user_roledep = BI.Constants.getConstant("plugin.integration.userCreate")[1];
+ var Model = BI.inherit(Fix.Model, {
+ state: function () {
+ return {
+ config: {}
+ }
+ },
+
+ computed: {
+ userType: function () {
+ return this.model.config.userStrategy.userType === user_only.userType ? user_only.value : user_roledep.value;
+ }
+ },
+
+ actions: {
+ initData: function (cb) {
+ var self = this;
+ Dec.Utils.getPluginIntegrateConfig(function (res) {
+ if (!res.errorCode) {
+ self.model.config = res;
+ cb();
+ }
+ })
+ },
+
+ changeUserType: function (v) {
+ this.model.config.userStrategy.userType = (v === user_only.value ? user_only.userType : user_roledep.userType)
+ },
+
+ saveData: function (value) {
+ Dec.Utils.updatePluginIntegrateConfig(value, function (res) {
+ if (res.data === "success") {
+ BI.Msg.toast("保存成功!", {level: "success"})
+ } else {
+ BI.Msg.toast("保存失败!", {level: "error"})
+ }
+ })
+ }
+ }
+ });
+ BI.model("dec.plugin.system.integration.model", Model);
+})();
\ No newline at end of file
diff --git a/src/main/resources/com/fr/plugin/decision/integration/web/js/pane.js b/src/main/resources/com/fr/plugin/decision/integration/web/js/pane.js
new file mode 100644
index 0000000..52c25af
--- /dev/null
+++ b/src/main/resources/com/fr/plugin/decision/integration/web/js/pane.js
@@ -0,0 +1,510 @@
+!(function () {
+ var WIDGET_HEIGHT = 24;
+ var user_only = BI.Constants.getConstant("plugin.integration.userCreate")[0],
+ user_roledep = BI.Constants.getConstant("plugin.integration.userCreate")[1];
+ var dep_legnth = BI.Constants.getConstant("plugin.integration.dep")[0];
+
+ var Pane = BI.inherit(BI.Widget, {
+ props: {
+ baseCls: "dec-plugin-system-integration"
+ },
+ _store: function () {
+ return BI.Models.getModel("dec.plugin.system.integration.model");
+ },
+ beforeInit: function (cb) {
+ this.store.initData(cb);
+ },
+ render: function () {
+ var self = this;
+ var createUserInfoPane = {
+ type: "bi.vertical",
+ lgap: 10, bgap: 10,
+ ref: function (_ref) {
+ self.createUserInfoPane = _ref;
+ },
+ invisible: !self.model.config.createUserTurnOn,
+ items: [{
+ type: "bi.vertical_adapt",
+ height: 24,
+ items: [{
+ type: "bi.label",
+ textAlign: "left",
+ cls: "dec-font-weight-bold",
+ text: "创建远程连接同名目录",
+ title: "创建远程连接同名目录",
+ width: 210
+ }, {
+ type: "dec.switch_button",
+ value: self.model.config.remoteFolder,
+ ref: function (_ref) {
+ self.remoteFolder = _ref;
+ }
+ }]
+ }, {
+ type: "bi.vertical_adapt",
+ height: WIDGET_HEIGHT,
+ items: [{
+ type: "bi.label",
+ textAlign: "left",
+ cls: "dec-font-weight-bold",
+ text: "用户创建策略",
+ width: 210
+ }, {
+ type: "dec.plugin.system.integration.group",
+ value: self.model.userType,
+ items: BI.Constants.getConstant("plugin.integration.userCreate"),
+ changeAction: function (e) {
+ self.depStrategyPane.setVisible(e === user_roledep.value);
+ self.store.changeUserType(e);
+ },
+ ref: function (_ref) {
+ self.userCreate = _ref;
+ }
+ }]
+ }, {
+ type: "bi.vertical_adapt",
+ height: WIDGET_HEIGHT,
+ items: [{
+ type: "bi.label",
+ textAlign: "left",
+ cls: "dec-font-weight-bold",
+ text: "密码策略",
+ width: 210
+ }, {
+ type: "dec.plugin.system.integration.group",
+ value: self.model.config.userStrategy.pwdStrategy,
+ items: BI.Constants.getConstant("plugin.integration.pwd"),
+ ref: function (_ref) {
+ self.pwd = _ref;
+ }
+ }]
+ }, {
+ type: "bi.vertical",
+ bgap: 10,
+ invisible: self.model.userType === user_only.value,
+ items: [{
+ type: "bi.vertical_adapt",
+ height: WIDGET_HEIGHT,
+ items: [{
+ type: "bi.label",
+ textAlign: "left",
+ cls: "dec-font-weight-bold",
+ text: "创建部门策略",
+ width: 210
+ }, {
+ type: "dec.plugin.system.integration.group",
+ value: self.model.config.userStrategy.depStrategy || dep_legnth.value,
+ items: BI.Constants.getConstant("plugin.integration.dep"),
+ ref: function (_ref) {
+ self.dep = _ref;
+ },
+ changeAction: function (e) {
+ var depLen = e == 0;
+ self.specialRootId.setVisible(depLen);
+ self.separator.setVisible(!depLen);
+ },
+ }]
+ }, {
+ type: "dec.label.editor.item",
+ textWidth: 210,
+ invisible: BI.isKey(self.model.config.userStrategy.depStrategy) && self.model.config.userStrategy.depStrategy != 0,
+ cls: "dec-font-weight-bold",
+ text: "特殊根部门ID",
+ editorWidth: 180,
+ value: self.model.config.userStrategy.specialRootId,
+ ref: function (_ref) {
+ self.specialRootId = _ref
+ }
+ }, {
+ type: "dec.label.editor.item",
+ textWidth: 210,
+ invisible: self.model.config.userStrategy.depStrategy != 1,
+ cls: "dec-font-weight-bold",
+ text: "分隔符",
+ editorWidth: 180,
+ value: self.model.config.userStrategy.separator,
+ ref: function (_ref) {
+ self.separator = _ref
+ }
+ }],
+ ref: function (_ref) {
+ self.depStrategyPane = _ref;
+ }
+ }]
+ };
+
+
+ return {
+ type: "bi.vertical",
+ cls: "bi-card",
+ hgap: 10,
+ items: [{
+ type: "dec.card.vertical",
+ text: "配置项",
+ content: {
+ type: "bi.vertical",
+ vgap: 10,
+ items: [{
+ type: "dec.label.editor.item",
+ textWidth: 210,
+ cls: "dec-font-weight-bold",
+ text: "帆软平台地址",
+ title: "帆软平台地址",
+ watermark: "http(s)://ip:port/webroot/decision",
+ editorWidth: 300,
+ value: self.model.config.decisionUrl,
+ ref: function (_ref) {
+ self.decisionUrl = _ref
+ }
+ }, {
+ type: "dec.label.editor.item",
+ textWidth: 210,
+ cls: "dec-font-weight-bold",
+ text: "Jwt 密钥值",
+ editorWidth: 300,
+ value: self.model.config.jwtKey,
+ ref: function (_ref) {
+ self.jwtKey = _ref
+ }
+ }, {
+ type: "bi.vertical_adapt",
+ height: 24,
+ items: [{
+ type: "bi.label",
+ textAlign: "left",
+ cls: "dec-font-weight-bold",
+ text: "认证完成创建用户",
+ title: "Jwt Token认证用户在平台中不存在时,自动在平台中创建用户信息",
+ width: 210
+ }, {
+ type: "dec.switch_button",
+ value: self.model.config.createUserTurnOn,
+ listeners: [{
+ eventName: "EVENT_CHANGE",
+ action: function () {
+ self.createUserInfoPane.setVisible(this.getValue());
+ }
+ }],
+ ref: function (_ref) {
+ self.createUserTurnOn = _ref;
+ }
+ }]
+ }, createUserInfoPane, {
+ type: "bi.vertical_adapt",
+ height: 24,
+ items: [{
+ type: "bi.label",
+ textAlign: "left",
+ cls: "dec-font-weight-bold",
+ text: "模板预览参数校验",
+ width: 210
+ }, {
+ type: "dec.switch_button",
+ value: self.model.config.paraCheckTurnOn,
+ listeners: [{
+ eventName: "EVENT_CHANGE",
+ action: function () {
+ self.paraCheckPane.setVisible(this.getValue());
+ }
+ }],
+ ref: function (_ref) {
+ self.paraCheckTurnOn = _ref;
+ }
+ }]
+ }, {
+ type: "bi.vertical",
+ lgap: 10, bgap: 10,
+ invisible: !self.model.config.paraCheckTurnOn,
+ items: [{
+ type: "dec.label.editor.item",
+ textWidth: 210,
+ cls: "dec-font-weight-bold",
+ text: "校验地址",
+ title: "校验地址",
+ editorWidth: 180,
+ value: self.model.config.paraCheckUrl,
+ ref: function (_ref) {
+ self.paraCheckUrl = _ref
+ }
+ }],
+ ref: function (_ref) {
+ self.paraCheckPane = _ref
+ }
+ }, {
+ type: "bi.vertical_adapt",
+ height: 24,
+ items: [{
+ type: "bi.label",
+ textAlign: "left",
+ cls: "dec-font-weight-bold",
+ text: "远程设计连接开启token校验",
+ width: 210
+ }, {
+ type: "dec.switch_button",
+ value: self.model.config.remoteTokenAuth,
+ ref: function (_ref) {
+ self.remoteTokenAuth = _ref;
+ }
+ }]
+ }, {
+ type: "bi.vertical_adapt",
+ height: 24,
+ items: [{
+ type: "bi.label",
+ textAlign: "left",
+ cls: "dec-font-weight-bold",
+ text: "用户信息是否进行AES加密",
+ title: "Jwt Token中的 subject 字段是否使用 AES/ECB/PKCS5Padding 加密",
+ width: 210
+ }, {
+ type: "dec.switch_button",
+ value: self.model.config.aesEncryptSubject,
+ ref: function (_ref) {
+ self.aesEncryptSubjectButton = _ref;
+ },
+ listeners: [{
+ eventName: "EVENT_CHANGE",
+ action: function () {
+ self.aesEncryptKeyPane.setVisible(this.getValue());
+ }
+ }],
+ }]
+ }, {
+ type: "bi.vertical",
+ lgap: 10, bgap: 10,
+ invisible: !self.model.config.aesEncryptSubject,
+ items: [{
+ type: "dec.label.editor.item",
+ textWidth: 210,
+ cls: "dec-font-weight-bold",
+ text: "AES加密密钥(16位)",
+ title: "AES加密密钥",
+ editorWidth: 180,
+ value: self.model.config.aesEncryptKey,
+ ref: function (_ref) {
+ self.aesEncryptKeyEditor = _ref
+ }
+ }],
+ ref: function (_ref) {
+ self.aesEncryptKeyPane = _ref
+ }
+ }, /*{
+ type: "bi.vertical_adapt",
+ height: 24,
+ items: [{
+ type: "bi.label",
+ textAlign: "left",
+ cls: "dec-font-weight-bold",
+ text: "自定义登录页",
+ title: "页面未登录时跳转至指定URL地址替代帆软登录页",
+ width: 210
+ }, {
+ type: "dec.switch_button",
+ value: self.model.config.portalURLTurnOn,
+ ref: function (_ref) {
+ self.portalURLTurnOnBtn = _ref;
+ },
+ listeners: [{
+ eventName: "EVENT_CHANGE",
+ action: function () {
+ self.portalURLPane.setVisible(this.getValue());
+ }
+ }],
+ }]
+ }, {
+ type: "bi.vertical",
+ lgap: 10, bgap: 10,
+ invisible: !self.model.config.portalURLTurnOn,
+ items: [{
+ type: "dec.label.editor.item",
+ textWidth: 210,
+ cls: "dec-font-weight-bold",
+ text: "地址",
+ title: "自定义登录页地址",
+ editorWidth: 180,
+ value: self.model.config.portalURL,
+ ref: function (_ref) {
+ self.portalURLEditor = _ref
+ }
+ }],
+ ref: function (_ref) {
+ self.portalURLPane = _ref
+ }
+ }, */{
+ type: "bi.vertical_adapt",
+ height: 24,
+ items: [{
+ type: "bi.label",
+ textAlign: "left",
+ cls: "dec-font-weight-bold",
+ text: "跨域功能",
+ title: "开启后会在 response 中会添加允许的请求头",
+ width: 210
+ }, {
+ type: "dec.switch_button",
+ value: self.model.config.crossDomain,
+ ref: function (_ref) {
+ self.crossDomainBtn = _ref;
+ }
+ }]
+ }]
+ },
+ listeners: [{
+ eventName: "EVENT_CHANGE",
+ action: function () {
+ self.validate() && self.store.saveData(self.getValue());
+ }
+ }]
+ }, {
+ type: "bi.vertical_adapt",
+ height: 24,
+ items: [{
+ type: "bi.label",
+ textAlign: "left",
+ cls: "dec-font-weight-bold",
+ text: "显示唤醒设计器",
+ title: "平台首页地址显示唤醒设计器功能",
+ width: 210
+ }, {
+ type: "dec.switch_button",
+ value: self.model.config.webStartDesigner,
+ ref: function (_ref) {
+ self.webStartDesigner = _ref;
+ },
+ listeners: [{
+ eventName: "EVENT_CHANGE",
+ action: function () {
+ self.downloadURLPane.setVisible(this.getValue());
+ }
+ }],
+ }]
+ }, {
+ type: "bi.vertical",
+ lgap: 10, vgap: 10,
+ invisible: !self.model.config.webStartDesigner,
+ items: [{
+ type: "dec.label.editor.item",
+ textWidth: 210,
+ cls: "dec-font-weight-bold",
+ text: "64位设计器下载地址",
+ title: "64位设计器下载地址",
+ editorWidth: 500,
+ value: self.model.config.downloadUrl64,
+ ref: function (_ref) {
+ self.downloadUrl64 = _ref
+ }
+ }, {
+ type: "dec.label.editor.item",
+ textWidth: 210,
+ cls: "dec-font-weight-bold",
+ text: "32位设计器下载地址",
+ title: "32位设计器下载地址",
+ editorWidth: 500,
+ value: self.model.config.downloadUrl32,
+ ref: function (_ref) {
+ self.downloadUrl32 = _ref
+ }
+ }],
+ ref: function (_ref) {
+ self.downloadURLPane = _ref
+ }
+ }]
+
+ }
+ },
+
+ aesValidate: function () {
+ var editor = this.aesEncryptKeyEditor;
+ return editor.getValue().length == 16 ? true : (editor.showError("AES密钥长度需为16"), false);
+ },
+
+ validate: function () {
+ if (!BI.isKey(this.decisionUrl.getValue())) {
+ BI.Msg.toast("帆软平台地址不允许为空", {level: "error"});
+ return false;
+ }
+ if (this.aesEncryptSubjectButton.getValue()) {
+ return this.aesValidate();
+ }
+ if (this.webStartDesigner.getValue()) {
+ if (!BI.isKey(this.downloadUrl32.getValue())) {
+ BI.Msg.toast("32位下载地址不允许为空", {level: "error"});
+ return false;
+ }
+ if (!BI.isKey(this.downloadUrl64.getValue())) {
+ BI.Msg.toast("64位下载地址不允许为空", {level: "error"});
+ return false;
+ }
+ }
+ return true;
+ },
+
+ getValue: function () {
+ var self = this;
+ var value = {
+ decisionUrl: self.decisionUrl.getValue(),
+ jwtKey: self.jwtKey.getValue(),
+ remoteTokenAuth: self.remoteTokenAuth.getValue(),
+ aesEncryptSubject: self.aesEncryptSubjectButton.getValue(),
+ aesEncryptKey: self.aesEncryptKeyEditor.getValue(),
+ /*portalURLTurnOn: self.portalURLTurnOnBtn.getValue(),
+ portalURL: self.portalURLEditor.getValue(),*/
+ crossDomain: self.crossDomainBtn.getValue(),
+ remoteFolder: self.remoteFolder.getValue()
+ };
+
+ if (self.createUserTurnOn.getValue()) {
+ var userStrategy = {
+ pwdStrategy: self.pwd.getValue()
+ };
+ switch (self.userCreate.getValue()) {
+ case user_only.value:
+ BI.extend(userStrategy, {
+ userType: user_only.userType
+ });
+ break;
+ case user_roledep.value:
+ BI.extend(userStrategy, {
+ userType: user_roledep.userType,
+ depStrategy: self.dep.getValue(),
+ specialRootId: self.specialRootId.getValue(),
+ separator: self.separator.getValue()
+ });
+ break;
+ }
+ BI.extend(value, {
+ createUserTurnOn: true,
+ userStrategy: userStrategy
+ });
+ } else {
+ BI.extend(value, {
+ createUserTurnOn: false
+ })
+ }
+
+ if (self.paraCheckTurnOn.getValue()) {
+ BI.extend(value, {
+ paraCheckTurnOn: true,
+ paraCheckUrl: self.paraCheckUrl.getValue()
+ })
+ } else {
+ BI.extend(value, {
+ paraCheckTurnOn: false
+ })
+ }
+
+ var webStartDesigner = self.webStartDesigner.getValue();
+ value.webStartDesigner = webStartDesigner;
+ if (webStartDesigner) {
+ BI.extend(value, {
+ downloadUrl64: self.downloadUrl64.getValue(),
+ downloadUrl32: self.downloadUrl32.getValue(),
+ })
+ }
+ console.log(value);
+ return value;
+ }
+ });
+
+ BI.shortcut("dec.plugin.system.integration", Pane);
+})();
\ No newline at end of file
diff --git a/src/main/resources/com/fr/plugin/decision/integration/web/js/plugin.js b/src/main/resources/com/fr/plugin/decision/integration/web/js/plugin.js
new file mode 100644
index 0000000..3dfbf97
--- /dev/null
+++ b/src/main/resources/com/fr/plugin/decision/integration/web/js/plugin.js
@@ -0,0 +1,35 @@
+!(function () {
+ BI.Plugin.config(function (type, options) {
+ }, function (type, object) {
+ object.element.attr("shortcut", object.options.type);
+ });
+
+ Dec.Utils = Dec.Utils || {};
+ BI.extend(Dec.Utils, {
+ getPluginIntegrateConfig: function (callback) {
+ Dec.reqGet("/third/auth/conf", "", callback);
+ },
+ updatePluginIntegrateConfig: function (val, callback) {
+ Dec.reqPost("/third/auth/conf", val, callback);
+ },
+ getWebStartInfo: function (callback) {
+ Dec.reqGet("/third/auth/username/token", "", callback);
+ }
+ });
+
+ BI.config("dec.constant.system.tabs", function (items) {
+ items.push({
+ value: "integration",
+ text: "登录集成",
+ cardType: "dec.plugin.system.integration"
+ });
+ return items;
+ });
+
+ var commonPath = Dec.fineServletURL + "/file?path=com/fr/plugin/decision/integration/web/js/";
+ BI.$import(commonPath + "constant.js");
+ BI.$import(commonPath + "button_group.item.js");
+ BI.$import(commonPath + "model.pane.js");
+ BI.$import(commonPath + "pane.js");
+ BI.$import(commonPath + "protocolcheck.js");
+})();
\ No newline at end of file
diff --git a/src/main/resources/com/fr/plugin/decision/integration/web/js/protocolcheck.js b/src/main/resources/com/fr/plugin/decision/integration/web/js/protocolcheck.js
new file mode 100644
index 0000000..31bba67
--- /dev/null
+++ b/src/main/resources/com/fr/plugin/decision/integration/web/js/protocolcheck.js
@@ -0,0 +1,263 @@
+(function (f) {
+ if (typeof exports === "object" && typeof module !== "undefined") {
+ module.exports = f()
+ } else if (typeof define === "function" && define.amd) {
+ define([], f)
+ } else {
+ var g;
+ if (typeof window !== "undefined") {
+ g = window
+ } else if (typeof global !== "undefined") {
+ g = global
+ } else if (typeof self !== "undefined") {
+ g = self
+ } else {
+ g = this
+ }
+ g.protocolCheck = f()
+ }
+})(function () {
+ var define, module, exports;
+ return (function e(t, n, r) {
+ function s(o, u) {
+ if (!n[o]) {
+ if (!t[o]) {
+ var a = typeof require == "function" && require;
+ if (!u && a) return a(o, !0);
+ if (i) return i(o, !0);
+ var f = new Error("Cannot find module '" + o + "'");
+ throw f.code = "MODULE_NOT_FOUND", f
+ }
+ var l = n[o] = {exports: {}};
+ t[o][0].call(l.exports, function (e) {
+ var n = t[o][1][e];
+ return s(n ? n : e)
+ }, l, l.exports, e, t, n, r)
+ }
+ return n[o].exports
+ }
+
+ var i = typeof require == "function" && require;
+ for (var o = 0; o < r.length; o++) s(r[o]);
+ return s
+ })({
+ 1: [function (require, module, exports) {
+ function _registerEvent(target, eventType, cb) {
+ if (target.addEventListener) {
+ target.addEventListener(eventType, cb);
+ return {
+ remove: function () {
+ target.removeEventListener(eventType, cb);
+ }
+ };
+ } else {
+ target.attachEvent(eventType, cb);
+ return {
+ remove: function () {
+ target.detachEvent(eventType, cb);
+ }
+ };
+ }
+ }
+
+ function _createHiddenIframe(target, uri) {
+ var iframe = document.createElement("iframe");
+ iframe.src = uri;
+ iframe.id = "hiddenIframe";
+ iframe.style.display = "none";
+ target.appendChild(iframe);
+
+ return iframe;
+ }
+
+ function openUriWithHiddenFrame(uri, failCb, successCb) {
+
+ var timeout = setTimeout(function () {
+ failCb();
+ handler.remove();
+ }, 1000);
+
+ var iframe = document.querySelector("#hiddenIframe");
+ if (!iframe) {
+ iframe = _createHiddenIframe(document.body, "about:blank");
+ }
+
+ var handler = _registerEvent(window, "blur", onBlur);
+
+ function onBlur() {
+ clearTimeout(timeout);
+ handler.remove();
+ successCb();
+ }
+
+ iframe.contentWindow.location.href = uri;
+ }
+
+ function openUriWithTimeoutHack(uri, failCb, successCb) {
+
+ var timeout = setTimeout(function () {
+ failCb();
+ handler.remove();
+ }, 1000);
+
+ //handle page running in an iframe (blur must be registered with top level window)
+ var target = window;
+ while (target != target.parent) {
+ target = target.parent;
+ }
+
+ var handler = _registerEvent(target, "blur", onBlur);
+
+ function onBlur() {
+ clearTimeout(timeout);
+ handler.remove();
+ successCb();
+ }
+
+ window.location = uri;
+ }
+
+ function openUriUsingFirefox(uri, failCb, successCb) {
+ var iframe = document.querySelector("#hiddenIframe");
+
+ if (!iframe) {
+ iframe = _createHiddenIframe(document.body, "about:blank");
+ }
+
+ try {
+ iframe.contentWindow.location.href = uri;
+ successCb();
+ } catch (e) {
+ if (e.name == "NS_ERROR_UNKNOWN_PROTOCOL") {
+ alert("Un Kown!")
+ failCb();
+ }
+ }
+ }
+
+ function openUriWithIE11UsingRegistry(uri, failCb, successCb) {
+ var shell = new ActiveXObject("WScript.shell");
+ try {
+ var reg = shell.RegRead("HKEY_CLASSES_ROOT\\glcloud\\URL Protocol");
+ if (reg) {
+ console.log(reg);
+ window.location.href = uri;
+ }
+ successCb();
+ } catch (e) {
+ failCb();
+ }
+
+ }
+
+ function openUriUsingIEInOlderWindows(uri, failCb, successCb) {
+ if (getInternetExplorerVersion() === 10) {
+ openUriUsingIE10InWindows7(uri, failCb, successCb);
+ } else if (getInternetExplorerVersion() === 9 || getInternetExplorerVersion() === 11) {
+ /*openUriWithHiddenFrame(uri, failCb, successCb);*/
+ openUriWithIE11UsingRegistry(uri, failCb, successCb);
+ } else {
+ openUriInNewWindowHack(uri, failCb, successCb);
+ }
+ }
+
+ function openUriUsingIE10InWindows7(uri, failCb, successCb) {
+ var timeout = setTimeout(failCb, 6000);
+ window.addEventListener("blur", function () {
+ clearTimeout(timeout);
+ successCb();
+ });
+
+ var iframe = document.querySelector("#hiddenIframe");
+ if (!iframe) {
+ iframe = _createHiddenIframe(document.body, "about:blank");
+ }
+ try {
+ iframe.contentWindow.location.href = uri;
+ } catch (e) {
+ failCb();
+ clearTimeout(timeout);
+ }
+ }
+
+ function openUriInNewWindowHack(uri, failCb, successCb) {
+ var myWindow = window.open('', '', 'width=0,height=0');
+
+ myWindow.document.write("");
+
+ setTimeout(function () {
+ try {
+ myWindow.location.href;
+ myWindow.setTimeout("window.close()", 1000);
+ successCb();
+ } catch (e) {
+ myWindow.close();
+ failCb();
+ }
+ }, 1000);
+ }
+
+ function openUriWithMsLaunchUri(uri, failCb, successCb) {
+ navigator.msLaunchUri(uri,
+ successCb,
+ failCb
+ );
+ }
+
+ function checkBrowser() {
+ var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
+ return {
+ isOpera: isOpera,
+ isFirefox: typeof InstallTrigger !== 'undefined',
+ isSafari: Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0,
+ isChrome: !!window.chrome && !isOpera,
+ isIE: /*@cc_on!@*/false || !!document.documentMode // At least IE6
+ }
+ }
+
+ function getInternetExplorerVersion() {
+ var rv = -1;
+ if (navigator.appName === "Microsoft Internet Explorer") {
+ var ua = navigator.userAgent;
+ var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
+ if (re.exec(ua) != null)
+ rv = parseFloat(RegExp.$1);
+ } else if (navigator.appName === "Netscape") {
+ var ua = navigator.userAgent;
+ var re = new RegExp("Trident/.*rv:([0-9]{1,}[\.0-9]{0,})");
+ if (re.exec(ua) != null) {
+ rv = parseFloat(RegExp.$1);
+ }
+ }
+ return rv;
+ }
+
+ module.exports = function (uri, failCb, successCb) {
+ function failCallback() {
+ failCb && failCb();
+ }
+
+ function successCallback() {
+ successCb && successCb();
+ }
+
+ if (navigator.msLaunchUri) { //for IE and Edge in Win 8 and Win 10
+ openUriWithMsLaunchUri(uri, failCb, successCb);
+ } else {
+ var browser = checkBrowser();
+
+ if (browser.isFirefox) {
+ openUriUsingFirefox(uri, failCallback, successCallback);
+ } else if (browser.isChrome) {
+ openUriWithTimeoutHack(uri, failCallback, successCallback);
+ } else if (browser.isIE) {
+ openUriUsingIEInOlderWindows(uri, failCallback, successCallback);
+ } else {
+ //not supported, implement please
+ }
+ }
+ }
+
+ }, {}]
+ }, {}, [1])(1)
+});
diff --git a/src/main/resources/com/fr/plugin/decision/integration/web/js/web_start.js b/src/main/resources/com/fr/plugin/decision/integration/web/js/web_start.js
new file mode 100644
index 0000000..a3d830f
--- /dev/null
+++ b/src/main/resources/com/fr/plugin/decision/integration/web/js/web_start.js
@@ -0,0 +1,26 @@
+!(function () {
+ BI.config("dec.constant.header.items", function (items) {
+ return BI.concat([{
+ type: "bi.button",
+ text: "启动设计器",
+ ghost: true,
+ css: {color: "white"},
+ handler: function () {
+ Dec.Utils.getWebStartInfo(function (res) {
+ if (res.data) {
+ console.log(res);
+ window.protocolCheck(res.data.url, function () {
+ var agent = navigator.userAgent.toLowerCase();
+ var is64 = agent.indexOf("win64") >= 0 || agent.indexOf("wow64") >= 0 || agent.indexOf("x64") >= 0;
+ var downloadURL = is64 ? res.data.download64 : res.data.download32;
+ console.log(downloadURL);
+ window.open(downloadURL);
+ })
+ } else {
+ BI.Msg.toast(res.errorMsg, {level: "error"})
+ }
+ })
+ }
+ }], items);
+ });
+})();
\ No newline at end of file