體檢預約流程
用戶可以通過如下操作流程進行體檢預約:
1、在移動端首頁點擊體檢預約,頁面跳轉到套餐列表頁面
2、在套餐列表頁面點擊要預約的套餐,頁面跳轉到套餐詳情頁面
3、在套餐詳情頁面點擊立即預約,頁面跳轉到預約頁面
4、在預約頁面錄入體檢人信息,包括手機號,點擊發送驗證碼
5、在預約頁面錄入收到的手機短信驗證碼,點擊提交預約,完成體檢預約
體檢預約
頁面調整
在預約頁面(/pages/orderInfo.html)進行調整
展示預約的套餐信息
第一步:從請求路徑中獲取當前套餐的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="'http://pqjroc654.bkt.clouddn.com/'+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.post("/setmeal/findById.do?id=" + id).then((response) => { this.setmeal = response.data.data; }); }
手機號校驗
第一步:在頁面導入的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
<div class="input-row"> <label>手機號</label> <input v-model="orderInfo.telephone" type="text" class="input-clear" placeholder="請輸入手機號"> <input style="font-size: x-small;" id="validateCodeButton" @click="sendValidateCode()" type="button" value="發送驗證碼"> </div>
//發送驗證碼 sendValidateCode(){ //獲取用戶輸入的手機號 var telephone = this.orderInfo.telephone; //校驗手機號輸入是否正確 if (!checkTelephone(telephone)) { this.$message.error('請輸入正確的手機號'); return false; } }
30秒倒計時效果
前面在sendValidateCode方法中進行了手機號校驗,如果校驗通過,需要顯示30秒倒計時效果
//發送驗證碼 sendValidateCode(){ //獲取用戶輸入的手機號 var telephone = this.orderInfo.telephone; //校驗手機號輸入是否正確 if (!checkTelephone(telephone)) { this.$message.error('請輸入正確的手機號'); return false; } validateCodeButton = $("#validateCodeButton")[0]; clock = window.setInterval(doLoop, 1000); //一秒執行一次 }
其中,validateCodeButton和clock是在healthmobile.js文件中定義的變量,doLoop是在healthmobile.js文件中定義的方法

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; //重置時間 } }
發送ajax請求
在按鈕上顯示30秒倒計時效果的同時,需要發送ajax請求,在后台給用戶發送手機驗證碼

//發送驗證碼 sendValidateCode(){ //獲取用戶輸入的手機號 var telephone = this.orderInfo.telephone; //校驗手機號輸入是否正確 if (!checkTelephone(telephone)) { this.$message.error('請輸入正確的手機號'); return false; } validateCodeButton = $("#validateCodeButton")[0]; clock = window.setInterval(doLoop, 1000); //一秒執行一次 axios.post("/validateCode/send4Order.do?telephone=" + telephone).then((response) => { if(!response.data.flag){ //驗證碼發送失敗 this.$message.error('驗證碼發送失敗,請檢查手機號輸入是否正確'); } }); }
創建ValidateCodeController,提供方法發送短信驗證碼,並將驗證碼保存到redis

