☰
WinForm酒店管理系统源码解析:VB.NET三层架构与数据库设计实战
2026/10/22 9:08:10 网站建设 项目流程

简介:这份资源是面向VB开发者的WinForm宾馆酒店管理系统完整源码,适合新手及有一定经验的开发人员借鉴学习,可用于课程设计、毕业设计或中小型酒店管理类项目的二次开发。压缩包共354个文件,约3.59MB,以124个vb源码文件为核心,配合58个resx与58个resources资源文件、38个ico图标、17个sql脚本及mdf、ldf数据库文件,另有config配置、dll引用、vbproj工程与sln解决方案等,构成可直接编译运行的完整工程。源码含数据库脚本与较完整的代码注释,覆盖客房、入住、结账等酒店业务模块,便于读者梳理WinForm界面布局、事件处理与数据库访问的分层写法,也能参考SQL建表与数据初始化思路。目前已有216人学习下载,适合作为VB桌面应用入门与实战参考的练手项目。

1. 从一份 WinForm 宾馆酒店管理系统源码说起:VB 老栈为什么还有人要

你可能在找一份能直接跑起来的 WinForm 宾馆酒店管理系统源码,要求里写得很清楚:VB 开发语言、带数据库脚本。这不是猎奇,而是很现实的需求——很多中小型宾馆、招待所、民宿的前台系统,至今仍跑在 Windows 桌面端,用 VB.NET 加 SQL Server 的组合,稳定、好维护、招人成本低。我见过不少做外包的朋友,接到的单子就是“给我一套能改的酒店管理源码,我要加个会员折扣和钟点房计费”。

这套东西解决的核心问题就三件:房态管理、入住退房结算、账目可查。适合谁?一是接私活需要快速交付的开发者,二是想学 WinForm 三层架构的在校学生,三是酒店内部想自己维护系统的 IT 人员。但我要先泼一盆冷水:网上流传的所谓“完整源码”,十份里有八份跑不起来,缺表、缺存储过程、连接字符串写死、报表控件没授权。所以这篇不吹源码多完美,而是把这类系统的结构、数据库设计、关键代码和踩坑点拆开讲,让你拿到任何一份类似源码都能自己救活它。

2. 先看懂结构再动手:WinForm 酒店系统的三层骨架与数据库表设计

拿到一份 VB.NET 的 WinForm 酒店系统,别急着按 F5。先花二十分钟把解决方案资源管理器展开,看清楚它是不是标准的三层结构。绝大多数能用的源码都遵循 UI 层、业务逻辑层、数据访问层的划分,只是命名可能叫Hotel.UI、Hotel.BLL、Hotel.DAL,也可能土一点叫frmMain、Class1、Module1。结构看不懂,后面改一个字段能把你改崩溃。

2.1 三层结构在 VB.NET 里的典型落地方式

UI 层就是一堆.vb窗体文件,frmLogin.vb、frmRoomManage.vb、frmCheckIn.vb这类。业务层通常是一个个类,比如RoomManager.vb里放CheckIn()、CheckOut()方法。数据访问层最常见的是用SqlHelper类封装ExecuteNonQuery、ExecuteDataTable,也有直接用TableAdapter拖控件的。我一般会先找App.config里的连接字符串,把它改成自己本机的实例名,这是让项目活过来的第一步。

<!-- App.config 里典型连接字符串,先改 Data Source 和 Initial Catalog --> <connectionStrings> <add name="HotelConn" connectionString="Data Source=.;Initial Catalog=HotelDB;User ID=sa;Password=123456" providerName="System.Data.SqlClient"/> </connectionStrings>

这段配置里Data Source=.表示本机默认实例,如果你装的是 Express 版就得写成.\SQLEXPRESS。Initial Catalog是数据库名,必须和你待会儿执行的脚本建出来的库名一致。很多源码跑不起来就是这里没改,报“无法打开登录所请求的数据库”。改完连接字符串,再去 SQL Server 里把脚本跑一遍,顺序不能反。

2.2 数据库脚本里必须有的六张核心表

一份能用的酒店系统数据库脚本,不管字段名怎么变,这六张表跑不掉:房间信息表、房型价格表、客人信息表、入住登记表、消费明细表、操作员表。我拿最常见的命名给你列一下,你对照手里的脚本看缺哪张。

