ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

ASP药店管理系统毕业设计:从数据库设计到IIS部署

ASP药店管理系统毕业设计:从数据库设计到IIS部署

简介:面向ASP课程设计与毕业设计场景,这套资源提供药店管理系统从选题、设计到答辩的完整项目方案。系统采用ASP服务器端脚本,结合数据库构建B/S架构,可在IIS环境下调试部署;功能模块覆盖药品信息管理、销售记录统计、客户资料维护、库存自动增减及管理员分级权限,并涉及SQL注入防护等基本安全考虑,直接展示了Web开发、数据库操作与页面交互的完整链路。压缩包共133个文件,以82个asp页面源码为主,配合CSS样式、图片素材、数据库文件(mdf/ldf),以及开题报告、毕业论文Word文档和答辩PPT,整体仅5.23MB,结构清晰便于对照阅读。阅读源码可学习ASP编程技巧、数据查询与表单处理思路;文档部分对项目背景、技术选型、系统设计与预期成果进行系统阐述,配合答辩PPT可完整呈现项目全貌。资源已有406人学习,适合需要快速理解ASP项目架构、掌握模块划分与文档写作规范的毕业生参考。

1. 认清ASP药店管理系统:它是什么、为什么毕业设计还在用它

大多数人在 2024 年听到 ASP 的第一反应是“这技术不是早就淘汰了吗”。但如果你在某个高校的信息管理专业、或者被指定了“不能用框架、要写原始动态网页”的课程设计题目,ASP + Access 这套组合反而是最容易在一学期内跑通并完成答辩的方案。所谓 ASP 药店管理系统,本质是一套跑在 Windows IIS 上的 Web 进销存应用:管理员维护药品档案、供应商信息,收银员在前台下销售单、自动扣减库存,库管员做入库、盘点和过期预警,最后按日、按月统计销售额与毛利。

这类系统最大的价值不是技术先进,而是业务闭环完整、页面链路短、数据库结构直白——药品表、库存表、订单表之间的一对多关系,恰好能把课堂上学的关系数据库、会话状态、前后端交互全部串起来。对于想快速完成毕业设计的人,它比 SSM 或 Spring Boot 那套“起步就要配 Maven、配 Redis”的方案省掉大量环境折腾的时间;对于想维护老系统的从业者,它又是一个典型的遗留系统样本,值得花两天读懂它的写法和坑。我用它给 A 同学做了一版可答辩的完整交付物,这套思路你完全可以照着复现。

2. 先把需求拆清楚:药店管理系统的模块划分与页面流转设计

2.1 六个核心功能模块与它们的数据来源

做毕业设计最容易犯的错是一上来就建表写代码,结果做到一半发现缺供应商字段、缺销售明细表,再回头改数据库结构。先把功能模块钉死,再倒推表和页面,后面会顺很多。一套常规的 ASP 药店管理系统,按业务角色拆成六个模块就够了。

  • 系统管理:用户登录、修改密码、管理员对收银员/库管员账号的增删改查。数据落在 users 表,登录成功后把用户名和角色写进 Session。
  • 药品档案管理:药品的 CRUD,关键字段是药品编码、通用名、规格、生产厂商、批准文号、零售价、会员价。药品编码建议用字符串而不是自增 ID,方便对接扫码枪和手工录入。
  • 库存管理:入库单录入、出库单录入、库存调拨、库存盘点、库存预警。库存预警的核心逻辑是“当前库存量 ≤ 最低库存量”时在主页和列表页高亮显示。
  • 销售收银:选择药品、加入购物车、计算总价、确认结账、生成销售单并扣减库存。这是整个系统里最需要事务保证的环节。
  • 供应商管理:维护供应商编号、名称、联系人、电话,入库时通过下拉框关联供应商。这个模块单独一张表,但入库单要引用它。
  • 统计报表:今日/本月销售额、销售明细、药品销售排行、过期药品清单。报表在答辩中是最容易讲出彩的部分,因为能体现聚合查询和分组统计能力。

