手机验证码

  • 1. 体检预约流程
  • 2.体检预约
    • 2.1页面调整
      • 2.1.1 展示预约的套餐信息
      • 2.1.2 手机号校验
      • 2.1.3 30秒倒计时效果
      • 2.1.4 发送ajax请求
      • 2.1.5 日历展示
      • 2.1.6 提交预约请求
    • 2.2 后台代码
      • 2.2.1 Controller
      • 2.2.3 服务实现类
      • 2.2.4 Dao接口
  • 3. 预约成功页面展示
    • 3.1 页面调整
    • 3.2 后台代码
      • 3.2.1 Controller
      • 3.2.2 服务接口
      • 3.2.4 Dao接口
      • 3.2.5 Mapper映射文件
    • 工具类
      • 生成验证码工具类
      • 日期格式转化工具
      • js正则校验工具类
  • 4.总结

1. 体检预约流程

用户可以通过如下操作流程进行体检预约:
1、在移动端首页点击体检预约,页面跳转到套餐列表页面
2、在套餐列表页面点击要预约的套餐,页面跳转到套餐详情页面
3、在套餐详情页面点击立即预约,页面跳转到预约页面
4、在预约页面录入体检人信息,包括手机号,点击发送验证码
5、在预约页面录入收到的手机短信验证码,点击提交预约,完成体检预约

2.体检预约

2.1页面调整

在预约页面(/pages/orderInfo.html)进行调整

2.1.1 展示预约的套餐信息

第一步:从请求路径中获取当前套餐的id

<script>
var id = getUrlParam("id");//套餐id
</script>