表名(常见命名)作用关键字段
Room / 房间表记录每个房间的物理状态RoomID, RoomNo, TypeID, Status
RoomType / 房型表定义标间、大床、钟点房价格TypeID, TypeName, Price, HourPrice
Guest / 客人表存身份证、姓名、联系方式GuestID, IDCard, Name, Phone
CheckIn / 入住表一次入住一条记录CheckInID, RoomID, GuestID, InTime, OutTime, Deposit
Consume / 消费表房间内商品、赔偿等ConsumeID, CheckInID, ItemName, Amount
Operator / 操作员表登录账号与权限OpID, OpName, Password, Role

房态字段Status是整张表的灵魂,一般用 0 表示空闲、1 表示已入住、2 表示维修、3 表示预订。你改代码前先确认这套编码,不然后面写查询条件全是错的。房型表里Price和HourPrice分开存,是因为钟点房和全天房计费逻辑不同,很多新手在这里翻车,把钟点房也按全天价算,客人当场就炸了。

2.3 用 SQL 脚本建库并插入测试数据的完整步骤

别指望源码作者给你备份文件,.bak还原经常因为版本不兼容失败。我习惯直接跑.sql脚本。打开 SSMS,新建查询,先执行建库语句,再执行建表,最后插测试数据。下面这段是建库和房间表的精简版,你可以照着补全其他表。

-- 建库,注意排序规则用中文环境常见的 CREATE DATABASE HotelDB COLLATE Chinese_PRC_CI_AS; GO USE HotelDB; GO -- 房间表,Status 用 tinyint 省空间 CREATE TABLE Room ( RoomID INT IDENTITY(1,1) PRIMARY KEY, RoomNo VARCHAR(10) NOT NULL UNIQUE, TypeID INT NOT NULL, Status TINYINT NOT NULL DEFAULT 0 -- 0空闲 1入住 2维修 3预订 ); GO -- 插入几条测试房,方便登录后立刻看到房态图 INSERT INTO Room (RoomNo, TypeID, Status) VALUES ('101', 1, 0), ('102', 1, 0), ('201', 2, 0), ('202', 2, 0); GO

执行顺序必须是先CREATE DATABASE再USE,否则表会建到 master 库里,这是血泪经验。IDENTITY(1,1)让 RoomID 自增,插入时不用管。UNIQUE约束保证房号不重复,前台录入时如果重复会直接报错,比事后查半天强。测试数据先插四间房,够你验证房态图刷新和入住流程了。跑完脚本,回到 VB 项目里点启动,能弹出登录窗体,说明数据库这关过了。

3. 入住退房这条主线怎么跑通:VB.NET 关键代码与事务处理

结构看懂了,数据库也建好了,接下来就是最核心的入住和退房。这两个动作串起了房态、客人、账目三张表,写不好就是数据不一致——客人退了房,房间还是“已入住”,下一个客人来了开不了房。我一般会先把入住流程的代码逻辑理一遍,再动手改。

3.1 入住登记:一次插入三张表的顺序不能乱

入住要干的事:写客人表(如果是新客人)、写入住表、把房间状态改成已入住。三步必须在一个事务里,否则第二步失败,客人信息就成孤儿数据了。VB.NET 里用SqlTransaction包起来,下面这段是数据访问层的典型写法。

' 入住方法,放在 DAL 层,传入客人信息和房间号 Public Function CheckIn(ByVal guestName As String, ByVal idCard As String, _ ByVal roomId As Integer, ByVal deposit As Decimal) As Boolean Dim conn As New SqlConnection(connStr) conn.Open() Dim tran As SqlTransaction = conn.BeginTransaction() Try ' 第一步:插入或获取客人ID,这里简化成先查再插 Dim guestId As Integer Dim cmdGuest As New SqlCommand( _ "IF NOT EXISTS(SELECT 1 FROM Guest WHERE IDCard=@id) " & _ "INSERT INTO Guest(Name,IDCard) VALUES(@name,@id); " & _ "SELECT GuestID FROM Guest WHERE IDCard=@id", conn, tran) cmdGuest.Parameters.AddWithValue("@name", guestName) cmdGuest.Parameters.AddWithValue("@id", idCard) guestId = Convert.ToInt32(cmdGuest.ExecuteScalar()) ' 第二步:写入住记录,OutTime 先留空 Dim cmdIn As New SqlCommand( _ "INSERT INTO CheckIn(RoomID,GuestID,InTime,Deposit) " & _ "VALUES(@rid,@gid,GETDATE(),@dep)", conn, tran) cmdIn.Parameters.AddWithValue("@rid", roomId) cmdIn.Parameters.AddWithValue("@gid", guestId) cmdIn.Parameters.AddWithValue("@dep", deposit) cmdIn.ExecuteNonQuery() ' 第三步:改房态为已入住 Dim cmdRoom As New SqlCommand( _ "UPDATE Room SET Status=1 WHERE RoomID=@rid AND Status=0", conn, tran) cmdRoom.Parameters.AddWithValue("@rid", roomId) If cmdRoom.ExecuteNonQuery() = 0 Then Throw New Exception("房间已被占用,请刷新房态") End If tran.Commit() Return True Catch ex As Exception tran.Rollback() Throw ex Finally conn.Close() End Try End Function