模块划分完后,要立刻列一张页面清单,把每个页面归属到模块下。我一般会整理成下面这样的表格发给对方,直接照着建文件夹和文件。

模块页面文件说明
登录login.asp / check_login.asp / logout.asp登录页、认证处理、退出
框架index.asp / top.asp / left.asp / main.asp后台主框架,左侧导航
药品管理drug_list.asp / drug_add.asp / drug_edit.asp / drug_del.asp列表分页、增删改
库存管理stock_in.asp / stock_out.asp / stock_check.asp / stock_warn.asp出入库、盘点、预警
销售收银sale_cart.asp / sale_do.asp / sale_list.asp购物车、结算、销售历史
供应商supplier_list.asp / supplier_add.asp / supplier_edit.asp供应商档案维护
报表report_today.asp / report_month.asp / report_sale_rank.asp日结、月报、排行

2.2 页面流转:从登录到收银台的请求路径

有了页面清单,还要把页面之间的跳转关系画清楚。ASP 时代最经典的布局是上下左框架:顶部 top.asp 显示当前登录用户和退出按钮,左侧 left.asp 是模块菜单,右侧 main.asp 为主内容区。登录成功后跳转到 index.asp,然后所有操作都在框架内容区完成。

以收银流程为例,完整链路是这样的:收银员打开 sale_cart.asp → 输入药品编码或名称模糊查询 → 点击“加入购物车”把参数提交给 sale_cart.asp 自身(用隐藏表单 + 数组或字符串拼接保存购物车状态)→ 点击“结算”跳转到 sale_do.asp → sale_do.asp 在事务里写 sale_order 主表和 sale_item 明细表,同时更新 stock 表 → 成功后跳转到 sale_ok.asp 显示单据号和找零信息。这条链路中,sale_do.asp 是绝对核心,因为它同时操作三张表,任何一步失败都必须回滚。

页面流转设计还有一个容易忽略的细节:每个编辑页面在提交后要区分“新增成功”和“修改成功”。ASP 里常见做法是在 edit 页面里放一个隐藏字段action=add|edit,提交到同一个 save.asp,根据 action 值决定执行 INSERT 还是 UPDATE。这样代码量最少,也最符合课程设计的“单入口保存”预期。

2.3 权限模型:管理员、收银员、库管员怎么分

权限控制在毕业设计里不需要做成 RBAC 那么重,三张角色表就够:在 users 表里加一个 role 字段,值为 admin / cashier / keeper。admin 能进所有页面,cashier 只能进销售和查询类页面,keeper 只能进入库、盘点、库存预警页面。实现上不需要单独建权限表,只要在每个页面顶部加一段 Session 角色判断。

<% if Session("username") = "" then Response.Redirect "login.asp?msg=timeout" end if if Session("role") <> "admin" and Session("role") <> "cashier" then Response.Redirect "index.asp?msg=denied" end if %>

这段代码的逻辑是:先判断有没有登录,没登录说明 Session 过期,弹回登录页并带参数msg=timeout;再判断角色,非管理员且非收银员不允许访问销售页面。Session("role")的赋值发生在 check_login.asp 登录校验成功后,值来自 users 表的 role 字段。需要注意 ASP 的 Session 默认 20 分钟过期,如果用户在页面停留太久后再提交,Session 会被清空,所以每个需要权限判断的页面都必须放上面这段检查,不能只靠登录页拦一次。

3. 数据库设计先行:表结构、关系与进销存字段怎么落

3.1 七张核心表的设计清单

页面清单确定后,数据库表结构基本是固定的。一个毕业设计级别的 ASP 药店管理系统,七张表足够覆盖全部功能,字段不需要追求范式极致,但要保证业务能闭环。我常用的设计如下。

users 用户表

