
第6章 移动端开发-体检预约1. 移动端开发1.1 移动端开发方式随着移动互联网的兴起和手机的普及目前移动端应用变得愈发重要成为了各个商家的必争之地。例如我们可以使用手机购物、支付、打车、玩游戏、订酒店、购票等以前只能通过PC端完成的事情现在通过手机都能够实现而且更加方便而这些都需要移动端开发进行支持那如何进行移动端开发呢移动端开发主要有三种方式1、基于手机API开发原生APP2、基于手机浏览器开发移动web3、混合开发混合APP1.1.1 基于手机API开发手机端使用手机API例如使用Android、ios 等进行开发服务端只是一个数据提供者。手机端请求服务端获取数据json、xml格式并在界面进行展示。这种方式相当于传统开发中的C/S模式即需要在手机上安装一个客户端软件。这种方式需要针对不同的手机系统分别进行开发目前主要有以下几个平台1、苹果ios系统版本开发语言是Objective-C2、安卓Android系统版本开发语言是Java3、微软Windows phone系统版本开发语言是C#4、塞班symbian系统版本开发语言是C此种开发方式举例手机淘宝、抖音、今日头条、大众点评1.1.2 基于手机浏览器开发生存在浏览器中的应用基本上可以说是触屏版的网页应用。这种开发方式相当于传统开发中的B/S模式也就是手机上不需要额外安装软件直接基于手机上的浏览器进行访问。这就需要我们编写的html页面需要根据不同手机的尺寸进行自适应调节目前比较流行的是html5开发。除了直接通过手机浏览器访问还可以将页面内嵌到一些应用程序中例如通过微信公众号访问html5页面。这种开发方式不需要针对不同的手机系统分别进行开发只需要开发一个版本就可以在不同的手机上正常访问。本项目会通过将我们开发的html5页面内嵌到微信公众号这种方式进行开发。1.1.3 混合开发是半原生半Web的混合类App。需要下载安装看上去类似原生App访问的内容是Web网页。其实就是把HTML5页面嵌入到一个原生容器里面。1.2 微信公众号开发要进行微信公众号开发首先需要访问微信公众平台官网https://mp.weixin.qq.com/1.2.1 帐号分类在微信公众平台可以看到有四种帐号类型服务号、订阅号、小程序、企业微信原企业号。本项目会选择订阅号这种方式进行公众号开发。1.2.2 注册帐号要开发微信公众号首先需要注册成为会员然后就可以登录微信公众平台进行自定义菜单的设置。注册页面https://mp.weixin.qq.com/cgi-bin/registermidpage?actionindexlangzh_CNtoken选择订阅号进行注册输入邮箱、邮箱验证码、密码、确认密码等按照页面流程进行注册1.2.3 自定义菜单注册成功后就可以使用注册的邮箱和设置的密码进行登录登录成功后点击左侧“自定义菜单”进入自定义菜单页面在自定义菜单页面可以根据需求创建一级菜单和二级菜单其中一级菜单最多可以创建3个每个一级菜单下面最多可以创建5个二级菜单。每个菜单由菜单名称和菜单内容组成其中菜单内容有3中形式发送消息、跳转网页、跳转小程序。1.2.4 上线要求如果是个人用户身份注册的订阅号则自定义菜单的菜单内容不能进行跳转网页因为个人用户目前不支持微信认证而跳转网页需要微信认证之后才有权限。如果是企业用户首先需要进行微信认证通过后就可以进行跳转网页了跳转网页的地址要求必须有域名并且域名需要备案通过。2. 需求分析和环境搭建2.1 需求分析用户在体检之前需要进行预约可以通过电话方式进行预约此时会由体检中心客服人员通过后台系统录入预约信息。用户也可以通过手机端自助预约。本章节开发的功能为用户通过手机自助预约。预约流程如下1、访问移动端首页2、点击体检预约进入体检套餐列表页面3、在体检套餐列表页面点击具体套餐进入套餐详情页面4、在套餐详情页面点击立即预约进入预约页面5、在预约页面录入体检人相关信息点击提交预约效果如下图2.2 搭建移动端工程本项目是基于SOA架构进行开发前面我们已经完成了后台系统的部分功能开发在后台系统中都是通过Dubbo调用服务层发布的服务进行相关的操作。本章节我们开发移动端工程也是同样的模式所以我们也需要在移动端工程中通过Dubbo调用服务层发布的服务如下图2.2.1 导入maven坐标在health_common工程的pom.xml文件中导入阿里短信发送的maven坐标!--阿里云短信-- dependency groupIdcom.aliyun/groupId artifactIdaliyun-java-sdk-core/artifactId version3.3.1/version /dependency dependency groupIdcom.aliyun/groupId artifactIdaliyun-java-sdk-dysmsapi/artifactId version1.0.0/version /dependency !--腾讯云短信-- dependency groupIdcom.tencentcloudapi/groupId artifactIdtencentcloud-sdk-java/artifactId version3.1.62/version!-- 注这里只是示例版本号请获取并替换为 最新的版本号 -- /dependency2.2.2 导入通用组件在health_common工程中导入如下通用组件ValidateCodeUtils工具类package com.liusp.utils; import java.util.Random; /** * 随机生成验证码工具类 */ public class ValidateCodeUtils { /** * 随机生成验证码 * param length 长度为4位或者6位 * return */ public static Integer generateValidateCode(int length){ Integer code null; if(length 4){ code new Random().nextInt(9999);//生成随机数最大为9999 if(code 1000){ code code 1000;//保证随机数为4位数字 } }else if(length 6){ code new Random().nextInt(999999);//生成随机数最大为999999 if(code 100000){ code code 100000;//保证随机数为6位数字 } }else{ throw new RuntimeException(只能生成4位或6位数字验证码); } return code; } /** * 随机生成指定长度字符串验证码 * param length 长度 * return */ public static String generateValidateCode4String(int length){ Random rdm new Random(); String hash1 Integer.toHexString(rdm.nextInt()); String capstr hash1.substring(0, length); return capstr; } }RedisMessageConstant常量类package com.liusp.constant; public class RedisMessageConstant { public static final String SENDTYPE_ORDER 001;//用于缓存体检预约时发送的验证码 public static final String SENDTYPE_LOGIN 002;//用于缓存手机号快速登录时发送的验证码 public static final String SENDTYPE_GETPWD 003;//用于缓存找回密码时发送的验证码 }2.2.3 health_mobile创建移动端工程health_mobile打包方式为war用于存放Controller在Controller中通过Dubbo可以远程访问服务层相关服务所以需要依赖health_interface接口工程。pom.xml?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd parent groupIdcom.liusp/groupId artifactIdhealth_parent/artifactId version0.0.1-SNAPSHOT/version !--relativePath/ lt;!ndash; lookup parent from repository ndash;gt;-- /parent modelVersion4.0.0/modelVersion groupIdcom.liusp/groupId artifactIdhealth_mobile/artifactId version0.0.1-SNAPSHOT/version packagingwar/packaging namehealth_mobile/name descriptionDemo project for Spring Boot/description properties java.version1.8/java.version project.build.sourceEncodingUTF-8/project.build.sourceEncoding project.reporting.outputEncodingUTF-8/project.reporting.outputEncoding spring-boot.version2.3.7.RELEASE/spring-boot.version /properties dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-tomcat/artifactId scopeprovided/scope /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope exclusions exclusion groupIdorg.junit.vintage/groupId artifactIdjunit-vintage-engine/artifactId /exclusion /exclusions /dependency !--Dubbo集成SpringBoot依赖-- dependency groupIdcom.alibaba.spring.boot/groupId artifactIddubbo-spring-boot-starter/artifactId version2.0.0/version /dependency !--zkclient zookeeper客户端-- dependency groupIdcom.101tec/groupId artifactIdzkclient/artifactId version0.10/version /dependency dependency groupIdcom.liusp/groupId artifactIdhealth_interface/artifactId version0.0.1-SNAPSHOT/version /dependency !-- 引入redis-- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency !--spring security-- !--dependency-- !--groupIdorg.springframework.boot/groupId-- !--artifactIdspring-boot-starter-security/artifactId-- !--/dependency-- !--文件上传-- dependency groupIdcommons-fileupload/groupId artifactIdcommons-fileupload/artifactId version1.3.3/version /dependency dependency groupIdcom.alibaba/groupId artifactIdfastjson/artifactId version1.2.72/version /dependency /dependencies dependencyManagement dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-dependencies/artifactId version${spring-boot.version}/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement build plugins plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-compiler-plugin/artifactId version3.8.1/version configuration source1.8/source target1.8/target encodingUTF-8/encoding /configuration /plugin plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId version2.3.7.RELEASE/version configuration mainClasscom.liusp.HealthMobileApplication/mainClass /configuration executions execution idrepackage/id goals goalrepackage/goal /goals /execution /executions /plugin /plugins /build /project静态资源CSS、html、img等详见资料application.yml 配置文件server: port: 80 #dubbo dubbo: #设置服务器应用名称 application: name: health_mobile #指定注册中心 registry: address: zookeeper://127.0.0.1:2181 #超时全局设置 10分钟 #checkfalse 不检查服务提供方开发阶段建议设置为false #checktrue 启动时检查服务提供方如果服务提供方没有启动则报错 consumer: timeout: 600000 check: false spring: redis: host: 127.0.0.1 #文件上传 servlet: multipart: enabled: true max-file-size: 10MB max-request-size: 100MB #设置日期格式 jackson: date-format: yyyy-MM-dd time-zone: GMT8SpringBoot启动类添加Dubbo支持注解//添加对Dubbo的支持 EnableDubbo3. 套餐列表页面动态展示移动端首页为/pages/index.html效果如下点击体检预约直接跳转到体检套餐列表页面/pages/setmeal.html3.1 完善页面3.1.1 展示套餐信息ul classlist li classlist-item v-forsetmeal in setmealList a classlink-page :hrefsetmeal_detail.html?idsetmeal.id img classimg-object f-left :srchttp://pqjroc654.bkt.clouddn.com/setmeal.img alt div classitem-body h4 classellipsis item-title{{setmeal.name}}/h4 p classellipsis-more item-desc{{setmeal.remark}}/p p classitem-keywords span{{setmeal.sex 0 ? 性别不限 : setmeal.sex 1 ? 男:女}}/span span{{setmeal.age}}/span /p /div /a /li /ul3.1.2 获取套餐列表数据var vue new Vue({ el:#app, data:{ setmealList:[] }, mounted (){ //发送ajax请求获取所有的套餐数据赋值给setmealList模型数据用于页面展示 axios.get(/setmeal/getAllSetmeal.do).then((res) { if(res.data.flag){ //查询成功给模型数据赋值 this.setmealList res.data.data; }else{ //查询失败弹出提示信息 this.$message.error(res.data.message); } }); } });3.2 后台代码3.2.1 Controller在health_mobile工程中创建SetmealController并提供getSetmeal方法在此方法中通过Dubbo远程调用套餐服务获取套餐列表数据package com.liusp.controller; import com.alibaba.dubbo.config.annotation.Reference; import com.liusp.constant.MessageConstant; import com.liusp.entity.Result; import com.liusp.pojo.Setmeal; import com.liusp.service.SetmealService; import org.springframework.web.bind.annotation.*; import java.util.List; RestController RequestMapping(/setmeal) public class SetmealController { Reference//(check false) private SetmealService setmealService; //获取所有套餐信息 RequestMapping(/getAllSetmeal.do) public Result getAllSetmeal(){ try{ ListSetmeal list setmealService.findAll(); return new Result(true, MessageConstant.GET_SETMEAL_LIST_SUCCESS,list); }catch (Exception e){ e.printStackTrace(); return new Result(false,MessageConstant.GET_SETMEAL_LIST_FAIL); } } }3.2.2 服务接口在SetmealService服务接口中扩展findAll方法public ListSetmeal findAll();3.2.3 服务实现类在SetmealServiceImpl服务实现类中实现findAll方法public ListSetmeal findAll() { return setmealDao.findAll(); }1233.2.4 Dao接口在SetmealDao接口中扩展findAll方法public ListSetmeal findAll();3.2.5 Mapper映射文件在SetmealDao.xml映射文件中扩展SQL语句select idfindAll resultTypecom.liusp.pojo.Setmeal select * from t_setmeal /select4. 套餐详情页面动态展示前面我们已经完成了体检套餐列表页面动态展示点击其中任意一个套餐则跳转到对应的套餐详情页面/pages/setmeal_detail.html并且会携带此套餐的id作为参数提交。请求路径格式http://localhost/pages/setmeal_detail.html?id10在套餐详情页面需要展示当前套餐的信息包括图片、套餐名称、套餐介绍、适用性别、适用年龄、此套餐包含的检查组信息、检查组包含的检查项信息等。4.1 完善页面4.1.1 获取请求参数中套餐id在页面中已经引入了healthmobile.js文件此文件中已经封装了getUrlParam方法可以根据URL请求路径中的参数名获取对应的值function getUrlParam(paraName) { var url document.location.toString(); //alert(url); var arrObj url.split(?); if (arrObj.length 1) { var arrPara arrObj[1].split(); var arr; for (var i 0; i arrPara.length; i) { arr arrPara[i].split(); if (arr ! null arr[0] paraName) { return arr[1]; } } return ; } else { return ; } }在setmeal_detail.html中调用上面定义的方法获取套餐id的值script var id getUrlParam(id); /script4.1.2 获取套餐详细信息script var vue new Vue({ el:#app, data:{ imgUrl:null,//套餐对应的图片链接 setmeal:{} }, mounted(){ axios.post(/setmeal/findById.do?id id).then((response) { if(response.data.flag){ this.setmeal response.data.data; this.imgUrl http://pqjroc654.bkt.clouddn.com/ this.setmeal.img; } }); } }); /script4.1.3 展示套餐信息div classcontentBox div classcard div classproject-img img :srcimgUrl width100% height100% / /div div classproject-text h4 classtit{{setmeal.name}}/h4 p classsubtit{{setmeal.remark}}/p p classkeywords span{{setmeal.sex 0 ? 性别不限 : setmeal.sex 1 ? 男:女}}/span span{{setmeal.age}}/span /p /div /div div classtable-listbox div classbox-title i classicon-zhenspan classpath1/spanspan classpath2/span/i span套餐详情/span /div div classbox-table div classtable-title div classtit-item flex2项目名称/div div classtit-item flex3项目内容/div div classtit-item flex3项目解读/div /div div classtable-content ul classtable-list li classtable-item v-forcheckgroup in setmeal.checkGroups div classitem flex2{{checkgroup.name}}/div div classitem flex3 label v-forcheckitem in checkgroup.checkItems {{checkitem.name}} /label /div div classitem flex3{{checkgroup.remark}}/div /li /ul /div div classbox-button a clicktoOrderInfo() classorder-btn立即预约/a /div /div /div /div4.2 后台代码4.2.1 Controller在SetmealController中提供findById方法//根据id查询套餐信息 RequestMapping(/findById.do) public Result findById(int id){ try{ Setmeal setmeal setmealService.findById(id); return new Result(true,MessageConstant.QUERY_SETMEAL_SUCCESS,setmeal); }catch (Exception e){ e.printStackTrace(); return new Result(false,MessageConstant.QUERY_SETMEAL_FAIL); } }4.2.2 服务接口在SetmealService服务接口中提供findById方法public Setmeal findById(int id);4.2.3 服务实现类在SetmealServiceImpl服务实现类中实现findById方法public Setmeal findById(int id) { return setmealDao.findById(id); }4.2.4 Dao接口在SetmealDao接口中提供findById方法public Setmeal findById(int id);在CheckGroupDao接口中提供findCheckGroupById方法ListCheckGroup findCheckGroupById(Integer id);在CheckItemDao接口中提供findCheckItemById方法public ListCheckItem findCheckItemById(Integer id);4.2.5 Mapper映射文件此处会使用mybatis提供的关联查询在根据id查询套餐时同时将此套餐包含的检查组都查询出来并且将检查组包含的检查项都查询出来。SetmealDao.xml文件resultMap typecom.liusp.pojo.Setmeal idbaseResultMap id columnid propertyid/ result columnname propertyname/ result columncode propertycode/ result columnhelpCode propertyhelpCode/ result columnsex propertysex/ result columnage propertyage/ result columnprice propertyprice/ result columnremark propertyremark/ result columnattention propertyattention/ result columnimg propertyimg/ /resultMap resultMap typecom.liusp.pojo.Setmeal idfindByIdResultMap extendsbaseResultMap collection propertycheckGroups javaTypeArrayList ofTypecom.liusp.pojo.CheckGroup columnid selectcom.liusp.dao.CheckGroupDao.findCheckGroupById /collection /resultMap select idfindById resultMapfindByIdResultMap select * from t_setmeal where id#{id} /selectCheckGroupDao.xml文件resultMap typecom.liusp.pojo.CheckGroup idbaseResultMap id columnid propertyid/ result columnname propertyname/ result columncode propertycode/ result columnhelpCode propertyhelpCode/ result columnsex propertysex/ result columnremark propertyremark/ result columnattention propertyattention/ /resultMap resultMap typecom.liusp.pojo.CheckGroup idfindByIdResultMap extendsbaseResultMap collection propertycheckItems javaTypeArrayList ofTypecom.liusp.pojo.CheckItem columnid selectcom.liusp.dao.CheckItemDao.findCheckItemById /collection /resultMap !--根据套餐id查询检查项信息-- select idfindCheckGroupById resultMapfindByIdResultMap select * from t_checkgroup where id in (select checkgroup_id from t_setmeal_checkgroup where setmeal_id#{id}) /selectCheckItemDao.xml文件!--根据检查组id查询检查项信息-- select idfindCheckItemById resultTypecom.liusp.pojo.CheckItem select * from t_checkitem where id in (select checkitem_id from t_checkgroup_checkitem where checkgroup_id#{id}) /select