package com.itheima.controller; import com.aliyuncs.exceptions.ClientException; import com.itheima.constant.MessageConstant; import com.itheima.constant.RedisConstant; import com.itheima.constant.RedisMessageConstant; import com.itheima.entity.Result; import com.itheima.utils.JedisUtils; import com.itheima.utils.SMSUtils; import com.itheima.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.Jedis; import redis.clients.jedis.JedisPool; import java.util.Random; /** * 短信驗證碼 */ @RestController @RequestMapping("/validateCode") public class ValidateCodeController { @Autowired private JedisPool jedisPool; //體檢預約時發送手機驗證碼 @RequestMapping("/send4Order") public Result send4Order(String telephone){ Integer code = ValidateCodeUtils.generateValidateCode(4);//生成4位數字驗證碼 try { //發送短信 SMSUtils.sendShortMessage(SMSUtils.VALIDATE_CODE,telephone,code.toString()); } catch (ClientException e) { e.printStackTrace(); //驗證碼發送失敗 return new Result(false, MessageConstant.SEND_VALIDATECODE_FAIL); } System.out.println("發送的手機驗證碼為:" + code); //將生成的驗證碼緩存到redis jedisPool.getResource().setex( telephone + RedisMessageConstant.SENDTYPE_ORDER,5 * 60,code.toString()); //驗證碼發送成功 return new Result(true,MessageConstant.SEND_VALIDATECODE_SUCCESS); } }
頁面中使用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() { /*確認時觸發事件*/}, '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); }
提交預約請求
為提交預約按鈕綁定事件
<div class="box-button"> <button @click="submitOrder()" type="button" class="btn order-btn">提交預約</button> </div>

//提交預約 submitOrder(){ //校驗身份證號格式 if(!checkIdCard(this.orderInfo.idCard)){ this.$message.error('身份證號碼輸入錯誤,請重新輸入'); return ; } axios.post("/order/submit.do",this.orderInfo).then((response) => { if(response.data.flag){ //預約成功,跳轉到預約成功頁面 window.location.href="orderSuccess.html?orderId=" + response.data.data; }else{ //預約失敗,提示預約失敗信息 this.$message.error(response.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; } }
后台代碼
Controller
在health_mobile工程中創建OrderController並提供submitOrder方法

package com.itheima.controller; import com.alibaba.dubbo.config.annotation.Reference; import com.aliyuncs.exceptions.ClientException; import com.itheima.constant.MessageConstant; import com.itheima.constant.RedisConstant; import com.itheima.constant.RedisMessageConstant; import com.itheima.entity.Result; import com.itheima.pojo.Member; import com.itheima.pojo.Order; import com.itheima.pojo.Setmeal; import com.itheima.service.OrderService; import com.itheima.utils.JedisUtils; import com.itheima.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.HashMap; import java.util.Map; /** * 體檢預約 */ @RestController @RequestMapping("/order") public class OrderController { @Reference private OrderService orderService; @Autowired private JedisPool jedisPool; /** * 體檢預約 * @param map * @return */ @RequestMapping("/submit") public Result submitOrder(@RequestBody Map map){ String telephone = (String) map.get("telephone"); //從Redis中獲取緩存的驗證碼,key為手機號+RedisConstant.SENDTYPE_ORDER String codeInRedis = jedisPool.getResource().get( telephone + RedisMessageConstant.SENDTYPE_ORDER); String validateCode = (String) map.get("validateCode"); //校驗手機驗證碼 if(codeInRedis == null || !codeInRedis.equals(validateCode)){ return new Result(false, MessageConstant.VALIDATECODE_ERROR); } Result result =null; //調用體檢預約服務 try{ map.put("orderType", Order.ORDERTYPE_WEIXIN); result = orderService.order(map); }catch (Exception e){ e.printStackTrace(); //預約失敗 return result; } if(result.isFlag()){ //預約成功,發送短信通知 String orderDate = (String) map.get("orderDate"); try { SMSUtils.sendShortMessage(SMSUtils.ORDER_NOTICE,telephone,orderDate); } catch (ClientException e) { e.printStackTrace(); } } return result; } }
服務接口
在health_interface工程中創建體檢預約服務接口OrderService並提供預約方法
package com.itheima.service; import com.itheima.entity.Result; import java.util.Map; /** * 體檢預約服務接口 */ public interface OrderService { //體檢預約 public Result order(Map map) throws Exception; }
服務實現類
在health_service_provider工程中創建體檢預約服務實現類OrderServiceImpl並實現體檢預約方法。
體檢預約方法處理邏輯比較復雜,需要進行如下業務處理:
1、檢查用戶所選擇的預約日期是否已經提前進行了預約設置,如果沒有設置則無法進行預約
2、檢查用戶所選擇的預約日期是否已經約滿,如果已經約滿則無法預約
3、檢查用戶是否重復預約(同一個用戶在同一天預約了同一個套餐),如果是重復預約則無法完成再次預約
4、檢查當前用戶是否為會員,如果是會員則直接完成預約,如果不是會員則自動完成注冊並進行預約
5、預約成功,更新當日的已預約人數
實現代碼如下:

package com.itheima.service; import com.alibaba.dubbo.config.annotation.Service; import com.itheima.constant.MessageConstant; import com.itheima.dao.MemberDao; import com.itheima.dao.OrderDao; import com.itheima.dao.OrderSettingDao; import com.itheima.dao.SetmealDao; import com.itheima.entity.Result; import com.itheima.pojo.Member; import com.itheima.pojo.Order; import com.itheima.pojo.OrderSetting; import com.itheima.pojo.Setmeal; import com.itheima.utils.DateUtils; import org.apache.poi.ss.usermodel.DateUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 體檢預約服務 */ @Service(interfaceClass = OrderService.class) @Transactional public class OrderServiceImpl implements OrderService{ @Autowired private OrderSettingDao orderSettingDao; @Autowired private MemberDao memberDao; @Autowired private OrderDao orderDao; //體檢預約 public Result order(Map map) throws Exception { //檢查當前日期是否進行了預約設置 String orderDate = (String) map.get("orderDate"); Date date = DateUtils.parseString2Date(orderDate); OrderSetting orderSetting = orderSettingDao.findByOrderDate(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.ORDER_FULL); } //檢查當前用戶是否為會員,根據手機號判斷 String telephone = (String) map.get("telephone"); Member member = memberDao.findByTelephone(telephone); //防止重復預約 if(member != null){ Integer memberId = member.getId(); int setmealId = Integer.parseInt((String) map.get("setmealId")); Order order = new Order(memberId,date,null,null,setmealId); List<Order> list = orderDao.findByCondition(order); if(list != null && list.size() > 0){ //已經完成了預約,不能重復預約 return new Result(false,MessageConstant.HAS_ORDERED); } } //可以預約,設置預約人數加一 orderSetting.setReservations(orderSetting.getReservations()+1); orderSettingDao.editReservationsByOrderDate(orderSetting); if(member == null){ //當前用戶不是會員,需要添加到會員表 member = new Member(); member.setName((String) map.get("name")); member.setPhoneNumber(telephone); member.setIdCard((String) map.get("idCard")); member.setSex((String) map.get("sex")); member.setRegTime(new Date()); memberDao.add(member); } //保存預約信息到預約表 Order order = new Order(member.getId(), date, (String)map.get("orderType"), Order.ORDERSTATUS_NO, Integer.parseInt((String) map.get("setmealId"))); orderDao.add(order); return new Result(true,MessageConstant.ORDER_SUCCESS,order.getId()); } }
Dao接口

package com.itheima.dao; import com.itheima.pojo.OrderSetting; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; public interface OrderSettingDao { public void add(OrderSetting orderSetting); //更新可預約人數 public void editNumberByOrderDate(OrderSetting orderSetting); //更新已預約人數 public void editReservationsByOrderDate(OrderSetting orderSetting); public long findCountByOrderDate(Date orderDate); //根據日期范圍查詢預約設置信息 public List<OrderSetting> getOrderSettingByMonth(Map date); //根據預約日期查詢預約設置信息 public OrderSetting findByOrderDate(Date orderDate); }

package com.itheima.dao; import com.github.pagehelper.Page; import com.itheima.pojo.Member; import java.util.List; 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(); }

package com.itheima.dao; import com.itheima.pojo.Order; import java.util.List; import java.util.Map; public interface OrderDao { public void add(Order order); public List<Order> findByCondition(Order order); }
Mapper映射文件
OrderSettingDao.xml
<!--根據日期查詢預約設置信息--> <select id="findByOrderDate" parameterType="date" resultType="com.itheima.pojo.OrderSetting"> select * from t_ordersetting where orderDate = #{orderDate} </select> <!--更新已預約人數--> <update id="editReservationsByOrderDate" parameterType="com.itheima.pojo.OrderSetting"> update t_ordersetting set reservations = #{reservations} where orderDate = #{orderDate} </update>
MemberDao.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.itheima.dao.MemberDao" > <select id="findAll" resultType="com.itheima.pojo.Member"> select * from t_member </select> <!--根據條件查詢--> <select id="selectByCondition" parameterType="string" resultType="com.itheima.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.itheima.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.itheima.pojo.Member"> select * from t_member where id = #{id} </select> <!--根據id查詢會員--> <select id="findByTelephone" parameterType="string" resultType="com.itheima.pojo.Member"> select * from t_member where phoneNumber = #{phoneNumber} </select> <!--編輯會員--> <update id="edit" parameterType="com.itheima.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 <= #{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 >= #{value} </select> <!--總會員數--> <select id="findMemberTotalCount" resultType="int"> select count(id) from t_member </select> </mapper>
OrderDao.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.itheima.dao.OrderDao" > <resultMap id="baseResultMap" type="com.itheima.pojo.Order"> <id column="id" property="id"/> <result column="member_id" property="memberId"/> <result column="orderDate" property="orderDate"/> <result column="orderType" property="orderType"/> <result column="orderStatus" property="orderStatus"/> <result column="setmeal_id" property="setmealId"/> </resultMap> <!--新增--> <insert id="add" parameterType="com.itheima.pojo.Order"> <selectKey resultType="java.lang.Integer" order="AFTER" keyProperty="id"> SELECT LAST_INSERT_ID() </selectKey> insert into t_order (member_id,orderDate,orderType,orderStatus,setmeal_id) values (#{memberId},#{orderDate},#{orderType},#{orderStatus},#{setmealId}) </insert> <!--動態條件查詢--> <select id="findByCondition" parameterType="com.itheima.pojo.Order" resultMap="baseResultMap"> select * from t_order <where> <if test="id != null"> and id = #{id} </if> <if test="memberId != null"> and member_id = #{memberId} </if> <if test="orderDate != null"> and orderDate = #{orderDate} </if> <if test="orderType != null"> and orderType = #{orderType} </if> <if test="orderStatus != null"> and orderStatus = #{orderStatus} </if> <if test="setmealId != null"> and setmeal_id = #{setmealId} </if> </where> </select> </mapper>