字段类型说明
id自增主键
usernamevarchar(50)登录名,唯一
passwordvarchar(50)密码,明文仅限课程设计;项目建议 MD5
real_namevarchar(50)姓名
rolevarchar(20)admin / cashier / keeper

drugs 药品表

字段类型说明
drug_codevarchar(30)主键,药品编码
drug_namevarchar(100)通用名
specvarchar(50)规格,如 0.25g*24片
manufacturervarchar(100)生产厂商
approval_novarchar(50)批准文号
retail_pricedecimal(10,2)零售价
vip_pricedecimal(10,2)会员价
stock_minint最低库存预警线

stock 库存表

字段类型说明
id自增主键
drug_codevarchar(30)关联 drugs
quantityint当前库存数量
update_timedatetime最后变动时间

supplier 供应商表

字段包括 id、supplier_no、supplier_name、contact、phone、remark,用于入库单下拉选择。

purchase_in 入库单表

字段类型说明
id自增主键
drug_codevarchar(30)药品编码
supplier_idint供应商 ID
quantityint入库数量
purchase_pricedecimal(10,2)进货单价
in_timedatetime入库时间
operatorvarchar(50)经办人

sale_order 销售主表

字段类型说明
order_novarchar(30)单号,格式如 YYYYMMDDHHMMSS+随机数
cashiervarchar(50)收银员
total_amountdecimal(10,2)应收总额
receive_amountdecimal(10,2)实收金额
change_amountdecimal(10,2)找零金额
sale_timedatetime销售时间

sale_item 销售明细表

字段包括 id、order_no、drug_code、drug_name、price、quantity、subtotal。用 order_no 关联销售主表,一条订单对应多条明细。

表之间的关系很简单:stock.drug_code 和 drugs.drug_code 一对多,purchase_in.drug_code 外键到 drugs,sale_item.order_no 外键到 sale_order。药品编码为什么用字符串做主键?因为在真实的药店场景里,药品编码往往包含分类前缀或厂商代码,而且扫描枪扫出来就是字符串,如果自增 ID 反而要在代码里多做一次映射。

3.2 库存扣减与流水:交易一致性怎么在 ASP 里保证

药店系统最关键的逻辑是:库存数量不能为负,且每扣一笔库存必须对应一条销售明细。如果只简单执行UPDATE stock SET quantity = quantity - 1 WHERE drug_code = 'xxx',不考虑并发和流水,就会出现库存被扣成负数、但销售报表对不上账的情况。

正确的做法是把“写销售明细”和“扣库存”放到同一个数据库事务里。ASP 中通过 ADODB.Connection 对象的事务方法实现:先conn.BeginTrans,依次执行 INSERT 明细、UPDATE 库存,然后conn.CommitTrans提交;如果中间任何一步出错,conn.RollbackTrans回滚,库存不会被扣减。

不能先扣库存再写明细,也不能先写明细再扣库存,必须两步在事务里完成。另一个细节是扣库存的 SQL 要带上库存条件:

UPDATE stock SET quantity = quantity - 2 WHERE drug_code = 'P001' AND quantity >= 2

这个 SQL 的作用是用数据库本身的锁机制防止并发超卖。如果RecordsAffected返回 0,说明当前库存不足,直接回滚事务并提示“库存不足”。这是 Access 和 SQL Server 都支持的写法,比先 SELECT 再判断再 UPDATE 更可靠。

3.3 Access 还是 SQL Server:选型决定你后面少踩多少坑

很多人做完 ASP 开发才发现数据库选型出了问题。我建议按下面这张对比表来定,不要在写了一半时切换。

对比项Access .mdbSQL Server
环境要求无需安装,复制即用需单独安装服务
并发能力弱,适合单机演示强,支持多人同时操作
事务支持支持基本事务完整事务、锁机制
64 位系统兼容必须装 ACE 驱动原生支持
毕业设计答辩演示简单,拷到哪都能跑需演示服务器环境
数据库文件迁移直接复制 .mdb需要备份还原

