news 2026/9/13 6:30:58

VB.NET WinForm酒店管理系统源码剖析:数据库设计与房态流转

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
VB.NET WinForm酒店管理系统源码剖析:数据库设计与房态流转

简介:一套基于VB语言的WinForm宾馆酒店管理系统源码,附带完整数据库脚本,定位为适合新手及有一定经验开发者的学习与二次开发范例。压缩包约3.59MB,共354个文件,以124个VB代码文件为主,辅以resx、resources资源文件、ico图标以及17个SQL数据库脚本,同时包含少量配置、图片与可执行程序,目录结构清楚,便于按模块查阅,还附带可运行exe便于直接查看界面效果。程序老媛出品并亲测校正,代码注释完整,覆盖客户入住、客房分配、退房结算等酒店核心业务,数据库脚本可直接部署,能帮助读者理解WinForm界面与数据表的交互逻辑,也适合在课程设计或毕业设计中直接改造复用。目前已有216人学习下载,无论是学习桌面应用开发还是快速搭建管理系统原型,都有实际参考价值。

1. 一个 VB.NET WinForm 酒店管理系统源码里藏着什么

拿到这套源码时,第一感觉是工程文件比想象中干净:.vbprojapp.config、一堆.cache文件,外加一个数据库脚本。用 VB 写的 WinForm 酒店管理系统,在今天的开源生态里不算新鲜,但它把「界面事件驱动 + 多窗体协作 + 基于状态字段的房态管理」这套经典结构演示得很完整。相比那些动辄几十张表的中台系统,它的业务边界很明确:客房、客户、预订、入住、退房、结算,正好覆盖一间中小型宾馆的前台日常操作。

适合两类人。一类是刚接触 WinForm 的新手,能从窗体代码里看懂按钮事件、数据绑定、增删改查是怎么串起来的;另一类是准备做课设或毕业设计的老手,需要一套带数据库脚本、能直接改的底子。下文中所有涉及数据库、窗体、状态流转的说明,都基于 WinForm 与数据库脚本配合的典型做法展开,自己手头没有这份源码时,也可以对照思路复刻。

2. 数据库脚本先行:房态、订单与客户表的范式设计

2.1 从数据库脚本能看出业务边界

解压源码后,数据库脚本是最值得先读的文件。不管它是.sql还是嵌入式的数据库文件,核心表通常就几张:客房表、客户表、订单表、账单明细表。客房表负责记录房号、类型、价格和当前状态;订单表把客户与客房关联起来,记录入住时间、退房时间、预收金额;账单明细则记录加收、赔偿之类的额外费用。

这套设计虽然简单,却把酒店管理系统最常见的三类查询都支撑住了:查房态、查在住客人、查某段时间的营收。表与表之间是一对多的关系,一个客户可以有多张订单,一张订单只对应一间客房。下面这张表是我从这类系统里归纳出的核心字段结构,字段名在不同项目中略有差异,但用途相同:

表名核心字段作用
RoomInfoRoomNo, RoomType, Price, State记录每个房间的基础信息与当前状态
CustomerInfoCustomerID, Name, IDCard, Phone客户档案,入住时登记
OrderInfoOrderID, CustomerID, RoomNo, CheckInTime, CheckOutTime, Deposit, Amount订单主表,记录入住与退房信息
BillDetailBillID, OrderID, ItemName, ItemPrice订单上的额外消费明细

客房状态字段是这套源码里最关键的一个设计点。常见做法是用整数或短字符串表示状态,比如空房为 0、预订为 1、入住为 2、待打扫为 3。状态值尽量用代码维护,不要直接存中文,否则后续做统计时需要 CASE WHEN 转换,查询和界面绑定都会多一步。

2.2 建表脚本的关键约束与类型选择

打开数据库脚本,第一件事是看主键和外键的声明方式。客房表用房号做主键,这是业务上成立的约束,因为一间宾馆的房号天然唯一。订单表一般用自增编号做主键,字段时间类型用 DATETIME 或 SMALLDATETIME,金额字段用 DECIMAL(10,2),避免浮点误差。下面是常见的建表脚本片段:

CREATE TABLE RoomInfo ( RoomNo VARCHAR(10) PRIMARY KEY, RoomType VARCHAR(20) NOT NULL, Price DECIMAL(10, 2) NOT NULL, State INT NOT NULL DEFAULT 0 ); CREATE TABLE OrderInfo ( OrderID INT IDENTITY(1,1) PRIMARY KEY, CustomerID INT NOT NULL, RoomNo VARCHAR(10) NOT NULL, CheckInTime DATETIME NOT NULL, CheckOutTime DATETIME NULL, Deposit DECIMAL(10, 2) DEFAULT 0, Amount DECIMAL(10, 2) DEFAULT 0, FOREIGN KEY (RoomNo) REFERENCES RoomInfo(RoomNo) );

