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

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.telephone
                        if (!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.telephone
                        if (!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 {
    @Autowired
    private 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 {

    @Autowired
    private JedisPool jedisPool;

    @Reference
    private 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 {

    @Autowired
   private OrderSettingDao orderSettingDao;

    @Autowired
    private MemberDao memberDao;

    @Autowired
    private OrderDao orderDao;

    @Override
    public 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
    */
    @Override
    public 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 from
        t_order o, t_member m, t_setmeal s
       where 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
    */
    @Override
    public 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 from
        t_order o, t_member m, t_setmeal s
       where 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);//生成随机数,最大为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;
    }
}

日期格式转化工具

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工作薄对象,表示整个excel
        Workbook workbook = null;
        try {
            //获取excel文件的io流
            InputStream is = file.getInputStream();
            //根据文件后缀名不同(xls和xlsx)获得不同的Workbook实现类对象
            if(fileName.endsWith(xls)){
                //2003
                workbook = new HSSFWorkbook(is);
            }else if(fileName.endsWith(xlsx)){
                //2007
                workbook = 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: //Boolean
                cellValue = 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语句查出来后记得起别名和前端名字保持一致。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值