养车记账本小程序开发实例

前言

自微信6.5.3版本开始,小程序正式跟大家见面了。最近利用业余时间做了个小程序,命名“养车记账本”。作为IT狗,经历了从注册开发者资质开始到正式上线的全过程,微信小程序官方 文档快速构建具备弹性伸缩能力的微信小程序 等不在此次叙述之列。本实例所用资源为腾讯云购买的微信小程序解决方案,选的其中的PHP环境。废话不多说,下面是关于这个小程序的一些分享(可以扫描二维码体验)。

准备工作

因为涉及的东西比较多,所以需要准备的东西也比较杂。当然,你得首先知道自己要做什么,毕竟这一切准备和将来的劳动都将为这个产品服务。

开发者资质、服务器、数据库、域名(需要预留至少20天备案时间)、SSL证书等可以通过申请、购买获得,工具及操作环境如下:

工具:Axure RP、putty、WinSCP、Photoshop、微信web开发者工具、Sublime Text、Zend Studio

操作环境:微信公众平台、腾讯云、业务服务器CentOS 7.2(nginx/1.10.1、php-fpm(PHP 5.6.26, Zend Engine v2.6.0))、会话管理服务器CentOS

7.2(PHP+Mysql+Apache)、微信小程序数据库MySQL

整个过程会产生一些费用,根据自己选择,大概几百元的样子。不过别担心,腾讯云可以满足所有需求,其中的微信小程序解决方案,就是专门针对小程序量身定制的。

申请资源

目前小程序的开放注册范围仅限于企业、政府、媒体和其他组织,是不开放对个人注册的,所以首先你需要给自己一个合法的身份,我是找了个朋友的企业注册的。申请小程序的开发权限,请移步微信公众平台。吐槽一句,现在开发者也流行缴纳“入会费”,这里需要300元。

其次是腾讯云注册,这里可以获得小程序用到的一切资源,包括域名、SSL证书、服务器、数据库等。小程序毕竟是腾讯的产品,服务器支持肯定自家的兼容更好些,腾讯云官网为广大开发者提供了微信解决方案服务器。首次购买半年需要516元,后期续费每月91元。

见到下面这个界面,就申请成功了。

域名(申请、解析、备案)

腾讯云可以申请域名,(.com域名一般需要45元左右),审核过程会持续4-5个工作日,之后需要解析域名,在腾讯云管理中心可以直接操作(登录腾讯云管理中心-云产品-域名服务-云解析)。域名备案比较耗时,要在指定背景下拍照。如果你距离指定地点比较近,可以到指定地点拍照,基本各省都有拍照点,见拍照点地图

如果不方便,可以申请邮寄给你个背景幕布,按照要求拍照,见办理拍照指南,备案材料提交管局审核,需要大概20个工作日(你得习惯这工作效率),所以最好提前申请域名,给备案预留出时间。

另外,域名实名认证也需要4-5天。

创建微信小程序服务器

在腾讯云购买小程序解决方案时需要选后台语言,决定了分配给你的是什么系统及环境的服务器(有PHP、Java、Node.js、.NET四种选择),后期可更改、重装系统。云端小程序构建完成后会得到三个服务器:业务服务器、会话管理服务器、MySQL数据库服务器,之后以短信形式发送分配的服务器登录密码,用户名统一是root。

在腾讯云登录服务器后可以在弹出的黑色命令窗口输入命令,见下图:

之后输入用户名和密码即登录成功。这个登录窗口是用canvas做的,不能拖动滚动条查看一屏以外的东西,相当憋屈。所以还得有个称手的工具,现在putty可以登场了。

运行putty之后输入公网IP地址,点击Open,之后输入用户名密码,就可以执行各种shell命令了。

至于上传下载文件,为了安全起见,默认安装的镜像 FTP 不支持,官方推荐用WinSCP/SecureFX,这两个都可以支持,操作跟 FTP 类似。

这里不得不说下关于三个服务器需要注意的点,或者说是坑。系统虽然可以重装,不过需谨慎,因为有些配置重装后并不能恢复成最初分配时的状态。下图是业务服务器重装界面(再次提醒:重装需谨慎):

