ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

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

VB.NET WinForm酒店管理系统源码剖析:数据库设计与房态流转 简介一套基于VB语言的WinForm宾馆酒店管理系统源码附带完整数据库脚本定位为适合新手及有一定经验开发者的学习与二次开发范例。压缩包约3.59MB共354个文件以124个VB代码文件为主辅以resx、resources资源文件、ico图标以及17个SQL数据库脚本同时包含少量配置、图片与可执行程序目录结构清楚便于按模块查阅还附带可运行exe便于直接查看界面效果。程序老媛出品并亲测校正代码注释完整覆盖客户入住、客房分配、退房结算等酒店核心业务数据库脚本可直接部署能帮助读者理解WinForm界面与数据表的交互逻辑也适合在课程设计或毕业设计中直接改造复用。目前已有216人学习下载无论是学习桌面应用开发还是快速搭建管理系统原型都有实际参考价值。1. 一个 VB.NET WinForm 酒店管理系统源码里藏着什么拿到这套源码时第一感觉是工程文件比想象中干净.vbproj、app.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 SdkMicrosoft.NET.Sdk PropertyGroup OutputTypeWinExe/OutputType RootNamespace宾馆酒店管理系统/RootNamespace AssemblyName宾馆酒店管理系统/AssemblyName TargetFrameworknet48/TargetFramework /PropertyGroup /ProjectRootNamespace决定了新窗体的默认命名空间AssemblyName是最终生成的 exe 文件名。从源码里看到app.config和app.config成对出现其实是 Visual Studio 在调试时复制生成的同名配置文件真正的配置源是前者。判断一个项目是否完整先看这三样.vbproj、app.config、Form源文件是否齐全。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 nameHotelDB connectionStringData Source.;Initial CatalogHotelDB;User IDsa;Password123456; providerNameSystem.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 IDCardid) 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 IDCardid), 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 State2 WHERE RoomNoroomNo 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 State3 WHERE RoomNoroomNo把房间置为待打扫。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 SubCellFormatting在单元格每次重绘时触发优点是取值动态数据刷新一次颜色自动跟随。相比在 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文件名是不够的RootNamespace、AssemblyName、所有窗体文件的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这里不再展开。源码能跑起来只是第一步把状态流转的花样试一遍把房态颜色与按钮交互打通这个项目才能真正变成自己的工具。改完连接字符串造一条昨天的订单走一遍退房流程比读十遍代码都有用。本文还有配套的精品资源点击获取
返回列表