逻辑说明:第一步用IF NOT EXISTS加SELECT组合,避免同一客人重复插入,ExecuteScalar直接拿到 GuestID。第二步GETDATE()取服务器时间,比客户端时间可靠。第三步的UPDATE带了AND Status=0条件,如果返回影响行数为 0,说明房间在别人那里已经开了,直接抛异常回滚。参数说明:@dep是押金,用Decimal类型,别用Double,金额算着算着就出小数误差。整个事务里任何一步抛异常,Rollback保证三张表回到原样,这就是后悔药。

3.2 退房结算:算清房费、消费和押金退还

退房比入住更容易出错,因为涉及金额计算。逻辑是:根据入住时间算房费,加上消费明细,减去押金,多退少补。房费计算要区分全天和钟点,我一般写个独立函数算,别塞在 SQL 里,不好调试。

' 计算房费,inTime 入住时间,outTime 退房时间,isHourRoom 是否钟点房 Public Function CalcRoomFee(ByVal inTime As DateTime, ByVal outTime As DateTime, _ ByVal dayPrice As Decimal, ByVal hourPrice As Decimal, _ ByVal isHourRoom As Boolean) As Decimal Dim span As TimeSpan = outTime - inTime If isHourRoom Then ' 钟点房按小时向上取整,不足一小时按一小时 Dim hours As Integer = Math.Ceiling(span.TotalHours) Return hours * hourPrice Else ' 全天房:超过中午12点算一天,这里简化成按24小时向上取整 Dim days As Integer = Math.Ceiling(span.TotalHours / 24) If days < 1 Then days = 1 Return days * dayPrice End If End Function

Math.Ceiling是向上取整的关键,钟点房 1 小时 10 分钟必须收 2 小时的钱,用Math.Round就少收了。全天房这里按 24 小时滚动算,实际酒店业常按“过中午 12 点加半天”算,你可以在days计算后加判断,根据outTime.Hour调整。参数isHourRoom从房型表里读,别在前台界面让收银员手选,选错就是账目纠纷。结算完把CheckIn表的OutTime更新,Room表Status改回 0,消费表该归档归档。

3.3 房态图刷新:用 DataGridView 绑定房间状态

前台最直观的就是房态图,一般用DataGridView或者一堆Button拼。用DataGridView绑定的好处是改数据源就刷新,不用手动改每个按钮颜色。下面是把房间列表读成DataTable再绑定的写法。

' 刷新房态图,在窗体 Load 和每次入住退房后调用 Private Sub RefreshRoomGrid() Dim dt As DataTable = dal.GetRoomList() ' 内部执行 SELECT * FROM Room dgvRooms.DataSource = dt ' 根据 Status 列给行上色,0绿1红2灰3黄 For Each row As DataGridViewRow In dgvRooms.Rows Select Case Convert.ToInt32(row.Cells("Status").Value) Case 0 : row.DefaultCellStyle.BackColor = Color.LightGreen Case 1 : row.DefaultCellStyle.BackColor = Color.LightCoral Case 2 : row.DefaultCellStyle.BackColor = Color.LightGray Case 3 : row.DefaultCellStyle.BackColor = Color.Khaki End Select Next End Sub

DataSource赋值后Rows才有效,顺序不能反。Cells("Status")里的列名必须和 SQL 查询出来的列名完全一致,大小写无所谓但拼写要一样。颜色只是辅助,真正判断房态永远以数据库Status字段为准,别信界面上按钮的颜色,那是给人看的,不是给程序看的。

4. 避坑与排查:这类 VB 酒店源码最常见的五个翻车点

我帮人救过不下十份这类源码,问题高度集中。下面五条按“现象 → 原因 → 解决”写,你遇到时直接对号入座。