这段脚本里有两个细节值得注意。State INT DEFAULT 0让新建客房默认是空房,避免插入数据时忘记赋值;CheckOutTime允许 NULL,因为退房之前这个字段没有意义。金额字段不推荐用 FLOAT,涉及钱的计算必须用定点数,这一点在实际开发中是常识,但在教学项目里往往被忽略。

数据库脚本如果导入失败,最常见的原因是两张表有外键关联,插入数据时要先插父表再插子表。其次是 SQL Server 的排序规则或自增列显示插入问题,往带 IDENTITY 的表里显式插入编号时,需要先执行SET IDENTITY_INSERT OrderInfo ON。这些排错点记下来,比背命令更有用。

2.3 预置数据:房态初始化的学问

脚本里通常会有一段 INSERT 语句,把整个楼层的房间初始化进去。初始化数据写得越规范,后面做界面时越省事。下面这种预置方式比较推荐:

INSERT INTO RoomInfo (RoomNo, RoomType, Price, State) VALUES ('101', '标准间', 168.00, 0), ('102', '标准间', 168.00, 0), ('201', '大床房', 228.00, 0), ('301', '套房', 388.00, 0);

所有房间初始状态都为 0,也就是空房。入住、预订、退房都是围绕这个状态值的变更来驱动的。调试时如果想模拟脏房间,直接手动把某行的 State 改成 3,界面上对应房间的颜色就会变化,这个特性在下一章会讲到。

3. 从 .vbproj 看项目结构:WinForm 代码的组织与依赖

3.1 .vbproj 与 cache 文件到底有什么用

工程目录里的宾馆酒店管理系统.vbproj是 VB.NET 项目的核心文件,它记录了项目类型、目标框架、引用的程序集、编译选项。用 Visual Studio 打开解决方案时,加载的就是这个文件。文件里大量.cache文件是设计时生成的中间产物,比如DesignTimeResolveAssemblyReferencesInput.cache用于设计时程序集解析,GenerateResource.cache用于资源文件生成,这些都不需要手动维护,删掉也不影响源码运行。

用文本编辑器打开.vbproj,能看到这样的结构:

<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>WinExe</OutputType> <RootNamespace>宾馆酒店管理系统</RootNamespace> <AssemblyName>宾馆酒店管理系统</AssemblyName> <TargetFramework>net48</TargetFramework> </PropertyGroup> </Project>

RootNamespace决定了新窗体的默认命名空间,AssemblyName是最终生成的 exe 文件名。从源码里看到app.configapp.config成对出现,其实是 Visual Studio 在调试时复制生成的同名配置文件,真正的配置源是前者。判断一个项目是否完整,先看这三样:.vbprojapp.configForm源文件是否齐全。

3.2 多窗体协作:从登录窗到主窗体的跳转方式

酒店管理系统通常有登录窗体、主窗体、客房管理窗体、订单窗体、房态汇总窗体。登录成功后,需要关闭登录窗体并打开主窗体。VB.NET 里常见有两种写法,一种是直接在Application.Run里传主窗体,另一种是在登录按钮事件中做跳转:

Private Sub btnLogin_Click(sender As Object, e As EventArgs) Handles btnLogin.Click If txtUser.Text = "admin" AndAlso txtPass.Text = "123456" Then Dim mainForm As New MainForm() mainForm.Show() Me.Hide() Else MessageBox.Show("用户名或密码错误") End If End Sub

这段代码是典型的 WinForm 多窗体协作方式:Me.Hide()隐藏登录窗体而不是关闭,因为关闭会触发窗体关闭事件,导致消息循环结束、程序退出。用Show()打开主窗体,两个窗体在同一个线程上共存。如果希望用户不操作主窗体时无法返回登录页,可以把登录窗体设为模态,或者干脆在登录成功后用Application.Run切换到主窗体,这个细节在项目中直接决定跳转体验。

3.3 连接字符串藏在 app.config 里

数据库脚本导入成功后,程序要能连上数据库,依赖的是app.config里的连接字符串。教学项目常见的问题是连接字符串直接写死在代码里,换台电脑就要重新编译。这套源码采用了配置文件方式,结构大致如下:

<connectionStrings> <add name="HotelDB" connectionString="Data Source=.;Initial Catalog=HotelDB;User ID=sa;Password=123456;" providerName="System.Data.SqlClient" /> </connectionStrings>

读取该连接的代码通常封装成一个公共模块:

Imports System.Configuration Module DbHelper Public ReadOnly Property ConnectionString As String Get Return ConfigurationManager.ConnectionStrings("HotelDB").ConnectionString End Get End Property End Module

把连接字符串统一放在配置文件中,最大的好处是环境迁移成本低。开发库、测试库、正式库对应不同的服务器地址或实例名,只需要改配置文件,不需要动编译后的 exe。如果脚本里使用的是相对路径,注意 SqlClient 不支持相对路径直接指向像App_Data这样的文件夹,要么用|DataDirectory|替代符,要么在启动时设置AppDomain.CurrentDomain.SetData指到实际目录。

4. 核心业务流程代码:预订、入住登记与退房结算

4.1 房态流转:一个被低估的状态机

从前台操作视角看,酒店管理系统管理的是房间,但从代码视角看,管理的是房态状态值。State每变化一次,房间在界面上的颜色就变一次。下面是这套系统里最常见的状态流转:

状态值含义触发事件
0空房退房完成、预定取消
1已预订前台下单预订
2已入住客人办理入住
3待打扫客人退房,保洁未确认

预订操作先锁定房间,此时 State 从 0 变 1;客人到店后办理入住,State 再变 2;退房结清账目后,State 变 3;保洁确认打扫后,State 回到 0。每一步都对应一个执行按钮和一个 Update 语句。这里最容易犯的错误是直接修改主订单表状态而不联动更新客房表,最终导致房间显示已入住但订单已经退房,数据不一致。

4.2 入住登记:多表操作必须放进事务

客人办理入住时,系统需要做三件事:新增或更新客户信息、新增订单记录、修改客房状态。这三步任何一步失败,整个操作就应当回滚。教学项目里经常看到三步操作分开执行,中间没有事务控制,这样一旦第三步更新房态失败,客人已经入住了,房间却还显示空房。

下面是带事务的典型写法:

Public Function CheckIn(customerName As String, cardId As String, roomNo As String) As String Dim conn As New SqlConnection(ConnectionString) conn.Open() Dim trans As SqlTransaction = conn.BeginTransaction() Try Dim sqlCustomer As String = "IF NOT EXISTS (SELECT 1 FROM CustomerInfo WHERE IDCard=@id) " & "INSERT INTO CustomerInfo(Name, IDCard) VALUES(@name, @id)" Dim cmdCustomer As New SqlCommand(sqlCustomer, conn, trans) cmdCustomer.Parameters.AddWithValue("@name", customerName) cmdCustomer.Parameters.AddWithValue("@id", cardId) cmdCustomer.ExecuteNonQuery() Dim sqlOrder As String = "INSERT INTO OrderInfo(CustomerID, RoomNo, CheckInTime, Deposit) " & "VALUES((SELECT CustomerID FROM CustomerInfo WHERE IDCard=@id), @roomNo, GETDATE(), 0)" Dim cmdOrder As New SqlCommand(sqlOrder, conn, trans) cmdOrder.Parameters.AddWithValue("@id", cardId) cmdOrder.Parameters.AddWithValue("@roomNo", roomNo) cmdOrder.ExecuteNonQuery() Dim sqlRoom As String = "UPDATE RoomInfo SET State=2 WHERE RoomNo=@roomNo" Dim cmdRoom As New SqlCommand(sqlRoom, conn, trans) cmdRoom.Parameters.AddWithValue("@roomNo", roomNo) cmdRoom.ExecuteNonQuery() trans.Commit() Return "ok" Catch ex As Exception trans.Rollback() Return ex.Message Finally conn.Close() End Try End Function

这段代码的逻辑说明:所有 SQL 命令挂在同一个SqlTransaction对象上,事务从BeginTransaction()开始,全部成功后Commit(),任何一个步骤抛异常就Rollback(),保证三张表的数据要么全部更新成功,要么全部不生效。Parameters.AddWithValue用参数化查询,避免拼 SQL 字符串导致的注入问题。

参数说明:@name是客户姓名,@id是身份证号,@roomNo是房号,GETDATE() 取数据库服务器当前时间,避免客户端与服务器时间不一致。AddWithValue虽然简洁,但性能不是最优,大量数据写入时建议显式指定 SqlDbType。

