最近要将一个H5应用上架浙里办,踩坑无数,下面记录分享下改造和应用上架流程。
加官方钉钉群
浙里办文档很差,限制很多,对接麻烦,经常需要在群里直接提问官方人员,有时项目的一些配置也需要在群里找人配置,因此必须先加官方钉钉公开群:34143965。
群文件里有些开发需要的文档和工具。
一些概念
业主工作台和开发商工作台
首先明确两个概念,浙里办后台分为业主工作台和开发商工作台。
- 业主工作台
业主指需求方(政府单位),需要在政务内网下才能访问,可以创建应用、提交正式审核、添加开发人员等。要在业主工作台进行操作,需要沟通让业主进行操作;或者使用远程工具/代理工具链接政务内网,登录相关政府人员浙政钉账号。
- 开发商工作台 IRS-应用发布子系统
可在外网直接访问,由开发人员操作,部署代码等。登录需要使用浙政钉账号,如果开发人员手机号没有开通浙政钉,需要先找业主开通。
浙里办APP和支付宝小程序
浙里办上架需要满足两个平台可用:
-
浙里办APP
-
支付宝内的浙里办小程序
两者都是一套代码,统一部署,目前只发现在单点登录时需要特殊处理。
注册应用
首先要在业主工作台注册要上架的应用(如上所说,在政务内网下操作),在注册的时候将对应的开发人员手机号添加到应用的开发人员列表中,添加的手机号需要开通浙政钉账号。
具体的注册流程在钉钉群文件里有操作视频。
需要注意的是,注册应用有一个编目的概念,在外层注册的应用,只是一个分组,要在分组内再创建具体的应用(看操作视频)
注册应用并添加开发人员后,开发者登录开放商工作台,就可以看到刚才创建的应用了。
本地调试应用
在群文件内下载「政务中台Debug工具」,需要登录,账号需要找业主要(非浙政钉账号)。
登录之后需要用手机上的浙里办扫码关联,并且浙里办在调试过程中要一直在前台运行,不能锁屏。
输入自己的本地url开始调试。
应用改造
下面介绍应用上架需
要做的对接及改造工作。
引入sdk
<script src="//assets.zjzwfw.gov.cn/assets/ZWJSBridge/1.0.1/zwjsbridge.js"></script>
会在window挂载ZWJSBridge变量,在ZWJSBridge上调用各种sdk能力
如扫码:
window.ZWJSBridge.scan({ type: 'qrCode' }).then(res => console.log(res))
接口走浙里办RPC网关
浙里办要求所有的接口调用都必须走他们的网关中转,因此所有接口都要在开发商工作台内进行注册。
接口注册
点击新建API,根据提示输入
API名称不可重复,因此注意定好命名规范(命名空间),后续在应用内调用接口是需要使用API名称作为唯一标识。
注意,「API是否需要登录」 只能选「否」(选择是之后调用接口会失败,后来在钉钉群里问了官方人员才知道这里只能选否,真坑啊)。
下一步,填写服务信息,生产环境和联调环境目标地址都要是HTTPS。
第三步参数映射关系,关键要选择正确的传参方式,两种方式不能混用。其他入参出参可以不填,网关都是透传的。需要注意的是,不支持动态路由传参,如 news/:id
接口调用
应用内不能直接使用ajax进行调用了,安装官方npm包 @aligov/jssdk-mgop
import { mgop } from '@aligov/jssdk-mgop'
// 封装请求
function service(config) {
return new Promise((resolve, reject) => {
mgop({
api: `mgop.brand.bawu.${zlbApi}`, // 必须 host: 'https://mapi.zjzwfw.gov.cn/', // 固定值
dataType: 'JSON',
header: headers, // 透传
data: data || params, // 无论是query还是body形式,都使用data入参,在RPC转发时会根据上面配置接口参数映射关系时选择的方式处理
type: method,
appKey: zlbAppKey, // 在开发上后台获取
onSuccess: data => {
resolve(data.data)
},
onFail: err => {
reject(err)
}
})
})
}
请求联调环境目标地址
默认请求会发到配置的正式环境中,给header添加isTestUrl: '1' 可以将请求发到联调环境目标地址(又是文档中没有说明,在群里问出来的,坑)
文件上传
RPC网关不支持文件流格式,一种方式是转为base64上传,实际体验并不好。
官方人员说上传文件功能「可自行实现」,即文件上传可以不走RPC网关,直接调用自己的服务。
单点登录
浙里办要求必须使用单点登录进行免登
申请开通单点登录
单点登录需要先在业主工作台进行申请
1、生成浙里办登录地址
浙里办硬性要求用户数据都要走浙里办的用户中心登录,所以才有这个独立开发的登录页面。
前端需要把需要登录操作的页面跳转直接改为浙里办提供的登录地址 puser.zjzwfw.gov.cn/sso/mobile.… xxxx)
申请应用时会有一个**接入码给你,这个就是浙里办登录地址后面的servicecode** ****,把接入码写在登录地址上,再加上 goto 参数组成一个登录后的跳转地址
2、配置回调地址(前端地址)
在浙里办技术群(群号:34143965,这个用普通钉钉去加)里找对应的对接人进行回调地址的配置(提供接入码和回调地址,接入码就是accesskey,可以在业主单位-工作台上找到)。
在开发阶段,可以提供本地项目启动后的地址(配置回调地址随时可以更改,跟对接人说就行了)。在上线阶段,配置正式的地址,也就是应用管理下面二维码的访问地址
3、ticket票据认证,前端sp路由跳转
在回调地址配置完后,用户操作一个涉及到读取用户信息却没有登录的地方时,就跳转到步骤一的登录地址,然后用户输入浙里办的账号密码点击登录,这时候登录成功后页面会自动跳转到步骤二设置的回调地址,并且自动跳转时会在回调地址的 URL 路径上带上登录时返回给你的 ****ticket ****和 sp 参数。ticket ****是要通过后端提供的接口去获取用户信息,sp是在步骤一设置的 goto 参数。拿到用户信息之后,前端就可以保存用户信息,用浙里办提供的原生存储方法也可以,用浏览器的 localstorage 也行,拿到我们需要的 sp 之后就可以做路由的跳转了
登录流程
开通后,会得到登录地址(支付宝环境和浙里办APP环境不同)
export const ZLB_APP_LOGIN_URL = `https://puser.zjzwfw.gov.cn/sso/mobile.do?action=oauth&scope=1&servicecode=${ZLB_SERVICE_CODE}&redirectUrl=${ZLB_LOCAL_PAGE}&goto=${ZLB_LOCAL_PAGE}`
export const ZLB_ALIPAY_LOGIN_URL = `https://puser.zjzwfw.gov.cn/sso/alipay.do?action=ssoLogin&scope=1&servicecode=${ZLB_SERVICE_CODE}&redirectUrl=${ZLB_PROD_PAGE}&goto=${ZLB_LOCAL_PAGE}`
servicecode 在申请开通单点登录后拿到的固定值。
redirectUrl 登录成功之后要跳转到的地址,可以是应用的中间页,也可以是首页,域名只能是代码部署到浙里办之后生成的域名,若要本地调试,需要在钉钉群里找人配置白名单。
goto 用户在登录前真正要进入的目标地址。
下面以应用首页不需要登录为例:
- 首先在浙里办进入应用时,浙里办会要求用户登录,登录成功后进入我们应用的首页
https://abc.com(此时我们的应用内还是未登录状态)。
- 用户点击「个人信息」按钮(对应路由
home/profile),应用内判断个人信息页面需要登录并且当前未登录,则跳转到上面的登录页(支付宝与浙里办APP的跳转路径略有不同)
location.replace('https://puser.zjzwfw.gov.cn/sso/mobile.do?action=oauth&scope=1&servicecode=1234&redirectUrl=https://abc.com/loginPage&goto=home/profile')
- 在登录页,浙里办会根据
servicecode校验你的应用,生成一个ticket,然后重定向页面到redirectUrl(这里是/loginPage路由作为中间登录页 ),并且把ticket和goto拼到query中(goto会变回sp参数),如:abc.com/loginPage?t…
- 在
loginPage进行登录,从query中取ticket,调用后端接口传入ticket(后端会用这个ticket去调用浙里办的服务,并与自己应用的用户体系进行绑定)。
- 使用ticket登录成功后取
sp参数,拿到用户原本想要进入的页面路由信息,进行跳转。
配置登录回调白名单
redirectUrl不能随便设置,否则会报错,要本地调试,需要在钉钉群里看群公告,@对应的人员,提供appkey和要配置的域名,让他们帮忙配置。
适老化
上架必须做适老化适配。
在浙里办app首页可以开启「长辈版」,进入我们的应用后,通过sdk方法可以拿到当前用户是否开启了长辈模式
sdk.getUiStyle().then(res => {
this.isElder = res.uiStyle === 'elder' // 是否开启长辈模式
})
然后用修改less变量、注入样式文件、修改类名等方式修改全局样式。
主要是文字和图标进行放大,审查不是非常严格。
信息脱敏
姓名、手机号、身份证号等需要做信息脱敏(打星号),官方文档要求必须后端脱敏,审核并不严格,可以尝试在前端脱敏。
埋点
在index.html里添加如下代码,进行基础埋点:
除了appId,其余是固定值
<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://d.alicdn.com/alilog/mlog/aplus.js?id=202951085';
f.parentNode.insertBefore(j, f);
})(window, document, 'script', 'aplus_queue');
aplus_queue.push({
action: 'aplus.setMetaInfo',
arguments: ['aplus-waiting', 'MAN']
});
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']
});
aplus_queue.push({
action: 'aplus.setMetaInfo',
arguments: ['appId', '12345678'] // 工作台找appId
});
</script>
登录后设置用户信息
登录成功后,设置后续埋点的用户信息
// ...
const userinfo = await login() // 使用ticket登录,拿到用户信息,此处需要让后端返回浙里办的一些用户信息
window.aplus_queue.push({
action: 'aplus.setMetaInfo',
arguments: ['_hold', 'BLOCK']
})
window.ZWJSBridge.getUUID().then(({ uuid }) => {
const { zlbUserid, zlbLoginname } = userInfo
window.aplus_queue.push({ action: 'aplus.setMetaInfo', arguments: ['_user_nick', zlbLoginname] }) // 浙里办的loginname
window.aplus_queue.push({ action: 'aplus.setMetaInfo', arguments: ['_user_id', zlbUserid] }) // 浙里办的userid
window.aplus_queue.push({ action: 'aplus.setMetaInfo', arguments: ['_dev_id', uuid] })
window.aplus_queue.push({
action: 'aplus.setMetaInfo',
arguments: ['_hold', 'START']
})
})
PV
在路由守卫里进行PV埋点
function sendZlbPV(page) {
const sdk = window.ZWJSBridge
const getLocation = sdk.getLocation()
const getUserType = sdk.getUserType()
Promise.all([getUserType, getLocation]).then(([userTypeData, locationData]) => {
const { userType } = userTypeData
const { longitude, latitude } = locationData
window.aplus_queue.push({
action: 'aplus.sendPV',
arguments: [
{
is_auto: false
},
{
miniAppId: zlbAppId,
miniAppName: 'xxx',
long: longitude,
lati: latitude,
userType
}
]
})
})
}
\
埋点信息校验
可以在network里看上报的信息,采用的1像素的gif方式,看url拼接的参数。
至于上报是否成功,在工作台是无法查看的,需要钉钉群里联系工作人员看埋点统计数据。
禁用外链
浙里办禁止外链跳转,在部署代码时,会全局扫描代码,如果检测到location.href,href的值的域名是绝对路径且域名不是浙里办官方的域名,就会报存在外链,检测不通过。
另外,如果href的值是一个变量,因为无法确定是否外链,也会直接不通过。
网格通项目使用了vant,部署浙里办时检测到vant编译后的源码里存在如下代码
该行代码导致检测不通过,我这里fork出一份进行了注释,需要可以自取 vant-brand
注:该行代码的作用是,有些组件支持接收url属性,点击进行外链跳转(如tabbar组件),这里注释后不可再使用url进行外链跳转。
部署代码
前端应用需要部署在浙里办
构建规范
浙里办部署需要提交源码(不包含node_modules),提交后会运行npm i和npm run build进行构建,因此:
-
依赖包需要是npm上公开的包,不能是私服里的包
-
script中需要有build命令进行构建(无法指定命令,因此无法区分构建环境,有需求的话只能部署前手动修改) -
构建产物目标是根目录下的
build文件夹
提交代码
在开发工作台对应的应用点击部署发布,在测试环境点击发布,将源码压缩后提交,写上版本号开发部署。
部署过程比较慢,编译完会扫描检测,部署完成后会生成一个二维码,使用浙里办扫描二维码就可以访问了(也可以复制地址在本地调试工具中打开进行调试)
后续点击提交审核就会把部署的代码提交到正式环境,即构建是不区分正式和测试环境的,因此在提交正式审核前需要手动修改代码,区分环境。
提交审核
官方会提供一些自查文件,如运维材料、安全测试报告、适老化适配完成申请报告等,需要前端、后端、运维、测试各自完成,这里不再详细说。
开发商提交审核
在部署代码的界面点击提交审核,这一步其实是提交给业主,然后在业主工作台再点击提交,并且将上面的审核材料上传,将应用提交给浙里办的工作人员进行审核。