现象一:登录报“未将对象引用设置到对象的实例”。原因通常是App.config里连接字符串名字和代码里ConfigurationManager.ConnectionStrings("XXX")的 XXX 不一致,取出来是Nothing。解决:全局搜索ConnectionStrings(,把名字和配置文件里的name属性对齐,一个字符都不能差。

现象二:房态图一片空白,但数据库里明明有房间。原因多半是DataGridView的AutoGenerateColumns被设成False,而列没手动加全。解决:在设计器里把AutoGenerateColumns改回True,或者检查DataPropertyName是否和字段名匹配。也有可能是查询语句里WHERE Status < 9这种条件把数据过滤没了。

现象三:退房后房间还是红色(已入住)。原因是退房代码只更新了CheckIn表的OutTime,忘了UPDATE Room SET Status=0。解决:把退房的三步操作也包进事务,和入住对称。检查时直接看Room表的Status字段,别只看界面。

现象四:金额计算出现 0.30000000000000004 这种小数。原因是用Double或Single存金额。解决:数据库字段用DECIMAL(18,2),VB 里用Decimal类型,AddWithValue时传Decimal,别传Double。已经错的数据用ROUND函数修。

现象五:换台电脑部署就提示“SQL Server 不存在或访问被拒绝”。原因是连接字符串里写死了sa密码或者实例名。解决:部署前把Data Source改成目标机的实例名,如果目标机用 Windows 身份验证,把连接字符串改成Integrated Security=True,去掉User ID和Password。

注意:改任何数据库相关代码前,先在测试库上跑,别直接动生产数据。我见过有人把UPDATE Room SET Status=0写成不带WHERE的,全酒店房间瞬间变空闲,前台直接乱套。

5. 让这套源码真正能用:报表导出与权限控制的进阶技巧

跑通入住退房只是及格线,要让这套 WinForm 酒店系统真正交付使用,还得补两个东西:营业报表和操作员权限。这两块很多源码要么没有,要么写得极简,我一般会自己加。

报表这块,VB.NET 里最省事的是用Microsoft.ReportViewer控件,但部署时目标机要装运行时,容易翻车。我现在更倾向直接导出 Excel,用DataTable写CSV或者引用NPOI库。下面这段是把当日入住明细导成 CSV 的写法,不依赖 Office。

' 导出当日入住明细到 CSV,路径由 SaveFileDialog 选 Private Sub ExportDailyReport() Dim dt As DataTable = dal.GetTodayCheckIn() ' 查 CheckIn join Room join Guest Dim sb As New StringBuilder() ' 表头,用逗号分隔,字段里如果有逗号要加引号,这里简化 sb.AppendLine("房号,客人姓名,入住时间,押金") For Each row As DataRow In dt.Rows sb.AppendLine(String.Format("{0},{1},{2},{3}", _ row("RoomNo"), row("Name"), _ Convert.ToDateTime(row("InTime")).ToString("yyyy-MM-dd HH:mm"), _ row("Deposit"))) Next Dim sfd As New SaveFileDialog() sfd.Filter = "CSV文件|*.csv" If sfd.ShowDialog() = DialogResult.OK Then ' 用 GB2312 编码,否则 Excel 打开中文乱码 IO.File.WriteAllText(sfd.FileName, sb.ToString(), _ System.Text.Encoding.GetEncoding("GB2312")) End If End Sub

关键点在最后一行编码,用UTF8的话 Excel 直接打开会乱码,得用GB2312或者带 BOM 的 UTF8。String.Format拼接时如果客人姓名里有逗号,CSV 结构就乱了,严谨做法是给每个字段加双引号并把内部双引号转义,你可以自己补上。报表查询尽量走单独的 SQL,别复用入住那个方法,不然字段不够。

权限控制方面,别只做一个登录就完事。操作员表里加个Role字段,0 是收银员、1 是经理。收银员只能入住退房,经理才能改房价、删账目。在窗体加载时判断角色,把不该点的按钮Enabled = False。我习惯在frmMain的Load事件里统一做一次权限初始化,而不是每个按钮里写一遍判断,不然漏一个就是越权漏洞。

最后说个我自己的教训:早些年接这类系统,总想着把功能做全,结果交付日期一拖再拖。后来学乖了,先把入住退房报表这条主线跑通,让前台能用起来,再根据实际反馈加钟点房、会员、预订。源码是死的,业务是活的,能跑起来、数据不错、账目对得上,比什么花哨功能都强。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询