4.3 退房结账:时间差与金额计算

退房的核心是计算金额。常见计费规则是按天计费,超过中午 12 点按半天或全天加收,具体规则由商务策略决定。教学项目大多简化成按小时差折算,代码大致是这样:

Private Function CalcAmount(checkIn As DateTime, checkOut As DateTime, price As Decimal) As Decimal Dim totalHours As Double = (checkOut - checkIn).TotalHours If totalHours <= 2 Then Return price * 0.5 ElseIf totalHours <= 24 Then Return price Else Dim days As Integer = Math.Ceiling(totalHours / 24) Return days * price End If End Function

这里使用Math.Ceiling向上取整,入住超过 24 小时就算两天。在最后一天,其实时间计算容易产生争议,测试时尤其要注意跨天场景。验证退房功能时,可以把系统时间调成不同时间点,或者直接修改数据库中的CheckInTime,再走一遍退房流程,观察金额是否符合预期。别忘了退房完成后还要执行UPDATE RoomInfo SET State=3 WHERE RoomNo=@roomNo,把房间置为待打扫。

5. 房态看板与界面交互:DataGridView 绑定、状态着色与布局

5.1 用 DataGridView 直接呈现房态表

WinForm 项目里展示房间信息最常用的控件是 DataGridView。它可以绑定 DataTable,也可以手动添加列。房态看板的基本思路:把 RoomInfo 表全部查出来,按楼层或房间类型分组显示。加载代码常见写法如下:

Private Sub LoadRoomState() Dim dt As New DataTable() Using conn As New SqlConnection(ConnectionString) Dim sql As String = "SELECT RoomNo AS 房号, RoomType AS 类型, Price AS 价格, " & "CASE State WHEN 0 THEN '空房' WHEN 1 THEN '预订' WHEN 2 THEN '入住' WHEN 3 THEN '待打扫' END AS 状态 " & "FROM RoomInfo ORDER BY RoomNo" Dim da As New SqlDataAdapter(sql, conn) da.Fill(dt) End Using dgvRooms.DataSource = dt End Sub

这段代码用 SqlDataAdapter 填充 DataTable,再直接把 DataTable 赋给 DataGridView 的 DataSource。需要注意:查询里的AS 房号是中文别名,DataGridView 会自动把列头显示为别名。如果希望列宽、对齐方式更可控,可以关闭自动生成列,手动添加 DataGridViewTextBoxColumn,再把DataPropertyName设为字段名。

5.2 状态着色:CellFormatting 事件的正确用法

房态看板如果全是白底黑字,前台根本看不清哪些房间空着、哪些有人住。WinForm 里给某一列不同值设置不同颜色的标准做法是处理 DataGridView 的 CellFormatting 事件,而不是在加载数据时遍历设置颜色:

Private Sub dgvRooms_CellFormatting(sender As Object, e As DataGridViewCellFormattingEventArgs) Handles dgvRooms.CellFormatting If dgvRooms.Columns(e.ColumnIndex).Name = "状态" Then Dim stateText As String = Convert.ToString(e.Value) Select Case stateText Case "空房" e.CellStyle.BackColor = Color.LightGreen Case "预订" e.CellStyle.BackColor = Color.Orange Case "入住" e.CellStyle.BackColor = Color.LightCoral Case "待打扫" e.CellStyle.BackColor = Color.Gray End Select End If End Sub

CellFormatting在单元格每次重绘时触发,优点是取值动态,数据刷新一次,颜色自动跟随。相比在 DataTable 上改行背景色,这种方式把表现逻辑和业务数据分离,是 WinForm 界面美化里很值得借鉴的思路。C# WinForm 项目处理主题时也走同样的路子,这跟语言无关,VB.NET 和 C# 共用同一套 WinForms 机制。

还有一个细节:CellFormatting 里设置的颜色优先级高于默认行颜色,但如果同时设置了整个 DataGridView 的 AlternatingRowsDefaultCellStyle,默认交替色不会覆盖这里设置的e.CellStyle.BackColor,因为后者是针对单个单元格的显式赋值。这点在调试界面颜色异常时可以省下不少时间。

5.3 界面布局与操作效率

宾馆前台的操作集中在预订、入住、换房、退房这四件事上。源码里的主窗体通常用 MenuStrip 做功能入口,中间放一个 TabControl 切换房态总览、客户管理、订单记录等页面。布局上,房态总览可以放进 TableLayoutPanel 或 FlowLayoutPanel,用按钮模拟房间卡片,每个按钮的 BackColor 代表房态,Click 事件打开对应的操作窗体。这种卡片式房态图比纯 DataGridView 更直观,在 winform 项目案例里很常见。

