diff --git a/README.md b/README.md index 30165b8..1a93767 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ # open-JSD-8016 -JSD-8016 开源任务材料 \ No newline at end of file +JSD-8016 开源任务代码\ +免责说明:该源码为第三方爱好者提供,不保证源码和方案的可靠性,也不提供任何形式的源码教学指导和协助!\ +仅作为开发者学习参考使用!禁止用于任何商业用途!\ +为保护开发者隐私,开发者信息已隐去!若原开发者希望公开自己的信息,可联系hugh处理。 \ No newline at end of file diff --git a/doc/钉钉扫码多公司版本配置说明.docx b/doc/钉钉扫码多公司版本配置说明.docx new file mode 100644 index 0000000..d00e10f Binary files /dev/null and b/doc/钉钉扫码多公司版本配置说明.docx differ diff --git a/lib/fr-plugin-dingtalk-10.4.983.jar b/lib/fr-plugin-dingtalk-10.4.983.jar new file mode 100644 index 0000000..a4f0202 Binary files /dev/null and b/lib/fr-plugin-dingtalk-10.4.983.jar differ diff --git a/lib/hutool-all-4.3.2.jar b/lib/hutool-all-4.3.2.jar new file mode 100644 index 0000000..e651efb Binary files /dev/null and b/lib/hutool-all-4.3.2.jar differ diff --git a/lib/taobao-sdk-java-auto_1479188381469-20200420.jar b/lib/taobao-sdk-java-auto_1479188381469-20200420.jar new file mode 100644 index 0000000..0366c65 Binary files /dev/null and b/lib/taobao-sdk-java-auto_1479188381469-20200420.jar differ diff --git a/plugin.xml b/plugin.xml new file mode 100644 index 0000000..9dec2a4 --- /dev/null +++ b/plugin.xml @@ -0,0 +1,39 @@ + + + com.fr.plugin.sln5470 + + yes + 1.1.5 + 10.0 + 2021-01-01 + fr.open + + + + [2020-04-22]项目启动

+

[2020-11-19]修复路径404bug

+

[2020-11-19]只开启扫码功能

+

[2021-02-02]插件对于新版本钉钉插件依赖关系,必须要安装钉钉插件后才能使用。

+

[2021-05-26]适配新版本登录界面按钮。

