1.前言 浙政钉,是浙江省弄的一个专有钉钉,但是很多的客户还是要使用浙政钉的,我觉得开发和测试真是一个巨大的麻烦,比如前端测试,后端测试,谁用谁知道,真是操蛋的玩意。
在钉钉的 “杭州市‘浙政钉2.0’应用迁移” 交流群里,有一个文件叫做 “浙政钉应用上钉对接文档-IRS(杭州).docx” 文件。
2.专有钉钉 在文章中,需要创建一个专有钉钉账号,这个专有钉钉账号的创建又是一个问题,是通过邀请注册机制进行的,我去哪里找谁注册呢?入住开放平台,当前专有钉钉平台仅支持邀请制入驻:
客户合作伙伴:专有钉钉客户合作的开发者,由专有钉钉客户邀请入驻,请联系客户侧对接人获取入驻方式。
平台合作伙伴:专有钉钉平台合作的开发者,由平台邀请入驻,目前仅开放定向入驻。
(1)专有钉钉入住 登陆专有钉钉开放平台 注册页面,进行ISV的申请和入住。
这里我还遇到了一个问题,就是我们公司的账号不知道是谁申请的,也找不到人了,但是就是注册不了,只能请群里的大佬来处理了,让他把账号改掉。
(2)创建账号 审核通过后,会有一个管理员。管理员登录专有钉钉 可以查看后台管理。
(3)项目开发
3.应用上架 根据群内文章,应用上架需要在IRS上申请。IRS分为两部分,一部分是业主工作台,一部分是开发商工作台。
业主工作台 政务外网打开业主工作台 ,创建治理侧应用发布,治理侧负责将应用发布至浙政钉APP。创建治理侧发布后,开发商首次登录开发商工作台时,系统通过手机号码自动将治理侧应用同步至开发商工作台的治理侧名称列表中,应用状态为部署准备中(应用发布类型为H5或小程序+PC,代码托管)。
文档中只讲了关于应用发布的方法,没有讲如何创建应用。也就是说,进行应用发布的前提就是在工作台上已经有了一个应用,至于如何创建的这个应用,我猜测是通过应用编目的方式进行的。通过应用注册 功能,进行应用创建,然后一步步的创建一个应用,创建完成之后。
开发商工作台 业主侧创建了应用之后,开发商就可以在开发商工作台 查看了。这里我遇到了一个问题就是,开始的时候是看不见的,但是后来又看见了,我估计是在业主工作台点击了发布应用的关系,这个待定。
(1) 业主登陆 “业主工作台”,创建应用,填写基本信息
(2) 开发商登陆 “开发商工作台”,进行应用管理,上传代码,提交代码部署 在开发商工作台上,有治理测名称列表,找到相关的应用,进行应用部署发布
(3) 业主在工作台上进行项目验收,准备相关材料填写相关内容,进行上架。“浙政钉”应用上架需要提交的材料清单,里面包括了很多的内容:“浙政钉“ xxxx应用联系人-模板、“浙政钉”xxxx应用上架自查-模板、“浙政钉”xxxx应用系统部署文档-模板、“浙政钉”xxxx应用系统缺省页设计展示-模版、“浙政钉”xxxx应用系统应急预案-模版、”浙政钉“ xxxx应用安全报告、扩大使用范围的模板、情况说明模板指引、系统压测报告说明
(4) 如果需要升级 “版本是需提交发布的版本,请先联系业主发起版本升级,再进行代码上传。”
(5) 代码更新 如果需要代码更新,前端工程需要提交到开发商工作台上,在压缩包中需要能执行 npm run build 命令,并生成 build 目录,build 目录里面就是打包后到代码。进入到 业主工作台,然后找到 “工作台” -> “我的资源” -> “应用发布” -> “治理侧发布”,找到相应的应用,在操作里面选择 “重新部署”,然后登陆开发商工作台,进行代码部署就可以了。
4.获取access_token 使用官方提供的sdk进行access_token的获取,首先在pom.xml中添加依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <dependency > <groupId > dingding</groupId > <artifactId > dingding</artifactId > <scope > system</scope > <version > 1.2.0</version > <systemPath > ${project.basedir}/lib/zwdd-sdk-java-1.2.0.jar</systemPath > </dependency > <dependency > <groupId > joda-time</groupId > <artifactId > joda-time</artifactId > <version > 2.10</version > </dependency > <dependency > <groupId > com.alibaba</groupId > <artifactId > fastjson</artifactId > <version > 1.2.47</version > </dependency > <dependency > <groupId > org.apache.commons</groupId > <artifactId > commons-lang3</artifactId > </dependency > <dependency > <groupId > org.apache.httpcomponents</groupId > <artifactId > httpclient</artifactId > <version > 4.5.13</version > </dependency >
(2) 编写java代码,获取tokan
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @RequestMapping("gettoken") public AjaxResult gettoken () { try { ExecutableClient executableClient = ExecutableClient.getInstance(); executableClient.setAccessKey(appkey); executableClient.setSecretKey(appsecret); executableClient.setDomainName("openplatform.dg-work.cn" ); executableClient.setProtocal("https" ); executableClient.init(); String api = "/gettoken.json" ; PostClient postClient = executableClient.newPostClient(api); String apiResult = postClient.post(); System.out.println(apiResult); }catch (Exception e){ logger.error("gettoken" ,e); } return AjaxResult.error(); }
问题 (1) Could not initialize class com.alibaba.xxpt.gateway.shared.client.http.ExecutableClient 我使用的是 zwdd-sdk-java-1.2.0.jar 这个开发包,使用了测试用例进行token获取,结果报错了,还有就是错误:java.lang.NoClassDefFoundError: org/apache/http/client/ResponseHandler,也是同样的问题
【解决】 主要就是没有配置 httpclient 这个依赖,并且版本版本要是最新的,我这里旧版本不行。
5.免密登录 (1)在浙政钉中打开应用,根据手机和pc端不同,分别会在url后面携带一个免登授权码。pc端为 auth_code,手机端为 code。 (2)通过免登授权码,构造请求 “/rpc/oauth2/dingtalk_app_user.json”,获取用户信息,和自己项目中的用户体系进行比较,从而获取是否可以进行免登操作。
6.扫码登陆 扫码登录和免密登录内容不一样,根据群里沟通的结果,如果需要进行扫码登录,还需要在irs上申请组件。和专有钉钉不太一样,需要先走通专有钉钉的扫码登录。 (1)打开irs首页
(2)搜索 “扫码登录”,申请扫码登录组件
(3) 根据应用信息填写相关内容 申请之后,相关的 appid 和 appkey,浙政钉的相关工作人员,会把信息发送到申请人的手机号上,开发人员需要去业主单位那里去拿。
应用标志 client_id 会在 appid 的基础上增加一个 _dingoa 后缀。
(4)接入钉钉扫码登录 通过两种方式接入钉钉扫码登录:
方式一:独立页面 使用专有钉钉提供的扫码登录页面,根据相关参数构造以下页面,URL中的client_id和redirect_uri两个参数的值填入第三方web系统的应用标识和回调地址。专有钉钉用户扫码登录并确认后,会302到你指定的redirect_uri,并向url参数中追加临时授权码code(此code非authcode)及state两个参数。获取到code之后,就可以通过code获取用户身份信息了。
方式二:内嵌iframe 在页面中通过iframe嵌入页面,参数和第一种一样,就是多了一个 embedMode=true 的参数。
扫码成功后,需要在页面中监听扫码结果
1 2 3 4 5 6 7 <script type="application/javascript" > window .addEventListener ('message' , function (event ) { alert (JSON .stringify (event.data )); }); </script>
(6) 配置ip白名单 调用接口gettoken获取token的时候,还需要配置ip地址白名单。我没有找到配置的地方,只能通过群里的运维人员进行添加和修改了。
(5) 获取用户信息
通过 /gettoken.json 接口,根据appkey,和appscret获取调用的凭证。
通过服务端的 /rpc/oauth2/getuserinfo_bycode.json ,传入获取到的临时授权码,可以获取用户信息,这里没有手机号。我发现我找了很多的地方,还是没有找到直接获取用户手机号的方法。根据群里的消息说明:“经过大数据局评估,处于安全考虑,目前暂时无法提供获取人员信息返回手机号的功能。”
7.应用埋点 浙政钉的应用埋点,主要分为稳定性分析和流量分析,如果是单页应用,就需要在每一个页面上都填入相应的内容。
(1)稳定性代码 在首页 head 中增加如下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 <script src ='https://wpkgate-emas.ding.zj.gov.cn/static/wpk-jssdk.1.0.2/wpkReporter.js' crossorigin ='true' > </script > <script > try { const config={ bid :'Civil_air_defense__zzdpro' , signkey :'1234567890abcdef' , gateway :'https://wpkgate-emas.ding.zj.gov.cn' }; const wpk=new wpkReporter (config); wpk.installAll (); window ._wpk =wpk; }catch (err){ console .error ('WpkReporterinitfail' ,err); } </script > <script > (function (w, d, s, q, i ) { var key=(new Date ()).getTime (); w[q] = w[q] || []; var f = d.getElementsByTagName (s)[0 ], j = d.createElement (s); j.async = true ; j.id = "beacon-aplus" ; j.src = "https://alidt.alicdn.com/alilog/mlog/aplus_cloud.js?_=" +key; f.parentNode .insertBefore (j, f); })(window , document , "script" , "aplus_queue" ); aplus_queue.push ({ action : 'aplus.setMetaInfo' , arguments : ['aplus-rhost-v' , 'alog-api.ding.zj.gov.cn' ] }); aplus_queue.push ({ action : 'aplus.setMetaInfo' , arguments : ['aplus-rhost-g' , 'alog-api.ding.zj.gov.cn' ] }); var u = navigator.userAgent var isAndroid = u.indexOf ('Android' ) > -1 var isIOS = !!u.match (/\(i[^;]+;( U;)? CPU.+Mac OS X/ ) aplus_queue.push ({ action : 'aplus.setMetaInfo' , arguments : ['appId' , isAndroid ? '28302650' : isIOS ? '28328447' : '47130293' ] }); </script >
注意 这里有一个地方需要注意,原先的我的 bid 写错误了:Civil_air_defense__zzdpro,正确的 bid 应该是:Civil_air_defense_zzdpro,所以导致了稳定性监控没有接入成功
(2)流量分析代码 加入了流量分析之后,需要通过执行pv操作上报流量代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 export function queue (metaId, metaName, metaPath ) { if (!aplus_queue){ return } aplus_queue.push ({ action : "aplus.setMetaInfo" , arguments : ["aplus-rhost-v" , "alog-api.ding.zj.gov.cn" ], }); aplus_queue.push ({ action : "aplus.setMetaInfo" , arguments : ["aplus-rhost-g" , "alog-api.ding.zj.gov.cn" ], }); var u = navigator.userAgent ; var isAndroid = u.indexOf ("Android" ) > -1 ; var isIOS = !!u.match (/\(i[^;]+;( U;)? CPU.+Mac OS X/ ); aplus_queue.push ({ action : "aplus.setMetaInfo" , arguments : [ "appId" , isAndroid ? "28302650" : isIOS ? "28328447" : "47130293" , ], }); aplus_queue.push ({ action : "aplus.setMetaInfo" , arguments : ["_hold" , "BLOCK" ], }); aplus_queue.push ({ action : "aplus.setMetaInfo" , arguments : ["aplus-waiting" , "MAN" ], }); aplus_queue.push ({ action : "aplus.sendPV" , arguments : [ { is_auto : false , }, { sapp_id : '26318' , sapp_name : 'Civil_air_defense' , page_id : metaId, page_name : metaName, page_url : metaPath, }, ], }); aplus_queue.push ({ action : "aplus.setMetaInfo" , arguments : ["_user_id" , userInfo.accountId ||"34268160" ], }); aplus_queue.push ({ action : "aplus.setMetaInfo" , arguments : ["_user_nick" , userInfo.nickNameCn ||"人防工程" ], }); aplus_queue.push ({ action : "aplus.setMetaInfo" , arguments : ["_hold" , "START" ], }); }
在单页应用中,每一个页面中都要执行 queue 函数
1 2 3 4 onMounted (() => { queue ("login" ,"用户登陆" ,"pages/login/index" ) });
测试,查询 埋点情况
另外一个版本,主要不同点就是这个域名不同,后来咨询过了,这个域名已经废弃了,不用了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 <script src ='https://wpk-gate.zjzwfw.gov.cn/static/wpk-jssdk.1.0.2/wpkReporter.js' crossorigin ='true' > </script > <script > try { const config = { bid : 'Civil_air_defense_zzdpro' , signkey : '1234567890abcdef' , gateway : 'https://wpk-gate.zjzwfw.gov.cn' }; const wpk = new wpkReporter (config); wpk.installAll (); window ._wpk = wpk; } catch (err) { console .error ('WpkReporter init fail' , err); } </script > <script > (function (w, d, s, q, i ) { w[q] = w[q] || []; var f = d.getElementsByTagName (s)[0 ],j = d.createElement (s); j.async = true ; j.id = 'beacon-aplus' ; j.src = 'https://alidt.alicdn.com/alilog/mlog/aplus_cloud.js' ; f.parentNode .insertBefore (j, f); })(window , document , 'script' , 'aplus_queue' ); aplus_queue.push ({ action : 'aplus.setMetaInfo' , arguments : ['aplus-rhost-v' , 'alog.zjzwfw.gov.cn' ] }); aplus_queue.push ({ action : 'aplus.setMetaInfo' , arguments : ['aplus-rhost-g' , 'alog.zjzwfw.gov.cn' ] }); var u = navigator.userAgent var isAndroid = u.indexOf ('Android' ) > -1 var isIOS = !!u.match (/\(i[^;]+;( U;)? CPU.+Mac OS X/ ) aplus_queue.push ({ action : 'aplus.setMetaInfo' , arguments : ['appId' , isAndroid ? '28302650' : isIOS ? '28328447' : '47130293' ] }); </script >
通用页面函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 export function queue (metaId, metaName, metaPath ) { if (!aplus_queue){ return } aplus_queue.push ({ action : "aplus.setMetaInfo" , arguments : ["_hold" , "BLOCK" ], }); aplus_queue.push ({ action : "aplus.setMetaInfo" , arguments : ["aplus-waiting" , "MAN" ], }); aplus_queue.push ({ action : "aplus.sendPV" , arguments : [ { is_auto : false , }, { sapp_id : '26318' , sapp_name : 'Civil_air_defense' , page_id : metaId, page_name : metaName, page_url : metaPath, }, ], }); aplus_queue.push ({ action : "aplus.setMetaInfo" , arguments : ["_user_id" , userInfo.accountId ||"34268160" ], }); aplus_queue.push ({ action : "aplus.setMetaInfo" , arguments : ["_user_nick" , userInfo.nickNameCn ||"人防工程" ], }); aplus_queue.push ({ action : "aplus.setMetaInfo" , arguments : ["_hold" , "START" ], }); }