资讯详情

资讯详情

C#酒店客房管理系统课程设计:源码解析与SQL Server数据库实战

简介基于C#的酒店客房管理系统课程设计完整包面向高校计算机相关专业学生与C#入门开发者。系统实现登录验证、用户管理、房间类型与房间管理、物品管理、预订查询、入住登记、退房结算、员工设置等完整流程采用C#与SQL Server 2005协同开发结构清晰适合课程设计或毕业设计参考。压缩包共369个文件核心为cs源码与resx界面资源配合xsd数据集定义、mdf/ldf数据库文件、sln解决方案及doc设计报告包体大小仅3.08MB轻量易得。已有36人学习浏览。内含可编译工程与数据库脚本既可对照报告梳理业务逻辑也能直接运行演示模块划分有助于二次开发是学习WinForm分层编程与酒店管理信息系统的实用素材。1. C# 酒店客房管理系统课程设计这套源码能复现到什么程度不少朋友下载 C# 酒店客房管理系统源码是为了课程设计答辩或者快速补一个能跑的 Demo。这套基于 C# SQL Server 2005 的客房管理系统功能覆盖了登录、用户添加、房间与房间类型管理、物品管理、预订查询、客户入住、出租方式管理、退房以及员工设置典型的中小规模 WinForms 课设项目。拿到手之后最关心的通常是三件事数据库能不能挂上、界面能不能跑起来、核心业务逻辑能不能讲清楚。这篇文章会从项目结构、数据层设计、入住退房流程到常见坑位逐层拆解帮你把源码变成真正能讲解、能演示的东西。2. 读懂项目结构从文件清单反推架构2.1 文件清单里藏着的信息解压后你会看到宾馆管理.csproj、app.config、宾馆管理DataSet17.Designer.cs、宾馆管理DataSet18.Designer.cs等文件。csproj是 Visual Studio 的项目文件双击就能用 VS 打开app.config是配置文件连接字符串就写在里面而DataSet17、DataSet18这两个强类型数据集文件暴露了系统的数据访问方式——不是手写SqlConnection到处 new而是通过 Visual Studio 的 DataSet 设计器生成强类型数据集和 TableAdapter。为什么课程设计普遍这么做因为强类型 DataSet 有智能提示字段名写错会在编译期报错而不是运行期抛异常答辩时打开DataSet.Designer.cs能直接看到每张表的映射关系比贴一堆 SQL 字符串好看得多。代价是项目结构里会有大量自动生成的代码不建议手改改错了整个数据层都可能崩。connectionStrings add name宾馆管理.Properties.Settings.宾馆管理ConnectionString connectionStringData Source.;Initial CatalogHotelDB;Integrated SecurityTrue providerNameSystem.Data.SqlClient / /connectionStrings上面是app.config里典型的连接字符串配置。Data Source.指向本机默认数据库实例Initial Catalog是数据库名称Integrated SecurityTrue表示用 Windows 身份验证登录 SQL Server。如果你的机器装的是命名实例比如SQLEXPRESS需要把Data Source改成.\SQLEXPRESS否则运行时会报“建立到服务器的连接时发生错误”。2.2 数据库核心表设计与关系系统功能点对应到数据库层面至少需要这几张表房间表Room、房间类型表RoomType、客人表Guest、入住记录表CheckIn、预订记录表Reservation、物品表Item、用户表Users、员工表Employee。其中房间表和房间类型表通过类型编号关联入住记录表通过房间号与房间表关联、通过客人编号与客人表关联。CREATE TABLE RoomType ( TypeId INT PRIMARY KEY IDENTITY(1,1), TypeName NVARCHAR(50) NOT NULL, Price DECIMAL(10,2) NOT NULL, Description NVARCHAR(200) ); CREATE TABLE Room ( RoomId INT PRIMARY KEY, RoomNo NVARCHAR(20) NOT NULL, TypeId INT FOREIGN KEY REFERENCES RoomType(TypeId), Status INT DEFAULT 0, Remark NVARCHAR(200) ); CREATE TABLE CheckIn ( CheckInId INT PRIMARY KEY IDENTITY(1,1), RoomId INT FOREIGN KEY REFERENCES Room(RoomId), GuestName NVARCHAR(50) NOT NULL, IdCard NVARCHAR(18), CheckInDate DATETIME DEFAULT GETDATE(), CheckOutDate DATETIME, TotalAmount DECIMAL(10,2) );Room.Status字段建议用整数状态机0 空闲、1 已入住、2 脏房待打扫、3 维修中。这样在 C# 代码里就是一个switch或enum比存字符串好维护。入住时把房间状态从 0 改成 1退房时改成 2待清洁。这个设计在课程设计答辩里属于加分项因为它在数据层面把业务流程落下来了而不是只在界面上做个样子。2.3 用强类型 DataSet 还是手写 SQL打开DataSet17.Designer.cs能看到 TableAdapter 自动生成的查询方法比如GetDataByStatus(int status)这对应房间状态查询InsertCheckIn(...)对应入住记录的插入。强类型数据集的好处是 UI 层直接this.checkInTableAdapter.Fill(this.hotelDBDataSet.CheckIn)就能把数据灌进DataGridView。但也有一个实际的坑如果数据库里改了表结构比如加了字段VS 里必须右键数据集设计器选择“配置”重新执行向导否则运行时会报“列名无效”。最常见的翻车现场是机房电脑装的 SQL Server 版本和你本机不一致附加数据库后字段类型对不上填表时直接抛强类型数据集校验异常。我的习惯是拿到源码后先不急着看窗体代码而是先打开数据集设计器确认每张表的查询语句再去看界面。3. 登录、用户与员工管理权限模块的通用写法3.1 登录窗体判断逻辑登录界面的实现思路基本一致接收TextBox输入的用户名和密码拼 SQL 去用户表查询返回行数大于 0 就放行否则提示“用户名或密码错误”。课程设计源码里很多是字符串拼接但建议换成参数化查询在答辩时能主动说出“这里用了参数化查询防止 SQL 注入”属于亮点回答。private void btnLogin_Click(object sender, EventArgs e) { string username txtUsername.Text.Trim(); string password txtPassword.Text.Trim(); if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password)) { MessageBox.Show(用户名和密码不能为空); return; } string connStr ConfigurationManager.ConnectionStrings[HotelDB].ConnectionString; string sql SELECT COUNT(*) FROM Users WHERE Usernameu AND Passwordp; using (SqlConnection conn new SqlConnection(connStr)) using (SqlCommand cmd new SqlCommand(sql, conn)) { cmd.Parameters.AddWithValue(u, username); cmd.Parameters.AddWithValue(p, password); conn.Open(); int count (int)cmd.ExecuteScalar(); if (count 0) { this.DialogResult DialogResult.OK; this.Close(); } else { MessageBox.Show(用户名或密码错误); } } }几个关键点ExecuteScalar()返回首行首列正是COUNT(*)的结果AddWithValue是参数化查询最常见的写法注意前后不能加单引号否则参数化就失去意义了。这段代码在课设里基本可以原样照搬但建议改成直接调用数据集里的UsersTableAdapter风格更统一。3.2 用户添加与员工设置用户添加功能通常放在管理员菜单里需要做的操作是检查用户名是否重复、密码是否为空、确认密码是否一致然后INSERT INTO Users。源码里密码大多是明文存储这里可以做一个很轻量的升级用MD5哈希后存储。private string GetMd5Hash(string input) { using (MD5 md5 MD5.Create()) { byte[] bytes md5.ComputeHash(Encoding.UTF8.GetBytes(input)); StringBuilder sb new StringBuilder(); foreach (byte b in bytes) { sb.Append(b.ToString(x2)); } return sb.ToString(); } } private void btnAddUser_Click(object sender, EventArgs e) { string username txtNewUser.Text.Trim(); string password txtNewPassword.Text.Trim(); string confirm txtConfirmPassword.Text.Trim(); if (password ! confirm) { MessageBox.Show(两次密码不一致); return; } // 检查用户名是否已存在 string connStr ConfigurationManager.ConnectionStrings[HotelDB].ConnectionString; string checkSql SELECT COUNT(*) FROM Users WHERE Usernameu; // 这里省略了连接代码逻辑与上面登录一致 // 插入密码存哈希值 string insertSql INSERT INTO Users(Username, Password, RoleId) VALUES(u, p, r); // 执行时 p 传入 GetMd5Hash(password) }MD5 在真正安全要求高的场景里已经被判了死刑但作为课程设计里展示“不是明文存密码”的意识足够了。员工设置功能本质上是同一个表的增删改查只是多加了一个角色字段比如RoleId1管理员、RoleId2前台。源码里如果员工设置是独立窗体大概率是同一套模板复制出来的重点看它有没有做权限判断——有些课设源码里普通用户也能进员工设置界面这是逻辑漏洞答辩时被问到容易卡壳。4. 入住与退房整个系统的核心业务闭环4.1 房间状态与出租方式入住操作的前提是理解出租方式管理。出租方式通常指全日租、钟点房、长包房等每种方式对应不同的计费规则。系统里通常单独维护一张RentType表字段包括RentTypeId、TypeName、PriceRate比如钟点房按小时计费、全日租按天计费。房间类型表里的Price是基础价格出租方式的PriceRate是倍率实际费用 房间价格 × 倍率 × 时长。这个设计在数据流上很顺入住时选择房间号 → 系统自动带出房间类型价格 → 再选择出租方式 → 前台输入预计入住天数 → 系统计算预收款。源码里如果出租方式编辑是独立完成的模块说明作者在刻意区分“房间本身的价格”和“售卖方式的价格策略”这在答辩时可以主动提一句逻辑分层。4.2 入住流程一个事务里完成多表写入入住操作涉及三件事往入住记录表插一条数据、把房间状态改成已入住、把客人信息写入客人表。这三件事不能分开执行否则会出现“记录写了但房间状态没变”的不一致情况。正确做法是放在一个 SQL 事务里。private bool CheckIn(int roomId, string guestName, string idCard, int rentTypeId, int days, decimal prepay) { string connStr ConfigurationManager.ConnectionStrings[HotelDB].ConnectionString; using (SqlConnection conn new SqlConnection(connStr)) { conn.Open(); SqlTransaction transaction conn.BeginTransaction(); try { // 1. 查询房间类型价格和出租方式倍率 string queryPrice SELECT rt.Price * rent.PriceRate FROM Room r JOIN RoomType rt ON r.TypeId rt.TypeId CROSS JOIN RentType rent WHERE r.RoomIdroomId AND rent.RentTypeIdrentTypeId; SqlCommand cmdPrice new SqlCommand(queryPrice, conn, transaction); cmdPrice.Parameters.AddWithValue(roomId, roomId); cmdPrice.Parameters.AddWithValue(rentTypeId, rentTypeId); decimal unitPrice (decimal)cmdPrice.ExecuteScalar(); // 2. 插入入住记录 string insertCheckIn INSERT INTO CheckIn(RoomId, GuestName, IdCard, CheckInDate, RentTypeId, TotalAmount, Prepay) VALUES(roomId, guestName, idCard, GETDATE(), rentTypeId, totalAmount, prepay); SqlCommand cmdInsert new SqlCommand(insertCheckIn, conn, transaction); cmdInsert.Parameters.AddWithValue(roomId, roomId); cmdInsert.Parameters.AddWithValue(guestName, guestName); cmdInsert.Parameters.AddWithValue(idCard, idCard); cmdInsert.Parameters.AddWithValue(rentTypeId, rentTypeId); cmdInsert.Parameters.AddWithValue(totalAmount, unitPrice * days); cmdInsert.Parameters.AddWithValue(prepay, prepay); cmdInsert.ExecuteNonQuery(); // 3. 更新房间状态为已入住 string updateRoom UPDATE Room SET Status1 WHERE RoomIdroomId; SqlCommand cmdUpdate new SqlCommand(updateRoom, conn, transaction); cmdUpdate.Parameters.AddWithValue(roomId, roomId); cmdUpdate.ExecuteNonQuery(); transaction.Commit(); return true; } catch { transaction.Rollback(); return false; } } }这段代码回答了一个很多初学者问的问题为什么需要事务因为入住这个动作一旦中途出错比如插入记录成功但更新状态失败数据就不一致了。BeginTransaction()之后所有SqlCommand都要带上同一个transaction对象提交时用transaction.Commit()出错时Rollback()。注意最后using块会自动释放连接所以不需要手动conn.Close()。4.3 退房流程与费用结算退房是入住的逆向操作从入住记录表里找到未退房的记录 → 计算实际费用 → 更新入住记录的退房时间和实收金额 → 把房间状态改为待清洁。计算费用时注意一个细节如果入住时收了预收款退房时要算出应退差价。private decimal CheckOut(int checkInId, int roomId, int actualDays) { string connStr ConfigurationManager.ConnectionStrings[HotelDB].ConnectionString; decimal refundAmount 0; using (SqlConnection conn new SqlConnection(connStr)) { conn.Open(); SqlTransaction transaction conn.BeginTransaction(); try { // 查询入住记录的预付金额和单价 string query SELECT Prepay, TotalAmount / DATEDIFF(DAY, CheckInDate, GETDATE()) FROM CheckIn WHERE CheckInIdid; SqlCommand cmdQuery new SqlCommand(query, conn, transaction); cmdQuery.Parameters.AddWithValue(id, checkInId); SqlDataReader reader cmdQuery.ExecuteReader(); decimal prepay 0; decimal unitPrice 0; if (reader.Read()) { prepay (decimal)reader[0]; unitPrice (decimal)reader[1]; } reader.Close(); decimal actualAmount unitPrice * actualDays; refundAmount prepay - actualAmount; // 更新入住记录 string updateCheckIn UPDATE CheckIn SET CheckOutDateGETDATE(), ActualAmountamount WHERE CheckInIdid; SqlCommand cmdUpdate new SqlCommand(updateCheckIn, conn, transaction); cmdUpdate.Parameters.AddWithValue(amount, actualAmount); cmdUpdate.Parameters.AddWithValue(id, checkInId); cmdUpdate.ExecuteNonQuery(); // 房间状态改为待清洁 string updateRoom UPDATE Room SET Status2 WHERE RoomIdroomId; SqlCommand cmdRoom new SqlCommand(updateRoom, conn, transaction); cmdRoom.Parameters.AddWithValue(roomId, roomId); cmdRoom.ExecuteNonQuery(); transaction.Commit(); } catch { transaction.Rollback(); } } return refundAmount; }这里用DATEDIFF(DAY, CheckInDate, GETDATE())反推实际住的天数是最粗粒度的做法存在跨天时间边界问题。实际课设里可以接受但你应该心里有数如果客人是凌晨 1 点入住、当天中午退房这个算法会算出 0 天。更合理的做法是在界面上让操作员手动选择退房日期或者按小时计算分段收费。源码里多数采用手动输入天数的方案这也不算错反而在答辩时你可以主动说出这个边界情况展示思考深度。5. 避坑手册SQL Server 2005 环境下的常见问题5.1 附加数据库失败提示“版本高于当前实例”现象附加.mdf文件时SQL Server Management Studio 报错“数据库版本高于当前实例支持的版本”。原因源码里的.mdf是用高版本 SQL Server如 2012 或 2016生成的而你的机器上装的是 SQL Server 2005 Express低版本实例无法直接加载高版本数据库文件。解决不要试图降级数据库文件正确做法是在高版本 SQL Server 上附加数据库后用“生成脚本”功能把表结构和数据导出成.sql文件再到 2005 实例里执行脚本重建库。实际操作中更省事的方案是装一个 SQL Server 2019 Developer 版直接在 2019 上附加然后检查连接字符串指向的实例名是否匹配。作为课程设计环境装新版不丢人运行效果反而更稳定。5.2 连接字符串实例名不匹配现象程序一启动就报“建立到服务器的连接时发生错误”或者“在建立与服务器的连接时出错”。原因app.config里的Data Source.指向默认实例但你的机器上 SQL Server 是命名实例常见的是.\SQLEXPRESS两者对不上。机房电脑尤其容易出现——很多课件里让学生装 Express 版实例名默认带SQLEXPRESS后缀。解决打开app.config把连接字符串的Data Source改成你的实际实例名。不知道怎么查实例名的话打开 SQL Server Management Studio 登录窗口服务器名称下拉框里显示的内容就是你要填的。如果是本机默认实例且安装了最新版直接写Data Sourcelocalhost也行。5.3 “文件正在使用中”无法附加或还原数据库现象附加数据库时提示“无法打开物理文件拒绝访问”或“文件被占用”重启 SQL Server 服务也不行。原因.mdf文件被另一个 SQL Server 实例进程占用或者文件权限不足。常见场景是你先前用管理工具附加过一次关闭工具后进程没释放锁。解决先用如下命令确认数据库状态然后强制离线再附加EXEC sp_detach_db HotelDB;然后复制.mdf和.ldf到你自己有完全控制权的目录比如C:\Data\右键设置文件安全权限给 SQL Server 服务账户读取和写入权限再重新附加。这个坑非常玄学有时候明明路径没问题就是附加不上十有八九是权限问题。代码里也可以预建数据库直接从CREATE DATABASE脚本起步绕过附加过程。5.4 DataGridView 显示数据后修改不生效现象程序运行后能正常显示房间列表但直接在DataGridView单元格里改数据、点保存按钮后数据库里没有变化。原因很多课设源码里的 DataGridView 采用只读绑定或者保存按钮只调用了dataGridView.Update()而没有调用 TableAdapter 的Update方法。Update()方法只是刷新界面显示不会把改动写回数据库。真正写回需要调用生成的 TableAdapter 更新方法并且确保数据集里每张表有主键。解决不要试图在 UI 事件里逐个单元格写更新 SQL直接使用设计器生成的绑定方法private void btnSaveRoom_Click(object sender, EventArgs e) { this.roomTableAdapter.Update(this.hotelDBDataSet.Room); MessageBox.Show(保存成功); }使用前提Room表必须定义了主键否则 TableAdapter 无法生成UPDATE语句保存时会报“对于不返回任何键列信息的 SELECT 语句不支持动态 SQL 生成”。这是把 DataSet 拖进设计器时没有正确配置主键导致的需要回到数据源管理器里检查表定义。5.5 查询时当天入住记录查不到现象预订查询里输入当天日期查询结果为空但数据库里明明有数据。原因日期比较时直接用了CheckInDate date而数据库里CheckInDate是DATETIME类型包含了时分秒。前端传入的时间通常是“2024-06-01 00:00:00”实际存储的是“2024-06-01 14:23:11”等值比较永远查不出来。解决查询条件改成日期范围或者用CONVERT把字段转成短日期格式再比较。推荐用范围写法可以走索引SELECT * FROM CheckIn WHERE CheckInDate start AND CheckInDate DATEADD(DAY, 1, end);这个坑在入住、退房、记录查询里几乎都会出现血泪经验是凡是涉及日期查询的代码都默认按范围查询来写不要用等号。源码里如果原本用的是等号建议全部替换掉。6. 进阶落地把系统迁到新版 SQL Server 并写一份能演示的测试脚本拿到了这套源码光让它跑起来不算本事需要展示出“我真正调试过、验证过”的痕迹。最推荐的操作是完成两件事把数据库从 SQL Server 2005 迁移到 Express 或 Developer 最新版以及写一段自动化测试脚本验证核心流程。因为新机器上装 2005 往往不兼容 Windows 10/11而且新版 Management Studio 对初学者友好得多答辩演示时不容易出幺蛾子。先做数据库迁移。步骤很简单在高版本 SQL Server 上附加数据库文件右键数据库选择“任务 → 生成脚本”勾选“包含数据”生成一个完整的.sql文件然后在目标实例里执行这个脚本。执行成功后打开app.config把连接字符串改成目标实例名。到这里数据库环境就已经换好了不需要改任何一行 C# 代码——因为数据访问层走的是 TableAdapter 生成的标准SqlClient协议层面完全兼容。接着写一段简单的验证脚本用Console项目或直接在 SQL Server 里跑一段多步骤 SQL模拟“添加用户 → 创建房型 → 入住 → 退房 → 查询记录”的完整链路BEGIN TRAN; INSERT INTO RoomType(TypeName, Price) VALUES (标准间, 268.00); DECLARE typeId INT SCOPE_IDENTITY(); INSERT INTO Room(RoomId, RoomNo, TypeId, Status) VALUES (1, 1001, typeId, 0); INSERT INTO CheckIn(RoomId, GuestName, IdCard, CheckInDate, RentTypeId, TotalAmount, Prepay) VALUES (1, N张三, 110101199001011234, GETDATE(), 1, 536.00, 600.00); UPDATE Room SET Status 1 WHERE RoomId 1; SELECT * FROM CheckIn WHERE GuestName N张三; ROLLBACK;包在ROLLBACK里是为了让测试数据不污染正式库反复执行也不会产生垃圾记录。这段脚本的价值是你可以很清楚地展示“入住时房间状态从 0 变成 1退房时从 1 变成 2”的状态流转过程。在答辩现场用数据来证明程序逻辑比口头描述更有说服力。最后说一个我自己踩过的坑。以前拿到类似的课设源码第一反应是打开 Visual Studio 直接按 F5结果报错一脸懵。后来学乖了拿到任何源码包第一步永远是先打开app.config确认数据库连接第二步打开数据表设计器检查主键与关系第三步跑一遍数据库脚本最后才运行程序。从那以后我每次接手别人的 C# 项目都强制走一遍这个流程五分钟内就能判断这套源码值不值得深入看。希望帮到你。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →