车后市APP:Cordova项目转换并兼容公众号总结
2022年5月12日 7766 5Cordova与React Native一样,是目前主流的Hybrid模式APP开发框架,而相对于H5代码的衔接,Cordova在这方面的兼容性更好一些,因为它没有定义具体的语法,将具体的业务逻辑留给ionic或者AngularJS等单页应用的开发框架去实现,自己负责与原生设备接口交互,包括相机(拍照)、相册、GPS定位、扫码、文件管理、支付等等。作为一款车后市APP,酷爱洗车APP是基于延誉宝会员管理系统深度定制合作的物联网项目,随着业务的不断扩展,APP端的已经满足自助洗车客户对移动端应用入口的需求,因此,开发小程序和公众号成为运营推广的必然要求。但是考虑开发成本,特别是后期的运营维护,如果两套代码,必然增加后续维护的负担,造成系统升级的项目投入增加,因此,最好的解决方案,就是实现Cordova项目可以兼容微信公众号,如此这样,iPhone、安卓和公众号三个入口使用一份代码,便于BUG修复,并提高系统运维的效率,从而在服务器端大数据运维投入不变的前提下,减轻客户端的技术投入压力。
Cordova与React Native一样,是目前主流的Hybrid模式APP开发框架,而相对于H5代码的衔接,Cordova在这方面的兼容性更好一些,因为它没有定义具体的语法,将具体的业务逻辑留给ionic或者AngularJS等单页应用的开发框架去实现,自己负责与原生设备接口交互,包括相机(拍照)、相册、GPS定位、扫码、文件管理、支付等等。
作为一款车后市APP,酷爱洗车APP是基于延誉宝会员管理系统深度定制合作的物联网项目,随着业务的不断扩展,APP端的已经满足自助洗车客户对移动端应用入口的需求,因此,开发小程序和公众号成为运营推广的必然要求。但是考虑开发成本,特别是后期的运营维护,如果两套代码,必然增加后续维护的负担,造成系统升级的项目投入增加,因此,最好的解决方案,就是实现Cordova项目可以兼容微信公众号,如此这样,iPhone、安卓和公众号三个入口使用一份代码,便于BUG修复,并提高系统运维的效率,从而在服务器端大数据运维投入不变的前提下,减轻客户端的技术投入压力。
实现将Cordova APP转公众号项目,主要解决以下几个接口的兼容:
1、微信粉丝身份的自动获取。
这个步骤在原生APP开发中是没有的,也不需要的,因此需要服务器接口支持。对应的AngularJS的代码如下:
if(is_in_weixin_browser && !is_in_wxa_browser){
//公众号更换头像
console.log(is_in_weixin_browser);
console.log(is_in_wxa_browser);$scope.get_weixin_jssdk_params();
对应的get_weixin_jssdk_params函数处理过程如下,除了获取openid之外,还需要控制分享转发的图片、文字和链接。
//请求接口,获取微信JSSDK参数
$scope.get_weixin_jssdk_params = function () {var current_url = document.location.href;
$http({
method: ‘post’,//请求方式
url: http_server + “/index.php?g=Yanyubao&m=Kuaicar&a=weixin_jssdk_params”,//请求地址
data: {‘sellerid’: 123, ‘userid’: 456, ‘checkstr’: 789, ‘current_url’:current_url}//请求参数,如果使用JSON格式的对象则可为 data: JSON.stringify(obj),
//timeout: 8000//请求等待时间
})
.success(function (data) {
// console.log(1111111);
// console.log(data);
// console.log(1111111);
if (data.code == ‘1’) {if (typeof WeixinJSBridge == “undefined”){
if( document.addEventListener ){
document.addEventListener(‘WeixinJSBridgeReady’, onBridgeReady, false);
}else if (document.attachEvent){
document.attachEvent(‘WeixinJSBridgeReady’, onBridgeReady);
document.attachEvent(‘onWeixinJSBridgeReady’, onBridgeReady);
}
}else{
onBridgeReady();
}wx.config({
debug: false, /* 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。*/
appId: data.signPackage[‘appId’], /* 必填,公众号的唯一标识*/
timestamp: data.signPackage[‘timestamp’], /* 必填,生成签名的时间戳*/
nonceStr: data.signPackage[‘nonceStr’], /* 必填,生成签名的随机串*/
signature: data.signPackage[‘signature’],/* 必填,签名,见附录1*/
jsApiList: [
‘checkJsApi’,
‘onMenuShareTimeline’,
‘onMenuShareAppMessage’,
‘onMenuShareQQ’,
‘onMenuShareWeibo’,
‘hideMenuItems’,
‘showMenuItems’,
‘hideAllNonBaseMenuItem’,
‘showAllNonBaseMenuItem’,
‘translateVoice’,
‘startRecord’,
‘stopRecord’,
‘onRecordEnd’,
‘playVoice’,
‘pauseVoice’,
‘stopVoice’,
‘uploadVoice’,
‘downloadVoice’,
‘chooseImage’,
‘previewImage’,
‘uploadImage’,
‘downloadImage’,
‘getNetworkType’,
‘openLocation’,
‘getLocation’,
‘hideOptionMenu’,
‘showOptionMenu’,
‘closeWindow’,
‘scanQRCode’,
‘chooseWXPay’,
‘openProductSpecificView’,
‘addCard’,
‘chooseCard’,
‘openCard’
] /* 必填,需要使用的JS接口列表,所有JS接口列表见附录2*/
});}
})
.error(function (data, header, config, status) {//处理响应失败
// 当ajax请求出现错误时,显示一个提示信息
$ionicLoading.show({
animation: ‘fade-in’,
showBackdrop: false,
template: “网络延迟,请重新尝试”,
duration: 3000
});
});}
//请求接口,获取微信JSSDK参数==end===
2、会员头像的读取和上传。
这个地方对比两个代码就明白了,不是使用File控件,而是调用公众号的文件读取和上传接口,调用这些接口的好处在于可以自动压缩上传的图片。
Cordova下的代码:
buttonClicked: function (index) {
if (index == 0) {
//type = ‘camera’;
var srcType = Camera.PictureSourceType.CAMERA;
} else if (index == 1) {
//type = ‘gallery’;
var srcType = Camera.PictureSourceType.SAVEDPHOTOALBUM;
}
var options = setOptions(srcType);
//var func = createNewFileEntry;options.targetHeight = 1200;
options.targetWidth = 1200;
navigator.camera.getPicture(function cameraSuccess(imageUri) {
console.log(imageUri);
$scope.temp_image = imageUri;
$scope.head_logo = imageUri;
//$scope.uoload_img();//弹出缓冲提示
$scope.showLoadingToast();
//这里使用定时器是为了缓存一下加载过程,防止加载过快
$timeout(function () {
//停止缓冲提示
$scope.hideLoadingToast();
}, 1000);
}, function cameraError(error) {
console.debug(“Unable to obtain picture: ” + error, “app”);//显示等待
//弹出缓冲提示
$scope.showLoadingToast();
//这里使用定时器是为了缓存一下加载过程,防止加载过快
$timeout(function () {
//停止缓冲提示
$scope.hideLoadingToast();
}, 2500);
}, options);}
});
$timeout(function () {
hideSheet();
}, 3000);
公众号中的代码:
$scope.choosePicMenu = function () {
wx.chooseImage({
count: 1, // 默认9
sizeType: [‘original’, ‘compressed’], // 可以指定是原图还是压缩图,默认二者都有
sourceType: [‘album’, ‘camera’], // 可以指定来源是相册还是相机,默认二者都有
success: function (res) {
var localIds = res.localIds; // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片wx.uploadImage({
localId: localIds[0], // 需要上传的图片的本地ID,由chooseImage接口获得
isShowProgressTips: 1, // 默认为1,显示进度提示
success: function (res) {
var serverId = res.serverId; // 返回图片的服务器端ID$http({
method: ‘post’,//请求方式
url: http_server + “/index.php?g=Yanyubao&m=Kuaicar&a=get_image_url_by_weixin_media_id”,//请求地址
data: {‘serverId’: serverId}//请求参数,如果使用JSON格式的对象则可为 data: JSON.stringify(obj),
//timeout: 8000//请求等待时间
})
.success(function (data) {
if (data.code == ‘1’) {
console.log(2222222222);
console.log(data);
console.log(2222222222);
$scope.head_logo = data.url2;//$ionicLoading.hide();
}
})
.error(function (data, header, config, status) {
//处理响应失败
// 当ajax请求出现错误时,显示一个提示信息
$ionicLoading.show({
animation: ‘fade-in’,
showBackdrop: false,
template: “网络延迟,请重新尝试”,
duration: 3000
});
})}
});}
});}
3、用户GPS坐标(经纬度)的读取。
由于国内的地图中GPS坐标是加密的,因此获取微信信息,调用百度地图的接口是稳定的。
///读取gps经纬度信息
$scope.getGeolocation = function () {if(is_in_cordova_browser){
// 使用百度地图插件进行定位
baidumap_location.getCurrentPosition(function (result) {
console.log(JSON.stringify(result, null, 4));
//alert(JSON.stringify(result, null, 4));$scope.latitude = result.latitude;//纬度
$scope.longitude = result.longitude;//经度
console.log(‘纬度:’ + $scope.latitude + ‘经度:’ + $scope.longitude);$scope.get_machine_list();
}, function (error) {
console.log(‘调用百度地图插件失败:’+error);
alert(‘调用地图失败:’+error);
});}else if(is_in_weixin_browser && !is_in_wxa_browser ){
navigator.geolocation.getCurrentPosition(updataPosiyion);}
}function updataPosiyion(position){
var latitudeP = position.coords.latitude;
var longitudeP = position.coords.longitude;var point = new BMap.Point(longitudeP,latitudeP);
$scope.latitude = point.lat;//纬度
$scope.longitude = point.lng;//经度
console.log(‘纬度:’ + $scope.latitude + ‘经度:’ + $scope.longitude);$scope.get_machine_list();
}
});
4、二维码扫描控件的调用。
使用公众号的二维码扫描接口,其实比使用Cordova插件的二维码扫码功能简单多了,而且用户体验更好。
if(is_in_cordova_browser){
/////调扫码插件
cordova.plugins.barcodeScanner.scan(
function (result) {
var machine_url = result.text;
console.log(‘洗车机机器url’ + machine_url);
var machineurl=encodeURI(machine_url);$scope.check_machine(machineurl);
},
function (error) {
console.log(“Scanning failed: ” + error);
}
);
}else if(is_in_weixin_browser && !is_in_wxa_browser){
//alert(11111111);
wx.scanQRCode({needResult: 1, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果,
scanType: [“qrCode”,”barCode”], // 可以指定扫二维码还是一维码,默认二者都有
success: function (res) {
var result = res.resultStr; // 当needResult 为 1 时,扫码返回的结果
//console.log(‘扫码结果:’+result);
//alert(result);
var machineurl=encodeURI(result);$scope.check_machine(machineurl);
}
});
}
5、微信支付收款接口的调用。
这里调用的肯定是微信的公众号,而不是H5收款接口,首先参考第一点,获取当前的用户的openid,在调用微信支付APP接口的地方做判断,调用对应的公众号内支付接口。
首先需要隐藏支付宝收款,毕竟是在公众号里面。
//在微信环境隐藏支付宝支付
$scope.is_in_weixin_browser = is_in_weixin_browser;
原生微信支付接口调用:
//微信APP支付
$scope.wechat_pay = function(payInfo,orderid,payment_type){
// console.table(payInfo);
//payInfo = JSON.parse(payInfo);
//console.log(payInfo.sub_mch_id);// alert(JSON.stringify(payInfo));
var params = {
partnerid: payInfo.partnerid, // merchant id
prepayid: payInfo.prepayid, // prepay id
noncestr: payInfo.noncestr, // nonce
timestamp: payInfo.timestamp, // timestamp
sign: payInfo.sign, // signed string
};
console.log(params.partnerid);
console.log(params.prepayid);
console.log(params.noncestr);
console.log(params.timestamp);
console.log(params.sign);// alert(JSON.stringify(params));
Wechat.sendPaymentRequest(params, function () {
// alert(“Success”);
$scope.order_buy_result(orderid,payment_type);
// alert(“回调成功”);
var alertPopup = $ionicPopup.alert({
title: ‘友情提示’,
template: “<p style=’text-align: center’>支付完成</p>”,
okText: ‘确定’, // String (默认: ‘OK’)。OK按钮的文字。
okType: ‘button-assertive’, // String (默认: ‘button-positive’)。OK按钮的类型。
});
alertPopup.then(function(res) {
//跳转到支付成功页面$state.go(“pay_success”, {‘orderid’:orderid}, {reload:true});
return false;
});}, function (reason) {
//alert(“reason=>”+reason);var alertPopup = $ionicPopup.alert({
title: ‘友情提示’,
template: “<p style=’text-align: center’>”+reason+”</p>”,
okText: ‘确定’, // String (默认: ‘OK’)。OK按钮的文字。
okType: ‘button-assertive’, // String (默认: ‘button-positive’)。OK按钮的类型。
});console.log(“Failed: ” + reason);
});}
公众号支付的代码:
//微信公众号支付
$scope.wechat_pay_jsapi = function(payInfo,openid){
// console.log(2233244);
// console.log(payInfo);
// console.log(2233244);
//payInfo = JSON.parse(payInfo);
//console.log(payInfo.sub_mch_id);//var prepayid = payInfo.prepay_id; // prepay id
//var appid = payInfo.appid; //appid
var parameters = encodeURIComponent(payInfo.parameters); //appid//alert( weixin_payment_failure);
var jump_to_url = encodeURIComponent(weixin_payment_success);
var jump_url_failure = encodeURIComponent(weixin_payment_failure);var pay_url = weixin_payment_dir;
pay_url += ‘?parameters=’+ parameters + ‘&jump_to_url=’ + jump_to_url + ‘&jump_url_failure=’ + jump_url_failure ;
// console.log(123);
// console.log(pay_url);
// console.log(123);
//alert(pay_url);
window.location= pay_url;}
公众号收款首先要获取当前粉丝的openid,对应的接口调用代码:
//
$scope.check_openid_cache = function (){
var o_weixin_openid = localStorage.getItem(“o_weixin_openid”);
//alert(‘o_weixin_openid:’+o_weixin_openid);
//alert(encodeURIComponent(document.location.href));if (!o_weixin_openid ) {
//alert(‘o_weixin_openid == undefined’);
location.href = http_server + “/index.php?g=Yanyubao&m=ShopApp&a=weixin_oauthor&sellerid=”+ sellerid +”&returl=”+encodeURIComponent(document.location.href);
return;
}
//alert(‘222222’);return o_weixin_openid;
}$scope.get_openid_from_server = function () {
//检查是否要从服务器获取openid
var weixin_openid_token = $location.search().weixin_openid_token;//alert(‘weixin_openid_token:’+weixin_openid_token);
//alert(window.location.href);
//alert(“get from url, openid token:”+weixin_openid_token);if (weixin_openid_token) {
alert(‘ready to get weixin_get_openid’);//同步ajax请求
$http({method: ‘post’,//请求方式
url:http_server + “/index.php?g=Yanyubao&m=ShopApp&a=weixin_get_openid”,//请求地址data: {“weixin_openid_token” : weixin_openid_token} //请求参数,如果使用JSON格式的对象则可为 data: JSON.stringify(obj),
})
.success(function (res) {
//alert(‘success:’+res);window.localStorage.setItem(“o_weixin_openid”, res.openid);
})
.error(function (res, header, config, status) {//处理响应失败
// 当ajax请求出现错误时,显示一个提示信息
$ionicLoading.show({
animation: ‘fade-in’,
showBackdrop: false,
template: “网络延迟,请重新尝试”,
duration: 3000
});
});}
else{
console.log(‘$scope.check_openid_cache()’);
//alert(2888888);
$scope.check_openid_cache();}
}
通过对以上接口的兼容性改造,Cordova项目实现了对微信公众号的兼容。最后再讨论一个基础的问题,即如何判断公众号环境还是Cordova环境。这个问题的解决方案来自对User Agent的判断。Cordova环境的UA可以自己定义,这个可以在config.xml中指定,根据这个字符串可以判断。在微信浏览器中,UA是带有字符串“MicroMessenger”的,但是需要同时判断是否在小程序的浏览器中,需要引用:
https://res.wx.qq.com/open/js/jweixin-1.3.2.js
这个判断在小程序的官方文档中也有说明,因为小程序浏览器中是不能调用公众号的接口。