Browse Source

开源任务材料

10.0
LAPTOP-SB56SG4Q\86185 3 years ago
parent
commit
2b2f78dd3c
  1. 5
      README.md
  2. BIN
      doc/钉钉扫码多公司版本配置说明.docx
  3. BIN
      lib/fr-plugin-dingtalk-10.4.983.jar
  4. BIN
      lib/hutool-all-4.3.2.jar
  5. BIN
      lib/taobao-sdk-java-auto_1479188381469-20200420.jar
  6. 39
      plugin.xml
  7. 59
      src/main/java/com/fr/plugin/AllScanBeans.java
  8. 51
      src/main/java/com/fr/plugin/ConfigComponent.java
  9. 33
      src/main/java/com/fr/plugin/ConfigJSHander.java
  10. 42
      src/main/java/com/fr/plugin/GetQRHandle.java
  11. 128
      src/main/java/com/fr/plugin/HtmlUtils.java
  12. 10
      src/main/java/com/fr/plugin/LDAPLocalFinder.java
  13. 21
      src/main/java/com/fr/plugin/PluginInitializeFilterBridge.java
  14. 153
      src/main/java/com/fr/plugin/SCDBAccessProvider.java
  15. 15
      src/main/java/com/fr/plugin/SacnCycleMonitor.java
  16. 292
      src/main/java/com/fr/plugin/ScanCallBackHandle.java
  17. 51
      src/main/java/com/fr/plugin/ScanComponent.java
  18. 108
      src/main/java/com/fr/plugin/ScanConfig.java
  19. 11
      src/main/java/com/fr/plugin/ScanControllerBridge.java
  20. 12
      src/main/java/com/fr/plugin/ScanFunction.java
  21. 19
      src/main/java/com/fr/plugin/ScanHttpHander.java
  22. 41
      src/main/java/com/fr/plugin/ScanJS.java
  23. 32
      src/main/java/com/fr/plugin/ScanJSHander.java
  24. 31
      src/main/java/com/fr/plugin/beans/ScanAllConfigBean.java
  25. 71
      src/main/java/com/fr/plugin/beans/ScanConfigBean.java
  26. 61
      src/main/java/com/fr/plugin/dao/ScanConfigDao.java
  27. 73
      src/main/java/com/fr/plugin/entitys/ScanConfigEntity.java
  28. 69
      src/main/java/com/fr/plugin/web/controllers/ScanController.java
  29. 17
      src/main/resources/com/fr/plugin/buynew.html
  30. 69
      src/main/resources/com/fr/plugin/new.html
  31. 331
      src/main/resources/com/fr/plugin/scan.tpl
  32. 3
      src/main/resources/com/fr/plugin/scanBuy.tpl
  33. 496
      src/main/resources/com/fr/plugin/web/dds/dds.js

5
README.md

@ -1,3 +1,6 @@
# open-JSD-8016
JSD-8016 开源任务材料
JSD-8016 开源任务代码\
免责说明:该源码为第三方爱好者提供,不保证源码和方案的可靠性,也不提供任何形式的源码教学指导和协助!\
仅作为开发者学习参考使用!禁止用于任何商业用途!\
为保护开发者隐私,开发者信息已隐去!若原开发者希望公开自己的信息,可联系hugh处理。

BIN
doc/钉钉扫码多公司版本配置说明.docx

Binary file not shown.

BIN
lib/fr-plugin-dingtalk-10.4.983.jar

Binary file not shown.

BIN
lib/hutool-all-4.3.2.jar

Binary file not shown.

BIN
lib/taobao-sdk-java-auto_1479188381469-20200420.jar

Binary file not shown.

39
plugin.xml

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<plugin>
<id>com.fr.plugin.sln5470</id>
<name><![CDATA[钉钉扫码多项目]]></name>
<active>yes</active>
<version>1.1.5</version>
<env-version>10.0</env-version>
<jartime>2021-01-01</jartime>
<vendor>fr.open</vendor>
<description><![CDATA[钉钉扫码登陆控件]]></description>
<function-recorder class="com.fr.plugin.ScanFunction"/>
<change-notes>
<![CDATA[
<p>[2020-04-22]项目启动</p>
<p>[2020-11-19]修复路径404bug</p>
<p>[2020-11-19]只开启扫码功能</p>
<p>[2021-02-02]插件对于新版本钉钉插件依赖关系,必须要安装钉钉插件后才能使用。</p>
<p>[2021-05-26]适配新版本登录界面按钮。</p>
]]>
</change-notes>
<main-package>com.fr.plugin</main-package>
<!--插件生命周期接口-->
<lifecycle-monitor class="com.fr.plugin.SacnCycleMonitor"/>
<extra-decision>
<!--插件注入HttpHandler-->
<HttpHandlerProvider class="com.fr.plugin.ScanHttpHander"/>
<WebResourceProvider class="com.fr.plugin.ScanJSHander"/>
<WebResourceProvider class="com.fr.plugin.ConfigJSHander"/>
<!--注册Spring的Controller-->
<ControllerRegisterProvider class="com.fr.plugin.ScanControllerBridge"/>
</extra-decision>
<extra-core>
<DBAccessProvider class="com.fr.plugin.SCDBAccessProvider"/>
</extra-core>
<dependence>
<item type="plugin" key="com.fr.plugin.dingtalk"/>
</dependence>
</plugin>

59
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<ScanConfigBean> 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();
}
}

51
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;
}
};
}
}

33
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()};
}
}

42
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<String , Object> 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);
}
}
}

128
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;
}
}

10
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";
}
}

21
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 {
}
}

153
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<Boolean>() {
@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<ScanConfigBean>() {
@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<Boolean>() {
@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<ScanConfigBean> like(String key ){
try{
return accessor.runQueryAction(new DBAction<List<ScanConfigBean>>() {
@Override
public List<ScanConfigBean> run(DAOContext context) throws Exception {
List<ScanConfigBean> result = new ArrayList<ScanConfigBean>();
List<ScanConfigEntity> 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<ScanConfigBean>();
}
}

15
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) {
}
}

292
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<ScanConfigEntity> 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<User> 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;
}
}

51
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;
}
};
}
}

108
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<String> appId = Holders.simple("");
@Identifier(value = "Secret", name = "扫码app_secret", description = "", status = Status.SHOW)
private Conf<String> secret = Holders.simple("");
// @Identifier(value = "loginKey", name = "登录AppKey", description = "", status = Status.SHOW)
// private Conf<String> loginKey = Holders.simple("");
//
// @Identifier(value = "LoginSecret", name = "登录app_secret", description = "", status = Status.SHOW)
// private Conf<String> loginSecret = Holders.simple("");
@Identifier(value = "openLogin", name = "登录界面启动开关", description = "", status = Status.SHOW)
private Conf<Boolean> openLogin = Holders.simple(true);
@Identifier(value = "defaultScan", name = "默认扫码登录", description = "", status = Status.SHOW)
private Conf<Boolean> defaultScan = Holders.simple(false);
@Identifier(value = "onlyScan", name = "只保留扫码登录", description = "", status = Status.SHOW)
private Conf<Boolean> 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<String>) appId.clone();
cloned.secret = (Conf<String>) secret.clone();
// cloned.loginKey = (Conf<String>) loginKey.clone();
// cloned.loginSecret = (Conf<String>) loginSecret.clone();
//
cloned.onlyScan = (Conf<Boolean>) onlyScan.clone();
cloned.defaultScan = (Conf<Boolean>) defaultScan.clone();
cloned.openLogin = (Conf<Boolean>) openLogin.clone();
return cloned;
}
}

11
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};
}
}

12
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 "功能点检测";
}
}

19
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;
}
}

41
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<String, Object> 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";
}
}

32
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()};
}
}

31
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;
}
}

71
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;
}
}