另外,DataGridView 的行选中与双击事件对操作效率影响很大。双击某一行时,取出当前行的房号字段,然后带参数打开入住或退房窗体:

Private Sub dgvRooms_CellDoubleClick(sender As Object, e As DataGridViewCellEventArgs) Handles dgvRooms.CellDoubleClick If e.RowIndex < 0 Then Return Dim roomNo As String = dgvRooms.Rows(e.RowIndex).Cells("房号").Value.ToString() Dim stateText As String = dgvRooms.Rows(e.RowIndex).Cells("状态").Value.ToString() If stateText = "空房" OrElse stateText = "预订" Then Dim checkInForm As New CheckInForm(roomNo) checkInForm.ShowDialog() End If End Sub

把房号通过构造函数传给子窗体,子窗体加载时直接锁定这个房间,比在子窗体里再下拉选择房号少一次操作。前台高峰期,这种细节对效率提升明显。

6. 二次开发时最容易踩的三个坑:重命名、时间测试与报表导出

拿到源码后第一步不是急着读代码,而是先确认能否用 Visual Studio 正常打开并运行。调试前需要修改app.config里的连接字符串,数据库脚本成功导入后才能看到真实数据。正常情况下启动后能看到主界面和房态列表,点开各功能菜单,增删改查都能跑通。

第一个坑是项目重命名。用「宾馆酒店管理系统」做项目名没问题,但要在整个解决方案里改名时,只改.vbproj文件名是不够的,RootNamespaceAssemblyName、所有窗体文件的Namespace声明、app.config里的连接字符串名称,这些都要同步改。否则编译能通过,运行后事件绑定失效或找不到资源,报的错还特别隐蔽。建议用 Visual Studio 的重构功能逐项处理,不要手动批量替换。

第二个坑是退房金额的时间测试。前台不可能为了验证功能等到第二天,直接改数据库里的CheckInTime是最快的办法。测试时把入住时间改成 25 小时之前,再执行退房,观察费用是不是按两天计算。对照 4.3 的算法,2 小时以内按半天、24 小时以内按一天、超过 24 小时向上取整,这三条边界值都要测到。

第三个常用的操作是账单导出。简易做法是拿 DataTable 直接导成 Excel 兼容的 CSV 文件:

Dim sb As New StringBuilder() For Each row As DataRow In dt.Rows sb.AppendLine(String.Join(",", row.ItemArray.Select(Function(x) x.ToString()))) Next IO.File.WriteAllText("账单.csv", sb.ToString(), Encoding.UTF8)

这段代码里String.Join把每一行的字段拼接成逗号分隔的字符串,Select(Function(x) x.ToString())把 DataRow 的列集合转换成字符串数组,最后以 UTF-8 编码写盘。注意用 UTF-8 写出的 CSV 在 Excel 里打开中文不会乱码。如果要求带格式的 Excel,再引入 NPOI 或直接用 Excel 组件生成 xlsx,这里不再展开。

源码能跑起来只是第一步,把状态流转的花样试一遍,把房态颜色与按钮交互打通,这个项目才能真正变成自己的工具。改完连接字符串,造一条昨天的订单,走一遍退房流程,比读十遍代码都有用。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 6:28:14

Basilisk模拟实战:C语言自适应网格流体仿真与Shell脚本自动化

简介&#xff1a;压缩包服务于博士阶段基于 Basilisk 的数值模拟研究&#xff0c;聚焦 C 语言开发与 Shell 自动化流程&#xff0c;适合正在攻读数理、流体或地球物理方向、需要快速上手开源模拟框架的研究生。包内共 11 个文件&#xff0c;包含 C 源文件与头文件、Shell 脚本、…

作者头像 李华
网站建设 2026/9/13 6:28:07

AI驱动游戏出海增长:从买量瓶颈到专属语言引擎的实战策略

做游戏出海这几年&#xff0c;大家心里都清楚&#xff0c;最头疼的从来不是产品研发本身&#xff0c;而是“好不容易做出来的游戏&#xff0c;怎么让海外玩家愿意下载、愿意留下”。买量成本一年比一年高&#xff0c;本地化又经常是“译了但没完全译”&#xff0c;玩家一眼看出…

作者头像 李华
网站建设 2026/9/13 6:25:54

企业AI平台接入能力横评:业务系统72小时打通实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华