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

二、批量导入预约设置信息
预约设置信息对应的数据表为t_ordersetting,预约设置操作对应的页面为ordersetting.html
t_ordersetting表结构:

批量导入预约设置信息操作过程:
1、点击模板下载按钮下载Excel模板文件
2、将预约设置信息录入到模板文件中
3、点击上传文件按钮将录入完信息的模板文件上传到服务器
4、通过POI读取上传文件的数据并保存到数据库
1. 完善页面
1.1 提供模板文件
资料中已经提供了Excel模板文件ordersetting_template.xlsx,将文件放在health_backend工程的 template目录

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; } }
在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>
三、日历展示预约设置信息
前面已经完成了预约设置功能,现在就需要通过日历的方式展示出来每天设置的预约人数。
在页面中已经完成了日历的动态展示,我们只需要查询当前月份的预约设置信息并展示到日历中即可, 同时在日历中还需要展示已经预约的人数,效果如下:

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>
四、基于日历实现预约设置
为通过点击日历中的设置按钮来设置对应日期的可预约人数。效果如下:

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>
浙公网安备 33010602011771号