我的习惯是:对方只是要一份能过答辩的完整项目,用 Access;对方说“以后我想自己开药店试一下”或者老师要求用 SQL Server,再用 SQL Server。Access 版连接串是Provider=Microsoft.Jet.OLEDB.4.0;Data Source=...,在 64 位 IIS 上大概率报“未找到提供程序”,解决办法是安装Microsoft.ACE.OLEDB.12.0驱动并把应用程序池“启用 32 位应用程序”设为 True。这个问题我在第 5 章会当作典型坑展开讲。

4. IIS 上搭环境并写核心代码:连接、登录、分页与销售单据

4.1 IIS 配置与 ASP 运行环境:三个必开开关

ASP 只能在 Windows IIS 上运行,步骤是:控制面板 → 启用或关闭 Windows 功能 → Internet Information Services → 应用程序开发功能 → 勾选 ASP。装完之后还要处理三个开关,否则页面会一片空白或直接 500。

  • 启用 ASP 父路径:IIS 管理器中选中站点,双击 ASP,把“启用父路径”设为 True。否则Server.MapPath("../data/db.mdb")这类相对路径代码直接报错。
  • 默认文档:添加 login.asp 或 index.asp 为默认页,否则访问http://localhost/时会出现目录浏览或 403。
  • 应用程序池启用 32 位应用程序:对应 Access 数据库连接问题,需要在站点所属应用程序池的“高级设置”里把“启用 32 位应用程序”改为 True。

配置完成后,在站点根目录放一个最简单的test.asp,内容只写一行:

<% Response.Write "ASP is running" %>

浏览器访问 http://localhost/test.asp,能看到文字就说明 ASP 解析正常;如果浏览器直接让你下载这个文件或者显示源代码,说明 ASP 功能没启用,回第一步检查。

4.2 数据库连接封装:conn.asp 的标准写法

ASP 项目里最值得反复写的一段代码就是数据库连接。不要在每个页面都复制连接串,而是单独建一个conn.asp,每个需要操作数据库的页面在最顶部用<!--#include file="conn.asp"-->引入。连接代码要封装成函数,方便统一出错处理。

<% Dim conn, connStr connStr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & Server.MapPath("data/db.mdb") Set conn = Server.CreateObject("ADODB.Connection") conn.Open connStr Sub CloseConn() If IsObject(conn) Then conn.Close Set conn = Nothing End If End Sub %>

这段代码的关键点有两个。第一,Provider=Microsoft.ACE.OLEDB.12.0是适配 64 位系统的 Access 驱动,如果电脑上没装这个驱动,换成Microsoft.Jet.OLEDB.4.0也能跑,但仅限 32 位环境。第二,Server.MapPath("data/db.mdb")把网站的虚拟路径映射为物理磁盘路径,数据库文件放在站点下的 data 文件夹里。CloseConn()子过程用于页面结尾释放连接,在 ASP 这种页面级脚本模型里,连接用完必须关闭,否则 IIS 会堆积连接池导致数据库文件被锁定。

4.3 登录与 Session:ASP 里最简单的权限控制

登录页逻辑不复杂:表单 POST 提交用户名和密码,check_login.asp 查库比对,成功就写 Session 并跳转,失败就回原页带错误参数。下面这段是 check_login.asp 的核心。

<% Dim username, password, rs, sql username = Request.Form("username") password = Request.Form("password") sql = "SELECT id, real_name, role FROM users WHERE username='" & username & "' AND password='" & password & "'" Set rs = Server.CreateObject("ADODB.Recordset") rs.Open sql, conn If Not rs.EOF Then Session("username") = username Session("real_name") = rs("real_name") Session("role") = rs("role") Response.Redirect "index.asp" Else Response.Redirect "login.asp?msg=error" End If rs.Close Set rs = Nothing %>