+ ]]> +
+ com.fr.plugin + + + + + + + + + + + + + + + + +
+ diff --git a/src/main/java/com/fr/plugin/AllScanBeans.java b/src/main/java/com/fr/plugin/AllScanBeans.java new file mode 100644 index 0000000..f797756 --- /dev/null +++ b/src/main/java/com/fr/plugin/AllScanBeans.java @@ -0,0 +1,59 @@ +package com.fr.plugin; + +import com.fr.decision.fun.impl.BaseHttpHandler; +import com.fr.decision.webservice.Response; +import com.fr.plugin.beans.ScanConfigBean; +import com.fr.plugin.context.PluginContexts; +import com.fr.third.fasterxml.jackson.core.JsonGenerationException; +import com.fr.third.fasterxml.jackson.databind.JsonMappingException; +import com.fr.third.fasterxml.jackson.databind.ObjectMapper; +import com.fr.third.springframework.web.bind.annotation.RequestMethod; +import com.fr.web.utils.WebUtils; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.util.List; + +public class AllScanBeans extends BaseHttpHandler { + @Override + public RequestMethod getMethod() { + return RequestMethod.GET; + } + + @Override + public String getPath() { + return "/getConfigBean"; + } + + @Override + public boolean isPublic() { + return true; + } + + @Override + public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { + List beanList = SCDBAccessProvider.like(null); + for (ScanConfigBean scanConfigBean : beanList) { + scanConfigBean.setAppSecret(""); + scanConfigBean.setAppIdSecret(""); + scanConfigBean.setAppKey(""); + } + Response ok = Response.ok(beanList); + WebUtils.printAsString(httpServletResponse,serialize(ok)); + } + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + public static String serialize(Object object) { + Writer write = new StringWriter(); + try { + objectMapper.writeValue(write, object); + } catch (Exception e) { + e.printStackTrace(); + } + return write.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/com/fr/plugin/ConfigComponent.java b/src/main/java/com/fr/plugin/ConfigComponent.java new file mode 100644 index 0000000..52fa698 --- /dev/null +++ b/src/main/java/com/fr/plugin/ConfigComponent.java @@ -0,0 +1,51 @@ +package com.fr.plugin; + +import com.fr.web.struct.Component; +import com.fr.web.struct.Filter; +import com.fr.web.struct.browser.RequestClient; +import com.fr.web.struct.category.FileType; +import com.fr.web.struct.category.ScriptPath; +import com.fr.web.struct.category.StylePath; + +public class ConfigComponent extends Component { + public static final ConfigComponent KEY = new ConfigComponent(); + + /** + * 返回需要引入的JS脚本路径 + * + * @param client 请求客户端描述 + * @return JS脚本路径 + */ + @Override + public ScriptPath script(RequestClient client) { + return ScriptPath.build("com/fr/plugin/web/dds/dds.js"); + } + + /** + * 返回需要引入的CSS样式路径 + * + * @param client 请求客户端描述 + * @return CSS样式路径 + */ + @Override + public StylePath style(RequestClient client) { + //如果不需要就直接返回 StylePath.EMPTY; + return StylePath.EMPTY; + } + + /** + * 通过给定的资源过滤器控制是否加载这个资源 + * + * @return 资源过滤器 + */ + @Override + public Filter filter() { + return new Filter() { + @Override + public boolean accept() { + //任何情况下我们都在平台组件加载时加载我们的组件 + return true; + } + }; + } +} diff --git a/src/main/java/com/fr/plugin/ConfigJSHander.java b/src/main/java/com/fr/plugin/ConfigJSHander.java new file mode 100644 index 0000000..7745183 --- /dev/null +++ b/src/main/java/com/fr/plugin/ConfigJSHander.java @@ -0,0 +1,33 @@ +package com.fr.plugin; + +import com.fr.decision.fun.impl.AbstractWebResourceProvider; +import com.fr.decision.web.MainComponent; +import com.fr.web.struct.Atom; + +public class ConfigJSHander extends AbstractWebResourceProvider { + + /** + * 需要附加到的主组件 + * + * @return 主组件 + */ + + @Override + public Atom attach() { + return MainComponent.KEY; + } + + /** + * 客户端所需的组件 + * + * @return 组件 + */ + @Override + public Atom client() { + return ConfigComponent.KEY; + } + + public Atom[] clients() { + return new Atom[]{this.client()}; + } +} diff --git a/src/main/java/com/fr/plugin/GetQRHandle.java b/src/main/java/com/fr/plugin/GetQRHandle.java new file mode 100644 index 0000000..ed51c50 --- /dev/null +++ b/src/main/java/com/fr/plugin/GetQRHandle.java @@ -0,0 +1,42 @@ +package com.fr.plugin; + +import com.fr.decision.fun.impl.BaseHttpHandler; +import com.fr.plugin.context.PluginContexts; +import com.fr.third.springframework.web.bind.annotation.RequestMethod; +import com.fr.web.utils.WebUtils; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.HashMap; + +public class GetQRHandle extends BaseHttpHandler { + @Override + public RequestMethod getMethod() { + return null; + } + + @Override + public String getPath() { + return "/getQR"; + } + + @Override + public boolean isPublic() { + return true; + } + + @Override + public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { + ScanConfig instance = ScanConfig.getInstance(); + HashMap params = new HashMap<>(); + params.put("appId",instance.getAppId()); + params.put("callback",httpServletRequest.getParameter("callback")); + if (PluginContexts.currentContext().isAvailable()) { + // 做认证通过的事情 + WebUtils.writeOutTemplate("com/fr/new.html",httpServletResponse,params); + } else { + // 做认证未通过的事情 + WebUtils.writeOutTemplate("com/fr/buynew.html",httpServletResponse,params); + } + } +} diff --git a/src/main/java/com/fr/plugin/HtmlUtils.java b/src/main/java/com/fr/plugin/HtmlUtils.java new file mode 100644 index 0000000..dbd1925 --- /dev/null +++ b/src/main/java/com/fr/plugin/HtmlUtils.java @@ -0,0 +1,128 @@ +package com.fr.plugin; + +import com.dingtalk.api.DefaultDingTalkClient; +import com.dingtalk.api.DingTalkClient; +import com.dingtalk.api.request.OapiSnsGetuserinfoBycodeRequest; +import com.dingtalk.api.request.OapiUserGetRequest; +import com.dingtalk.api.request.OapiUserGetUseridByUnionidRequest; +import com.dingtalk.api.response.OapiSnsGetuserinfoBycodeResponse; +import com.dingtalk.api.response.OapiUserGetResponse; +import com.dingtalk.api.response.OapiUserGetUseridByUnionidResponse; +import com.fr.json.JSONObject; +import com.fr.log.FineLoggerFactory; +import com.fr.third.javax.annotation.Nullable; +import com.taobao.api.ApiException; + +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; + +public class HtmlUtils { + public static String getUserUnid(String appId, String sercet, String code) { + DefaultDingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/sns/getuserinfo_bycode"); + OapiSnsGetuserinfoBycodeRequest req = new OapiSnsGetuserinfoBycodeRequest(); + req.setTmpAuthCode(code); + try { + OapiSnsGetuserinfoBycodeResponse response = client.execute(req, appId, sercet); + FineLoggerFactory.getLogger().info("获取用户UNid详情:" + response.getBody()); + String unionid = response.getUserInfo().getUnionid(); + FineLoggerFactory.getLogger().info("获取用户unid:" + unionid); + return unionid; + } catch (ApiException e) { + e.printStackTrace(); + } + return ""; + } + + public static OapiUserGetResponse getUserMobByUnid(String unid, String appId, String appKey) throws Exception { + + DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/user/getUseridByUnionid"); + OapiUserGetUseridByUnionidRequest request = new OapiUserGetUseridByUnionidRequest(); + request.setUnionid(unid); + request.setHttpMethod("GET"); + String accessToken = getAccessToken(appId, appKey); + OapiUserGetUseridByUnionidResponse response = client.execute(request, accessToken); + FineLoggerFactory.getLogger().info("获取用户userId详情:" + response.getBody()); + if (response.getErrcode() != 0) { + throw new Exception("访问接口报错:" + response.getErrmsg()); + } + String userid = response.getUserid(); + FineLoggerFactory.getLogger().info("获取用户UserId:" + userid); + OapiUserGetRequest userrequest = new OapiUserGetRequest(); + client = new DefaultDingTalkClient("https://oapi.dingtalk.com/user/get"); + userrequest.setUserid(userid); + userrequest.setHttpMethod("GET"); + OapiUserGetResponse response1 = client.execute(userrequest, accessToken); + FineLoggerFactory.getLogger().info("获取用户mob详情:" + response1.getBody()); + String mobile = response1.getMobile(); + FineLoggerFactory.getLogger().info("获取用户手机号:" + mobile); + return response1; + } + + private static String getAccessToken(String appid, String appkey) { + String url = String.format("https://oapi.dingtalk.com/gettoken?appkey=%s&appsecret=%s", appid + , appkey); + JSONObject jsonObject = httpRequest(url, "GET", null); + if (jsonObject.getInt("errcode") == 0) { + return jsonObject.getString("access_token"); + } + return ""; + } + + private static void closeStream(Closeable var0) { + if (var0 != null) { + try { + var0.close(); + } catch (IOException var2) { + } + + } + } + + private static int CONNECTION_TIME_OUT = 120000; + private static int READ_TIME_OUT = 120000; + + @Nullable + public static JSONObject httpRequest(String var0, String var1, String var2) { + JSONObject var3 = null; + StringBuilder var4 = new StringBuilder(); + HttpURLConnection var5 = null; + + try { + URL var6 = new URL(var0); + var5 = (HttpURLConnection) var6.openConnection(); + var5.setUseCaches(false); + var5.setRequestMethod(var1); + var5.setConnectTimeout(CONNECTION_TIME_OUT); + var5.setReadTimeout(READ_TIME_OUT); + if (var2 != null) { + var5.setDoOutput(true); + var5.setRequestProperty("Content-Type", "application/json; charset=utf-8"); + OutputStream var7 = var5.getOutputStream(); + var7.write(var2.getBytes("UTF-8")); + closeStream(var7); + } + + InputStream var16 = var5.getInputStream(); + InputStreamReader var8 = new InputStreamReader(var16, "UTF-8"); + BufferedReader var9 = new BufferedReader(var8); + + String var10; + while ((var10 = var9.readLine()) != null) { + var4.append(var10); + } + + closeStream(var9); + closeStream(var8); + closeStream(var16); + var3 = new JSONObject(var4.toString()); + return var3; + } catch (Exception var14) { + } finally { + if (var5 != null) { + var5.disconnect(); + } + } + return null; + } +} diff --git a/src/main/java/com/fr/plugin/LDAPLocalFinder.java b/src/main/java/com/fr/plugin/LDAPLocalFinder.java new file mode 100644 index 0000000..aa55b05 --- /dev/null +++ b/src/main/java/com/fr/plugin/LDAPLocalFinder.java @@ -0,0 +1,10 @@ +package com.fr.plugin; + +import com.fr.stable.fun.impl.AbstractLocaleFinder; + +public class LDAPLocalFinder extends AbstractLocaleFinder { + @Override + public String find() { + return "/com/fr/plugin/i18n/lan"; + } +} diff --git a/src/main/java/com/fr/plugin/PluginInitializeFilterBridge.java b/src/main/java/com/fr/plugin/PluginInitializeFilterBridge.java new file mode 100644 index 0000000..a354535 --- /dev/null +++ b/src/main/java/com/fr/plugin/PluginInitializeFilterBridge.java @@ -0,0 +1,21 @@ +package com.fr.plugin; + +import com.fr.decision.fun.impl.AbstractEmbedRequestFilterProvider; + +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +public class PluginInitializeFilterBridge extends AbstractEmbedRequestFilterProvider { + @Override + public void init(FilterConfig filterConfig) { + ScanConfig.getInstance(); + } + + @Override + public void filter(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException, ServletException { + + } +} diff --git a/src/main/java/com/fr/plugin/SCDBAccessProvider.java b/src/main/java/com/fr/plugin/SCDBAccessProvider.java new file mode 100644 index 0000000..58eb6a9 --- /dev/null +++ b/src/main/java/com/fr/plugin/SCDBAccessProvider.java @@ -0,0 +1,153 @@ +package com.fr.plugin; + +import com.fr.db.fun.impl.AbstractDBAccessProvider; +import com.fr.decision.base.util.UUIDUtil; +import com.fr.log.FineLoggerFactory; +import com.fr.plugin.beans.ScanConfigBean; +import com.fr.plugin.dao.ScanConfigDao; +import com.fr.plugin.entitys.ScanConfigEntity; +import com.fr.stable.StringUtils; +import com.fr.stable.db.accessor.DBAccessor; +import com.fr.stable.db.action.DBAction; +import com.fr.stable.db.dao.DAOContext; +import com.fr.stable.db.dao.DAOProvider; + +import java.util.ArrayList; +import java.util.List; + +public class SCDBAccessProvider extends AbstractDBAccessProvider { + + private static DBAccessor accessor; + @Override + public DAOProvider[] registerDAO() { + return new DAOProvider[]{ + ScanConfigDao.DAO + }; + } + + public static DBAccessor getAccessor() { + return accessor; + } + + public static void setAccessor(DBAccessor accessor) { + SCDBAccessProvider.accessor = accessor; + } + + @Override + public void onDBAvailable(DBAccessor dbAccessor) { + SCDBAccessProvider.accessor = dbAccessor; + } + + /** + * 业务对象——增 + * @param bean + */ + public static void add( final ScanConfigBean bean ){ + try{ + accessor.runDMLAction(new DBAction() { + @Override + public Boolean run(DAOContext context) throws Exception { + context.getDAO(ScanConfigDao.class).add( loadScanConfigEntity(bean,true) ); + return true; + } + }); + }catch(Throwable e){ + FineLoggerFactory.getLogger().error(e,"Add ScanConfigBean Error:{}",e.getMessage()); + } + } + + /** + * 业务对象——删 + * @param id + */ + public static ScanConfigBean delete( final String id ){ + try{ + return accessor.runDMLAction(new DBAction() { + @Override + public ScanConfigBean run(DAOContext context) throws Exception { + ScanConfigEntity entity = context.getDAO(ScanConfigDao.class).getById(id); + if( null == entity ){ + return null; + } + context.getDAO(ScanConfigDao.class).remove( id ); + return toScanConfigBean(entity); + } + }); + }catch(Throwable e){ + FineLoggerFactory.getLogger().error(e,"Delete ScanConfigBean[{}] Error:{}",id,e.getMessage()); + } + return null; + } + + /** + * 业务对象——改 + * @param bean + */ + public static void update( ScanConfigBean bean ){ + try{ + accessor.runDMLAction(new DBAction() { + @Override + public Boolean run(DAOContext context) throws Exception { + context.getDAO(ScanConfigDao.class).update( loadScanConfigEntity(bean,false) ); + return true; + } + }); + }catch(Throwable e){ + FineLoggerFactory.getLogger().error(e,"Update ScanConfigBean Error:{}",e.getMessage()); + } + } + + + private static ScanConfigEntity loadScanConfigEntity( ScanConfigBean bean, boolean isAdd ){ + ScanConfigEntity entity = new ScanConfigEntity(); + if(isAdd || StringUtils.isEmpty( bean.getId() ) ){ + bean.setId(UUIDUtil.generate()); + } + entity.setId( bean.getId() ); + entity.setAppFlag( bean.getAppFlag() ); + entity.setAppId( bean.getAppId() ); + entity.setAppName( bean.getAppName() ); + entity.setAppKey( bean.getAppKey() ); + entity.setAppIdSecret( bean.getAppIdSecret() ); + entity.setAppSecret( bean.getAppSecret() ); + return entity; + } + + private static ScanConfigBean toScanConfigBean( ScanConfigEntity entity ){ + ScanConfigBean bean = new ScanConfigBean(); + bean.setId( entity.getId() ); + bean.setAppId( entity.getAppId() ); + bean.setAppName( entity.getAppName() ); + bean.setAppKey(entity.getAppKey()); + bean.setAppIdSecret(entity.getAppIdSecret()); + bean.setAppSecret(entity.getAppSecret()); + bean.setAppFlag(entity.getAppFlag()); + return bean; + } + + + + /** + * 业务对象——查 + * @param key + * @return + */ + public static List like(String key ){ + try{ + return accessor.runQueryAction(new DBAction>() { + @Override + public List run(DAOContext context) throws Exception { + List result = new ArrayList(); + List list = context.getDAO(ScanConfigDao.class).likeByKey(key); + for (ScanConfigEntity scanConfigEntity : list) { + result.add(toScanConfigBean(scanConfigEntity)); + } + return result; + } + }); + }catch(Throwable e){ + FineLoggerFactory.getLogger().error(e,"Get ScanConfigBeans{} Error:{}",key,e.getMessage()); + } + return new ArrayList(); + } +} diff --git a/src/main/java/com/fr/plugin/SacnCycleMonitor.java b/src/main/java/com/fr/plugin/SacnCycleMonitor.java new file mode 100644 index 0000000..b326e78 --- /dev/null +++ b/src/main/java/com/fr/plugin/SacnCycleMonitor.java @@ -0,0 +1,15 @@ +package com.fr.plugin; + +import com.fr.plugin.context.PluginContext; +import com.fr.plugin.observer.inner.AbstractPluginLifecycleMonitor; + +public class SacnCycleMonitor extends AbstractPluginLifecycleMonitor { + @Override + public void afterRun(PluginContext pluginContext) { + ScanConfig instance = ScanConfig.getInstance(); + } + + @Override + public void beforeStop(PluginContext pluginContext) { + } +} diff --git a/src/main/java/com/fr/plugin/ScanCallBackHandle.java b/src/main/java/com/fr/plugin/ScanCallBackHandle.java new file mode 100644 index 0000000..7bcf778 --- /dev/null +++ b/src/main/java/com/fr/plugin/ScanCallBackHandle.java @@ -0,0 +1,292 @@ +package com.fr.plugin; + +import com.dingtalk.api.response.OapiUserGetResponse; +import com.fr.base.TableData; +import com.fr.base.TemplateUtils; +import com.fr.data.NetworkHelper; +import com.fr.decision.authority.AuthorityContext; +import com.fr.decision.authority.data.User; +import com.fr.decision.fun.impl.BaseHttpHandler; +import com.fr.decision.mobile.terminal.TerminalHandler; +import com.fr.decision.webservice.bean.authentication.OriginUrlResponseBean; +import com.fr.decision.webservice.utils.DecisionStatusService; +import com.fr.decision.webservice.v10.login.LoginService; +import com.fr.decision.webservice.v10.login.TokenResource; +import com.fr.file.DatasourceManager; +import com.fr.general.ComparatorUtils; +import com.fr.general.data.DataModel; +import com.fr.log.FineLoggerFactory; +import com.fr.plugin.beans.ScanConfigBean; +import com.fr.plugin.dao.ScanConfigDao; +import com.fr.plugin.dingtalk.server.config.DingTalkConfig; +import com.fr.plugin.dingtalk.server.config.DingTalkMemberManagementConfig; +import com.fr.plugin.dingtalk.server.helper.DingTalkDecUserHelper; +import com.fr.plugin.dingtalk.server.helper.DingTalkUserHelper; +import com.fr.plugin.dingtalk.server.log.DingTalkLoggerFactory; +import com.fr.plugin.entitys.ScanConfigEntity; +import com.fr.script.Calculator; +import com.fr.security.JwtUtils; +import com.fr.stable.StringUtils; +import com.fr.stable.db.action.DBAction; +import com.fr.stable.db.dao.DAOContext; +import com.fr.stable.query.QueryFactory; +import com.fr.stable.query.restriction.RestrictionFactory; +import com.fr.stable.web.Device; +import com.fr.third.springframework.web.bind.annotation.RequestMethod; +import com.fr.web.utils.WebUtils; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class ScanCallBackHandle extends BaseHttpHandler { + @Override + public RequestMethod getMethod() { + return null; + } + + @Override + public String getPath() { + return "/scanLogin"; + } + + @Override + public boolean isPublic() { + return true; + } + + @Override + public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { + String code = WebUtils.getHTTPRequestParameter(httpServletRequest, "code"); + FineLoggerFactory.getLogger().info("鉴权成功!拿到的code为:" + code); + String state = WebUtils.getHTTPRequestParameter(httpServletRequest, "state"); + if (StringUtils.isBlank(code)) { + WebUtils.printAsString(httpServletResponse, "本接口仅限钉钉调用"); + return; + } + if (StringUtils.isNotBlank(state)) { + ScanConfigEntity entity = SCDBAccessProvider.getAccessor().runQueryAction(context -> { + List entities = context.getDAO(ScanConfigDao.class).find(QueryFactory.create().addRestriction(RestrictionFactory.eq("appId", state))); + return entities.get(0); + }); + if (entity != null) { + String appId = entity.getAppId(); + String secret = entity.getAppIdSecret(); + String appKey = entity.getAppKey(); + String appSecret = entity.getAppSecret(); + String userUnid = HtmlUtils.getUserUnid(appId, secret, code); + OapiUserGetResponse userMob = HtmlUtils.getUserMobByUnid(userUnid,appKey,appSecret); + String userName = dingTalkUserToDecUser(userMob); + if (userName != null) { + if (isUserExist(userName)) { + String token = login(httpServletRequest, httpServletResponse, userName); + FineLoggerFactory.getLogger().debug("单点登录成功!生成token为:" + token); + String callBack = httpServletRequest.getParameter("redirectUrl"); + Cookie[] cookies = httpServletRequest.getCookies(); + for (Cookie cookie : cookies) { + if (StringUtils.equals(cookie.getName(), "myorgin")) { + String value = cookie.getValue(); + try { + cookie.setMaxAge(0);//删除cookies + String originUrl = ((OriginUrlResponseBean) DecisionStatusService.originUrlStatusService().get(value)).getOriginUrl(); + httpServletResponse.addCookie(cookie); + sendRedirect(httpServletResponse, originUrl); + return; + } catch (Exception e) { + } + } + } + if (StringUtils.isNotBlank(callBack)) { + sendRedirect(httpServletResponse, callBack); + } else { + sendRedirect(httpServletResponse, getHomeURI(httpServletRequest)); + } + return; + } else { + FineLoggerFactory.getLogger().warn(String.format("平台用户中无%s用户!", userMob)); + } + } + } + } + + WebUtils.printAsString(httpServletResponse, "扫码获取用户信息失败"); + return; + } + + + /** + * 通过钉钉认证之后的响应 来获取用户名 + * + * @param userMob + * @return + * @throws Exception + */ + public static String dingTalkUserToDecUser(OapiUserGetResponse userMob) throws Exception { + DingTalkMemberManagementConfig config = DingTalkConfig.getInstance().getMemberManagementConfig(); + if (userMob != null && config != null) { + String jobNumber; + jobNumber = null; + String userid = userMob.getUserid(); + label65: + switch (config.getMatchingFSWay()) { + case 0: + FineLoggerFactory.getLogger().debug("当前匹配方式为工号匹配"); + jobNumber = userMob.getJobnumber(); + break; + case 1: + FineLoggerFactory.getLogger().debug("当前匹配方式为手机号匹配"); + String mobile = userMob.getMobile(); + if (StringUtils.isEmpty(mobile)) { + FineLoggerFactory.getLogger().warn("未能获取到该钉钉用户%s的手机号,请于钉钉管理后台中核查!"); + } else { + List var11 = AuthorityContext.getInstance().getUserController().find(QueryFactory.create().addRestriction(RestrictionFactory.eq("mobile", mobile))); + if (var11 != null && !var11.isEmpty()) { + Iterator var12 = var11.iterator(); + + while (true) { + if (!var12.hasNext()) { + break label65; + } + + User var13 = (User) var12.next(); + if (var13.isEnable()) { + jobNumber = var13.getUserName(); + break label65; + } + + FineLoggerFactory.getLogger().warn(String.format("当前平台用户%s被禁用,请在决策平台用户管理中设置!", var13.getUserName())); + } + } else { + FineLoggerFactory.getLogger().warn(String.format("没有在平台用户中找到手机号为%s的用户!", mobile)); + } + } + break; + case 2: + FineLoggerFactory.getLogger().debug("当前匹配方式为手动匹配"); + jobNumber = DingTalkUserHelper.getDecUserName(userid); + if (StringUtils.isEmpty(jobNumber)) { + FineLoggerFactory.getLogger().warn(String.format("请检查fine_dingtalk_user_relation表中是否存在dingTalkUser为%s的用户匹配关系", userid)); + } + break; + case 3: + FineLoggerFactory.getLogger().debug("当前匹配方式为数据集匹配"); + if (StringUtils.isNotEmpty(config.getDataSet())) { + TableData var5 = DatasourceManager.getInstance().getTableData(config.getDataSet()); + DataModel var6 = var5.createDataModel(Calculator.createCalculator()); + + for (int var7 = 0; var7 < var6.getRowCount(); ++var7) { + int var8 = config.getDataSetDingTalkUserIdColumn(); + if (ComparatorUtils.equals(var6.getValueAt(var7, var8).toString(), userid)) { + int var9 = config.getDataSetFsUserNameColumn(); + jobNumber = var6.getValueAt(var7, var9).toString(); + FineLoggerFactory.getLogger().debug(String.format("匹配第%s行第%s列的平台用户为:" + jobNumber, var7, var9)); + User var10 = DingTalkDecUserHelper.getDecUserByName(jobNumber); + if (var10 != null) { + if (var10.isEnable()) { + break; + } + + FineLoggerFactory.getLogger().warn(String.format("数据集中%s用户对应平台用户不可用", jobNumber)); + } else { + FineLoggerFactory.getLogger().warn(String.format("数据集中%s用户不在平台用户中", jobNumber)); + } + } + } + } else { + FineLoggerFactory.getLogger().warn("没有设置匹配数据集,请在钉钉管理中设置!"); + } + } + if (StringUtils.isEmpty(jobNumber)) { + FineLoggerFactory.getLogger().warn("钉钉用户匹配平台用户失败!"); + } else { + FineLoggerFactory.getLogger().debug("钉钉用户匹配平台用户结果为:" + jobNumber); + } + + return jobNumber; + } else { + return null; + } + } + + /** + * 登录 + * + * @param req + * @param res + * @param username + * @return + */ + private String login(HttpServletRequest req, HttpServletResponse res, String username) { + try { + String oldToken = TokenResource.COOKIE.getToken(req); + if ((oldToken == null) || (!checkTokenValid(req, oldToken, username))) { + String token = LoginService.getInstance().login(req, res, username); + req.setAttribute("fine_auth_token", token); + FineLoggerFactory.getLogger().error("fr CookieFilter is over with username is ###" + username); + return token; + } else { + FineLoggerFactory.getLogger().error("no need login: {}", username); + } + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + return ""; + } + + /** + * 校验token是否有效 + */ + public static boolean checkTokenValid(HttpServletRequest req, String token, String currentUserName) { + try { + //当前登录用户和token对应的用户名不同,需要重新生成token + if (!ComparatorUtils.equals(currentUserName, JwtUtils.parseJWT(token).getSubject())) { + FineLoggerFactory.getLogger().info("username changed:" + currentUserName); + return false; + } + Device device = NetworkHelper.getDevice(req); + LoginService.getInstance().loginStatusValid(token, TerminalHandler.getTerminal(req, device)); + return true; + } catch (Exception ignore) { + return false; + } + } + + private User getUserByMob(String mob) throws Exception { + List mobile = AuthorityContext.getInstance().getUserController().find(QueryFactory.create().addRestriction(RestrictionFactory.eq("mobile", mob))); + if (!mobile.isEmpty()) { + return mobile.get(0); + } + return null; + } + + public boolean isUserExist(String var0) { + if (StringUtils.isEmpty(var0)) { + return false; + } else { + try { + List var1 = AuthorityContext.getInstance().getUserController().find(QueryFactory.create().addRestriction(RestrictionFactory.eq("userName", var0))); + return var1 != null && !var1.isEmpty(); + } catch (Exception var2) { + return false; + } + } + } + + private void sendRedirect(HttpServletResponse res, String url) { + res.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); + res.setHeader("Location", url); + } + + private String getHomeURI(HttpServletRequest request) { + String url = "/"; + try { + url = TemplateUtils.render("${fineServletURL}"); + } catch (Exception e) { + e.printStackTrace(); + } + return url; + } +} diff --git a/src/main/java/com/fr/plugin/ScanComponent.java b/src/main/java/com/fr/plugin/ScanComponent.java new file mode 100644 index 0000000..3a45391 --- /dev/null +++ b/src/main/java/com/fr/plugin/ScanComponent.java @@ -0,0 +1,51 @@ +package com.fr.plugin; + +import com.fr.web.struct.Component; +import com.fr.web.struct.Filter; +import com.fr.web.struct.browser.RequestClient; +import com.fr.web.struct.category.FileType; +import com.fr.web.struct.category.ScriptPath; +import com.fr.web.struct.category.StylePath; + +public class ScanComponent extends Component { + public static final ScanComponent KEY = new ScanComponent(); + + /** + * 返回需要引入的JS脚本路径 + * + * @param client 请求客户端描述 + * @return JS脚本路径 + */ + @Override + public ScriptPath script(RequestClient client) { + return ScriptPath.build("com.fr.plugin.ScanJS&time=" + System.currentTimeMillis(), FileType.CLASS); + } + + /** + * 返回需要引入的CSS样式路径 + * + * @param client 请求客户端描述 + * @return CSS样式路径 + */ + @Override + public StylePath style(RequestClient client) { + //如果不需要就直接返回 StylePath.EMPTY; + return StylePath.EMPTY; + } + + /** + * 通过给定的资源过滤器控制是否加载这个资源 + * + * @return 资源过滤器 + */ + @Override + public Filter filter() { + return new Filter() { + @Override + public boolean accept() { + //任何情况下我们都在平台组件加载时加载我们的组件 + return true; + } + }; + } +} diff --git a/src/main/java/com/fr/plugin/ScanConfig.java b/src/main/java/com/fr/plugin/ScanConfig.java new file mode 100644 index 0000000..1f20e85 --- /dev/null +++ b/src/main/java/com/fr/plugin/ScanConfig.java @@ -0,0 +1,108 @@ +package com.fr.plugin; + +import com.fr.config.*; +import com.fr.config.holder.Conf; +import com.fr.config.holder.factory.Holders; + +@Visualization(category = "钉钉扫码配置") +public class ScanConfig extends DefaultConfiguration { + private static volatile ScanConfig config = null; + + public static ScanConfig getInstance() { + if (config == null) { + config = ConfigContext.getConfigInstance(ScanConfig.class); + } + return config; + } + + @Identifier(value = "AppId", name = "扫码AppID", description = "", status = Status.SHOW) + private Conf appId = Holders.simple(""); + + @Identifier(value = "Secret", name = "扫码app_secret", description = "", status = Status.SHOW) + private Conf secret = Holders.simple(""); + +// @Identifier(value = "loginKey", name = "登录AppKey", description = "", status = Status.SHOW) +// private Conf loginKey = Holders.simple(""); +// +// @Identifier(value = "LoginSecret", name = "登录app_secret", description = "", status = Status.SHOW) +// private Conf loginSecret = Holders.simple(""); + + @Identifier(value = "openLogin", name = "登录界面启动开关", description = "", status = Status.SHOW) + private Conf openLogin = Holders.simple(true); + @Identifier(value = "defaultScan", name = "默认扫码登录", description = "", status = Status.SHOW) + private Conf defaultScan = Holders.simple(false); + @Identifier(value = "onlyScan", name = "只保留扫码登录", description = "", status = Status.SHOW) + private Conf onlyScan = Holders.simple(false); + + public Boolean getDefaultScan() { + return defaultScan.get(); + } + + public void setDefaultScan( Boolean defaultScan) { + this.defaultScan .set(defaultScan); + } + + public Boolean getOnlyScan() { + return onlyScan.get(); + } + + public void setOnlyScan( Boolean onlyScan) { + this.onlyScan .set(onlyScan); + } + + public String getAppId() { + return appId.get(); + } + + public void setAppId(String appId) { + this.appId.set(appId); + } + + public String getSecret() { + return secret.get(); + } + + public void setSecret(String secret) { + this.secret.set(secret); + } +// +// public String getLoginKey() { +// return loginKey.get(); +// } +// +// public void setLoginKey(String loginKey) { +// this.loginKey.set(loginKey); +// } +// +// public String getLoginSecret() { +// return loginSecret.get(); +// } +// +// public void setLoginSecret(String loginSecret) { +// this.loginSecret.set(loginSecret); +// } + + public Boolean getOpenLogin() { + return openLogin.get(); + } + + public void setOpenLogin(Boolean openLogin) { + this.openLogin.set(openLogin); + } + + @Override + public Object clone() throws CloneNotSupportedException { + ScanConfig cloned = (ScanConfig) super.clone(); + cloned.appId = (Conf) appId.clone(); + cloned.secret = (Conf) secret.clone(); + +// cloned.loginKey = (Conf) loginKey.clone(); +// cloned.loginSecret = (Conf) loginSecret.clone(); +// + cloned.onlyScan = (Conf) onlyScan.clone(); + cloned.defaultScan = (Conf) defaultScan.clone(); + cloned.openLogin = (Conf) openLogin.clone(); + return cloned; + } + +} diff --git a/src/main/java/com/fr/plugin/ScanControllerBridge.java b/src/main/java/com/fr/plugin/ScanControllerBridge.java new file mode 100644 index 0000000..f71b85a --- /dev/null +++ b/src/main/java/com/fr/plugin/ScanControllerBridge.java @@ -0,0 +1,11 @@ +package com.fr.plugin; + +import com.fr.decision.fun.impl.AbstractControllerRegisterProvider; +import com.fr.plugin.web.controllers.ScanController; + +public class ScanControllerBridge extends AbstractControllerRegisterProvider { + @Override + public Class[] getControllers() { + return new Class[]{ScanController.class}; + } +} diff --git a/src/main/java/com/fr/plugin/ScanFunction.java b/src/main/java/com/fr/plugin/ScanFunction.java new file mode 100644 index 0000000..393e337 --- /dev/null +++ b/src/main/java/com/fr/plugin/ScanFunction.java @@ -0,0 +1,12 @@ +package com.fr.plugin; + +import com.fr.plugin.transform.ExecuteFunctionRecord; +import com.fr.plugin.transform.FunctionRecorder; + +@FunctionRecorder(localeKey = "scan") +public class ScanFunction { + @ExecuteFunctionRecord + public String name() { + return "功能点检测"; + } +} diff --git a/src/main/java/com/fr/plugin/ScanHttpHander.java b/src/main/java/com/fr/plugin/ScanHttpHander.java new file mode 100644 index 0000000..c6a7b1e --- /dev/null +++ b/src/main/java/com/fr/plugin/ScanHttpHander.java @@ -0,0 +1,19 @@ +package com.fr.plugin; + +import com.fr.decision.fun.HttpHandler; +import com.fr.decision.fun.impl.AbstractHttpHandlerProvider; +import com.fr.stable.fun.Authorize; + + +public class ScanHttpHander extends AbstractHttpHandlerProvider { + HttpHandler[] actions = new HttpHandler[]{ + new ScanCallBackHandle(), + new GetQRHandle(), + new AllScanBeans() + }; + + @Override + public HttpHandler[] registerHandlers() { + return actions; + } +} diff --git a/src/main/java/com/fr/plugin/ScanJS.java b/src/main/java/com/fr/plugin/ScanJS.java new file mode 100644 index 0000000..38de80e --- /dev/null +++ b/src/main/java/com/fr/plugin/ScanJS.java @@ -0,0 +1,41 @@ +package com.fr.plugin; + +import com.fr.base.TemplateUtils; +import com.fr.gen.TextGenerator; +import com.fr.plugin.context.PluginContexts; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.HashMap; +import java.util.Map; + +public class ScanJS implements TextGenerator { + + + public String text(HttpServletRequest req, HttpServletResponse res) throws Exception { + Map renderMap = new HashMap(); + ScanConfig instance = ScanConfig.getInstance(); + String appid = instance.getAppId(); + renderMap.put("openScan", instance.getOpenLogin()); + renderMap.put("defaultScan", instance.getDefaultScan()); + renderMap.put("onlyScan", instance.getOnlyScan()); + renderMap.put("appId", appid); + if (PluginContexts.currentContext().isAvailable()) { + // 做认证通过的事情 + return TemplateUtils.renderTemplate(this.template(), renderMap); + } else { + // 做认证未通过的事情 + return TemplateUtils.renderTemplate("/com/fr/plugin/scanBuy.tpl", renderMap); + + } + } + + + public String mimeType() { + return "text/javascript"; + } + + public String template() { + return "/com/fr/plugin/scan.tpl"; + } +} diff --git a/src/main/java/com/fr/plugin/ScanJSHander.java b/src/main/java/com/fr/plugin/ScanJSHander.java new file mode 100644 index 0000000..189d77f --- /dev/null +++ b/src/main/java/com/fr/plugin/ScanJSHander.java @@ -0,0 +1,32 @@ +package com.fr.plugin; + +import com.fr.decision.fun.impl.AbstractWebResourceProvider; +import com.fr.decision.web.LoginComponent; +import com.fr.web.struct.Atom; + +public class ScanJSHander extends AbstractWebResourceProvider { + + /** + * 需要附加到的主组件 + * + * @return 主组件 + */ + + @Override + public Atom attach() { + return LoginComponent.KEY; + } + + /** + * 客户端所需的组件 + * + * @return 组件 + */ + @Override + public Atom client() { + return ScanComponent.KEY; + } + public Atom[] clients() { + return new Atom[]{this.client()}; + } +} diff --git a/src/main/java/com/fr/plugin/beans/ScanAllConfigBean.java b/src/main/java/com/fr/plugin/beans/ScanAllConfigBean.java new file mode 100644 index 0000000..febe37a --- /dev/null +++ b/src/main/java/com/fr/plugin/beans/ScanAllConfigBean.java @@ -0,0 +1,31 @@ +package com.fr.plugin.beans; + +public class ScanAllConfigBean { + private Boolean openLogin = false; + private Boolean defaultScan = false; + private Boolean onlyScan = false; + + public Boolean getOpenLogin() { + return openLogin; + } + + public void setOpenLogin(Boolean openLogin) { + this.openLogin = openLogin; + } + + public Boolean getDefaultScan() { + return defaultScan; + } + + public void setDefaultScan(Boolean defaultScan) { + this.defaultScan = defaultScan; + } + + public Boolean getOnlyScan() { + return onlyScan; + } + + public void setOnlyScan(Boolean onlyScan) { + this.onlyScan = onlyScan; + } +} diff --git a/src/main/java/com/fr/plugin/beans/ScanConfigBean.java b/src/main/java/com/fr/plugin/beans/ScanConfigBean.java new file mode 100644 index 0000000..5cc5547 --- /dev/null +++ b/src/main/java/com/fr/plugin/beans/ScanConfigBean.java @@ -0,0 +1,71 @@ +package com.fr.plugin.beans; + +import com.fr.stable.db.entity.BaseEntity; + +public class ScanConfigBean extends BaseEntity { + private String id = ""; + private String appId = ""; + private String appIdSecret = ""; + private String appKey = ""; + private String appSecret = ""; + private String appName = ""; + private String appFlag = ""; + + public String getAppIdSecret() { + return appIdSecret; + } + + public void setAppIdSecret(String appIdSecret) { + this.appIdSecret = appIdSecret; + } + + public String getAppSecret() { + return appSecret; + } + + public void setAppSecret(String appSecret) { + this.appSecret = appSecret; + } + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public String getAppKey() { + return appKey; + } + + public void setAppKey(String appKey) { + this.appKey = appKey; + } + + public String getAppName() { + return appName; + } + + public void setAppName(String appName) { + this.appName = appName; + } + + public String getAppFlag() { + return appFlag; + } + + public void setAppFlag(String appFlag) { + this.appFlag = appFlag; + } +} diff --git a/src/main/java/com/fr/plugin/dao/ScanConfigDao.java b/src/main/java/com/fr/plugin/dao/ScanConfigDao.java new file mode 100644 index 0000000..bb9898f --- /dev/null +++ b/src/main/java/com/fr/plugin/dao/ScanConfigDao.java @@ -0,0 +1,61 @@ +package com.fr.plugin.dao; + +import com.fr.plugin.entitys.ScanConfigEntity; +import com.fr.stable.StringUtils; +import com.fr.stable.db.dao.BaseDAO; +import com.fr.stable.db.dao.DAOProvider; +import com.fr.stable.db.session.DAOSession; +import com.fr.stable.query.QueryFactory; +import com.fr.stable.query.condition.QueryCondition; +import com.fr.stable.query.restriction.RestrictionFactory; + +import java.util.List; + +public class ScanConfigDao extends BaseDAO { + public ScanConfigDao(DAOSession daoSession) { + super(daoSession); + } + + protected Class getEntityClass() { + return ScanConfigEntity.class; + } + + public final static DAOProvider DAO = new DAOProvider() { + @Override + public Class getEntityClass() { + return ScanConfigEntity.class; + } + + @Override + public Class getDAOClass() { + return ScanConfigDao.class; + } + }; + + public void add(ScanConfigEntity entity) throws Exception { + getSession().persist(entity); + } + + public void update(ScanConfigEntity entity) throws Exception { + getSession().merge(entity); + } + + public ScanConfigEntity getById(String id) throws Exception { + return getSession().getById(id, ScanConfigEntity.class); + } + + public void remove(String id) throws Exception { + getSession().remove(QueryFactory.create() + .addRestriction(RestrictionFactory.eq("id", id)), + this.getEntityClass()); + } + + public List likeByKey(String key) throws Exception { + QueryCondition condition = QueryFactory.create(); + if (StringUtils.isNotBlank(key)) { + condition.addRestriction(RestrictionFactory.like("app_name", key)); + } + return find(condition); + } + +} diff --git a/src/main/java/com/fr/plugin/entitys/ScanConfigEntity.java b/src/main/java/com/fr/plugin/entitys/ScanConfigEntity.java new file mode 100644 index 0000000..4eb24b9 --- /dev/null +++ b/src/main/java/com/fr/plugin/entitys/ScanConfigEntity.java @@ -0,0 +1,73 @@ +package com.fr.plugin.entitys; + +import com.fr.stable.db.entity.BaseEntity; +import com.fr.third.javax.persistence.Column; +import com.fr.third.javax.persistence.Entity; +import com.fr.third.javax.persistence.Table; + +@Entity +@Table( + name = "fine_dd_scan_login_config" +) +public class ScanConfigEntity extends BaseEntity { + @Column(name = "app_id") + private String appId = ""; + @Column(name = "app_key") + private String appKey = ""; + @Column(name = "app_id_secret") + private String appIdSecret = ""; + @Column(name = "app_secret") + private String appSecret = ""; + @Column(name = "app_name") + private String appName = ""; + @Column(name = "app_flag") + private String appFlag = ""; + + public String getAppIdSecret() { + return appIdSecret; + } + + public void setAppIdSecret(String appIdSecret) { + this.appIdSecret = appIdSecret; + } + + public String getAppSecret() { + return appSecret; + } + + public void setAppSecret(String appSecret) { + this.appSecret = appSecret; + } + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public String getAppKey() { + return appKey; + } + + public void setAppKey(String appKey) { + this.appKey = appKey; + } + + public String getAppName() { + return appName; + } + + public void setAppName(String appName) { + this.appName = appName; + } + + public String getAppFlag() { + return appFlag; + } + + public void setAppFlag(String appFlag) { + this.appFlag = appFlag; + } +} diff --git a/src/main/java/com/fr/plugin/web/controllers/ScanController.java b/src/main/java/com/fr/plugin/web/controllers/ScanController.java new file mode 100644 index 0000000..1ce9c84 --- /dev/null +++ b/src/main/java/com/fr/plugin/web/controllers/ScanController.java @@ -0,0 +1,69 @@ +package com.fr.plugin.web.controllers; + +import com.fr.decision.webservice.Response; +import com.fr.decision.webservice.annotation.LoginStatusChecker; +import com.fr.plugin.SCDBAccessProvider; +import com.fr.plugin.ScanConfig; +import com.fr.plugin.beans.ScanAllConfigBean; +import com.fr.plugin.beans.ScanConfigBean; +import com.fr.third.springframework.stereotype.Controller; +import com.fr.third.springframework.web.bind.annotation.*; + +/** + * @author fr.open + * @version 10.0 + * Created by fr.open on 2021-06-26 + **/ +@Controller("ScanController") +@LoginStatusChecker(required = true) +@RequestMapping(value = "/ddScan/config") +public class ScanController { + + @RequestMapping(value = "/scan_entity", method = RequestMethod.POST) + @ResponseBody + public Response add(@RequestBody ScanConfigBean bean) throws Exception { + SCDBAccessProvider.add(bean); + return Response.ok(bean); + } + + @RequestMapping(value = "/scan_entity/{entity}", method = RequestMethod.DELETE) + @ResponseBody + public Response delete(@PathVariable("entity") String entity) throws Exception { + return Response.ok(SCDBAccessProvider.delete(entity)); + } + + @RequestMapping(value = "/scan_entity", method = RequestMethod.PUT) + @ResponseBody + public Response update(@RequestBody ScanConfigBean bean) throws Exception { + SCDBAccessProvider.update(bean); + return Response.ok(bean); + } + + @RequestMapping(value = "/scan_entity", method = RequestMethod.GET) + @ResponseBody + public Response like(@RequestParam(value = "key",required = false) String key) throws Exception { + return Response.ok(SCDBAccessProvider.like(key)); + } + + @RequestMapping(value = "/allDefConfig", method = RequestMethod.GET) + @ResponseBody + public Response allDefConfig() throws Exception { + ScanAllConfigBean bean = new ScanAllConfigBean(); + ScanConfig scanConfig = ScanConfig.getInstance(); + bean.setDefaultScan(scanConfig.getDefaultScan()); + bean.setOpenLogin(scanConfig.getOpenLogin()); + bean.setOnlyScan(scanConfig.getOnlyScan()); + return Response.ok(bean); + } + @RequestMapping(value = "/changeConfig", method = RequestMethod.PUT) + @ResponseBody + public Response changeConfig(@RequestBody ScanAllConfigBean bean) throws Exception { + if (bean != null) { + ScanConfig scanConfig = ScanConfig.getInstance(); + scanConfig.setDefaultScan(bean.getDefaultScan()); + scanConfig.setOpenLogin(bean.getOpenLogin()); + scanConfig.setOnlyScan(bean.getOnlyScan()); + } + return Response.ok(bean); + } +} \ No newline at end of file diff --git a/src/main/resources/com/fr/plugin/buynew.html b/src/main/resources/com/fr/plugin/buynew.html new file mode 100644 index 0000000..71dd2cf --- /dev/null +++ b/src/main/resources/com/fr/plugin/buynew.html @@ -0,0 +1,17 @@ + + + + + 钉钉扫码 + + + + +

+ 请购买授权后使用 +

+ + diff --git a/src/main/resources/com/fr/plugin/new.html b/src/main/resources/com/fr/plugin/new.html new file mode 100644 index 0000000..d4e928b --- /dev/null +++ b/src/main/resources/com/fr/plugin/new.html @@ -0,0 +1,69 @@ + + + + + 钉钉扫码 + + + + + +

+ + diff --git a/src/main/resources/com/fr/plugin/scan.tpl b/src/main/resources/com/fr/plugin/scan.tpl new file mode 100644 index 0000000..31cf52d --- /dev/null +++ b/src/main/resources/com/fr/plugin/scan.tpl @@ -0,0 +1,331 @@ +(function () { +// 为平台增加一个footer + BI.Plugin.registerObject("dec.login.login", function (widget) { + var openScan = eval("${openScan}"); + var defaultScan = eval("${defaultScan}"); + var onlyScan = eval("${onlyScan}"); + // if (openScan) { + // var target = $(widget.element).find(".bi-multi-select-item").parent(); + // var item = $("
") + // item.click(function () { + // window.zxltabs.setSelect(DecCst.Login.Tabs.QR_LOGIN) + // }); + // item.insertAfter(target); + // } + if (openScan) { + var target = $(widget.element).find(".bi-multi-select-item").parent().parent(); + var item = $("
") + item.click(function () { + window.zxltabs.setSelect(DecCst.Login.Tabs.QR_LOGIN) + }); + item.insertAfter(target); + } + }); +})(); + +var onlyScan = eval("${onlyScan}"); +var defaultScan = eval("${defaultScan}"); +if(getQueryString("isAdmin") === "1"){ + onlyScan=false; + defaultScan=false; +} +function setCookie(name, value) { + var Days = 1; + var exp = new Date(); + exp.setTime(exp.getTime() + Days * 24 * 60 * 60 * 1000); + document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString(); +} + +function getQueryString(name) { + var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i"); + var r = window.location.search.substr(1).match(reg); + if (r != null) { + return decodeURIComponent(r[2]); + } + return null; +} + +if (onlyScan) { + var tempScan = getQueryString("notScan"); + if (tempScan) { + onlyScan = false; + defaultScan = false; + } +} +DecCst = DecCst || {}; +DecCst.Login.Tabs.QR_LOGIN = "qr_login"; +(function () { + var e = BI.inherit(BI.Widget, { + props: { + baseCls: "dec-login-index" + }, + _store: function () { + return BI.Models.getModel("dec.model.login.index") + }, + watch: { + selectedTab: function (e) { + this.tab.setSelect(e) + } + }, + render: function () { + var t = this; + return { + type: "bi.tab", + cls: "bi-card", + single: !0, + cardCreator: BI.bind(this._createCard, this), + showIndex: this.model.selectedTab, + ref: function (e) { + t.tab = e + } + } + }, + mounted: function () { + this.store.initData() + window.zxltabs = this.tab; + //如果有一个notScan参数就,不是只扫码 + if (defaultScan) { + this.tab.setSelect(DecCst.Login.Tabs.QR_LOGIN) + } + }, + _createCard: function (e) { + var t = this; + + switch (e) { + case DecCst.Login.Tabs.LOGIN: + + if (onlyScan) { + return { + type: "dec.login.QR" + }; + } else { + return { + type: "dec.login.login", + listeners: [{ + eventName: "EVENT_LOGIN", + action: function (e) { + t.store.login(e) + } + }] + }; + } + case DecCst.Login.Tabs.FORGET_PASSWORD: + return { + type: "dec.login.forget" + }; + case DecCst.Login.Tabs.AUTHENTICATION: + return { + type: "dec.login.authentication", + listeners: [{ + eventName: "EVENT_LOGIN", + action: function (e) { + t.store.login(e) + } + }] + }; + case DecCst.Login.Tabs.PASSWORD_TOKEN: + return { + type: "dec.login.password", + listeners: [{ + eventName: "EVENT_SAVE", + action: function (e) { + t.store.toSuccess(e) + } + }] + }; + case DecCst.Login.Tabs.QR_LOGIN: + return { + type: "dec.login.QR" + }; + case DecCst.Login.Tabs.SUCCESS_PWD: + return { + type: "dec.login.password.success", + username: this.model.loginInfo.username, + pwd: this.model.userInfo.pwd, + loginValidity: this.model.loginInfo.validity, + listeners: [{ + eventName: "EVENT_LOGIN", + action: function (e) { + t.store.login(e) + } + }] + }; + case DecCst.Login.Tabs.SUCCESS_TOKEN: + return { + type: "dec.login.password.token", + listeners: [{ + eventName: "EVENT_LOGIN", + action: function (e) { + t.store.login(e) + } + }] + }; + case DecCst.Login.Tabs.PASSWORD_OLD: + return { + type: "dec.login.single", + username: this.model.loginInfo.username, + listeners: [{ + eventName: "EVENT_BACK", + action: function () { + t.store.setTab(DecCst.Login.Tabs.LOGIN) + } + }, { + eventName: "EVENT_SURE", + action: function (e) { + t.store.toSuccess(e) + } + }] + }; + case DecCst.Login.Tabs.VERIFY_BING: + return { + type: "dec.login.verify" + }; + case DecCst.Login.Tabs.LOCKED: + return { + type: "dec.login.locked" + }; + default: + return { + type: "bi.layout" + } + } + } + }); + BI.shortcut("dec.login.index", e) +}()); +function getPath(path, isPublic) { + if (!path || path[0] !== '/') { + path = '/' + path; + } + return isPublic === true + ? '/plugin/public/com.fr.plugin.sln5470' + path + : '/plugin/private/com.fr.plugin.sln5470' + path; +} +;(function () { + var e = BI.inherit(BI.Widget, { + props: { + baseCls: "" + }, + _store: function () { + return BI.Models.getModel("dec.model.login.login") + }, + beforeInit:function (callback){ + //初始化的时候先获取有哪些钉钉服务器可以选择 + var self = this; + Dec.reqGet(getPath("getConfigBean", true), {}, function (data) { + console.info(data) + self.datas = data.data; + callback(); + }) + }, + mounted: function () { + if(this.firstId){ + this.comb.setValue(this.firstId); + this.changeSelect(this.firstId); + } + }, + changeSelect:function ( value){ + var self=this; + var appId = value||self.comb.getValue()[0]; + var host = window.location.protocol + "//" + window.location.host;//左良测试 + var cal = host + "${fineServletURL}"; + var target = encodeURIComponent(cal); + //拿到loginTmpCode后就可以在这里构造跳转链接进行跳转了 + var orgin = getQueryString("origin") + if (orgin) { + setCookie("myorgin", orgin); + } + var callBack = encodeURIComponent(cal + "/plugin/public/com.fr.plugin.sln5470/scanLogin?redirectUrl=" + target); + var goto = encodeURIComponent('https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=' + appId + '&response_type=code&scope=snsapi_login&redirect_uri=' + callBack); + var url = "https://login.dingtalk.com/login/qrcode.htm?&state="+appId+"&goto=" + goto + "&style=" + encodeURIComponent('border:none;background-color:#FFFFFF;'); + $(self.iframe.element).attr("src",url) + self.iframe.setVisible(true) + // self.comb.setVisible(false) + var handleMessage = function (event) { + var origin = event.origin; + var host = window.location.protocol + "//" + window.location.host;//左良测试 + var callback = host + "${fineServletURL}"; + console.log("origin", event.origin); + if (origin == "https://login.dingtalk.com") { //判断是否来自ddLogin扫码事件。 + var loginTmpCode = event.data; + var url = "https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid="+appId+"&response_type=code&scope=snsapi_login&state="+appId+"&redirect_uri=" + callback + "&loginTmpCode=" + loginTmpCode; + window.location.href = url; + } + }; + if (typeof window.addEventListener != 'undefined') { + window.addEventListener('message', handleMessage, false); + } else if (typeof window.attachEvent != 'undefined') { + window.attachEvent('onmessage', handleMessage); + } + + }, + render: function () { + var t = this; + var url=""; + var items=BI.map(this.datas,function (i,v){ + return { + text: v.appName, + value: v.appId + } + }) + var vv=null; + if(items.length){ + t.firstId=items[0].value; + } + return { + type: "bi.absolute", + items: [ + { + el:{ + css:{ + zIndex:999 + }, + type: "bi.text_value_combo", + ref: function (e) { + t.comb=e; + }, + value:vv, + text:"请选择登录的公司信息", + listeners: [{ + eventName: "EVENT_CHANGE", + action: function () { + t.changeSelect(); + } + }], + width: 201, + height:24, + items: items + }, + bottom: 30, + left:35 + }, + { + el: { + type: "bi.default", + invisible:true, + ref:function (e){ + t.iframe=e; + }, + element: "" + }, + top: 0, + height: "100%", + right: 0, + bottom: 0, + left: 0 + }, { + el: { + type: "bi.icon_button", + cls: "toast-error-font", + handler: function () { + window.zxltabs.setSelect(DecCst.Login.Tabs.LOGIN) + } + }, + top: 10, + right: 20 + }] + } + } + }); + BI.shortcut("dec.login.QR", e) +})(); diff --git a/src/main/resources/com/fr/plugin/scanBuy.tpl b/src/main/resources/com/fr/plugin/scanBuy.tpl new file mode 100644 index 0000000..71f758e --- /dev/null +++ b/src/main/resources/com/fr/plugin/scanBuy.tpl @@ -0,0 +1,3 @@ +;(function () { + alert("钉钉扫码插件授权过期或未授权") +})(); \ No newline at end of file diff --git a/src/main/resources/com/fr/plugin/web/dds/dds.js b/src/main/resources/com/fr/plugin/web/dds/dds.js new file mode 100644 index 0000000..32bc6b5 --- /dev/null +++ b/src/main/resources/com/fr/plugin/web/dds/dds.js @@ -0,0 +1,496 @@ +BI.config("dec.provider.management", function (provider) { + provider.inject({ + modules: [ + { + value: "dd.scan.config", + id: "decision-management-custom-manage", + text: "钉钉扫码配置", + cardType: "dd.scan.config", + cls: "setting-font", + dev: true + } + ] + }); +}); + +function getPath(path, isPublic) { + if (!path || path[0] !== '/') { + path = '/' + path; + } + return isPublic === true + ? '//ddScan/config/' + path + : '/ddScan/config/' + path; +} + +// 组件实现,效果为使用绝对布局组件放置了一个iframe +var Wid = BI.inherit(BI.Widget, { + + props: { + baseCls: "" + }, + + render: function () { + return { + type: "bi.vertical", + vgap: 30, + items: [ + { + type: "dd.scan.top.row", + height: 300 + }, + { + tgap:30, + type: "dd.scan.bottom.config.all.area" + } + ] + }; + } +}); +BI.shortcut("dd.scan.config", Wid); +// 组件实现,效果为使用绝对布局组件放置了一个iframe +var allConfigWid = BI.inherit(BI.Widget, { + + props: { + baseCls: "", + css: { + background: "#e3e3e3" + } + }, + beforeInit: function (callback) { + var self = this; + Dec.reqGet(getPath("allDefConfig", false), {}, function (data) { + console.info(data) + self.data = data.data; + callback(); + }) + }, + saveAction: function () { + var config = { + openLogin: this.openLogin.isSelected(), + onlyScan: this.onlyScan.isSelected(), + defaultScan: this.defaultScan.isSelected(), + } + Dec.reqPut(getPath("changeConfig", false), config, function (data) { + BI.Msg.toast("更新成功") + }); + }, + render: function () { + var self = this; + return { + type: "bi.vertical", + width:400, + hgap:40, + items: [ + { + type: "bi.multi_select_item", + text: "默认开启扫码", + ref: function (e) { + self.defaultScan = e; + }, + selected: self.data.defaultScan, + }, + { + type: "bi.multi_select_item", + text: "仅开启扫码(不保留密码登陆)", + ref: function (e) { + self.onlyScan = e; + }, + selected: self.data.onlyScan, + }, + { + type: "bi.multi_select_item", + text: "打开扫码入口", + ref: function (e) { + self.openLogin = e; + }, + selected: self.data.openLogin, + }, + { + type: "bi.right", + items: [ + { + type: "bi.button", + text: "保存", + level: "info", + height: 30, + handler: function () { + self.saveAction(); + }, + } + ] + } + ] + }; + } +}); +BI.shortcut("dd.scan.bottom.config.all.area", allConfigWid); +// 组件实现,效果为使用绝对布局组件放置了一个iframe +var toprow = BI.inherit(BI.Widget, { + + props: { + baseCls: "" + }, + beforeInit: function (callBack) { + var self = this; + Dec.reqGet(getPath("scan_entity", false), {}, function (data) { + console.info(data) + self.datas = data.data; + callBack(); + }) + }, + reloadList:function (){ + var self=this; + Dec.reqGet(getPath("scan_entity", false), {}, function (data) { + var da= data.data; + self.leftArea.populate(da); + }) + }, + saveAction: function () { + var self=this; + var value = this.rightArea.getValue(); + if(value.id){ + Dec.reqPut(getPath("scan_entity", false), value, function (data) { + console.info(data) + BI.Msg.toast("更新成功" + data.data.appName) + self.reloadList(); + }); + }else{ + Dec.reqPost(getPath("scan_entity", false), value, function (data) { + console.info(data) + self.reloadList(); + BI.Msg.toast("保存成功" + data.data.appName) + }); + } + }, + delAction: function (item) { + var self=this; + Dec.reqDelete(getPath("scan_entity/" + item.id, false), item, function (data) { + BI.Msg.toast("删除成功" + data.data.appName) + self.reloadList(); + }); + }, + changeAction: function (item) { + this.rightArea.setValue(item); + }, + createAction: function () { + this.rightArea.reset(); + }, + render: function () { + var self = this; + return { + type: "bi.horizontal", + vgap: 30, + items: [ + { + type: "dd.scan.top.config.left.area", + height: 200, + lists: this.datas, + ref:function (e){ + self.leftArea=e; + }, + listeners: [{ + eventName: "CREATENEW", + action: function () { + self.createAction(); + } + }, { + eventName: "DELETED", + action: function (item) { + self.delAction(item); + } + }, { + eventName: "CLICKITEM", + action: function (item) { + self.changeAction(item); + } + },], + }, + { + type: "dd.scan.top.config.right.area", + height: 400, + ref: function (e) { + self.rightArea = e; + }, + listeners: [{ + eventName: "SAVE", + action: function () { + self.saveAction(); + } + }], + } + ] + }; + } +}); +BI.shortcut("dd.scan.top.row", toprow); + +// 这个组件需要编辑功能所以继承到BI.Single 这样可以继承到getValue和setValue +var rightArea = BI.inherit(BI.Single, { + + props: { + //这个item是父级传下来的,如果有这个item 就表示这次是编辑而不是新建 + item: {} + }, + getValue: function () { + return { + id: this.options.item.id, + appName: this.appName.getValue(), + appId: this.appId.getValue(), + appKey: this.appKey.getValue(), + appIdSecret: this.appIdSecret.getValue(), + appSecret: this.appSecret.getValue(), + } + }, + setValue: function (item) { + this.options.item = item; + this.appName.setValue(item.appName); + this.appId.setValue(item.appId); + this.appSecret.setValue(item.appSecret); + this.appKey.setValue(item.appKey); + this.appIdSecret.setValue(item.appIdSecret); + }, + render: function () { + var self = this; + var item = this.options.item; + return { + type: "bi.vertical", + hgap: 10, + vgap:10, + items: [ + { + type: "bi.horizontal_adapt", + height: 30, + width:300, + columnSize: [120, "fill"], + items: [ + { + type: "bi.label", + text: "公司名称" + }, + { + type: "bi.editor", cls: "bi-border", + value: item.appName, + width: 200, + height: 24, + ref: function (e) { + self.appName = e; + } + } + ] + }, + { + type: "bi.horizontal_adapt", + width:300, + columnSize: [120, "fill"], + height: 30, + items: [ + { + type: "bi.label", + text: "appId" + }, + { + type: "bi.editor", cls: "bi-border", + value: item.appId, + width: 200, + height: 24, + ref: function (e) { + self.appId = e; + } + } + ] + },{ + type: "bi.horizontal_adapt", + width:300, + columnSize: [120, "fill"], + height: 30, + items: [ + { + type: "bi.label", + text: "扫码的appSercet" + }, + { + type: "bi.editor", cls: "bi-border", + value: item.appIdSecret, + width: 200, + height: 24, + ref: function (e) { + self.appIdSecret = e; + } + } + ] + }, { + type: "bi.horizontal_adapt", + width:300, + columnSize: [120, "fill"], + height: 30, + items: [ + { + type: "bi.label", + text: "appKey" + }, + { + type: "bi.editor", cls: "bi-border", + value: item.appKey, + width: 200, + height: 24, + ref: function (e) { + self.appKey = e; + } + } + ] + }, { + type: "bi.horizontal_adapt", + width:300, + columnSize: [120, "fill"], + height: 30, + items: [ + { + type: "bi.label", + text: "应用的appSecret" + }, + { + type: "bi.editor", cls: "bi-border", + width: 200, + height: 24, + value: item.appSecret, + ref: function (e) { + self.appSecret = e; + } + } + ] + }, + { + type: "bi.right", + items: [ + { + type: "bi.button", + text: "保存", + level: "info", + height: 30, + handler: function () { + //这里只发送一个事件出去,由父级组件调用getValue来实现 + self.fireEvent("SAVE"); + }, + } + ] + } + ] + }; + } +}); +BI.shortcut("dd.scan.top.config.right.area", rightArea); + +// 组件实现,效果为使用绝对布局组件放置了一个iframe +var topConfigWid = BI.inherit(BI.Widget, { + + props: { + lists: [] + }, + renderEditorItem: function (datas) { + var self = this; + return BI.map(datas, function (i, v) { + return { + type: "dd.scan.config.item", + item: v, + listeners: [{ + eventName: "DELETED", + action: function (item) { + self.fireEvent("DELETED", item); + }, + }, { + eventName: "CLICKITEM", + action: function (item) { + + self.fireEvent("CLICKITEM", item); + } + }] + } + }); + }, + populate:function (items){ + this.list.populate(this.renderEditorItem(items)) + }, + render: function () { + var self = this; + return { + type: "bi.vertical", + vgap: 10, + items: [ + { + type: "bi.label", + text: "钉钉扫码配置列表" + }, + { + type: "bi.button_group", + chooseType: BI.Selection.Single, + value: 2, + ref:function (e){ + self.list=e; + }, + items: this.renderEditorItem(this.options.lists), + layouts: [{ + vgap:10, + type: "bi.vertical", + }] + }, + { + type: "bi.center_adapt", + items: [ + { + type: "bi.button", + text: "新建", + level: "common", + height: 30, + width: 100, + handler: function () { + self.fireEvent("CREATENEW") + }, + + } + ] + } + ] + }; + } +}); +BI.shortcut("dd.scan.top.config.left.area", topConfigWid); + + +// 组件实现,效果为使用绝对布局组件放置了一个iframe +var cofigItem = BI.inherit(BI.Widget, { + + props: { + item: { + appName: "", + id: "" + } + }, + + render: function () { + var self = this; + return { + type: "bi.horizontal_adapt", + width:300, + columnSize: [200, "fill"], + hgap: 30, + items: [ + { + type: "bi.text_button", + text: this.options.item.appName, + handler: function () { + self.fireEvent("CLICKITEM", self.options.item) + } + }, + { + type: "bi.button", + iconCls: "close-font", + height: 14, + handler: function () { + self.fireEvent("DELETED", self.options.item) + } + } + ] + }; + } +}); +BI.shortcut("dd.scan.config.item", cofigItem);