船志健康项目-预约管理-预约设置5

一、需求分析

 前面我们已经完成了检查项管理、检查组管理、套餐管理等。接下来我们需要进行预约设置,其实就是设置每一天的体检预约最大数量。客户可以通过微信端在线预约,在线预约时需要选择体检的时间,如果客户选择的时间已经预约满则无法进行预约。

  image

二、批量导入预约设置信息

 预约设置信息对应的数据表为t_ordersetting,预约设置操作对应的页面为ordersetting.html

 t_ordersetting表结构:

 image

 批量导入预约设置信息操作过程:

  1、点击模板下载按钮下载Excel模板文件

  2、将预约设置信息录入到模板文件中

  3、点击上传文件按钮将录入完信息的模板文件上传到服务器

  4、通过POI读取上传文件的数据并保存到数据库

1. 完善页面

 1.1 提供模板文件

  资料中已经提供了Excel模板文件ordersetting_template.xlsx,将文件放在health_backend工程的 template目录

   image

  xlsx中日期:2025/12/12,可预约数量:3

 1.2 实现模板文件下载

  为模板下载按钮绑定事件实现模板文件下载ordersetting.html

<el-button style="margin-bottom: 20px;margin-right: 20px" type="primary" @click="downloadTemplate()">模板下载</el-button>
                //下载模板文件
                downloadTemplate(){
                    window.location.href="../../template/ordersetting_template.xlsx";
                }

 1.3 文件上传

  使用ElementUI的上传组件实现文件上传并绑定相关事件

                            <el-upload action="/ordersetting/upload.do"
                                       name="excelFile"
                                       :show-file-list="false"
                                       :on-success="handleSuccess"
                                       :before-upload="beforeUpload">
                                <el-button type="primary">上传文件</el-button>
                            </el-upload>
                //上传成功提示
                handleSuccess(response, file) {
                    if(response.flag){
                        this.$message({
                            message: response.message,
                            type: 'success'
                        });
                    }else{
                        this.$message.error(response.message);
                    }
                    console.log(response, file, fileList);
                }

                //上传之前进行文件格式校验
                beforeUpload(file){
                    if(file.type != undefined){
                        const isXLS = file.type === 'application/vnd.ms-excel';
                        if(isXLS){
                            return true;
                        }
                        const isXLSX = file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
                        if (isXLSX) {
                            return true;
                        }
                        this.$message.error('上传文件只能是xls或者xlsx格式!');
                        return false;
                    }else{
                        var name = file.name;
                        var extension = name.substr(name.lastIndexOf("."));
                        if(extension != "xls" || extension != "xlsx"){
                            return false;
                        }else{
                            return true;
                        }
                    }
                }

2. 后台代码

 2.1 Controller

  将资料中的POIUtils工具类复制到health_common工程

package com.itheima.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();
            //根据文件后缀名不同(Fxls和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;//2014/10/18
        }
        //把数字当成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;
    }
}
View Code

  在health_backend工程创建OrderSettingController并提供upload方法

package com.itheima.controller;

import com.alibaba.dubbo.config.annotation.Reference;
import com.itheima.constant.MessageConstant;
import com.itheima.entity.Result;
import com.itheima.pojo.OrderSetting;
import com.itheima.service.OrderSettingService;
import com.itheima.utils.POIUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 预约设置
 */
@RestController
@RequestMapping("/ordersetting")
public class OrderSettingController {

    @Reference
    private OrderSettingService orderSettingService;

    //Excel文件上传,并解析文件内容保存到数据库,实现预约设置数据批量导入
    @RequestMapping("/upload")
    public Result upload(@RequestParam("excelFile") MultipartFile excelFile){
        try {
            List<String[]> list = POIUtils.readExcel(excelFile);
            if(list != null && list.size() > 0){
                List<OrderSetting> orderSettingList = new ArrayList<>();
                for (String[] strings : list) {
                    String orderDate = strings[0];
                    String number = strings[1];
                    OrderSetting orderSetting = new OrderSetting(new Date(orderDate),Integer.parseInt(number));
                    orderSettingList.add(orderSetting);
                }
                //通过dubbo远程调用服务实现数据批量导入到数据库,入参不能传入MultipartFile
                orderSettingService.add(orderSettingList);
            }
        }catch (Exception e){
            e.printStackTrace();
            //文件解析失败
            return new Result(false, MessageConstant.IMPORT_ORDERSETTING_FAIL);
        }
        return new Result(true,MessageConstant.IMPORT_ORDERSETTING_SUCCESS);
    }
}

 2.2 服务接口

  创建OrderSettingService服务接口并提供新增方法