这里的rs("role")是从数据库读取的角色字段,写进 Session 之后,全站所有页面都能读取。需要提醒的是,这种字符串拼接 SQL 在真实项目里必被 SQL 注入打穿,第 5 章我会专门演示怎么改写成参数化查询。毕业设计阶段为了演示代码简洁可能先这么写,但答辩时老师问“如何防止 SQL 注入”,你必须有意识地回答出问题所在和解决方案。

4.4 药品列表分页与模糊查询:ASP 的分页写法

药品列表页是一个高频页面,必须做分页和关键字查询。ASP 里分页最省事的是用 Recordset 的 PageSize 和 AbsolutePage 属性,但数据量大时性能差;更主流的是用 SQL 的 TOP 加 NOT IN 做真分页。下面是我常用的写法。

<% Dim page, pageSize, keyword, sql, rs, totalCount, totalPage pageSize = 15 page = Request("page") If page = "" Then page = 1 keyword = Trim(Request("keyword")) sql = "SELECT COUNT(*) FROM drugs WHERE drug_name LIKE '%" & keyword & "%'" Set rs = conn.Execute(sql) totalCount = rs(0).Value totalPage = -Int(-totalCount / pageSize) rs.Close sql = "SELECT TOP " & pageSize & " * FROM drugs " & _ "WHERE drug_name LIKE '%" & keyword & "%' AND id NOT IN (" & _ "SELECT TOP " & (page - 1) * pageSize & " id FROM drugs " & _ "WHERE drug_name LIKE '%" & keyword & "%' ORDER BY id) ORDER BY id" Set rs = Server.CreateObject("ADODB.Recordset") rs.Open sql, conn %>

代码的要点是先用COUNT(*)查出总记录数,再算出总页数totalPage = -Int(-totalCount / pageSize)。这个负号取整法是 VBScript 里实现“向上取整”的常用技巧,因为 VBScript 没有内置 Ceil 函数。随后通过 TOP 加 NOT IN 实现跳页查询,注意页码越界时要修正 page 变量,比如If page < 1 Then page = 1和If page > totalPage Then page = totalPage,否则用户手动改 URL 参数会直接报错。

4.5 销售收银:扣库存与写流水的事务处理

sale_do.asp 是整个系统含金量最高的代码,它必须在一个事务里完成三件事:写 sale_order 主表、写 sale_item 明细表、更新 stock 库存表。下面给出的是核心逻辑,假设购物车数据以二维数组形式从 session 传入。

<% Dim orderNo, totalAmount, receiveAmount, changeAmount, i, drugCode, qty, price orderNo = Year(Now) & Right("0" & Month(Now), 2) & Right("0" & Day(Now), 2) & _ Right("0" & Hour(Now), 2) & Right("0" & Minute(Now), 2) & Right("0" & Second(Now), 2) totalAmount = Request.Form("total_amount") receiveAmount = Request.Form("receive_amount") changeAmount = receiveAmount - totalAmount conn.BeginTrans sql = "INSERT INTO sale_order(order_no, cashier, total_amount, receive_amount, change_amount, sale_time) " & _ "VALUES('" & orderNo & "', '" & Session("username") & "', " & totalAmount & ", " & receiveAmount & ", " & changeAmount & ", Now())" conn.Execute sql For i = 1 To Request.Form("item_count") drugCode = Request.Form("drug_code")(i) qty = Request.Form("quantity")(i) price = Request.Form("price")(i) conn.Execute "INSERT INTO sale_item(order_no, drug_code, drug_name, price, quantity, subtotal) " & _ "VALUES('" & orderNo & "', '" & drugCode & "', '" & Request.Form("drug_name")(i) & "', " & _ price & ", " & qty & ", " & price * qty & ")" conn.Execute "UPDATE stock SET quantity = quantity - " & qty & _ " WHERE drug_code = '" & drugCode & "' AND quantity >= " & qty If conn.Errors.Count > 0 Then conn.RollbackTrans Response.Redirect "sale_cart.asp?msg=stock_not_enough" End If Next conn.CommitTrans Response.Redirect "sale_ok.asp?order_no=" & orderNo %>