61
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<ScanConfigEntity> {
public ScanConfigDao(DAOSession daoSession) {
super(daoSession);
}
protected Class<ScanConfigEntity> getEntityClass() {
return ScanConfigEntity.class;
}
public final static DAOProvider DAO = new DAOProvider() {
@Override
public Class getEntityClass() {
return ScanConfigEntity.class;
}
@Override
public Class<? extends BaseDAO> 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<ScanConfigEntity> likeByKey(String key) throws Exception {
QueryCondition condition = QueryFactory.create();
if (StringUtils.isNotBlank(key)) {
condition.addRestriction(RestrictionFactory.like("app_name", key));
}
return find(condition);
}
}

73
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;
}
}

69
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);
}
}

17
src/main/resources/com/fr/plugin/buynew.html

@ -0,0 +1,17 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta charset="utf-8">
<title>钉钉扫码</title>
</head>
<script type="text/javascript">
</script>
<body>
<p style="text-align: center" id="scan">
请购买授权后使用
</p>
</body>
</html>

69
src/main/resources/com/fr/plugin/new.html

@ -0,0 +1,69 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta charset="utf-8">
<title>钉钉扫码</title>
</head>
<script src="https://g.alicdn.com/dingding/dinglogin/0.0.5/ddLogin.js"></script>
<script type="text/javascript">
var host = window.location.protocol+"//"+window.location.host+"${fineServletURL}";//左良测试new
function login() {
var appId="${appId}";
var callback="${callBack}";
var target = encodeURIComponent(callback);
var callBack = encodeURIComponent(host + "/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&state=STATE&redirect_uri=' + callBack);
/*
* 解释一下goto参数,参考以下例子:
* var url = encodeURIComponent('http://localhost.me/index.php?test=1&aa=2');
* var goto = encodeURIComponent('https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=appid&response_type=code&scope=snsapi_login&state=STATE&redirect_uri='+url)
*/
var obj = DDLogin({
id: "scan",
goto: goto,
style: "border:none;background-color:#FFFFFF;",
width: "365",
height: "400"
});
var handleMessage = function (event) {
var origin = event.origin;
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=STATE&redirect_uri="+callBack+"&loginTmpCode="+loginTmpCode;
console.log("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);
}
}
function setCookie(name, value) {
document.cookie = name + "=" + escape(value);
}
function getCookie(name) {
var arr, reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)");
if (arr = document.cookie.match(reg)) {
return unescape(arr[2]);
} else {
return null;
}
}
window.onload = login;
</script>
<body>
<p style="text-align: center" id="scan"></p>
</body>
</html>

331
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 = $("<div class=\"bi-left-right-vertical-adapt-layout bi-float-left-layout clearfix \" style=\"position: relative; margin-bottom: 30px;\"><div class=\"bi-flex-vertical-center-adapt-layout item-icon dir-font-14 bi-flex-horizontal-layout v-middle h-left\" style=\"width: 26px;position: absolute;top: 0px;bottom: 0px;right: 0px;\"><div class=\"bi-flex-center-adapt-layout\" style=\"position: relative; flex-shrink: 0;\"><i class=\"bi-single x-icon b-font horizon-center display-block\" style=\"position: relative; flex-shrink: 0; margin: 0px;\"></i></div></div></div>")
// 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 = $("<div class=\"bi-left-right-vertical-adapt-layout bi-float-left-layout clearfix \" style=\"position: relative; margin-bottom: 30px;\"><div class=\"bi-flex-vertical-center-adapt-layout item-icon dir-font-14 bi-flex-horizontal-layout v-middle h-left\" style=\"width: 26px;position: absolute;top: 0px;bottom: 0px;right: 0px;\"><div class=\"bi-flex-center-adapt-layout\" style=\"position: relative; flex-shrink: 0;\"><i class=\"bi-single x-icon b-font horizon-center display-block\" style=\"position: relative; flex-shrink: 0; margin: 0px;\"></i></div></div></div>")
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: "<iframe scrolling=\"no\" frameborder='0' style=\"width:98%; overflow: hidden\" src=\"" + url + "\"></iframe>"
},
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)
})();

3
src/main/resources/com/fr/plugin/scanBuy.tpl

@ -0,0 +1,3 @@
;(function () {
alert("钉钉扫码插件授权过期或未授权")
})();

496
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);
Loading…
Cancel
Save