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

来源:互联网 发布:kuka机器人基本编程 编辑:程序博客网 时间:2024/04/30 01:07

前言

自微信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))、会话管理服务器CentOS7.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 extensionwget 'https://mirrors.tuna.tsinghua.edu.cn/remi/enterprise/remi.repo' -O /etc/yum.repos.d/remi.repoyum --enablerepo=remi-php56 -y update phpyum --enablerepo=remi-php56 -y install php-mysqlservice 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 = 3306user_name = session_userpass_wd = 新密码data_base= cAuth

[db]host = 数据库MySQL内网地址port = 3306user_name = rootpass_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开发方面的帮助。

原文作者:蒋继超
原文链接:http://www.wxapp-union.com/portal.php?mod=view&aid=2833