package com.itheima.service;

import com.itheima.pojo.OrderSetting;
import java.util.List;
import java.util.Map;

public interface OrderSettingService {
    public void add(List<OrderSetting> orderSettingList);
}

 2.3 服务实现类

  创建服务实现类OrderSettingServiceImpl并实现新增方法

package com.itheima.service.impl;

import com.alibaba.dubbo.config.annotation.Service;
import com.itheima.dao.OrderSettingDao;
import com.itheima.pojo.OrderSetting;
import com.itheima.service.OrderSettingService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;

/**
 * 预约设置服务
 */
@Service(interfaceClass = OrderSettingService.class)
@Transactional
public class OrderSettingServiceImpl implements OrderSettingService {

    @Autowired
    private OrderSettingDao orderSettingDao;

    //批量导入预约设置数据
    @Override
    public void add(List<OrderSetting> orderSettingList) {
        if(orderSettingList != null && orderSettingList.size() >= 0){
            for (OrderSetting orderSetting : orderSettingList) {
                //判断当前日期是否已经进行了预约设置
                long count = orderSettingDao.findCountByOrderDate(orderSetting.getOrderDate());
                if(count > 0){
                    //已经进行了预约设置,执行更新操作
                    orderSettingDao.editNumberByOrderDate(orderSetting);
                }else{
                    //没有预约设置,执行添加操作
                    orderSettingDao.add(orderSetting);
                }
            }
        }
    }
}

 2.4 Dao接口

  创建Dao接口OrderSettingDao并提供更新和新增方法

package com.itheima.dao;

import com.itheima.pojo.OrderSetting;
import java.util.Date;
import java.util.List;
import java.util.Map;

public interface OrderSettingDao {
    public long findCountByOrderDate(Date orderDate);
    public void editNumberByOrderDate(OrderSetting orderSetting);
    public void add(OrderSetting orderSetting);
}

 2.5 Mapper映射文件

  创建Mapper映射文件OrderSettingDao.xml并提供相关SQL