这段代码的逻辑顺序很关键:先插入主表,再逐条插入明细、逐条扣库存,每条操作后检查conn.Errors.Count。一旦某条失败,立即回滚并跳回购物车。订单号用时间戳拼接生成,避免了自增 ID 在并发下可能重复的问题,也方便在销售列表页按单号关联主表和明细表。conn.Errors.Count是 ADODB 连接对象自带的错误集合,比 VBScript 的Err.Number更完整地反映数据库层错误。

5. 避坑排查:ASP 药店系统最容易翻车的 6 个环节

5.1 页面乱码:ASP 输出中文变“???”

现象:A 同学在本地调试时,添加药品后页面显示一堆问号和乱码,数据库里读出来的中文也是乱码。

原因:ASP 页面保存为 UTF-8 编码,但 IIS 默认的 Response 编码或数据库连接传输用的字符集是 GB2312,两边不一致。Access 数据库里的文本默认按系统区域设置存储,而页面声明的是 UTF-8。

解决:第一招,统一页面文件编码。所有 .asp 文件用记事本或编辑器另存为 UTF-8,然后在文件第一行写上<%@ Language="VBScript" CodePage=65001 %>,65001 就是 UTF-8 的代码页。第二招,如果是 Access 老库里有中文乱码,连接串里加上;Locale Identifier=2052指定简体中文区域。第三招,Response 输出时强制字符集:<meta http-equiv="Content-Type" content="text/html; charset=utf-8">。这三招同时做,基本能解决 90% 的乱码问题。

5.2 数据库连接:提示“未找到提供程序”或“操作必须使用一个可更新的查询”

现象:页面打开直接 500,错误信息是“未找到提供程序,该程序可能未正确安装”;或者页面能连上数据库,但执行 INSERT/UPDATE 时报“操作必须使用一个可更新的查询”。

原因:前者是 Access 驱动问题,64 位 IIS 没有 Jet 驱动;后者是 Access 数据库文件本身或所在目录对 IIS 进程没有“写”权限。很多同学直接把 .mdb 放在站点根目录,Users 组只有读取权限,ASP 能查不能改。

解决:“未找到提供程序”换用 ACE 驱动连接串,并把应用程序池的 32 位开启;“操作必须使用一个可更新的查询”,右键 data 文件夹 → 属性 → 安全 → 给 IIS 用户(IIS_IUSRS)添加完全控制权限。别用“Everyone 完全控制”这种粗暴方式,美化后会有安全风险,给 IIS_IUSRS 单独授权即可。

5.3 IIS 父路径与虚拟目录:404 和 500 的最常见来源

现象:页面能打开首页,但点进子页面报 HTTP 500.19 或 HTTP 404.3;有些页面能跑,有些页面引用绝对路径后 404。

原因:代码里用了../这种相对路径,而 IIS 默认禁用父路径;或者子目录被当成独立应用程序,导致 include 文件和数据库 MapPath 解析错误。

解决:把站点根目录的 ASP 设置里的“启用父路径”设为 True;如果项目里有子目录,确认子目录的“转换应用程序”没有开启,否则子目录会当成独立站点,include 的路径全乱。排查时先看 500 错误的具体错误代码,IIS 的“失败请求跟踪”功能能定位是哪一行代码出错。

5.4 库存负数:并发下单和重复提交导致的逻辑漏洞

现象:销售记录里出现库存负数,或者明明只卖了 5 件,库存却少了 10 件。

原因:sale_do.asp 里用了“先 SELECT 查库存,再 UPDATE 扣库存”的逻辑,两个用户同时下单时都读到库存足够,然后都执行扣减。另一种情况是购物车页重复点击“结算”按钮,表单被提交两次。