关于业务服务器:我选的是PHP环境服务器,购买后默认并不支持mysql,需要自己安装。下面是关于PHP环境业务服务器升级支持mysql扩展方法:

### Update php and install php-mysql extension
wget 'https://mirrors.tuna.tsinghua.edu.cn/remi/enterprise/remi.repo' -O /etc/yum.repos.d/remi.repo
yum --enablerepo=remi-php56 -y update php
yum --enablerepo=remi-php56 -y install php-mysql
service php-fpm restart

以上四行,从上到下按次序执行或保存为一文件,如update_php.sh(见附件), 然后执行

chmod +x update_php.sh

再执行

./update_php.sh

即可

业务服务器配置参考这里,修改Linux - /etc/qcloud/sdk.config文件。

业务服务器的文件存放路径/data/release/php-weapp-demo

关于会话服务器:首先查看《腾讯云小程序会话管理服务器BUG修复与升级方案》(见附件),然后如果你也重装了会话服务器,你可能还会发现一个问题,官方给出的三木聊天室demo链接失败。问题解决参考这里

1.执行

cd /opt/lampp/htdocs/mina_auth/system/db/
vi db.ini

记下配置里的host和pass_wd值,之后ctrl+Z退出。

2.执行

cd /opt/lampp/bin/

之后按照参考,本应执行

./mysql -h #ip -P #port -u #username -p #passwd(其中#ip、#port、#username、#passwd是在1.2步骤中查看到的具体信息)

但因为重装系统默认提供的IP和密码不正确,需要在微信小程序数据库MySQL账号管理里为session_user重置密码,见下图:

然后用mysql内网IP和新密码执行如下命令:

./mysql -h IP -P 3306 -u session_user -p新密码

或直接用root账号登录,执行:

mysql -h IP -P 3306 -u root -proot密码

(IP为微信小程序数据库MySQL内网地址,用户名和密码为登录数据库服务器的账号密码,注意参数-p后没有空格)

3.登录微信公众号打开开发设置,记下AppID(小程序ID)和AppSecret(小程序密钥)

4.执行(命令行后带;设置字段值不用引号,退出mysql命令行直接输入;回车):

use cAuth;
update cAppinfo set appid = 这里是AppID,secret =这里是 AppSecret;

5.为会话服务器云主机安全组添加默认安全组放通全部端口,并更改优先级在前面

6.更新/opt/lampp/htdocs/mina_auth/system/db/vi db.ini内容为以下:

[db]
host = 数据库MySQL内网地址
port = 3306
user_name = session_user
pass_wd = 新密码
data_base= cAuth

[db]
host = 数据库MySQL内网地址
port = 3306
user_name = root
pass_wd = root密码
data_base= cAuth

会话服务器文件存放路径/opt/lampp/htdocs

关于数据库服务器:如果有数据表格需要导入,如果你要导入的.sql文件大于2048k,可以压缩成zip,一般可以压缩到原来的1/20,压缩比率还是很可观的。

腾讯云实名认证

腾讯云实名认证后可以获得腾讯云的代金券,还可以更好的保障你的号安全,认证后你的腾讯云号也是独一无二的。具体见实名认证指引,大约1-2个工作日。

SSL证书

为了保护小程序应用安全,微信官方的需求文档要求每个微信小程序必须事先设置一个域名,并通过HTTPS请求进行网络通信,不满足条件的域名和协议无法请求。所以需要购买或申请SSL证书。目前正规渠道购买SSL证书还是很贵的,不过如果您选的是微信小程序方案,这个SSL证书是免费的(大约需要1个工作日审核下发),颁发后会获得一个压缩包,内含安全证书。安装ssl证书参考这里,把ssl证书压缩包内Nginx下的证书拷贝至/etc/nginx下

至此,环境算搭好了,剩下的就专注于你的小程序开发了。

小程序策划

我最初是想做一个车友会之类的小程序,但涉及车友互动发文之类,需要有互联网电子公告服务许可证, 总之各种条条框框。反正我是练手,还是绕过这个限制,改做记账本之类的小工具了,鉴于目前小程序只能匹配全名,只在个别关键词开启了模糊匹配,经过查验,“养车”和“记账”两个词都可以模糊搜索,所以名字就叫“养车记账本”了,可以给车主提供个专门记录养车所产生的费用的统计工具。从用户开始访问小程序开始,大概流程图如下:

主要涉及以下几个功能:增加记账、账单列表、账单筛选、修改账目、删除账目、账单统计、车型选择、分类设置等。

增加记账和修改账目可以共用同一个界面,根据实际需要,要用到账单分类、账单产生日期,再就是额度。记账页面操作简单,见后面设计图。

账单列表页和账单统计作为一个展示模块,需要能够通过设置筛选条件比如时间段、类别勾选来展示指定账目,展示方式为按时间列表,统计方式为饼图。

用户可以设置车型,可以不填。如果用户有不止一辆车,这个必须为必填,不过第一版先不考虑多辆车这个维度,后期再加。

至于分类,我大概归纳了一下用车、养车过程可能产生费用的方面,大概包括(停车费、加油费、养护、保险、罚款、高速、维修、购车、年检、改装、赔偿等),如果不够用可以在分类设置里增加分类,如果用不到的可以关闭,避免干扰。

再就是象征性的来个建议反馈、关于之类的。

小程序设计

小程序有推荐的设计规范,见微信小程序设计指南,为方便设计师进行设计,微信提供一套可供Web设计和小程序使用的基础控件库;同时提供方便开发者调用的资源。设计小程序推荐以iphone6的尺寸为标准,即750px*1334px。

其中图标我用的钢笔工具,选择的2像素路径描边,无填充色。一共分两种状态:选中状态和未选中状态。未选中状态描边色# 7a7e83,背景色# f5f5f5;选中状态描边色#ffffff,背景色# 1aad19 。

小程序开发

开始码代码了,开发分前端和后台。

小程序在前端上的贡献不得不点赞,wxss在css的基础上扩展了rpx(responsive pixel)单位,rpx换算px为屏幕宽度/750,px换算rpx为750/屏幕宽度。这样就可以根据屏幕宽度进行自适应,让前端开发可以解放出来不用过多考虑兼容问题。使用rpx规定屏幕宽为750rpx,比如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。wxml是 微信 的一套标签语言,结合基础组件、事件系统,可以构建出页面的结构。开发工具不大好用(遇到N次闪退,有个好的保存习惯还是不错的;还有就是调试时候如果需要点开非默认页面,有时候不能刷出Wxml结构,重启开发工具可以解决),但毕竟不是一天两天打造完美的,相信微信团队一定能做更好。如果不习惯也可以用你喜欢的工具。我的页面结构如下:

  "pages":[
    "pages/add/index/index",
    "pages/statistics/index/index",
    "pages/my/index/index",
    "pages/my/autoBrand/autoBrand",
    "pages/my/autoSerial/autoSerial",
    "pages/my/autoModel/autoModel",
    "pages/my/sort/sort",
    "pages/my/feedback/feedback",
    "pages/my/about/about",
    "pages/other/reLogin/reLogin"
  ],

如果问你,小程序放开的用户数据够吗?你一定说不够。那我们能做的就是物尽其用吧!如果你的接口涉及wx.getUserInfo当中的 openId,接口的明文内容将不包含这些敏感数据。如果需要获取敏感数据,需要对接口返回的加密数据( encryptedData )进行对称解密。可参考微信小程序客户端腾讯云增强 SDK。

在微信登录,解密encryptData后,可以在userInfo里可以看到用户信息

在登录后做了这些工作:获取用户信息,对照数据库,如果库里有该用户,将车型和类别设置存储到本地,以供其他页面调用;如果没有则保存该用户。如果库里有该用户且设备信息有变化则更新设备信息,如果没有则追加设备信息。如果用户拒绝获取用户信息,授权失败,显示重新授权教程。篇幅有限,只截取部分代码展示。代码如下:

// 用户登录
UserLoginFn:function(){
    var that = this;
    // qcloud.request() 方法和 wx.request() 方法使用是一致的,不过如果用户已经登录的情况下,会把用户的会话信息带给服务器,服务器可以跟踪用户
    qcloud.request({
        url: config.service.requestUrl, // 要请求的地址
        login: true, // 请求之前是否登录,如果该项指定为 true,会在请求之前进行登录
        success(result) {
            wx.setStorageSync('ThisUserInfo', result);
            var usr = result.data.data.userInfo;
            that.setData({
                ThisUserInfo: result
            });
            wx.setStorageSync('OpenID', usr.openId);
            // 用户登录(如果库里有该用户,将车型和类别设置存储到本地,如果没有则保存该用户)
            wx.request({
                url: 'https://xxx.php', //这里不是真实地址
                data: {
                    OpenID: usr.openId,
                    NickName: usr.nickName,
                    AvatarUrl: usr.avatarUrl,
                    Gender: usr.gender,
                    Country: usr.country,
                    Province: usr.province,
                    City: usr.city
                },
                success: function(res) {
                    if(res.data){
                        wx.setStorageSync('ThisUserInfoModelID', res.data.ModelID);
                        wx.setStorageSync('ThisUserInfoSortIDList', res.data.sortIDList);
                        that.sortFormat(); // 登录成功后为该用户格式化表单
                    }
                }
            });
            // 用户设备信息采集
            wx.getNetworkType({
                success: function(res) {
                    // 返回网络类型, 有效值:wifi/2g/3g/4g/unknown(Android下不常见的网络类型)/none(无网络)
                    var networkType = res.networkType;
                    wx.getSystemInfo({
                        success: function(res) {
                            // 如果库里有该用户且设备信息有变则更新设备信息,如果没有则追加设备信息
                            wx.request({
                                url: 'https://xxx.php', //这里不是真实地址
                                data: {
                                    OpenID: usr.openId,
                                    Model: res.model,
                                    System: res.system,
                                    Language: res.language,
                                    Version: res.version,
                                    Platform: res.platform,
                                    Network: networkType,
                                    WindowWidth: res.windowWidth,
                                    WindowHeight: res.windowHeight,
                                    DevicePixelRatio: res.pixelRatio
                                }
                            });
                        }
                    })
                }
            });
        },
        fail(error) {
            wx.showModal({
                title: '提示',
                content: '未授权不可使用,请删除后重新获取养车记账本。',
                showCancel: false,
                confirmText: '查看教程',
                confirmColor: '#3CC51F',
                success: function(res) {
                    if (res.confirm) {
                        wx.redirectTo({
                            url: '/pages/other/reLogin/reLogin'
                        });
                    }
                }
            });
        }
    });
},

前端开发过程中遇到的问题顺便提一句,canvas、textarea、video等组件使用原生渲染,如果需要弹层交互的话它们会挡住弹层,解决办法就是在弹层后将这些组件hidden属性设置为true,弹层消失时重置为false。另外还有个问题:我想把data里的数据保存成对象格式的,如果追加key值的话,不支持a.b:'c'这样追加,只能a:{b:'c'},但前者可以更直观的表示某一组赋值是给一个特定对象的,尤其有时候不能确定你要追加的key值就限于你指定的那几个的时候,但setData并不支持这种做法,不知是出于何种考虑,如果没有特殊原因还是希望能改进下。再有就是css不支持标签名选择器,也是目前支持标签比较单一,所以要想美化某个组件,必须给它实实在在赋个样式,略显臃肿。

后台开发语言我选的是PHP,主要是网上资料多,函数方法齐全。关于PHP对MySQL的增删改查操作网上很容易找到。在同事的指点下,采取了一些措施,防止SQL注入与XSS攻击,主要是过滤文本,做了字符串转换、格式化等操作。在账单查询页面,通过联合查询列出用户表和账单表信息,对查询结果装入数组,之后进行格式化输出供小程序使用,代码如下:

$accountArr = array();
$MonthList = array();
$dateTemp = '';
$switch = false;
function wk($date1) {
	$datearr = explode('-',$date1); //将传来的时间使用“-”分割成数组
	$year = $datearr[0]; //获取年份
	$month = sprintf('%02d',$datearr[1]); //获取月份 
	$day = sprintf('%02d',$datearr[2]); //获取日期
	$hour = $minute = $second = 0; //默认时分秒均为0
	$dayofweek = mktime($hour,$minute,$second,$month,$day,$year); //将时间转换成时间戳
	$shuchu = date('w',$dayofweek); //获取星期值
	$weekarray = array('星期日','星期一','星期二','星期三','星期四','星期五','星期六'); 
	return $weekarray[$shuchu]; 
}
while($row = mysql_fetch_array($result,MYSQL_ASSOC)){
	if( $switch && (substr($dateTemp,0,7) != substr($row['Date'],0,7)) ){
		$monthTemp = array( 
			'Month' => substr($dateTemp,0,7),
			'MonthList' => $MonthList
		);
		$accountArr[] = $monthTemp;
		$MonthList = array();
	}
	$temp = array( 
		'ID' => $row['ID'],
		'Date' => $row['Date'],
		'DateWk' => substr($row['Date'],8,2).'日 ('.wk($row['Date']).')',
		'SortID' => $row['SortID'],
		'Name' => $row['Name'],
		'Mony' => preg_replace('/^0+/','',$row['Mony'])
	);
	$MonthList[] = $temp;
	$dateTemp = $row['Date'];
	$switch = true;
}
$monthTemp = array( 
	'Month' => substr($dateTemp,0,7),
	'MonthList' => $MonthList
);
$accountArr[] = $monthTemp;
if($dateTemp == ''){ // 如果没有结果
	$accountArr = array(); //返回空数组
}
$str = json_encode($accountArr); //将数组转化为json格式的字符串
echo $str;

针对自己的项目,需要设计合理的数据库表以满足记账的需要。关于账单表字段设置如下:

CREATE TABLE `user_account` (
  `ID` int(10) UNSIGNED NOT NULL COMMENT '指针',
  `OpenID` varchar(32) NOT NULL COMMENT '用户ID',
  `SortID` int(10) UNSIGNED NOT NULL COMMENT '类别ID',
  `Mony` decimal(11,2) UNSIGNED ZEROFILL NOT NULL DEFAULT '000000000.00' COMMENT '金额',
  `Date` date NOT NULL COMMENT '添加日期'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='账目表';

此外还有用户表、设备表、类别表、车型表。除了车型表独立存在之外,其他几个表都是以OpenID字段相互关联的,以实现相互间的互查。

小程序测试、发布、监测

开发者工具 集成了开发调试、代码编辑及程序发布等功能,其中模拟器模拟微信小程序在客户端真实的逻辑表现,对于绝大部分的 API 均能在模拟器上呈现出正确的状态。自带的调试工具分为 6 大功能模块:Wxml、Console、Sources、Network、Appdata、Storage,平时开发的时候及时纠错,问题不大。马上就要车展了,鉴于时间关系,第一版就这样匆匆提交了。不过比较幸运,首次提交就通过审核了。

提交审核需要填一些简单的信息,有利于用户快速搜索出你的信息。之后可以在微信公众平台查看数据分析,其中的自定义分析功能强大(不过目前正在内测中,暂时只支持开发者测试数据上报;6.5.4及以上微信版本支持用户数据上报,用户微信版本更新以前无法收集数据。新版本覆盖全量用户前,数据可能有缺失),可以从不同角度分析访问者信息,为你进一步挖掘用户信息做足准备。

结语

随着小程序不断增长,越来越多的小程序渗透到网友生活、工作的方方面面。弱水三千只取一瓢。本着满足客户某项需求的前提下尽量做一个小而美的工具。

问题是养车记账本目前只具雏形,限于时间和个人能力,班门弄斧,只完成了基础功能,还有很多细节需要调整,功能也不太完善,比如筛选交互逻辑还需要进一步优化,每个网友现在也只能为一辆车记录账单,账单统计还可以列出筛选具体额度以及增加趣味评价,甚至可以考虑增加位置定位等功能……也恳请大家提出宝贵意见,未来一个月比较忙,等车展过后继续!

最后,感谢hjiang、ningtian给出的好点子,感谢wqcheng给出的服务器升级、部署方面的指导,感谢lightllchen在php开发方面的帮助。

附件:

本站文章资源均来源自网络,除非特别声明,否则均不代表站方观点,并仅供查阅,不作为任何参考依据!
如有侵权请及时跟我们联系,本站将及时删除!
如遇版权问题,请查看 本站版权声明
THE END
分享
二维码
海报
养车记账本小程序开发实例
本实例所用资源为腾讯云购买的微信小程序解决方案,选的其中的PHP环境。
<<上一篇
下一篇>>