大二C#实现酒店管理系统(C端展示、前台操作、登记入住、入住管理、职位管理、公告系统等)
前言项目技术介绍
1、开发工具:VS2022
2、数据库:SqlServer
3、前端+请求:HTML5+jQuery
4、第三方框架:Layui、视图可视化插件
项目声明:
该项目是大二老师布置的一个作业,我是根据老师给定的项目内容定向到酒店,从而在网上查找酒店所就有的基本的功能结合自己的想法来完成的,下面是我的一些项目展示,如果需要源码的可以在我的博客资源中下载(带项目技术指导,欢迎问答)
项目总体脑图介绍
1、C端展示
页面分析:
总体页面采用简介的布局方式,突出房间的可预订信息和房间价格,在右上角有一个头像信息,点击用户可以对自己的账号信息进行一个更改和房间预定的订单记录
在线预订展示
具体流程思路分析:
当用户第一次进入官网可预订房间是不需要登录的,当点击订单提交,先进行一个登录登录,如果有登录就会根据填写的预订信息进行房间的预订,如果没有登录会先跳转到登录页面完成C端登录才可以进行一个房间预订
部分代码展示
#region 1.2、用户登录逻辑 /// <summary> /// 1.2、用户登录逻辑 /// </summary> /// <param name="user">接收登录数据的视图</param> /// <returns></returns> public ActionResult UserEntrance(S_User user) { ReturnJson msg = new ReturnJson(); try { // 判断登录的信息身是否为空或不存在 if (!string.IsNullOrEmpty(user.Telephone) && !string.IsNullOrEmpty(user.UserPaw)) { try { // 将页面传递进来的数据进行数据库查询 S_User listUser = myModels.S_User.Where(o => o.Telephone == user.Telephone.Trim()).Single(); // 根据查询出来的数据进行登录 if (listUser.UserPaw == user.UserPaw) { // 判断账号的状态信息 if (listUser.UserStatus == true) { msg.State = true; msg.Text = "succeed"; // 保存数据到Session中 Session.Timeout = 24 * 60; Session["UserID"] = listUser.UserID; Session["UserName"] = listUser.UserName; } else { msg.Text = "您的账号状态信息有误,可能已封号"; } } else { msg.Text = "您输入的密码有误,请检查"; } } catch (Exception e) { Console.WriteLine(e); msg.Text = "该手机号还未注册,请注册"; } } else { msg.Text = "请输入完整的登录信息"; } } catch (Exception) { msg.Text = "登录数据异常"; } return Json(msg, JsonRequestBehavior.AllowGet); } #endregion
2、B端管理页面
B端登录分析:
带有科技感的背景,主要登录位置居中显示,通过员工的唯一员工号和密码进行一个B端操作的登录,验证码是防止非人机登录,区分大小写
基础代码演示
#region 2.1、员工登录逻辑 public ActionResult StaffRedirect(S_Staff staff) { ReturnJson msg = new ReturnJson(); // 事务开启 using (TransactionScope scope = new TransactionScope()) { // 判断登录的数据是否为空 if (!string.IsNullOrEmpty(staff.JobCode) && staff.JobCode.Length == 8 && !string.IsNullOrEmpty(staff.StaffPaw)) { try { // 根据工号查询员工信息 S_Staff listStaff = myModels.S_Staff.Where(m => m.JobCode == staff.JobCode).FirstOrDefault(); // 判断账号是否正确 if (listStaff == null) { msg.Text = "工号信息有误,请检查"; } else { // 判断员工的密码与状态是否正确 if (listStaff.StaffPaw == staff.StaffPaw) { if (listStaff.StaffStatus == true) { #region 考勤打卡 // 登录成功 msg.State = true; msg.Text = "succeed"; ViewBag.StaffName = listStaff.StaffName; // 保存到session Session.Timeout = 4 * 60; Session["StaffID"] = listStaff.StaffId; Session["StaffName"] = listStaff.StaffName; Session["StaffJobCode"] = listStaff.JobCode; Session["logInTime"] = DateTime.Now; } else { msg.Text = "该工号处于禁用状态,请联系管理员"; } } else { msg.Text = "请输入正确的登录密码"; } } } catch (Exception e) { Console.WriteLine(e); return Json("没有查询到该工号信息,请重新检查", JsonRequestBehavior.AllowGet); } } else { msg.Text = "请输入8位工号或密码"; } } return Json(msg, JsonRequestBehavior.AllowGet); } #endregion
3、B端导航首页展示
页面分析:
页面简介为主,突出每一个菜单的选项,中间是超一个导航位置,左侧是一级菜单,下面还可以分为二级菜单选项
又上角有一个登录信息和时间,B端员工可以修改自己的账号等信息
4、B端前台操作展示
页面分析:
页面主题以整洁为主,突出房间重点信息,右侧有快速看板,能一眼看出当前的登录信息和房间信息等,每一个房间的状态信息都通过不同的颜色来代表
页面布局代码
基于时间紧迫右边今日入住并没有完成实现,只写上一个静态
<div class="row p-3"> <!--中间显示区域--> <section class="col-10"> <!--顶部的模糊搜索--> <div class="layui-form-item"> <form class="layui-form"> <!--房间类型下拉框--> <div class="layui-inline" style="width: 150px;"> <select name="city" lay-filter="type" id="type"> <option value="">房间类型</option> </select> </div> <!--房间状态--> <div class="layui-inline" style="width: 150px;"> <select name="city" lay-filter="status"> <option value="">房间状态</option> <option value="1">可使用</option> <option value="2">已入住</option> <option value="3">待维修</option> <option value="5">待打扫卫生</option> </select> </div> <!--所属栋数下拉框--> <div class="layui-inline" style="width: 150px;"> <select name="city" lay-filter="HouseNumber" id="HouseNumber"> <option value="">选择栋数</option> </select> </div> <!--房间楼层下拉框--> <div class="layui-inline" style="width: 150px;"> <select name="city" lay-filter="floor" id="floor"> <option value="">选择楼层</option> </select> </div> </form> </div> <!--第二快捷功能按钮--> <div class="but"> <button type="button" class="btn btn-outline-primary" onclick="upDataIframe(this)" data-url="/Subscribe/Subscribe/SubscribeIndex">预约信息</button> <button type="button" class="btn btn-outline-secondary" onclick="upDataIframe(this)" data-url="/Check_InFor/CheckInMain/Index">入住信息</button> <button type="button" class="btn btn-outline-success" onclick="upDataIframe(this)" data-url="/Maintain/MaintenanceReport/Index">维修上报</button> <button type="button" class="btn btn-outline-danger" onclick="upDataIframe(this)" data-url="/CeaningRoom/Cleaning/Index">卫生打扫</button> <button type="button" class="btn btn-outline-warning" onclick="upDataIframe(this)" data-url="/Charts/ChartsData/Index">日常报表</button> <button type="button" class="btn btn-outline-info" onclick="upDataIframe(this)" data-url="">夜间查询</button> <button type="button" class="btn btn-outline-dark" onclick="LogProcessing()" data-url="/PositionManagement/PositionManagementMain/LogProcessingView">交班处理</button> <button class="layui-btn layui-btn-sm" lay-event="returnData" id="returnData" onclick="location.replace(location);" hidden>返回</button> </div> <!--中间显示房间信息--> <div class="cards mt-4" id="cards" style="width: 100%;"> @*<div class="card-body"> <h3 class="card-titles">101</h3> <div style="padding: 6px;"> <h4>单人间</h4> <p class="card-text">¥:88</p> <p>A栋 1楼</p> </div> </div>*@ </div> <!--分页器--> <div id="demo20"></div> </section> <!--右侧的操作也页--> <section class="col-2"> <div class="layui-card"> <div class="layui-card-header">可视面板<span></span></div> <div class="layui-card-body"> <!--操作员信息--> <div class="operator"> <p>操作员:@ViewBag.StaffName</p> <p>ID:@ViewBag.JobCode</p> <p>职位:@ViewBag.PositionName</p> </div> <!--默认分割线--> <hr> <!--页面颜色信息--> <div class="colors"> <p><i></i> 空房 <span>@ViewBag.vacantRoomCount</span></p> <p><i style="background: #ffc107;"></i> 入住 <span>@ViewBag.CheckCount</span></p> <p><i style="background: #6c757d;"></i> 待维修 <span>@ViewBag.maintainCount</span></p> <p><i style="background: #28a745;"></i> 打扫 <span>@ViewBag.sweepCount</span></p> <p><i style="background: #dc3545!important;"></i> 停用 <span>@ViewBag.blockUpCount</span></p> </div> <!--默认分割线--> <hr> <!--房间基础信息--> <div> <p>可入住房型:</p> <p>单人房:@ViewBag.singleRoomCount</p> <p>双人房:@ViewBag.doubleRoomCount</p> <p>豪华单人房:@ViewBag.luxurySingleRoomCount</p> <p>标准房:@ViewBag.normRoomCount</p> <p>双人标准房数量:@ViewBag.luxuryNormRoomCount</p> </div> <!--默认分割线--> <hr> <!--当日营销--> <div> <p style="font-size:16px;margin-bottom:5px;">今天入住:10</p> <div class="layui-collapse"> <div class="layui-colla-item"> <h2 class="layui-colla-title">房费总额:1000</h2> <div class="layui-colla-content">现金:100</div> <div class="layui-colla-content">微信:100</div> <div class="layui-colla-content">支付宝:100</div> </div> <div class="layui-colla-item"> <h2 class="layui-colla-title">押金收入:500</h2> <div class="layui-colla-content">现金:100</div> <div class="layui-colla-content">微信:100</div> <div class="layui-colla-content">支付宝:100</div> </div> <div class="layui-colla-item"> <h2 class="layui-colla-title">退还房费:100</h2> <div class="layui-colla-content">现金:100</div> <div class="layui-colla-content">微信:100</div> <div class="layui-colla-content">支付宝:100</div> </div> </div> </div> </div> </div> </section> </div>
5、B端入住管理
页面和实现思路分析:
1、双击房间可以根据房间状态进行对应的位置跳转页面,比如:空房间双击即可跳转到办理入住页面、打扫状态房间双击会跳转到卫生打扫页面
2、对于入住客户的必要信息是必填的否则不可以办理入住登记(姓名、电话、身份证等)
3、对于押金金额的限定,基于当前房间的价格一半为准
4、
登录入住逻辑代码
#region 六、前台登记入住 /// <summary> /// 六、前台登记入住 /// </summary> /// <param name="Register"></param> /// <returns></returns> public ActionResult CheckRnRegister2(B_Register Register, decimal? RegisterMoney, decimal? SecurityDeposit, decimal? consume, int? modeOfPayments) { ReturnJson msg = new ReturnJson(); TransactionOptions transactionOption = new TransactionOptions(); //设置事务隔离级别 transactionOption.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted; //设置事务超时时间,这里设置为8分钟 transactionOption.Timeout = new TimeSpan(0, 3, 0); //开启事务 using (var scope = new TransactionScope())//需要添加引用 System.Transactions { try { // 校验数据是否符合 if (!string.IsNullOrEmpty(Register.StaffName) && !string.IsNullOrEmpty(Register.Name) && !string.IsNullOrEmpty(Register.IDCard)) { // 校验手机号和身份证 if (IdCardHelper.CheckIdCard(Register.IDCard)) { if (!string.IsNullOrEmpty(Register.NameTel) || Regex.IsMatch(Register.NameTel, "^[1][3-9][0-9]{9}$")) { // 判断是否有入住时间个预离时间 if (Register.LnitialTime != null && Register.FinishTime != null) { // 根据身份证或手机号查询入住信息 List<B_Register> list = myModels.B_Register.Where(p => p.IDCard == Register.IDCard || p.NameTel == Register.NameTel).ToList(); for (int i = 0; i < list.Count(); i++) { if (list[i].StatusId == 2) { msg.Text = "该登记身份证或手机号已被登记入住,还未退房"; return Json(msg, JsonRequestBehavior.AllowGet); } } // 判断预离时间与入住时间是否符合(入住小于预离) if (Register.FinishTime > Register.LnitialTime) { // 根据房间的ID查询房间状态是否为可使用 try { var statu = myModels.B_RoomTable.Where(o => o.RoomId == Register.RoomId).FirstOrDefault(); if (statu.StatusId == 1) { // 判断押金是否符合房价的一半【押金大于房间定价的一半并不大于房价】 if (SecurityDeposit <= statu.ReservationPrice && SecurityDeposit >= (statu.ReservationPrice / 2)) { Register.StatusId = 2; Register.VIPID = 7; //Register.consume = Register.RegisterMoney; // 消费金额 // 办理入住 B_Payments payment = new B_Payments(); payment.PaymentTime = DateTime.Now; // 支付日期 payment.ModeOfPaymentId = modeOfPayments; // 支付方式 payment.RegisterMoney = RegisterMoney; // 房费 payment.SecurityDeposit = SecurityDeposit; // 押金 payment.BeyondTheAmount = 0; // 超时金额 payment.consume = RegisterMoney; // 消费金额 myModels.B_Payments.Add(payment); if (myModels.SaveChanges() > 0) { // 修改入住表的信息 Register.paymentID = payment.paymentID; // 报错入住信息表 myModels.B_Register.Add(Register); if (myModels.SaveChanges() > 0) { // 对房间表的使用状态进行重新赋值 B_RoomTable roomTable = myModels.B_RoomTable.Where(p => p.RoomId == Register.RoomId).Single(); // 对状态信息查询赋值 roomTable.StatusId = 2; // 已入住 myModels.Entry(roomTable).State = System.Data.Entity.EntityState.Modified; if (myModels.SaveChanges() > 0) { msg.State = true; msg.Text = "入住办理成功"; // 指示范围内的所有操作都已成功都完成 scope.Complete(); } else { msg.State = false; msg.Text = "房间状态修改失败"; } } else { msg.Text = "入住办理失败"; } } else { msg.State = false; msg.Text = "入住支付记录表报错失败"; } } else { msg.Text = "押金收取规则:大于或等于房价的一半与不超过房间价"; } } else { msg.Text = "该房间状态查询不可用,请客服更换"; } } catch (Exception e) { msg.Text = "数据异常:" + e.Message; } } else { msg.Text = "入住时间不能大于入住时间"; } } else { msg.Text = "入住时间和预离时间为空"; } } else { msg.Text = "请输入合法的手机号"; } } else { msg.Text = "请输入合法的身份证号"; } } else { msg.Text = "请按要求填写数据,才能办理入住登记"; } } catch (Exception e) { Console.WriteLine(e); } return Json(msg, JsonRequestBehavior.AllowGet); } } #endregion
6、B端入住管理
实现分析:
1、所有的办理入住信息都会在这个页面进行展示
2、当前页面会显示入住客户的主要信息,点击右侧的详情按钮可以查看到入住详细信息,包括入住金额和押金及退房时间等
3、有一个超时退房的时间计算,是客户办理入住的时间开始到第二天的入住办理时间为准(24小时),如果客户超时未退房的消费计算如下(1、超时一小时内不进行押金扣费,超过一小时扣取押金的50%,超过两小时未退房的扣取全部押金信息并强制退房标注)
超时未退房计计费代码如下:
#region 一、入住信息超时逻辑查询(修改入住状态和待付金额) public ActionResult overTime() { //开启事务 using (var scope = new TransactionScope())//需要添加引用 System.Transactions { // 循环遍历获取到所有入住的信息 var list = (from tb in myModels.B_Register where tb.StatusId == 2 || tb.StatusId == 9 || tb.StatusId == 1013 // 在住和超期 select tb).ToList(); // 遍历 try { for (var i = 0; i <= list.Count(); i++) { // -------- 计算入住时间到预里时间再到当前时间的实际入住天数 --------- var li = list[i].LnitialTime; // 入住时间 var FinishTime = list[i].FinishTime; // 预离时间 TimeSpan dayNumber = (TimeSpan)(FinishTime - li); int Day2 = dayNumber.Days; // 入住间隔的天数 // 查询每一条数据的预离时间跟当前时间进行对比 var RegisterId = list[i].RegisterId; // 入住ID // 根据获取到的入住ID查询每一条数据进行修改 B_Register reg = myModels.B_Register.Where(p => p.RegisterId == RegisterId).Single(); B_Payments payments = myModels.B_Payments.Where(p => p.paymentID == reg.paymentID).Single(); var DateNow = DateTime.Now; // 当前时间 // 判断入住时间、预离时间与当前时间是否有超(修改状态信息) TimeSpan differ = (TimeSpan)(DateNow - FinishTime); int Hours = differ.Hours; // 间隔小时 int Minutes = differ.Minutes;// 间隔分钟 if (Hours >= 1 && Hours < 2) { // 超时1小时处理 #region 一、对入住表的数据处理【未涉及金额修改】 // 对入住的状态进行修改 reg.StatusId = 9; // 超时未退房 reg.Timeout = Convert.ToString(Hours + "小时" + Minutes + "分钟"); // 超时天数 // 保存数据 myModels.Entry(reg).State = System.Data.Entity.EntityState.Modified; myModels.SaveChanges(); #endregion // 押金退房时再与超期金额相减扣除 // 超时金额计算【大于2小时直接扣除押金,一个小时扣除押金一半】 payments.BeyondTheAmount = Hours >= 1 ? (payments.SecurityDeposit / 2) : payments.SecurityDeposit; // 修改入住支付表信息 myModels.Entry(payments).State = System.Data.Entity.EntityState.Modified; myModels.SaveChanges(); } else if(Hours >= 2) { // 超时2小时处理 #region 一、对入住表的数据处理【押金抵扣】 // 对入住的状态进行修改 reg.StatusId = 9; // 超时未退房 reg.Timeout = Convert.ToString(Hours + "小时" + Minutes + "分钟"); // 超时天数 // 保存数据 myModels.Entry(reg).State = System.Data.Entity.EntityState.Modified; myModels.SaveChanges(); #endregion // 超时2小时处理 reg.StatusId = 1013; // 预期两小时强行退房 // 修改超期金额 = 押金 payments.BeyondTheAmount = payments.SecurityDeposit; // 保存数据 myModels.Entry(reg).State = System.Data.Entity.EntityState.Modified; myModels.SaveChanges(); // 讲房间设置为需要打扫 } } } catch (Exception e) { Console.WriteLine(e); } //提交事务(设定交易完成) scope.Complete(); return Json("", JsonRequestBehavior.AllowGet); } } #endregion
7、B端职位管理
思路分析:
1、作为一个前后端管理系统,他的权限操作更是重中之重,如果没有权限管理整个系统那么就没有他的存在意义
2、酒店管理系统分有如下几个权限角色,每一个角色拥有不同的功能
3、酒店系统的权限包含了页面的【增删盖查】这些具体的小功能,如下图二
权限赋值
8、日常报表
思路分析:
这是使用一款国内的可视化插件开发的页面,只需要将需要的数据查询出来然后将数据填写上对于的数据展示位置即可直观的分析当前的数据信息
9、B端VIP管理
思路分析:
当前的页面是为C端的用户账号来设计的,根据用户的在项预定的次数(入住成功)的+1,来进行一个VIP登记的提升,每一个VIP登记的会员优惠折扣都是不相同的,而目前的前台页面进行办理入住登记的并能使用VIP所拥有的优惠折扣
10、B端酒店公告系统
思路分析:
一个完整的项目怎么可能没有自己的一个完整的公告系统呢,公告系统对于日常的信息发布尤为重要,当酒店退出了一个新的活动的时候需要第一时间去推广给C端的用户那么公告就会是很好的推荐助手,而且公告也是可以进行一个发布身份的鉴定,区分B端工作人员还是用户进行接收
公告系统的代码如下:
#region 3.2、发布富文本 上传图片文件处理(在插件文件里的config.js中调用方法) /// <summary> /// 3.2、发布富文本 上传图片文件处理 /// </summary> /// <param name="upload">接收上传过来的文件,不能乱改,与上传文件的 name对应</param> /// <returns></returns> /* 文件的处理方式 一般我们会把文件保存到文件夹里面:这里我们在做发布公告的时候会创建几个文件夹,如下: Temp 临时文件夹(上传的文件,在这里临时保存) Notice 最终文件夹: Attachment附件, Image:富文本编辑器图片; NoticeCarousel 轮播图片; Text:富文本编辑器内容 */ public ActionResult UpEditorFile(HttpPostedFileBase upload) { // 封装 实例化 返回数据的状态 Ckeditor4UploadResult ck = new Ckeditor4UploadResult(); // 使用 try 捕获错误 try { // 判断上传的文件是否为空 if (upload != null) { // 1、获取上传文件的扩展名 如:.png string fileExtension = Path.GetExtension(upload.FileName); // 重新拼接文件的名称(上传时间-唯一标识.扩展名) string filename = DateTime.Now.ToString("yyyy-MM-dd") + "-" + Guid.NewGuid() + fileExtension; // 判断临时文件 Temp 是否存在(不存在就创建一个) if (!Directory.Exists(Server.MapPath("~/Document/Temp/"))) { Directory.CreateDirectory(Server.MapPath("~/Document/Temp/")); } // 保存文件的路径 string filePath = Server.MapPath("~/Document/Temp/") + filename; // 判断文件格式不为空 if (fileExtension != null) { // 将扩展名改为小写 fileExtension = fileExtension.ToLower(); // 判断是否具有该格式文件 if ("(.gif)|(.jpg)|(.bmp)|(.jpeg)|(.png)".Contains(fileExtension)) { // 保存已上传的文件 upload.SaveAs(filePath); // 图片的路径 var url = "/Document/Temp/" + filename; ck.uploaded = 1; // 上传成功的状态 ck.fileName = filename; // 重新拼接的文件名 ck.url = url; // 返回文件的路径 } else { ck.error.message = "文件只能上传图片格式"; } } } else { ck.error.message = "上传文件不能为空,请检查再重新上传"; } } catch (Exception e) { Console.WriteLine(e); ck.error.message = "图片上传失败"; } return Json(ck, JsonRequestBehavior.AllowGet); } #endregion