第二步:定义模型数据setmeal,用于套餐数据展示

 var vue = new Vue({el:'#app',data:{setmeal:{},//套餐信息orderInfo:{setmealId:id,sex:'1'}//预约信息},
<div class="card"><div class=""><img :src="setmeal.img" width="100%" height="100%" /></div><div class="project-text"><h4 class="tit">{{setmeal.name}}</h4><p class="subtit">{{setmeal.remark}}</p><p class="keywords"><span>{{setmeal.sex == '0' ? '性别不限' : setmeal.sex == '1' ? '男':'女'}}</span><span>{{setmeal.age}}</span></p></div><div class="project-know"><a href="orderNotice.html" class="link-page"><i class="icon-ask-circle"><span class="path1"></span><span class="path2"></span></i><span class="word">预约须知</span><span class="arrow"><i class="icon-rit-arrow"></i></span></a></div></div>

第三步:在VUE的钩子函数中发送ajax请求,根据id查询套餐信息

 mounted(){axios.get("/setmeal/find.do?id="+id).then((res)=>{if (res.data.flag){this.setmeal=res.data.data}})}

2.1.2 手机号校验

第一步:在页面导入的healthmobile.js文件中已经定义了校验手机号的方法

/*** 手机号校验1--以1为开头;2--第二位可为3,4,5,7,8,中的任意一位;3--最后以0-9的9个整数结尾。*/
function checkTelephone(telephone) {var reg=/^[1][3,4,5,7,8][0-9]{9}$/;if (!reg.test(telephone)) {return false;} else {return true;}
}

第二步:为发送验证码按钮绑定事件sendValidateCode

          //发送验证码sendValidateCode(){let telephone=this.orderInfo.telephoneif (!checkTelephone(telephone)){//校验不通过,提示错误信息this.$message.error("请输入正确的手机号");return ;}

2.1.3 30秒倒计时效果

前面在sendValidateCode方法中进行了手机号校验,如果校验通过,需要显示30秒倒计时效果

var clock = '';//定时器对象,用于页面30秒倒计时效果
var nums = 30;
var validateCodeButton;
//基于定时器实现30秒倒计时效果
function doLoop() {validateCodeButton.disabled = true;//将按钮置为不可点击nums--;if (nums > 0) {validateCodeButton.value = nums + '秒后重新获取';} else {clearInterval(clock); //清除js定时器validateCodeButton.disabled = false;validateCodeButton.value = '重新获取验证码';nums = 30; //重置时间}
}
    //id选择器,#(),   jquery和js对象相互转化// 转jquery $(js对象)   转js jquery对象.get(0)validateCodeButton=$("#validateCodeButton")[0];//在按钮上显示30秒倒计时效果window.setInterval(doLoop,1000);//定时器方法

2.1.4 发送ajax请求

在按钮上显示30秒倒计时效果的同时,需要发送ajax请求,在后台给用户发送手机验证码

   //发送验证码sendValidateCode(){let telephone=this.orderInfo.telephoneif (!checkTelephone(telephone)){//校验不通过,提示错误信息this.$message.error("请输入正确的手机号");return ;}//id选择器,#(),   jquery和js对象相互转化// 转jquery $(js对象)   转js jquery对象.get(0)validateCodeButton=$("#validateCodeButton")[0];//在按钮上显示30秒倒计时效果window.setInterval(doLoop,1000);//定时器方法axios.get("/validateCode/send4Order.do?telephone="+telephone).then((res)=>{if (!res.data.flag){//短信验证码发送失败this.$message.error(res.data.message);}})}

创建ValidateCodeController,提供方法发送短信验证码,并将验证码保存到redis

package com.ybb.controller;
import com.aliyuncs.exceptions.ClientException;
import com.ybb.constant.MessageConstant;
import com.ybb.constant.RedisMessageConstant;
import com.ybb.entity.Result;
import com.ybb.utils.SMSUtils;
import com.ybb.utils.ValidateCodeUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.JedisPool;/*** Description :* Version :1.0*/
@RestController
@RequestMapping("/validateCode")
public class ValidateCodeController {@Autowiredprivate JedisPool jedisPool;//发送验证码 用户在线体检预约的@RequestMapping("/send4Order")public Result send4Order(String telephone){//给用户发送验证码Integer validateCode = ValidateCodeUtils.generateValidateCode(4);String value = String.valueOf(validateCode);System.out.println(validateCode);try {/*     SMSUtils.sendShortMessage(SMSUtils.VALIDATE_CODE,telephone,value);*/     //根据业务用手机号+业务的形式保存//将验证码保存到redis(定时间)jedisPool.getResource().setex(telephone+ RedisMessageConstant.SENDTYPE_ORDER,500,value);return new Result(true,MessageConstant.SEND_VALIDATECODE_SUCCESS);} catch (Exception e) {e.printStackTrace();return new Result(false, MessageConstant.SEND_VALIDATECODE_FAIL);}}
}

2.1.5 日历展示

页面中使用DatePicker控件来展示日历。根据需求,最多可以提前一个月进行体检预约,所以日历控件
只展示未来一个月的日期

   <div class="date"><label>体检日期</label><i class="icon-date" class="picktime"></i><input v-model="orderInfo.orderDate" type="text" class="picktime" readonly></div>
        <script>//日期控件var calendar = new datePicker();calendar.init({'trigger': '.picktime',/*按钮选择器,用于触发弹出插件*/'type': 'date',/*模式:date日期;datetime日期时间;time时间;ym年月;*/'minDate': getSpecifiedDate(new Date(),1),/*最小日期*/'maxDate': getSpecifiedDate(new Date(),30),/*最大日期*/'onSubmit': function() { /*确认时触发事件*///var theSelectData = calendar.value;},'onClose': function() { /*取消时触发事件*/ }});</script>

其中getSpecifiedDate方法定义在healthmobile.js文件中

//获得指定日期后指定天数的日期
function getSpecifiedDate(date,days) {date.setDate(date.getDate() + days);//获取指定天之后的日期var year = date.getFullYear();var month = date.getMonth() + 1;var day = date.getDate();return (year + "-" + month + "-" + day);
}

2.1.6 提交预约请求

为提交预约按钮绑定事件

<div class="box-button"><button @click="submitOrder()" type="button" class="btn order-btn">提交预约</button></div>
 //提交预约submitOrder(){//先对身份证号进行校验let IdCard=this.orderInfo.idCard;if (!checkIdCard(IdCard)){this.$message.error("请输入正确的身份证号")return;};axios.post("/order/submit.do",this.orderInfo).then((res)=>{if (res.data.flag){//预约成功,跳转到成功页面window.location.href="orderSuccess.html?orderId="+res.data.data;}else {this.$message.error(res.data.message);}})}}

其中checkIdCard方法是在healthmobile.js文件中定义的

/*** 身份证号码校验* 身份证号码为15位或者18位,15位时全为数字,18位前17位为数字,最后一位是校验位,可能为数字或字符X*/
function checkIdCard(idCard){var reg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;if(reg.test(idCard)){return true;}else{return false;}
}

2.2 后台代码

2.2.1 Controller

在health_mobile工程中创建OrderController并提供submitOrder方法

package com.ybb.controller;import com.alibaba.dubbo.config.annotation.Reference;
import com.ybb.constant.MessageConstant;
import com.ybb.constant.RedisMessageConstant;
import com.ybb.entity.Result;
import com.ybb.pojo.Order;
import com.ybb.service.OrderService;
import com.ybb.utils.SMSUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.JedisPool;import java.util.Map;/*** Description :体检预约处理* Version :1.0*/
@RestController
@RequestMapping("/order")
public class OrderController {@Autowiredprivate JedisPool jedisPool;@Referenceprivate OrderService orderService;@RequestMapping("/submit")public Result submit(@RequestBody Map map) {String telephone = (String) map.get("telephone");//从redis缓存中拿到具体验证码String validateCodeInRedis = jedisPool.getResource().get(telephone + RedisMessageConstant.SENDTYPE_ORDER);String validateCode = (String) map.get("validateCode");//验证码比对if (validateCodeInRedis != null && validateCode != null && validateCode.equals(validateCodeInRedis)) {Result result=null;try {result = orderService.order(map);/*   SMSUtils.sendShortMessage(SMSUtils.ORDER_NOTICE,telephone,(String) map.get("orderDate"));*/return result;} catch (Exception e) {e.printStackTrace();return result;}}else {//比对失败return new Result(false, MessageConstant.VALIDATECODE_ERROR);}}@RequestMapping("/findById")public Result findById(Integer id){try {Map map=  orderService.findById(id);return new Result(true,MessageConstant.QUERY_ORDER_SUCCESS,map);}catch (Exception e){return new Result(false,MessageConstant.QUERY_ORDER_FAIL);}}}

在health_interface工程中创建体检预约服务接口OrderService并提供预约方法

public interface OrderService {public Result order(Map map) throws Exception;Map findById(Integer id);
}

2.2.3 服务实现类

在health_service_provider工程中创建体检预约服务实现类OrderServiceImpl并实现体检预约方法。
体检预约方法处理逻辑比较复杂,需要进行如下业务处理:
1、检查用户所选择的预约日期是否已经提前进行了预约设置,如果没有设置则无法进行预约
2、检查用户所选择的预约日期是否已经约满,如果已经约满则无法预约
3、检查用户是否重复预约(同一个用户在同一天预约了同一个套餐),如果是重复预约则无法完成再
次预约
4、检查当前用户是否为会员,如果是会员则直接完成预约,如果不是会员则自动完成注册并进行预约
5、预约成功,更新当日的已预约人数
实现代码如下:

package com.ybb.service.Impl;import com.alibaba.dubbo.config.annotation.Service;
import com.ybb.constant.MessageConstant;
import com.ybb.dao.MemberDao;
import com.ybb.dao.OrderDao;
import com.ybb.dao.OrderSettingDao;
import com.ybb.entity.Result;
import com.ybb.pojo.Member;
import com.ybb.pojo.Order;
import com.ybb.pojo.OrderSetting;
import com.ybb.service.OrderService;
import com.ybb.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;import java.util.Date;
import java.util.HashMap;
import java.util.Map;/*** Description :体检预约服务* Version :1.0*/
@Service(interfaceClass = OrderService.class)
public class OrderServiceImpl implements OrderService {@Autowiredprivate OrderSettingDao orderSettingDao;@Autowiredprivate MemberDao memberDao;@Autowiredprivate OrderDao orderDao;@Overridepublic Result order(Map map) throws Exception {//检查用户锁选择的预约日期是否已经提前进行了预约设置,如果没有设置则无法进行预约String orderDate = (String) map.get("orderDate");Date date = DateUtils.parseString2Date(orderDate);OrderSetting orderSetting= orderSettingDao.findCountByOrderDate(date);if (orderSetting==null){return new Result(false, MessageConstant.SELECTED_DATE_CANNOT_ORDER);}//检查用户预约的日期是否已经预约满了,如果已经约满则无法预约int number = orderSetting.getNumber();//可预约人数int reservations = orderSetting.getReservations();//已预约人数if (reservations>=number){//已经约满,无法预约return new Result(false,MessageConstant.ORDERSETTING_FAIL);}//检查用户是否重复预约(同一用户同一时间同一套餐),如果是重复预约那就无法完成再次预约String telephone = (String) map.get("telephone");Member member = memberDao.findByTelephone(telephone);if (member!=null){//判断是否在重复预约Integer id = member.getId();Date order_date = DateUtils.parseString2Date(orderDate);String setmealId = (String) map.get("setmealId");//调方法查询有没有这个对象,有则报错Member member1= orderDao.findByCondition(id,order_date,setmealId);if (member1!=null) {return new Result(false,MessageConstant.HAS_ORDERED);}}else {//不是会员的情况member=new Member();//检查当前用户是否为会员,如果是会员则完成注册,不是则自动完成注册并预约member.setName((String) map.get("name"));member.setSex((String) map.get("sex"));member.setIdCard((String) map.get("idCard"));member.setPhoneNumber(telephone);member.setRegTime(new Date());memberDao.add(member);}//保存信息Map map1=new HashMap();map1.put("member_id",member.getId());map1.put("orderDate",date);map1.put("orderType",Order.ORDERTYPE_WEIXIN);map1.put("orderStatus",Order.ORDERSTATUS_NO);map1.put("setmeal_id",Integer.parseInt((String) map.get("setmealId")));orderDao.add(map1);//预约成功,更新当日的已预约人数orderSetting.setReservations(orderSetting.getReservations()+1);orderSettingDao.editNumberByOrderDate(orderSetting);//检查return new Result(true,MessageConstant.ORDER_SUCCESS,member.getId());}/*** @param id 根据预约ID查询预约相关信息(体检人姓名,预约日期,套餐名称,名称类型)* @return java.util.Map*/@Overridepublic Map findById(Integer id) {Map map= orderDao.findById4Detail(id);System.out.println(map);return map;}
}

2.2.4 Dao接口

package com.ybb.dao;import com.ybb.pojo.OrderSetting;
import org.apache.ibatis.annotations.Param;import java.util.Date;
import java.util.List;
import java.util.Map;**OrderSettingDao**
/*** Created by Administrator* Date :2020/8/25* Description :* Version :1.0*/
public interface OrderSettingDao {void add(OrderSetting orderSetting);void editNumberByOrderDate(OrderSetting orderSetting);public OrderSetting findCountByOrderDate(Date orderDate);List<Map> getOrderSettingByMonth(@Param("begin") String begin,@Param("end") String end);public OrderSetting findByOrderDate(Date orderDate)throws Exception;}

xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.ybb.dao.OrderSettingDao"><insert id="add" parameterType="com.ybb.pojo.OrderSetting">insert into t_ordersetting(orderDate,number,reservations) values (#{orderDate},#{number},#{reservations})</insert><update id="editNumberByOrderDate" parameterType="com.ybb.pojo.OrderSetting">update t_ordersetting set reservations=#{reservations} where orderDate=#{orderDate}</update><select id="findCountByOrderDate" parameterType="date" resultType="com.ybb.pojo.OrderSetting">select  * from t_ordersetting where orderDate=#{orderDate}</select><!--根据日期范围查询--><select id="getOrderSettingByMonth" parameterType="string" resultType="map">select day(orderDate)date,number,reservations from t_ordersetting where orderDate between #{begin} and #{end}</select><select id="findByOrderDate" resultType="com.ybb.pojo.OrderSetting" parameterType="date">select * from</select>
</mapper>

memberDao

public interface MemberDao {public List<Member> findAll();public Page<Member> selectByCondition(String queryString);public void add(Member member);public void deleteById(Integer id);public Member findById(Integer id);public Member findByTelephone(String telephone);public void edit(Member member);public Integer findMemberCountBeforeDate(String date);public Integer findMemberCountByDate(String date);public Integer findMemberCountAfterDate(String date);public Integer findMemberTotalCount();}

xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.ybb.dao.MemberDao" ><select id="findAll" resultType="com.ybb.pojo.Member">select * from t_member</select><!--根据条件查询--><select id="selectByCondition" parameterType="string" resultType="com.ybb.pojo.Member">select * from t_member<if test="value != null and value.length > 0">where fileNumber = #{value} or phoneNumber = #{value} or name = #{value}</if></select><!--新增会员--><insert id="add" parameterType="com.ybb.pojo.Member"><selectKey resultType="java.lang.Integer" order="AFTER" keyProperty="id">SELECT LAST_INSERT_ID()</selectKey>insert into t_member(fileNumber,name,sex,idCard,phoneNumber,regTime,password,email,birthday,remark)values (#{fileNumber},#{name},#{sex},#{idCard},#{phoneNumber},#{regTime},#{password},#{email},#{birthday},#{remark})</insert><!--删除会员--><delete id="deleteById" parameterType="int">delete from t_member where id = #{id}</delete><!--根据id查询会员--><select id="findById" parameterType="int" resultType="com.ybb.pojo.Member">select * from t_member where id = #{id}</select><!--根据id查询会员--><select id="findByTelephone" parameterType="string" resultType="com.ybb.pojo.Member">select * from t_member where phoneNumber = #{phoneNumber}</select><!--编辑会员--><update id="edit" parameterType="com.ybb.pojo.Member">update t_member<set><if test="fileNumber != null">fileNumber = #{fileNumber},</if><if test="name != null">name = #{name},</if><if test="sex != null">sex = #{sex},</if><if test="idCard != null">idCard = #{idCard},</if><if test="phoneNumber != null">phoneNumber = #{phoneNumber},</if><if test="regTime != null">regTime = #{regTime},</if><if test="password != null">password = #{password},</if><if test="email != null">email = #{email},</if><if test="birthday != null">birthday = #{birthday},</if><if test="remark != null">remark = #{remark},</if></set>where id = #{id}</update><!--根据日期统计会员数,统计指定日期之前的会员数--><select id="findMemberCountBeforeDate" parameterType="string" resultType="int">select count(id) from t_member where regTime &lt;= #{value}</select><!--根据日期统计会员数--><select id="findMemberCountByDate" parameterType="string" resultType="int">select count(id) from t_member where regTime = #{value}</select><!--根据日期统计会员数,统计指定日期之后的会员数--><select id="findMemberCountAfterDate" parameterType="string" resultType="int">select count(id) from t_member where regTime &gt;= #{value}</select><!--总会员数--><select id="findMemberTotalCount" resultType="int">select count(id) from t_member</select></mapper>

OrderDao

public interface OrderDao {Map findById4Detail(Integer id);Member findByCondition(@Param("id") Integer id, @Param("orderDate") Date order_date, @Param("setmeal_id") String setmealId);void add(Map map);
}

xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.ybb.dao.OrderDao"><insert id="add" parameterType="map">insert into t_order (member_id,orderDate,orderType,orderStatus,setmeal_id)values(#{member_id},#{orderDate},#{orderType},#{orderStatus},#{setmeal_id})</insert><select id="findById4Detail" parameterType="int" resultType="map">select m.name member ,s.name setmeal,o.orderDate orderDate,o.orderType orderType fromt_order o, t_member m, t_setmeal swhere o.member_id=m.id and o.setmeal_id=s.id and o.member_id=#{id}</select><select id="findByCondition" resultType="com.ybb.pojo.Member">select * from t_order where id=#{id} and orderDate =#{orderDate} and setmeal_id =#{setmeal_id}</select></mapper>

3. 预约成功页面展示

前面已经完成了体检预约,预约成功后页面会跳转到成功提示页面(orderSuccess.html)并展示预约
的相关信息(体检人、体检套餐、体检时间等)。

3.1 页面调整

提供orderSuccess.html页面,展示预约成功后相关信息

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! --><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0,user-scalable=no,minimal-ui"><meta name="description" content=""><meta name="author" content=""><link rel="icon" href="../img/asset-favico.ico"><title>预约须知</title><link rel="stylesheet" href="../css/page-health-orderNotice.css" /><script src="../plugins/jquery/dist/jquery.min.js"></script><script src="../plugins/healthmobile.js"></script><script src="../plugins/vue/vue.js"></script><script src="../plugins/vue/axios-0.18.0.js"></script><script>var id = getUrlParam("orderId");</script></head><body data-spy="scroll" data-target="#myNavbar" data-offset="150"><div id="app" class="app"><!-- 页面头部 --><div class="top-header"><span class="f-left"><i class="icon-back" onclick="history.go(-1)"></i></span><span class="center">传智健康</span><span class="f-right"><i class="icon-more"></i></span></div><!-- 页面内容 --><div class="contentBox"><div class="notice-article"><div class="info-title"><span class="name">体检预约成功</span></div><div class="notice-item"><div class="item-title">预约信息</div><div class="item-content"><p>体检人:{{orderInfo.member}}</p><p>体检套餐:{{orderInfo.setmeal}}</p><p>体检日期:{{orderInfo.orderDate}}</p><p>预约类型:{{orderInfo.orderType}}</p></div></div><div class="notice-item"><div class="item-title">注意事项</div><div class="item-content"><p>1、体检前三天饮食不宜有太大变化,尽量保证清淡饮食,避免油腻、过甜、过咸的食物,避免饮食不均暴饮暴食,以免影响检查结果。</p><p>2、体检前要注意好好休息,尽量不要做剧烈运动以及情绪也不宜激动,要保证充足的睡眠时间,调整好自己的身体状态。</p><p>3、体检前不要饮酒,酒精会影响到检查的准确性,如导致甘油三酯、转氨酶等检查结果出现异常,干扰到很多检查项目。</p><p>4、体检当天应该保证穿着轻便、简单,另外女士要注意不要穿连裙、连裤袜,以及不要佩戴饰品等,以免影响检查。</p><p>5、女士朋友要注意避开经期,以防经期影响到相关的检查项目。</p><p>6、如果有前列腺或妇科B超检查,需要憋尿,检前可以适当饮水,以保证膀胱充盈后再进行检查。</p><p>7、如果有妇科检查,要注意检前不宜进行夫妻生活,夫妻生活会影响到阴道检查,干扰检查的结果。</p><p>8、做妇科检查前也不宜过度清洁,不宜使用阴道药物,避免过度清洁或是药物干扰,影响到检查的结果。</p><p>9、积极配合医师的检查,保持良好的心态,告知医师自己的真实情况,有助于医师帮助判断身体健康状况。</p></div></div></div></div></div></body><script>var vue = new Vue({el:'#app',data:{orderInfo:{}},mounted(){axios.post("/order/findById.do?id=" + id).then((response) => {console.log(response.data.data)if (response.data.flag){this.orderInfo = response.data.data;}else {this.$message.error(response.data.message)}});}});</script>
</html>

3.2 后台代码

3.2.1 Controller

在OrderController中提供findById方法,根据预约id查询预约相关信息

    @RequestMapping("/findById")public Result findById(Integer id){try {Map map=  orderService.findById(id);return new Result(true,MessageConstant.QUERY_ORDER_SUCCESS,map);}catch (Exception e){return new Result(false,MessageConstant.QUERY_ORDER_FAIL);}}

3.2.2 服务接口

在OrderService服务接口中扩展findById方法

    /*** @param id 根据预约ID查询预约相关信息(体检人姓名,预约日期,套餐名称,名称类型)* @return java.util.Map*/@Overridepublic Map findById(Integer id) {Map map= orderDao.findById4Detail(id);System.out.println(map);return map;}

3.2.4 Dao接口

在OrderDao接口中扩展findById4Detail方法

Map findById4Detail(Integer id);

3.2.5 Mapper映射文件

在OrderDao.xml映射文件中提供SQL语句

    <select id="findById4Detail" parameterType="int" resultType="map">select m.name member ,s.name setmeal,o.orderDate orderDate,o.orderType orderType fromt_order o, t_member m, t_setmeal swhere o.member_id=m.id and o.setmeal_id=s.id and o.member_id=#{id}</select>

工具类

生成验证码工具类

package com.ybb.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);//生成随机数,最大为9999if(code < 1000){code = code + 1000;//保证随机数为4位数字}}else if(length == 6){code = new Random().nextInt(999999);//生成随机数,最大为999999if(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;}
}

日期格式转化工具

package com.ybb.utils;import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.multipart.MultipartFile;public class POIUtils {private final static String xls = "xls";private final static String xlsx = "xlsx";private final static String DATE_FORMAT = "yyyy/MM/dd";/*** 读入excel文件,解析后返回* @param file* @throws IOException*/public static List<String[]> readExcel(MultipartFile file) throws IOException {//检查文件checkFile(file);//获得Workbook工作薄对象Workbook workbook = getWorkBook(file);//创建返回对象,把每行中的值作为一个数组,所有行作为一个集合返回List<String[]> list = new ArrayList<String[]>();if(workbook != null){for(int sheetNum = 0;sheetNum < workbook.getNumberOfSheets();sheetNum++){//获得当前sheet工作表Sheet sheet = workbook.getSheetAt(sheetNum);if(sheet == null){continue;}//获得当前sheet的开始行int firstRowNum  = sheet.getFirstRowNum();//获得当前sheet的结束行int lastRowNum = sheet.getLastRowNum();//循环除了第一行的所有行for(int rowNum = firstRowNum+1;rowNum <= lastRowNum;rowNum++){//获得当前行Row row = sheet.getRow(rowNum);if(row == null){continue;}//获得当前行的开始列int firstCellNum = row.getFirstCellNum();//获得当前行的列数int lastCellNum = row.getPhysicalNumberOfCells();String[] cells = new String[row.getPhysicalNumberOfCells()];//循环当前行for(int cellNum = firstCellNum; cellNum < lastCellNum;cellNum++){Cell cell = row.getCell(cellNum);cells[cellNum] = getCellValue(cell);}list.add(cells);}}workbook.close();}return list;}//校验文件是否合法public static void checkFile(MultipartFile file) throws IOException{//判断文件是否存在if(null == file){throw new FileNotFoundException("文件不存在!");}//获得文件名String fileName = file.getOriginalFilename();//判断文件是否是excel文件if(!fileName.endsWith(xls) && !fileName.endsWith(xlsx)){throw new IOException(fileName + "不是excel文件");}}public static Workbook getWorkBook(MultipartFile file) {//获得文件名String fileName = file.getOriginalFilename();//创建Workbook工作薄对象,表示整个excelWorkbook workbook = null;try {//获取excel文件的io流InputStream is = file.getInputStream();//根据文件后缀名不同(xls和xlsx)获得不同的Workbook实现类对象if(fileName.endsWith(xls)){//2003workbook = new HSSFWorkbook(is);}else if(fileName.endsWith(xlsx)){//2007workbook = new XSSFWorkbook(is);}} catch (IOException e) {e.printStackTrace();}return workbook;}public static String getCellValue(Cell cell){String cellValue = "";if(cell == null){return cellValue;}//如果当前单元格内容为日期类型,需要特殊处理String dataFormatString = cell.getCellStyle().getDataFormatString();if(dataFormatString.equals("m/d/yy")){cellValue = new SimpleDateFormat(DATE_FORMAT).format(cell.getDateCellValue());return cellValue;}//把数字当成String来读,避免出现1读成1.0的情况if(cell.getCellType() == Cell.CELL_TYPE_NUMERIC){cell.setCellType(Cell.CELL_TYPE_STRING);}//判断数据的类型switch (cell.getCellType()){case Cell.CELL_TYPE_NUMERIC: //数字cellValue = String.valueOf(cell.getNumericCellValue());break;case Cell.CELL_TYPE_STRING: //字符串cellValue = String.valueOf(cell.getStringCellValue());break;case Cell.CELL_TYPE_BOOLEAN: //BooleancellValue = String.valueOf(cell.getBooleanCellValue());break;case Cell.CELL_TYPE_FORMULA: //公式cellValue = String.valueOf(cell.getCellFormula());break;case Cell.CELL_TYPE_BLANK: //空值cellValue = "";break;case Cell.CELL_TYPE_ERROR: //故障cellValue = "非法字符";break;default:cellValue = "未知类型";break;}return cellValue;}
}

js正则校验工具类

//获取指定的URL参数值 http://localhost/pages/setmeal_detail.html?id=3&name=jack
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 "";}
}//获得当前日期,返回字符串
function getToday() {var today = new Date();var year = today.getFullYear();var month = today.getMonth() + 1;//0表示1月,1表示2月var day = today.getDate();return (year + "-" + month + "-" + day);
}//获得指定日期后指定天数的日期
function getSpecifiedDate(date,days) {date.setDate(date.getDate() + days);//获取指定天之后的日期var year = date.getFullYear();var month = date.getMonth() + 1;var day = date.getDate();return (year + "-" + month + "-" + day);
}/*** 手机号校验1--以1为开头;2--第二位可为3,4,5,7,8,中的任意一位;3--最后以0-9的9个整数结尾。*/
function checkTelephone(telephone) {var reg=/^[1][3,4,5,7,8][0-9]{9}$/;if (!reg.test(telephone)) {return false;} else {return true;}
}/*** 身份证号码校验* 身份证号码为15位或者18位,15位时全为数字,18位前17位为数字,最后一位是校验位,可能为数字或字符X*/
function checkIdCard(idCard){var reg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;if(reg.test(idCard)){return true;}else{return false;}
}var clock = '';//定时器对象,用于页面30秒倒计时效果
var nums = 30;
var validateCodeButton;
//基于定时器实现30秒倒计时效果
function doLoop() {validateCodeButton.disabled = true;//将按钮置为不可点击nums--;if (nums > 0) {validateCodeButton.value = nums + '秒后重新获取';} else {clearInterval(clock); //清除js定时器validateCodeButton.disabled = false;validateCodeButton.value = '重新获取验证码';nums = 30; //重置时间}
}

4.总结

昨天完成了页面的静态,今天要进行具体的预约功能实现了


redis中保存key时最好要遵循下面的规则

  • 唯一且非空的字段

  • 表明:主键:主键值:字段名称 字段值

  • 项目名:模块名称:业务名称:唯一值 值

  • 页面信息的回写,通过网页中传递id来获取对应的对象参数,从而回写

  • 手机号校验时引入工具类中的正则方法,输入后点击发送验证码之前会触发正则的校验,如果通过再
    30秒倒计时,也是用到工具类,用window提供的定时器每秒触发,同时发送axios请求到后端,后端根据传入的telephone和随机生成的数字,组合起来放入redis中。

预约的日历控件,根据业务控制预约的时间,这里以一个月的日期为准,只能显示当前日期的后面一天到30天的时间去预约。
身份证也是一个正则的校验,也是在提交之前去校验,然后把整体的数据提交到后端,根据后端传来的flag的true or false来判断是否需要成功跳转,或者报错误信息。
接下来是重点的业务了
因为传过来的值是组合起来的,用所拥有的对象没有能完美封装的,所有用map最为好。然后根据手机号和业务值,去redis中拿具体的验证码信息,和输入的验证码信息做对比,先判空,在判是否相等,这样在controller层把这个简单校验完成掉,再去调用service(dubbo注入)的业务会更好。

Service层

  1. 判断用户选择的预约日期是否在预约设置的范围内(1-30)天
  2. 检查用户预约的日期是否已经预约满了
  3. 检查用户是否重复预约(同一用户同一时间同一套餐)
 String orderDate = (String) map.get("orderDate");Date date = DateUtils.parseString2Date(orderDate);OrderSetting orderSetting= orderSettingDao.findCountByOrderDate(date);if (orderSetting==null){return new Result(false, MessageConstant.SELECTED_DATE_CANNOT_ORDER);}

1.比较简单,根据传入的orderDate去查输入的日期值是否在这个时间范围内,有则继续往下走,没有retrun(前面有写过)

 int number = orderSetting.getNumber();//可预约人数int reservations = orderSetting.getReservations();//已预约人数if (reservations>=number){//已经约满,无法预约return new Result(false,MessageConstant.ORDERSETTING_FAIL);}

2.上面已经拿到了那一天的对象了,直接调用两个方法,然后对比下即可。

 String telephone = (String) map.get("telephone");Member member = memberDao.findByTelephone(telephone);if (member!=null){//判断是否在重复预约Integer id = member.getId();Date order_date = DateUtils.parseString2Date(orderDate);String setmealId = (String) map.get("setmealId");//调方法查询有没有这个对象,有则报错Member member1= orderDao.findByCondition(id,order_date,setmealId);if (member1!=null) {return new Result(false,MessageConstant.HAS_ORDERED);}}else {//不是会员的情况member=new Member();//检查当前用户是否为会员,如果是会员则完成注册,不是则自动完成注册并预约member.setName((String) map.get("name"));member.setSex((String) map.get("sex"));member.setIdCard((String) map.get("idCard"));member.setPhoneNumber(telephone);member.setRegTime(new Date());memberDao.add(member);}

3.因为这里涉及到会员表,需要先根据电话号码去查询具体的member有没有,如果有,再进行进一步的判断里面的id,预约日期和预约的套餐id,如果还是能查到说明有重复预约,没有则没事。前面会员表查询时为了另一个业务,如果该用户还没有注册,就帮该用户注册一下。

 //保存信息Map map1=new HashMap();map1.put("member_id",member.getId());map1.put("orderDate",date);map1.put("orderType",Order.ORDERTYPE_WEIXIN);map1.put("orderStatus",Order.ORDERSTATUS_NO);map1.put("setmeal_id",Integer.parseInt((String) map.get("setmealId")));orderDao.add(map1);//预约成功,更新当日的已预约人数orderSetting.setReservations(orderSetting.getReservations()+1);orderSettingDao.editNumberByOrderDate(orderSetting);//检查return new Result(true,MessageConstant.ORDER_SUCCESS,member.getId());

4.帮用户注册后,需要保存到预约表中,这里因为数据库字段和类属性不匹配,就用了map封装,同时需要更新一个当日预约的人数,返回的时候返还id即可。


5.最后是预约成功后的数据回写,因为要涉及到三张表的字段,所以还是不匹配,只能用map封装,然后select语句查出来后记得起别名和前端名字保持一致。

移动端开发-体检预约 手机号校验 30秒倒计时 日历展示相关推荐

  1. 健康管理系统第七天(移动端_体检预约(手机号校验、发送验证码之后30秒倒计时效果、生成验证码、向手机发送验证码))

    一.体检预约流程 用户可以通过如下操作流程进行体检预约: 1.在移动端首页点击体检预约,页面跳转到套餐列表页面 2.在套餐列表页面点击要预约的套餐,页面跳转到套餐详情页面 3.在套餐详情页面点击立即预 ...

  2. 移动端开发-体检预约

    移动端开发-体检预约 目录 移动端开发-体检预约 1. 移动端开发 1.1 移动端开发方式 1.2 微信公众号开发 2. 需求分析和环境搭建 2.1 需求分析 2.2 搭建移动端工程 3. 套餐列表页 ...

  3. Day_06 传智健康项目-移动端开发-体检预约

    第6章 移动端开发-体检预约 1. 移动端开发 1.1 移动端开发方式 随着移动互联网的兴起和手机的普及,目前移动端应用变得愈发重要,成为了各个商家的必争之地.例如,我们可以使用手机购物.支付.打车. ...

  4. 传智健康2.0-6-移动端开发-体检预约

    第6章 移动端开发-体检预约 1. 移动端开发 1.1 移动端开发方式 随着移动互联网的兴起和手机的普及,目前移动端应用变得愈发重要,成为了各个商家的必争之地.例如,我们可以使用手机购物.支付.打车. ...

  5. XX健康:移动端开发-体检预约验证码30秒倒计时短信验证码获取与验证DatePicker日历展示提交预约复杂流程阿里短信工具类

    1. 体检预约流程 用户可以通过如下操作流程进行体检预约: 在移动端首页点击体检预约,页面跳转到套餐列表页面 在套餐列表页面点击要预约的套餐,页面跳转到套餐详情页面 在套餐详情页面点击立即预约,页面跳 ...

  6. XX健康:移动端开发-体检预约设计和实现微信公众号注册阿里短信服务

    1. 移动端开发 1.1 移动端开发方式 随着移动互联网的兴起和手机的普及,目前移动端应用变得愈发重要,成为了各个商家的必争之地.例如,我们可以使用手机购物.支付.打车.玩游戏.订酒店.购票等,以前只 ...

  7. 基于单片机的c语言倒计时程序,30秒倒计时c语言51单片机实现.doc

    30秒倒计时c语言51单片机实现 原理图: 程序: #include #define uchar unsigned char #define uint unsigned int sbit dula=P ...

  8. 使用 JavaScript 基于 定时器 实现30秒倒计时限制 验证码发送

    存放于工具 JS 文件中的代码 var clock = '';//定时器对象,用于页面30秒倒计时效果 var nums = 30; var validateCodeButton; //基于定时器实现 ...

  9. 两位数码管30秒倒计时c语言,基于51单片机共阴两位数码管显示倒计时

    <基于51单片机共阴两位数码管显示倒计时>由会员分享,可在线阅读,更多相关<基于51单片机共阴两位数码管显示倒计时(2页珍藏版)>请在人人文库网上搜索. 1.基于51单片机共阴 ...

最新文章

  1. 在python3环境安装builtwith模块
  2. 利用UltimateAndroid框架进行快速开发
  3. 我的世界java版背景图更换_《我的世界手机版》如何修改界面背景 界面背景修改攻略图文教程...
  4. 3G网络关闭,4G还会远吗?
  5. python返回元组_python – numpy.where返回一个元组的目的是什么?
  6. POJ2941 SDUT2371Homogeneous squares
  7. 【Django】django使用原生SQL的方法(附加说说为什么ORM上不了大台面)
  8. 【BERT】BERT的嵌入层是如何实现的?看完你就明白了
  9. 《麦肯锡方法》第9章 头脑风暴-思维导图
  10. ssh远程连接阿里云服务器
  11. 软件功能测试ok,S7_200解密软件,测试OK
  12. 经典网页设计:10个优秀的国外企业网站设计案例
  13. 永续合约短线交易技巧?
  14. vue3安装全家桶教程
  15. python官网怎么下载python,苹果电脑怎么下载python
  16. Windows10系统安装与初始化设置
  17. Access的DateAdd 函数
  18. Beyond compare添加插件
  19. Shell Programming
  20. 三种EXCEL去重统计方法

热门文章

  1. IDEA推出新字体,极度舒适
  2. 查看电脑IP端口配置/Windows Ipconfig命令教程
  3. java进阶----集合
  4. 辛酸,面试蚂蚁Offer的经验都在这里了!
  5. 计算机程序式版面,版式设计基本程序!与版式基本形式
  6. 携程PMO封面人物 | 关群 AUG Meetup
  7. 孙振耀担任海辉董事会主席 自2008年3月生效
  8. 相机和镜头选择时的一些参数
  9. 解决虚拟机设置共享文件夹失败,/mnt目录为空
  10. 网站搜索引擎优化,值得关注的4个策略有哪些?