解决:改成第 4 章那种带条件的 UPDATE:UPDATE stock SET quantity = quantity - X WHERE drug_code = 'xxx' AND quantity >= X,SQL 执行后通过RecordsAffected判断是否真正扣减成功。重复提交的问题在结算页加一个“防止重复提交”的 Session 标记:第一次进入 sale_do.asp 时写入一个随机 token,处理完后清空;重复提交时发现 token 不存在就直接拒绝。

5.5 SQL 注入:登录页面被万能密码打穿

现象:在登录框输入' or '1'='1这种字符串,居然能直接进入系统后台。

原因:4.3 里那种字符串拼接 SQL 的方式,会把用户输入的内容当成代码执行。' or '1'='1' --这种万能密码在后台拼接后变成恒真条件,绕过密码校验。

解决:最彻底的办法是放弃字符串拼接,改用 ADODB.Command 参数化查询。代码示例如下,用conn.Execute传参数的方式让数据库把用户输入当作纯数据而不是 SQL 代码来解析。毕业设计里能写参数化查询,在答辩时是一个明显的加分项。

<% Dim cmd, paramUser, paramPwd Set cmd = Server.CreateObject("ADODB.Command") cmd.ActiveConnection = conn cmd.CommandText = "SELECT id, real_name, role FROM users WHERE username=? AND password=?" cmd.Parameters.Append cmd.CreateParameter("@u", 200, 1, 50, username) cmd.Parameters.Append cmd.CreateParameter("@p", 200, 1, 50, password) Set rs = cmd.Execute %>

5.6 Access 数据库文件损坏:断电/强制重启后的恢复手段

现象:运行一段时间后,某天打开药品管理页报“不可识别的数据库格式”,或者提示“文件已在使用中”。

原因:Access 是基于文件的数据库,配合 ASP 的页面级连接模型,每次请求都有并发读写。如果程序在事务没有提交时服务器断电或 IIS 进程被杀,.mdb 文件就会损坏。

解决:治标的方法是定期用工具“压缩和修复数据库”;治本的方法是换 SQL Server,或者在程序里做到“连接使用后立即关闭”。答辩前强烈建议手动复制一份 .mdb 备份到站点外,一旦演示现场出问题,用备份还原。这是血泪经验——我见过不止一次演示现场数据库损坏导致答辩失败的案例。

6. 从毕业设计到能上线的系统:一个小重构值得做

6.1 把登录查询改成参数化 SQL:一个最小的安全改造

第 5 章避坑里提到了参数化查询,这里给出完整的落地版。把登录、药品搜索、库存扣减这些所有涉及动态参数的 SQL 都改成参数化,改动不会超过 10 个页面,但答辩时从“能用”变成“有安全意识”是质的区别。这个重构每个页面花五分钟,收益非常直接。

6.2 三个让答辩更稳的习惯:备份、日志、错误提示封装

我在交付任何 ASP 项目时都会额外埋三个东西:第一,data 文件夹外放一个backup.bat,双击就复制 .mdb 到带日期后缀的备份文件;第二,写一个log.asp公共文件,执行删除、更新等操作时往 sys_log 表写操作日志,答辩时老师问“如何审计”,直接演示日志表;第三,在 conn.asp 里加On Error Resume Next和统一的错误跳转页面,不要暴露数据库错误原文给浏览器。

收尾前分享一个带着教训的习惯:A 同学这版系统交付前,我让他把数据库损坏、忘记密码、Session 过期、药品重名这四种异常场景全部手动演练一遍,确保每种都有用户友好的提示而不是白屏或报错。毕业设计的技术含量不体现在功能有多花哨,而是“别人运行你的系统时遇到问题,系统会不会告诉你发生了什么”。这个习惯后来帮他在演示时从“差点翻车”变成了“从容处理”。如果你也是第一次写 ASP 系统,建议照这个思路做一遍,希望帮到你。

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

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进