资讯动态

C#酒店管理系统毕业设计:从数据库设计到核心模块实现

发布时间:2026/10/9 9:32:49 来源:尧图企业网站定制
简介一套基于C#与SQL Server的酒店管理系统完整源码面向毕业设计、C#课程实训及小型酒店管理系统二次开发。系统采用三层架构覆盖用户权限管理、客户档案、房间状态、预订/入住/退房、财务账单与报表分析等核心业务可让读者清晰看到酒店后台从数据库表结构到界面控件的完整交互链路。压缩包共55个文件以C#源码19个.cs、SQL数据库脚本、界面资源.resx/.resources和可执行程序为主辅以配置文件、图片素材及项目说明文档总体积仅1.15MB轻量易导入。已有232人学习浏览目录结构清晰适合按模块阅读。源码和配套SQL脚本可直接构建数据库并运行系统从中可以学习.NET三层架构在真实项目中的落地方式、SQL Server建表结构与常用查询语句以及预订、入住、退房等业务流程的状态转换。整套代码还体现了从需求分析、界面设计到编码测试、部署上线的完整开发路径对正在准备毕业设计或希望提升C#数据库开发能力的读者而言具有较强的实践参考价值。1. 基于 C# 的酒店管理系统毕业设计它解决什么问题值不值得照做毕业设计选题里酒店管理系统几乎是一道必考题。差别往往不在代码量而在数据库设计是否经得起追问、入住退房的业务闭环是否真正跑通。这套基于 C# 的酒店管理系统走的是最经典的桌面端路线C# 编写界面与业务逻辑SQL Server 承载数据交付物就是源码加一份建库脚本。适合需要完成毕业设计、想练手完整业务系统的新手照着复现也适合写了一半想回来补数据库设计的人对一遍思路。这套系统最有价值的地方是把酒店日常业务收敛成几条清晰的主线房态管理、客人入住、账单结算。答辩时能讲清楚「入住登记点击保存那一刻数据库里发生了什么」比多画一张用例图有用得多。下面从选型开始一层一层拆开讲。2. 技术选型与分层为什么 C# WinForms SQL Server 是这个课题最稳的组合动工之前先花十分钟想清楚技术栈和代码结构看起来不如直接拖控件来得快但这十分钟基本决定了后面一个月的节奏。这一章把选型理由和分包方式一次讲完后面写代码的时候就不用再纠结了。2.1 WinForms、WPF、ASP.NET Core 怎么选三个方向的适用边界毕业设计做管理系统第一关不是写代码是选界面框架。C# 生态里能出整套系统的路线有三条WinForms、WPF、ASP.NET CoreWeb 方向。大部分同学拿到「酒店管理系统」这个题目第一反应是 Web 管理系统更高级但这其实是一条岔路。酒店前台场景是典型的局域网桌面应用前台电脑要贴近业务操作桌面端比浏览器端更贴近真实使用环境WinForms 的学习曲线几乎是三套方案里最低的拖控件就能搭界面数据绑定和事件模型都是一两天能上手的东西。WPF 界面更现代但 MVVM 和依赖属性需要额外花更多时间毕业设计周期紧性价比不高如果老师明确要求 B/S 架构再考虑 ASP.NET Core那就不是本文走的路了。课题名写的是「基于 C# 的酒店管理系统」而没有特别说明 Web 端就选 WinForms这是最稳的做法也能让代码逻辑集中在业务本身而不是前端渲染上。版本选择上WinForms 项目用 .NET Framework 4.x 在兼容性上最保险Visual Studio 2022 默认支持这套项目模板编译产物自带依赖目标机器上不用额外装运行时如果老师不排斥新环境用 .NET 6/8 的 WinForms 模板也没问题只是要注意部署机器是否装了对应运行时。毕业设计图的是稳定交付不是秀新版本号。2.2 SQL Server 与 MySQL配套数据库怎么定数据库的选择上SQL Server 和 MySQL 都能做但配套这个课题我一般用 SQL Server。SQL Server Express 版免费和 Visual Studio 的服务资源管理器能直接连起来看表结构、跑查询调试阶段省掉一半在命令行和图形工具之间来回切换的时间。Express 版对单库 10GB 的上限对毕业设计的数据量绰绰有余。MySQL 的优点是开源跨平台但如果开发环境和验收环境都在 Windows 上SQL Server 的安装和服务管理更顺手另一个现实因素是 SQL Server 的 T-SQL 报错信息更直白写错了能直接定位到哪一行。如果选题要求必须用 MySQL 也不用慌——核心表结构差异只在数据类型名建库脚本改一改就能跑后面讲的表设计和业务代码思路完全通用。数据库脚本要单独保存成 .sql 文件放进源码包而不是只存一个 .mdf 附加文件。.mdf 附加数据库在交付时要额外处理路径和权限问题验收老师的电脑如果没有装完整版 SQL Server 会很折腾一份建库脚本在任何装了 SQL Server 的机器上都能一键执行这也是项目包里「SQL」这个交付物最重要的存在意义。2.3 三层分包与连接字符串从 Form1.cs 里把代码搬出去无论界面框架选哪个代码组织方式决定答辩老师的第一印象。常见做法是把项目分成四部分UI 层放窗体BLL 层放业务判断DAL 层放数据库访问Models 层放实体类。不建议把几百行代码全塞在 Form1.cs 里——那是运行没有问题、答辩一追问就露馅的典型写法。分层的直接收益是能讲清楚「界面不碰 SQL业务不直接连库」这也是老师最愿意听到的架构意识。先建一个 App.config把连接字符串放在配置里而不是写死在窗体代码中?xml version1.0 encodingutf-8 ? configuration connectionStrings add nameHotelDB connectionStringServer.;DatabaseHotelDB;Integrated SecurityTrue; providerNameSystem.Data.SqlClient / /connectionStrings /configuration这里 Server. 表示连接本机默认实例Database 指数据库名Integrated SecurityTrue 表示用 Windows 身份验证登录 SQL Server。注意如果 SQL Server 安装时改成了混合模式并且设置了 sa 密码连接字符串就要换成Server.;DatabaseHotelDB;User Idsa;Password你的密码。把这类参数集中在配置里后续换机器、换数据库时只改一处不用重新编译。有了连接字符串再写一个数据库访问的公共类把打开连接、执行 SQL、返回结果集这些重复动作收敛到一个文件里using System.Configuration; using System.Data; using System.Data.SqlClient; public static class DBHelper { private static readonly string connStr ConfigurationManager.ConnectionStrings[HotelDB].ConnectionString; public static DataTable Query(string sql, params SqlParameter[] parameters) { using (SqlConnection conn new SqlConnection(connStr)) using (SqlCommand cmd new SqlCommand(sql, conn)) { cmd.Parameters.AddRange(parameters); SqlDataAdapter adapter new SqlDataAdapter(cmd); DataTable dt new DataTable(); adapter.Fill(dt); return dt; } } public static object ExecuteScalar(string sql, params SqlParameter[] parameters) { using (SqlConnection conn new SqlConnection(connStr)) using (SqlCommand cmd new SqlCommand(sql, conn)) { cmd.Parameters.AddRange(parameters); conn.Open(); return cmd.ExecuteScalar(); } } }这个类做了一件事让后面的页面代码只需要写 SQL 和参数不用关心连接何时打开、何时释放。using 关键字保证连接用完后自动关闭避免连接数耗尽params SqlParameter[] 让调用方可以传任意多个参数后面登录、入住的代码都会用到。参数化不是可选项而是必选项——直接拼字符串的 SQL 在演示时看起来都正常但答辩时老师一句「如何防注入」就能让整个项目打上大问号。ExecuteScalar 和 Query 的差别在返回值前者只取第一行第一列适合拿 Count 这类聚合结果后者返回整个表适合绑定列表。分层这件事不神秘核心就一句变化最多的是业务规则变化最少的是连接数据库的方式把它们拆开改业务时不容易弄坏底层。如果觉得 BLL 层写起来抽象可以先放一个 HotelService 类把「计算退房金额」「校验身份证格式」这类代码从窗体里挪进来后文在入住退房模块会看到事务和金额计算的代码放 BLL 比放 UI 层好调试得多。3. 数据库设计把入住退房业务拆成四张核心表与一份可复现的 SQL 脚本先设计表再写代码顺序不能反。这一章从表设计一路走到可执行的建库脚本和初始化数据把这套房子的地基先打好。3.1 表设计主键、外键与状态字段的取舍酒店管理系统的核心库可以压到五张表管理员表 Admin、房间类型表 RoomType、房间表 Room、客人表 Customer、入住单表 CheckIn。管理员表存登录账号和密码哈希房间类型表存「单人间、标准间、豪华套房」这样的分类和基准房价房间表存物理房间编号、所属类型和当前状态客人表存入住人的身份信息和联系方式入住单表是整张库的业务核心记录每次入住从登记到退房的全过程。设计字段时有一条容易被忽略的准则用状态字段而不是用时间推断状态。比如 Room 表里的 Status 字段0 表示空闲、1 表示入住、2 表示维修这样查询「哪些房间可用」时是 WHERE Status 0 的等值查询对索引和数据量都友好如果靠 CheckIn 表的入住时间来反推房间是否空闲每次都要做聚合和关联逻辑复杂又容易出错。同理入住单里的 Status 字段0 表示在住、1 表示已退退房就是把状态置为 1 并写上退房时间和结算金额。冗余一个状态字段会牺牲一点点写入时的规范但换来的是查询和展示时的大量便利——对毕业设计这个体量是值得的。外键关系上Room 表通过 RoomTypeId 关联到 RoomTypeCheckIn 表通过 RoomId 和 CustomerId 分别关联 Room 和 Customer。建外键约束而不是只用普通字段是因为外键能在插入入住单时保证 RoomId 一定存在于 Room 表不会出现无中生有的房间号外键同时会建立索引联表查询的效率也有保障。3.2 建库建表 SQL 脚本带初始化数据一步到位接下来给一份可直接执行的 SQL 脚本。打开 SQL Server Management Studio新建查询按顺序执行CREATE DATABASE HotelDB; GO USE HotelDB; GO CREATE TABLE Admin ( AdminId INT IDENTITY(1,1) PRIMARY KEY, UserName NVARCHAR(50) NOT NULL UNIQUE, PasswordHash NVARCHAR(128) NOT NULL ); CREATE TABLE RoomType ( RoomTypeId INT IDENTITY(1,1) PRIMARY KEY, TypeName NVARCHAR(50) NOT NULL, Price DECIMAL(10,2) NOT NULL ); CREATE TABLE Room ( RoomId INT IDENTITY(1,1) PRIMARY KEY, RoomNo NVARCHAR(20) NOT NULL UNIQUE, RoomTypeId INT NOT NULL REFERENCES RoomType(RoomTypeId), Status TINYINT NOT NULL DEFAULT 0 ); CREATE TABLE Customer ( CustomerId INT IDENTITY(1,1) PRIMARY KEY, CustomerName NVARCHAR(50) NOT NULL, IdCard NVARCHAR(18) NOT NULL, Phone NVARCHAR(20) NULL ); CREATE TABLE CheckIn ( CheckInId INT IDENTITY(1,1) PRIMARY KEY, OrderNo NVARCHAR(32) NOT NULL UNIQUE, RoomId INT NOT NULL REFERENCES Room(RoomId), CustomerId INT NOT NULL REFERENCES Customer(CustomerId), CheckInTime DATETIME NOT NULL, CheckOutTime DATETIME NULL, ActualPrice DECIMAL(10,2) NULL, Status TINYINT NOT NULL DEFAULT 0 );这段脚本完整跑下来会得到五张空表。字段类型上用户名、房间号这类可能包含中文的字段统一用 NVARCHAR避免乱码和排序问题金额用 DECIMAL(10,2) 而不是 FLOAT浮点数在结算时会带来 0.1 0.2 不等于 0.3 的经典坑Status 用 TINYINT 就够一个字节存 0 到 255 的状态值绰绰有余。IDENTITY(1,1) 是自增主键的 SQL Server 写法不用在代码里手工维护主键值。提示如果本机已经存在 HotelDB 数据库执行 CREATE DATABASE 会直接报「数据库已存在」。无需删库换个库名比如 HotelDBDemo再执行即可也可以把脚本头部改成 IF DB_ID(HotelDB) IS NOT NULL DROP DATABASE HotelDB但要确认当前库可以覆盖时再用。再把最关键的初始化数据一并写入。管理员账号密码不能存明文这里预置 admin 账号密码用 SHA256 哈希后存入INSERT INTO Admin (UserName, PasswordHash) VALUES (admin, 240be518fabd2724ddb6f04eeb1da5967448d7e831c08c8fa822809f74c720a9); INSERT INTO RoomType (TypeName, Price) VALUES (单人间, 168.00), (标准间, 268.00), (豪华套房, 588.00); INSERT INTO Room (RoomNo, RoomTypeId, Status) VALUES (101, 1, 0), (102, 1, 0), (201, 2, 1), (202, 2, 1), (301, 3, 2); INSERT INTO Customer (CustomerName, IdCard, Phone) VALUES (张三, 110101199001011234, 13800001111), (李四, 110101199202022345, 13800002222); INSERT INTO CheckIn (OrderNo, RoomId, CustomerId, CheckInTime, Status) VALUES (20240618001, 3, 1, 2024-06-18 14:00:00, 0), (20240619001, 4, 2, 2024-06-19 10:00:00, 0);240be518 这一串是 admin123 的 SHA256 值登录时把用户输入的密码也做同样哈希再比对数据库中永远不出现明文密码。房间数据里特意让 101、102 空闲201、202 在住301 维修这样一打开界面就能看到三种房态。两条在住订单对应着 RoomId 3 和 4也正是 201 和 202保证房态和订单数据是互相咬合的一致状态——这一步直接决定演示现场能不能顺畅跑下去。3.3 参数化查询与存储过程数据访问层该用哪种方式建好表之后会面对一个选择业务代码里直接用 SQL 语句还是把常用操作写成存储过程对毕业设计建议直接用参数化 SQL而不是存储过程。理由有两条一是答辩时贴代码讲解参数化 SQL 的意图一行就能说清——Where 条件用 占位符而不是拼接字符串二是存储过程把逻辑藏进数据库代码里只剩一个调用名老师追问「这段逻辑在哪」时反而不容易展示。存储过程更适合大型系统里复杂报表或高频数据库操作这个体量的管理系统用不上。参数化 SQL 的写法以登录为例后面第 4 章会给出完整代码。这里先记住一条纪律所有值都通过 SqlParameter 传入不要在 SQL 语句里写textBox.Text这样的拼接。拼接写法在开发时几乎不出错但一旦遇到用户输入 OR 11这类内容整个查询条件就被改写这是最典型的注入漏洞来源也是答辩现场最容易被问到的问题。4. 核心模块实现登录、房间管理、入住登记的代码落地与事务边界数据库就位后进入编码阶段。这一章挑三个最核心的模块展开登录认证、房间管理、入住登记与退房结算每个模块都会给到能直接落地的代码。4.1 登录认证SHA256 与参数化查询的完整写法登录模块是系统的入口也是答辩演示的第一步。逻辑分三段界面取输入、对密码做哈希、参数化查询比对数据库。先准备一个哈希工具方法using System.Security.Cryptography; using System.Text; public static class HashHelper { public static string SHA256(string input) { using (SHA256 sha256 SHA256.Create()) { byte[] bytes Encoding.UTF8.GetBytes(input); byte[] hash sha256.ComputeHash(bytes); StringBuilder sb new StringBuilder(); foreach (byte b in hash) { sb.Append(b.ToString(x2)); } return sb.ToString(); } } }x2 表示把每个字节格式化为两位小写十六进制输出的 64 位字符串正是数据库里 PasswordHash 的存储格式。注意这里只做了单向哈希没有加盐更严谨的做法是在 Admin 表里加一个 Salt 字段密码哈希值变成 SHA256(Salt 明文密码)。毕业设计如果老师不追问先做纯 SHA256 够用HashHelper 这个入口预留好了后续加盐只需要改这一个方法。然后是登录按钮的点击事件private void btnLogin_Click(object sender, EventArgs e) { string username txtUsername.Text.Trim(); string passwordHash HashHelper.SHA256(txtPassword.Text); string sql SELECT COUNT(*) FROM Admin WHERE UserName user AND PasswordHash pwd; SqlParameter[] parameters { new SqlParameter(user, username), new SqlParameter(pwd, passwordHash) }; int count Convert.ToInt32(DBHelper.ExecuteScalar(sql, parameters)); if (count 0) { this.DialogResult DialogResult.OK; } else { MessageBox.Show(用户名或密码错误); } }SELECT COUNT(*) 返回的是匹配行数0 表示没这个人或密码不对。ExecuteScalar 在这里取的就是那个聚合数值比把整个表查回来再数行数干净。Trim() 去掉用户名首尾空格防止用户手滑多按了一下空格导致明明账号正确却登录失败。这段代码没有 if 里嵌 if、没有对象转字符串再拼接整个安全性逻辑都写在参数上答辩时讲起来很清楚。4.2 房间管理DataGridView 绑定与刷新时机房间列表页是前台每天看得最多的界面。最常见的实现是用 DataGridView 展示 Room 和 RoomType 联表查询的结果。加载方法如下private void LoadRoomList() { string sql SELECT r.RoomId, r.RoomNo, t.TypeName, t.Price, CASE r.Status WHEN 0 THEN 空闲 WHEN 1 THEN 入住 ELSE 维修 END AS StatusText FROM Room r JOIN RoomType t ON r.RoomTypeId t.RoomTypeId ORDER BY r.RoomNo; DataTable dt DBHelper.Query(sql); dgvRooms.DataSource dt; }联表查询在这里是把 Room 的房间号、RoomType 的价格放在一行展示。CASE WHEN 把数字状态翻译成中文显示界面层看到的是「空闲/入住/维修」数据库里存的还是 0/1/2。状态显示不做前端拼接可读性完全由 SQL 负责代码量也更少。绑定之后有一个容易被忽略的刷新问题添加或删除房间后如果直接再次调用 LoadRoomList 并重新赋值 DataSource界面能刷新但如果在设计器里手动设置过列直接用 DataTable 替换数据源有时会出现列错乱。更好的做法是让 DataGridView 绑定到一个 BindingSource 对象BindingSource bsRooms new BindingSource(); bsRooms.DataSource LoadRoomList(); dgvRooms.DataSource bsRooms; // 增删改后刷新 bsRooms.ResetBindings(false);BindingSource 相当于界面和数据之间的一个中转站ResetBindings(false) 的作用是让控件重新读取一遍数据源并刷新显示。等列表里有个几十条数据时这两种刷新方式的差异还看不出来但答辩演示时如果表格刷新后白屏观感会很差这里用 BindingSource 是更稳的写法。4.3 入住登记与退房结算一个事务边界讲清楚入住登记是整张数据库设计是否成立的检验点。业务要求是点保存那一刻Room 表里该房间的状态从 0 变为 1CheckIn 表里插入一条在住记录。这两件事要么都成功、要么都失败——如果房间状态改了但入住单没写进去前台查不到是谁住了这间房如果入住单写了但房间状态没改同一个房间会被再开一次。用 SqlTransaction 把两步包进同一个事务using (SqlConnection conn new SqlConnection(connStr)) { conn.Open(); SqlTransaction transaction conn.BeginTransaction(); try { string updateRoomSql UPDATE Room SET Status 1 WHERE RoomId roomId AND Status 0; using (SqlCommand cmd new SqlCommand(updateRoomSql, conn, transaction)) { cmd.Parameters.AddWithValue(roomId, roomId); int rows cmd.ExecuteNonQuery(); if (rows 0) { throw new Exception(房间状态已变化请刷新后重试); } } string insertOrderSql INSERT INTO CheckIn(OrderNo, RoomId, CustomerId, CheckInTime, Status) VALUES(orderNo, roomId, customerId, checkInTime, 0); using (SqlCommand cmd new SqlCommand(insertOrderSql, conn, transaction)) { cmd.Parameters.AddWithValue(orderNo, orderNo); cmd.Parameters.AddWithValue(roomId, roomId); cmd.Parameters.AddWithValue(customerId, customerId); cmd.Parameters.AddWithValue(checkInTime, DateTime.Now); cmd.ExecuteNonQuery(); } transaction.Commit(); } catch { transaction.Rollback(); throw; } }第一步 UPDATE 的 WHERE 条件里带上了 Status 0这一手是防止并发操作时重复开房如果房间已经不是空闲状态这次 Update 会更新 0 行然后通过 rows 0 主动抛异常整个事务回滚。第二步 Insert 写入住单注意每个 Command 都要把 transaction 对象传进去否则它还是在自己独立的连接上执行事务就白起了。所有 SqlParameter 都通过 AddWithValue 传入和登录模块一样不走拼接。退房结算的算法常见做法是「入住当天算一天房费退房当天中午 12 点前不另计12 点后加收半天」具体时点酒店可以按自己的标准调。代码上先把退房时间和入住时间相减拿到入住天数再叠加超时费TimeSpan span checkOutTime - checkInTime; int days span.Days; if (days 0) days 1; decimal amount days * roomPrice; if (checkOutTime.Hour 12 days 1) { amount roomPrice / 2; }这一段属于业务规则放在 BLL 层而不是窗体里更好。计算完金额后同样用一个事务把「更新 CheckIn 的退房时间、状态和实收金额」与「更新 Room 回到空闲」同步完成。回头看 3.1 节设计的 Status 字段——退房这一个动作改动了两张表状态字段就是这两张表之间的同步锚点。5. 常见坑与排查4 条验收现场最容易翻车的状况与对策代码写顺了不等于演示能顺利这一章把我见过的、自己也踩过的四类问题按「现象、原因、解决」完整拆开每一条都是现场能救命的。5.1 换一台电脑就连不上数据库连接字符串与 SQL Server 网络协议的双重检查现象在自己电脑上开发调试一切正常把整个项目拷到老师的笔记本上演示启动就报「在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误」。原因分两层。第一层是连接字符串写死了本机实例名比如 ServerMYPC\SQLEXPRESS换机器后自然是找不到的第二层是即使连接字符串改成了 Server.SQL Server 默认也没开 TCP/IP 协议外部工具和另一台机器访问不到这个实例。解决连接字符串只写 Server. 或 Serverlocalhost代表本机默认实例这样在本机演示最大概率能通如果要跨机器联调打开 SQL Server Configuration Manager在 SQL Server 网络配置里启用 TCP/IP并在 Windows 防火墙入站规则里放行 1433 端口。改完协议需要重启 SQL Server 服务才生效这是很多人改完还是连不上的原因。数据库文件和脚本建议统一放源码包的 Sql 目录下整个交付包路径清晰换机器时不容易丢。5.2 DataGridView 列顺序错乱与刷新白屏现象把 DataTable 直接赋给 DataGridView.DataSource 之后表格自动生成了所有列顺序跟 SQL 里 Select 的字段顺序不一致有时还多出 RoomId 这种用户不该看到的列增删一条数据后重新赋值界面闪一下或者变成白屏。原因DataGridView 的 AutoGenerateColumns 默认是 true它按数据源里列的元数据顺序生成而不是按你写的查询字段顺序直接用新 DataTable 替换 DataSource 时控件没有收到「同一份数据的更新」通知刷新时机不对。解决在设计器里手动把列建好每一列设置 DataPropertyName 对应数据源字段名然后把 AutoGenerateColumns 设为 false刷新时用 BindingSource 中转调用 bsRooms.ResetBindings(false)。先手动建列、再去掉自动生成表格显示完全由你控制不会被数据库表结构牵着走。5.3 入住单写进去了房态没变事务没包住两步写库现象点「入住登记」后CheckIn 表里多了一条在住记录但 Room 表的房间状态还是 0空闲前台再开这间房也不报错业务上直接乱掉。原因这是最常见的「两步写库只做了一步」的错误——把更新房间状态的 UPDATE 和插入入住单的 INSERT 分开执行中间没有任何保护如果第二个命令写错了或者界面抛出异常第一个命令的修改已经提交无法回滚。解决把两步操作放进同一个 SqlTransaction并且像 4.3 节那样在 UPDATE 里带上 Status 0 作为乐观锁。判断影响行数 rows 0 时主动抛异常让事务回滚——这样才能保证「要么都发生要么都不发生」。排查时用一条 SQL 就能验证问题SELECT r.RoomNo, r.Status, c.OrderNo FROM Room r LEFT JOIN CheckIn c ON r.RoomId c.RoomId AND c.Status 0 WHERE r.RoomNo 201;如果这行的 r.Status 是 0 但 c.OrderNo 不为空说明房态和在住订单已经不一致正是事务没包住造成的。这条查询也可以放到验收自查清单里作为数据一致性的体检项。5.4 演示时画面空白预置数据不够与输入校验缺失现象答辩现场打开房态页面看到一片空白现场录客人信息又慢又容易出错或者输入一个退房时间早于入住时间的日期程序不拦直接算出一个负数房费。原因初始化脚本里只放了一两行数据而演示路径里要展示「空闲、入住、维修」三种房态和查询效果数据撑不起来输入校验只做了必填没做范围校验边界数据自然漏过去了。解决把初始化脚本丰富到前面 3.2 节演示的那个程度——3 间空闲、2 间在住、1 间维修、3 位以上客人、若干条在住订单这样演示时每一步都有现成数据可用。代码里入住登记校验「退房时间必须晚于入住时间」「身份证号长度必须 15 或 18 位」这些校验放 BLL 层统一做答辩时也多了一个「我做了输入校验」的讲点。演示前把数据库脚本从头执行一遍确认所有状态都对着再上台。6. 从「能跑」到「能答辩」演示路径、检验单与答辩追问应对代码写完只是第一步能在现场流畅演示才是毕业设计的临门一脚。拿一张自测表把核心链路走一遍每一步的预期结果都确认无误后再上答辩场。演示顺序操作动作预期结果1 登录输入 admin / admin123进入主窗体2 查看房态打开房间列表页看到空闲、入住、维修三种状态3 入住登记选 101 空闲房并保存101 变为「入住」新增一条在住订单4 退房结算选 201 在住订单结算弹出账单金额201 回到「空闲」5 历史查询按客人姓名模糊搜索显示对应客人的历史入住记录答辩追问大概率围绕三个点表之间什么关系、怎么防 SQL 注入、事务用在哪里。答案都在前面章节里——Room 多对一 RoomTypeCheckIn 多对一 Room 和 Customer这是表关系所有 SQL 都用 SqlParameter 传参这是防注入入住登记和退房结算时用 SqlTransaction 保证房间状态与入住单同步这是事务。关于密码存储回答 SHA256 哈希、数据库不落明文再加一句「后续可以加盐强化」反而是加分项。我当年的演示机器在答辩现场因为没开 SQL Server 服务卡了整整两分钟连不上库指导老师的眉头当场就皱起来了。后来我养成了一个习惯凡是带数据库的演示一定提前在自己机器上把「重置脚本 → 配置检查 → 启动系统」完整走一遍宁可慢五分钟不丢演示分。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