<?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.OrderSettingDao">
    <!--新增-->
    <insert id="add" parameterType="com.itheima.pojo.OrderSetting">
        insert into t_ordersetting(orderDate,number,reservations)
        values
        (#{orderDate},#{number},#{reservations})
    </insert>

    <!--根据日期更新预约人数,#{orderDate}传入的是字符串
    -->
    <update id="editNumberByOrderDate" parameterType="com.itheima.pojo.OrderSetting">
        update t_ordersetting set number = #{number} where orderDate = #{orderDate}
    </update>

    <!--根据预约日期查询-->
    <select id="findCountByOrderDate" parameterType="date" resultType="long">
        select count(*)from t_ordersetting where orderDate = #{orderDate}
    </select>
</mapper>

三、日历展示预约设置信息

 前面已经完成了预约设置功能,现在就需要通过日历的方式展示出来每天设置的预约人数。

 在页面中已经完成了日历的动态展示,我们只需要查询当前月份的预约设置信息并展示到日历中即可, 同时在日历中还需要展示已经预约的人数,效果如下:

  image

1. 完善页面

 1.1 使用静态数据调试

  为了能够快速看到效果,我们可以先使用静态数据模拟,然后再改为发送ajax请求查询数据库。

  实现步骤:

  (1)预约设置数据对应的模型数据为leftobj,在initData方法最后为leftobj模型数据赋值:

vue-data中数据:
leftobj: []//用于装载页面显示的月份已经进行预约设置的数据
                //初始化当前页要展示的日期
                initData: function (cur) {
                    this.leftobj = [
                        { date: 1, number: 120, reservations: 1 },
                        { date: 3, number: 120, reservations: 1 },
                        { date: 4, number: 120, reservations: 120 },
                        { date: 6, number: 120, reservations: 1 },
                        { date: 8, number: 120, reservations: 1 }
                    ];
                }

   其中date表示日期,number表示可预约人数,reservations表示已预约人数

  (2)使用VUE的v-for标签遍历上面的leftobj模型数据,展示到日历上:

                                            <template>
                                                <template v-for="obj in leftobj">
                                                    <template v-if="obj.date == dayobject.day.getDate()">
                                                        <template v-if="obj.number > obj.reservations">
                                                            <div class="usual">
                                                                <p>可预约{{obj.number}}人</p>
                                                                <p>已预约{{obj.reservations}}人</p>
                                                            </div>
                                                        </template>
                                                        <template v-else>
                                                            <div class="fulled">
                                                                <p>可预约{{obj.number}}人</p>
                                                                <p>已预约{{obj.reservations}}人</p>
                                                                <p>已满</p>
                                                            </div>
                                                        </template>
                                                    </template>
                                                </template>
                                                <!--today vue提供的-->
                                                <button v-if="dayobject.day > today" @click="handleOrderSet(dayobject.day)" class="orderbtn">设置</button>
                                            </template>

 1.2 发送ajax获取动态数据

  将上面的静态模拟数据leftobj去掉,改为发送ajax请求,根据当前页面对应的月份查询数据库获取预约设置信息,将查询结果赋值给leftobj模型数据 

                    //发送ajax请求,根据当前页面对应的月份查询数据库,封装成相应结果赋值给leftobj模型数据,用于页面展示
                    axios.post("/ordersetting/getOrderSettingByMonth.do?date="+this.currentYear+"-"+this.currentMonth).then((res)=>{
                        if(res.data.flag){
                            this.leftobj = res.data.data;
                        }else{
                            this.$message.error(response.data.message);
                        }
                    });

2. 后台代码

 2.1 Controller 在OrderSettingController中提供getOrderSettingByMonth方法,根据月份查询预约设置信息

    /**
     * 根据日期查询预约设置数据(获取指定日期所在月份的预约设置数据)
     * @param date
     * @return
     */
    @RequestMapping("/getOrderSettingByMonth")
    public Result getOrderSettingByMonth(String date){//参数格式为:2024-11
        try{
            //不能返回OrderSetting,因为leftobj里面的Date字段是int类型,跟OrderSetting中Date类型匹配不上
            List<Map> list = orderSettingService.getOrderSettingByMonth(date);
            return new Result(true,MessageConstant.GET_ORDERSETTING_SUCCESS,list);
        }catch (Exception e){
            e.printStackTrace();
            return new Result(false, MessageConstant.GET_ORDERSETTING_FAIL);
        }
    }

 2.2 服务接口

  在OrderSettingService服务接口中扩展方法getOrderSettingByMonth

public List<Map> getOrderSettingByMonth(String date);

 2.3 服务实现类

  在OrderSettingServiceImpl服务实现类中实现方法getOrderSettingByMonth

    //根据月份查询对应的预约设置数据
    @Override
    public List<Map> getOrderSettingByMonth(String date) {//2024-11
        String beginDate = date+"-1";
        String endDate = date+"-31";
        Map<String,String> map = new HashMap();
        map.put("beginDate",beginDate);
        map.put("endDate",endDate);
        //调用DAO,根据日期范围查询预约设置数据
        List<OrderSetting> list = orderSettingDao.getOrderSettingByMonth(map);
        List<Map> result = new ArrayList<>();
        if(list != null && list.size() > 0){
            for (OrderSetting orderSetting : list) {
                Map<String,Object> ordersettingMap = new HashMap();
                ordersettingMap.put("date",orderSetting.getOrderDate().getDate());//获取几号
                ordersettingMap.put("number",orderSetting.getNumber());
                ordersettingMap.put("reservations",orderSetting.getReservations());
                result.add(ordersettingMap);
            }
        }
        return result;
    }

 2.4 Dao接口

  在OrderSettingDao接口中扩展方法getOrderSettingByMonth

public List<OrderSetting> getOrderSettingByMonth(Map date);

2.5 Mapper映射文件

  在OrderSettingDao.xml文件中扩展SQL

    <!--根据日期范围查询-->
    <select id="getOrderSettingByMonth" parameterType="map" resultType="com.itheima.pojo.OrderSetting">
        select * from t_ordersetting where orderDate between #{beginDate} and #{endDate}
    </select>

四、基于日历实现预约设置

 为通过点击日历中的设置按钮来设置对应日期的可预约人数。效果如下:

  image

1. 完善页面

 1.1 为设置按钮绑定事件

  为日历中的设置按钮绑定单击事件,当前日期作为参数

<button v-if="dayobject.day > today" @click="handleOrderSet(dayobject.day)" class="orderbtn">设置</button>
//预约设置
handleOrderSet(day){
 alert(day);
}

 1.2 弹出预约设置窗口并发送ajax请求

  完善handleOrderSet方法,弹出预约设置窗口,用户点击确定按钮则发送ajax请求

                //预约设置
                handleOrderSet(day){
                    //alert(day);//日期对象,当前日期年月日
                    //elementUI中的MessageBox弹框组件
                    //inputPattern正则表达式,[0-9]*[1-9][0-9]*匹配正整数
                    this.$prompt('请输入可预约人数','预约设置',{
                        confirmButtonText:'确定',
                        cancelButtonText:'取消',
                        inputPattern:/^[0-9]*[1-9][0-9]*$/,
                        inputErrorMessage:'只能输入正整数'
                    }).then(({value})=>{
                        //修改人数参数
                        var param = {
                            //2024-09-16
                            orderDate: this.formatDate(day.getFullYear(),day.getMonth()+1,day.getDate()),
                            number: value
                        };
                        //发送ajax请求,根据日期修改可预约人数
                        axios.post("/ordersetting/editNumberByDate.do",param).then((res)=>{
                            if(res.data.flag){
                                //修改后刷新数据
                                this.initData(this.formatDate(day.getFullYear(),day.getMonth()+1,1));
                                this.$message({
                                    message: res.data.message,
                                    type: "success"
                                });
                            }else{
                                this.$message.error(res.data.message);
                            }
                        });
                    }).catch(()=>{
                        this.$message({
                            message:'已取消',
                            type:"info"
                        });
                    });
                }

2. 后台代码

 2.1 Controller

  在OrderSettingController中提供方法editNumberByDate

    //根据指定日期修改可预约人数
    @RequestMapping("/editNumberByDate")
    public Result editNumberByDate(@RequestBody OrderSetting orderSetting){
        try{
            orderSettingService.editNumberByDate(orderSetting);
        }catch (Exception e){
            e.printStackTrace();
            return new Result(false, MessageConstant.ORDERSETTING_FAIL);
        }
        return new Result(true,MessageConstant.ORDERSETTING_SUCCESS);
    }

 2.2 服务接口

  在OrderSettingService服务接口中提供方法editNumberByDate

 public void editNumberByDate(OrderSetting orderSetting);

 2.3 服务实现类

  在OrderSettingServiceImpl服务实现类中实现editNumberByDate

    @Override
    public void editNumberByDate(OrderSetting orderSetting) {
        long count = orderSettingDao.findCountByOrderDate(orderSetting.getOrderDate());
        if(count>0){
            //当前日期已经进行了预约设置,进行修改操作
            orderSettingDao.editNumberByOrderDate(orderSetting);
        }else{
            //没有预约设置,进行添加操作
            orderSettingDao.add(orderSetting);
        }

 2.4 Dao接口

  在OrderSettingDao接口中提供方法

    public long findCountByOrderDate(Date orderDate);
    public void editNumberByOrderDate(OrderSetting orderSetting);

 2.5 Mapper映射文件

  在OrderSettingDao.xml映射文件中提供SQL

    <!--根据日期更新预约人数,#{orderDate}传入的是字符串
    -->
    <update id="editNumberByOrderDate" parameterType="com.itheima.pojo.OrderSetting">
        update t_ordersetting set number = #{number} where orderDate = #{orderDate}
    </update>

    <!--根据预约日期查询-->
    <select id="findCountByOrderDate" parameterType="date" resultType="long">
        select count(*) from t_ordersetting where orderDate = #{orderDate}
    </select>

 

posted on 2025-11-27 20:54  花溪月影  阅读(1)  评论(0)    收藏  举报