ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Classic ASP旅游系统实战:IIS部署、SQL Server连接与遗留系统维护

Classic ASP旅游系统实战:IIS部署、SQL Server连接与遗留系统维护

简介:本资源是一套完整的ASP+SQL旅游管理系统毕业设计项目包,面向计算机专业本科生、Web开发初学者及课程设计实践者,解决旅游业务信息化管理中的用户交互、线路展示、在线预订与后台运维等核心需求。压缩包共148个文件,含41个ASP动态页面(如jiudian.asp、lvyou.asp等,实现前后端逻辑)、36张JPG/PNG/GIF界面截图与系统示意图、33个MNO配置或数据文件、12个HTM静态页及1个答辩PPT、1个SQL Server主数据库文件(.mdf)与日志文件(.ldf),整体大小27.87MB,结构清晰,模块对应明确。已有171人学习下载,涵盖用户管理、旅游线路展示、订单处理、后台维护及安全性设计等完整流程,附带开题报告、论文与答辩PPT,可直接用于课程设计汇报或毕业设计参考,同时提供可运行源码与数据库脚本,便于调试、二次开发与技术原理验证。

1. 一个跑在 IIS 上的旅游管理老系统:不是怀旧,是接手维护时的真实战场

你刚被安排接手一个运行了八年的内部旅游管理系统,后台数据库是 SQL Server 2005,前端页面全是.asp后缀,IIS 日志里还夹着ADODB.Recordset的报错堆栈——别慌,这不是古董展柜里的标本,而是某高校继续教育学院仍在用的实操平台:课程报名、线路排期、导游调度、订单对账全靠它撑着。这个「asp+sql旅游管理系统设计」资源包,就是一套完整可部署的实战基线:含可直接编译运行的 Classic ASP 源码(非 ASP.NET)、配套 SQL Server 数据库脚本(含初始化数据)、结构清晰的毕业论文(含 ER 图与流程图)、开题报告中对 B/S 架构选型的务实分析,以及答辩 PPT 里对权限模块设计的逐层拆解。它不教你怎么写 React,但能让你三小时内把系统跑在本地 IIS 上,看清一个真实业务系统如何用Request.QueryString拿参数、用Server.CreateObject("ADODB.Connection")连库、用Response.Redirect做跳转——适合需要快速理解遗留系统逻辑、做功能补丁、或带学生做课程设计的开发者。别被“毕业设计”标签骗了,它的数据库事务处理和多表联查逻辑,比不少新写的 PHP 小项目更经得起压测。


2. 环境搭建与源码结构解析:从 IIS 配置到文件夹语义

2.1 IIS 与 Classic ASP 运行环境配置:Windows 10/11 下的最小可行路径

Classic ASP 不是插件,是 Windows 内置组件,但默认不启用。在 Windows 10/11 上,必须手动开启 IIS 及其 ASP 支持模块。打开「控制面板 → 程序 → 启用或关闭 Windows 功能」,勾选以下三项(缺一不可):

  • Internet Information Services
  • Internet Information Services → Web 管理工具 → IIS 管理控制台
  • Internet Information Services → 万维网服务 → 应用程序开发功能 →ASP(注意:不是 ASP.NET)

提示:勾选后需重启系统或至少重启 IIS 服务(iisreset命令)。若跳过此步,访问.asp页面会直接返回 404 或下载源码文本,这是新手最常卡住的第一关。

安装完成后,打开 IIS 管理器(inetmgr),右键「网站 → 添加网站」,设置如下:

  • 网站名称:TourSystem
  • 物理路径:指向你解压后的源码根目录(如D:\tour_system\src)
  • 绑定:http,端口8080(避免与已占端口冲突)
  • 应用程序池:选择或新建一个.NET CLR 版本为「无托管代码」的应用程序池(Classic ASP 不依赖 .NET 运行时)

配置完后,在浏览器访问http://localhost:8080/login.asp。若看到登录页,说明环境已通;若报错HTTP 错误 500.100,大概率是数据库连接字符串未更新或 SQL Server 服务未启动。

2.2 源码目录结构语义化解读:每个文件夹都在讲业务故事

该系统采用典型的 Classic ASP 分层结构,但命名直白,无框架抽象。解压后主目录结构如下(共 7 个核心文件夹 + 若干根文件):

/src ├── /admin ← 后台管理入口,含用户管理、线路审核、订单导出 ├── /common ← 公共函数库:conn.asp(数据库连接)、fun.asp(日期格式化、SQL 过滤) ├── /css ← 纯 CSS 文件,无预处理器,兼容 IE6+ ├── /images ← 图片资源,含 logo.gif、btn_bg.jpg 等 ├── /js ← JavaScript 脚本:checkform.js(表单验证)、calendar.js(简易日历控件) ├── /user ← 前台用户区:线路浏览、在线报名、订单查询 └── login.asp ← 根目录登录页(唯一入口)

关键点在于/common/conn.asp—— 它不是配置文件,而是硬编码连接字符串的执行体。打开它,你会看到类似这样的代码:

<% ' /common/conn.asp Dim conn, connstr connstr = "Provider=SQLOLEDB;Data Source=.;Initial Catalog=tour_db;User ID=sa;Password=123456;" Set conn = Server.CreateObject("ADODB.Connection") conn.Open connstr %>

注意:Data Source=.表示连接本机 SQL Server 实例;Initial Catalog=tour_db是数据库名;User ID=sa;Password=123456是明文账号密码。生产环境必须改用 Windows 身份验证或加密配置,但毕业设计包里就这么写——这是你接手后第一个要动的地方。

2.3 数据库脚本执行与初始数据注入:SQL Server 2005 兼容性实测

资源包中/database/tour_db.sql是完整的建库脚本。它包含三类语句:

  • CREATE DATABASE tour_db(建库)
  • CREATE TABLE ...(建表,含users,lines,orders,guides四张核心表)
  • INSERT INTO ...(插入 20+ 条测试数据,如 5 条旅游线路、3 个管理员账号)

执行步骤(推荐使用 SQL Server Management Studio):

  1. 新建查询 → 粘贴整个tour_db.sql内容 → 执行(F5)
  2. 若报错Database 'tour_db' does not exist,先手动创建空库tour_db,再执行建表与插入语句
  3. 关键校验:执行SELECT COUNT(*) FROM users,应返回3(admin/manager/user 三个角色账号)

提示:脚本中lines表的line_date字段为datetime类型,但插入值如'2023-05-01'无时分秒。SQL Server 2005 会自动补00:00:00,不影响查询,但若你在 SQL Server 2019 上执行,建议将插入值改为'2023-05-01 00:00:00'避免隐式转换警告。

2.4 核心页面流转链路:从登录到订单生成的 5 步闭环

系统虽老,但业务流清晰。以用户报名一条线路为例,请求链路如下:

  1. login.asp→ POST 到check_login.asp(验证账号密码,查users表)
  2. 验证成功 →Response.Redirect "user/index.asp"(跳转前台首页)
  3. user/index.asp→ 查询lines表显示所有线路 → 点击「报名」按钮 → GET 请求user/apply.asp?line_id=5
  4. user/apply.asp→ 用Request.QueryString("line_id")获取线路 ID → 查询lines表详情 → 渲染报名表单
  5. 表单 POST 到user/do_apply.asp→ 插入orders表(含user_id,line_id,apply_time)→Response.Redirect "user/order_success.asp"

这个链路没有 MVC,没有路由,但每一步都对应一个物理.asp文件,参数通过 URL QueryString 或 Form POST 传递。读懂这个链路,就等于拿到了系统的导航图。


3. 核心模块代码逐行剖析:数据库操作与权限控制黑匣子

3.1 用户登录验证模块:check_login.asp的三次防御

check_login.asp是系统第一道门,代码仅 30 行,却包含三层防护。我们逐段看:

<% ' /check_login.asp Dim username, password, sql, rs username = Trim(Request.Form("username")) ' 1. 接收并去空格 password = Trim(Request.Form("password")) ' 2. 基础过滤:禁止 SQL 注入关键词(毕业设计常见做法) If InStr(username, "'") > 0 Or InStr(password, "'") > 0 Then Response.Write "<script>alert('用户名或密码含非法字符');history.back();</script>" Response.End End If ' 3. 参数化查询?不,这里用字符串拼接,但加了单引号包裹与转义 username = Replace(username, "'", "''") ' 防止 ' or '1'='1' 注入 password = Replace(password, "'", "''") ' 4. 查询用户表 sql = "SELECT * FROM users WHERE username='" & username & "' AND password='" & password & "'" Set rs = Server.CreateObject("ADODB.Recordset") rs.Open sql, conn, 1, 1 If Not rs.EOF Then ' 登录成功:写入 Session Session("user_id") = rs("id") Session("username") = rs("username") Session("role") = rs("role") ' role: 1=admin, 2=manager, 3=user Response.Redirect "admin/index.asp" ' 管理员跳后台 Else Response.Redirect "login.asp?err=1" ' 失败回登录页 End If %>

逻辑说明与参数说明:

  • Trim()是必须的,防止用户输空格导致匹配失败;
  • InStr(..., "'")是初级防注入,只能拦住明显单引号,对UNION SELECT无效,但毕业设计够用;
  • Replace(..., "'", "''")是 SQL Server 的标准转义方式,'变成'',让数据库当字面量处理;
  • Session("role")存储角色值,后续所有页面用Session("role") = 1判断是否为管理员,这是全系统权限控制的基石。

3.2 线路查询与分页实现:user/index.asp中的手写分页算法

user/index.asp显示所有旅游线路,数据量大时需分页。它没用存储过程,而是在 ASP 层手写分页逻辑:

<% ' /user/index.asp - 分页核心段 Dim pageSize, currentPage, totalRecords, totalPages pageSize = 8 ' 每页显示 8 条 currentPage = Request.QueryString("page") If currentPage = "" Or Not IsNumeric(currentPage) Then currentPage = 1 currentPage = CInt(currentPage) ' 1. 先查总记录数(关键!不能用 SELECT COUNT(*) OVER(),SQL Server 2005 不支持) sql = "SELECT COUNT(*) FROM lines WHERE status=1" ' status=1 表示上线线路 Set rs = Server.CreateObject("ADODB.Recordset") rs.Open sql, conn, 1, 1 totalRecords = rs(0) rs.Close ' 2. 计算总页数 totalPages = Int((totalRecords + pageSize - 1) / pageSize) If totalPages < 1 Then totalPages = 1 ' 3. 计算 LIMIT 起始位置(SQL Server 2005 无 OFFSET/FETCH,用 TOP + NOT IN 模拟) ' 当前页数据:取第 (currentPage-1)*pageSize + 1 到 currentPage*pageSize 条 Dim startRow, endRow startRow = (currentPage - 1) * pageSize + 1 endRow = currentPage * pageSize ' 4. 构造分页查询(经典 TOP NOT IN 写法) sql = "SELECT TOP " & pageSize & " * FROM lines " & _ "WHERE id NOT IN (SELECT TOP " & (startRow - 1) & " id FROM lines WHERE status=1 ORDER BY id) " & _ "AND status=1 ORDER BY id" Set rs = Server.CreateObject("ADODB.Recordset") rs.Open sql, conn, 1, 1 %>

为什么用NOT IN而不用ROW_NUMBER()?
因为ROW_NUMBER() OVER (ORDER BY id)是 SQL Server 2005 SP2 才支持的特性,而毕业设计包默认按基础版 2005 编写。NOT IN虽性能差,但在百条数据内完全可用。startRow和endRow的计算公式Int((n + m - 1) / m)是整数向上取整的标准写法,避免totalRecords=15, pageSize=8时算出totalPages=1的错误。

3.3 订单生成事务处理:user/do_apply.asp的原子性保障

用户点击报名后,do_apply.asp需完成两个动作:插入订单记录 + 更新线路余位。必须保证二者同时成功或同时失败,否则出现「用户付了钱但没占位」的资损。代码用conn.BeginTrans实现:

<% ' /user/do_apply.asp Dim line_id, user_id, sql1, sql2 line_id = Request.Form("line_id") user_id = Session("user_id") conn.BeginTrans ' 开启事务 ' 1. 插入订单 sql1 = "INSERT INTO orders (user_id, line_id, apply_time, status) VALUES (" & _ user_id & ", " & line_id & ", GETDATE(), 1)" conn.Execute sql1 ' 2. 更新线路余位(减1) sql2 = "UPDATE lines SET remain_num = remain_num - 1 WHERE id = " & line_id & " AND remain_num > 0" conn.Execute sql2 ' 3. 检查第二步是否影响行数为0(即余位不足) If conn.Errors.Count > 0 Or conn.Execute("SELECT @@ROWCOUNT").GetString = "0" Then conn.RollbackTrans ' 回滚 Response.Write "<script>alert('报名失败:线路余位不足');history.back();</script>" Response.End Else conn.CommitTrans ' 提交 Response.Redirect "user/order_success.asp" End If %>

关键细节:

  • conn.Errors.Count > 0捕获 SQL 语法错误或约束冲突;
  • @@ROWCOUNT返回上一条UPDATE影响的行数,若为 0 说明remain_num > 0条件不满足,即余位已售罄;
  • GetString是 ADODB.Recordset 的方法,此处用于快速取单值,比rs(0)更轻量;
  • 事务粒度精准控制在「订单+余位」两个操作,不包裹查询逻辑,符合最小事务原则。

4. 常见问题排查:那些让你重启三次 IIS 的玄学时刻

4.1 现象:访问login.asp显示「HTTP 错误 500.100 - 内部服务器错误」

原因:IIS 未启用 ASP 功能,或应用程序池 .NET CLR 版本设为 v2.0/v4.0(Classic ASP 必须用「无托管代码」)
解决:

  1. 进入「Windows 功能」确认 ASP 已勾选;
  2. 在 IIS 管理器中,右键「应用程序池 → 高级设置」,检查「.NET CLR 版本」是否为<无>;
  3. 修改后执行iisreset /restart,而非仅刷新页面。

4.2 现象:登录后跳转admin/index.asp报错「对象不支持此属性或方法:'Session'」

原因:Session对象未启用,或global.asa文件缺失/损坏(该文件定义 Session_OnStart 等事件)
解决:

  1. 检查根目录是否存在global.asa(内容为空或仅含<SCRIPT RUNAT="Server">Sub Session_OnStart ... End Sub</SCRIPT>即可);
  2. 在 IIS 管理器中,右键网站 → 「属性 → 主目录 → 配置 → 选项」,确认「启用会话状态」已勾选;
  3. 若仍报错,临时在login.asp末尾加Response.Write "Session ID: " & Session.SessionID测试 Session 是否可读。

4.3 现象:user/index.asp显示「Microsoft OLE DB Provider for SQL Server 错误 '80040e4d' 登录失败」

原因:conn.asp中数据库连接字符串的User ID和Password与 SQL Server 实际配置不符,或 SQL Server 未启用混合模式认证
解决:

  1. 打开 SQL Server Management Studio,右键服务器 → 「属性 → 安全性」,确认「SQL Server 和 Windows 身份验证模式」已启用;
  2. 在「安全性 → 登录名」中,右键sa→ 「属性 → 状态」,确认「登录」设为「启用」;
  3. 修改conn.asp中密码为 SQL Server 中sa的真实密码(或改用 Windows 身份验证:connstr = "Provider=SQLOLEDB;Data Source=.;Initial Catalog=tour_db;Integrated Security=SSPI;")。

4.4 现象:报名成功后lines.remain_num未减少,但订单已生成

原因:do_apply.asp中UPDATE语句的WHERE条件remain_num > 0未生效,因remain_num字段为NULL(初始数据未设默认值)
解决:

  1. 执行 SQL:ALTER TABLE lines ADD CONSTRAINT DF_lines_remain_num DEFAULT 0 FOR remain_num;
  2. 更新现有 NULL 值:UPDATE lines SET remain_num = 0 WHERE remain_num IS NULL;
  3. 重新执行报名流程测试。

4.5 现象:中文乱码,登录页显示「??????」

原因:ASP 页面未声明 UTF-8 编码,且数据库字段为varchar(非nvarchar)
解决:

  1. 在所有.asp文件顶部添加:<%@ CodePage=65001 %>(65001 = UTF-8);
  2. 在<head>中加<meta charset="UTF-8">;
  3. 检查数据库字段:ALTER TABLE users ALTER COLUMN username NVARCHAR(50)(同理改password,realname等);
  4. 修改conn.asp连接字符串,追加;Charset=utf-8(部分 OLE DB 驱动支持)。

5. 二次开发实战:给老系统装上微信通知与导出 Excel 功能

5.1 微信模板消息接入:用 ASP 调用微信 API 发送订单提醒

系统原生无通知,用户报名后只能等邮件(且没配邮件服务)。我们给do_apply.asp加微信通知,不引入新框架,只用原生ServerXMLHTTP。

前提:你已在微信公众平台申请服务号,获取APPID、APPSECRET,并配置模板消息(模板 ID 如TM000123456)。

步骤:

  1. 在/common/wechat.asp中封装微信 Token 获取与消息发送函数:
<% ' /common/wechat.asp Function GetAccessToken() Dim http, url, responseText url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=YOUR_APPID&secret=YOUR_APPSECRET" Set http = Server.CreateObject("MSXML2.ServerXMLHTTP") http.Open "GET", url, False http.Send responseText = http.responseText GetAccessToken = Eval("(" & responseText & ")").access_token End Function Function SendOrderNotice(openid, order_id, line_name) Dim accessToken, http, url, postData, responseText accessToken = GetAccessToken() url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" & accessToken postData = "{""touser"":""" & openid & """,""template_id"":""TM000123456"",""data"":{""first"":{""value"":""您的订单已提交成功!"",""color"":""#173177""},""keyword1"":{""value"":""" & order_id & """,""color"":""#173177""},""keyword2"":{""value"":""" & line_name & """,""color"":""#173177""},""remark"":{""value"":""请耐心等待客服联系,祝旅途愉快!"",""color"":""#173177""}}}" Set http = Server.CreateObject("MSXML2.ServerXMLHTTP") http.Open "POST", url, False http.setRequestHeader "Content-Type", "application/json" http.Send postData responseText = http.responseText End Function %>
  1. 在do_apply.asp末尾(conn.CommitTrans后)调用:
' 在 CommitTrans 后添加 Dim openid, line_name openid = "oAbc1234567890xyz" ' 实际应从 users 表查,此处简化 line_name = "海南三亚五日游" ' 实际应查 lines 表 Call SendOrderNotice(openid, rs("id"), line_name) ' rs("id") 是刚插入的订单ID

注意:Eval("(" & json & ")")是 Classic ASP 解析 JSON 的土办法,依赖ScriptControl组件(Windows 自带)。若服务器禁用,需改用正则提取access_token字符串。

5.2 Excel 导出功能:用 ADO 生成.xls文件供后台下载

管理员需导出订单报表,原系统只有页面表格。我们用 ADO 的Recordset.Save方法直接生成 Excel 文件:

<% ' /admin/export_orders.asp Response.ContentType = "application/vnd.ms-excel" Response.AddHeader "Content-Disposition", "attachment;filename=orders_" & Year(Now) & Month(Now) & Day(Now) & ".xls" ' 查询订单及关联用户、线路信息 sql = "SELECT o.id as 订单ID, u.username as 用户名, l.line_name as 线路名称, " & _ "o.apply_time as 报名时间, o.status as 状态 " & _ "FROM orders o " & _ "JOIN users u ON o.user_id = u.id " & _ "JOIN lines l ON o.line_id = l.id " & _ "WHERE o.apply_time >= '" & DateAdd("d", -30, Now) & "' " & _ "ORDER BY o.apply_time DESC" Set rs = Server.CreateObject("ADODB.Recordset") rs.Open sql, conn, 1, 1 ' 直接输出 HTML 表格(Excel 能识别) Response.Write "<table border='1'><tr>" For i = 0 To rs.Fields.Count - 1 Response.Write "<th>" & rs.Fields(i).Name & "</th>" Next Response.Write "</tr>" Do While Not rs.EOF Response.Write "<tr>" For i = 0 To rs.Fields.Count - 1 Response.Write "<td>" & rs.Fields(i).Value & "</td>" Next Response.Write "</tr>" rs.MoveNext Loop Response.Write "</table>" %>

优势:

  • 不依赖第三方组件,纯 ASP 实现;
  • 输出为.xls(非.xlsx),兼容 Excel 2003+;
  • Response.ContentType和Content-Disposition强制浏览器下载而非打开;
  • 表头用中文,字段名直接映射,无需额外翻译层。

5.3 安全加固:从明文密码到参数化查询的渐进式改造

毕业设计包的conn.asp用明文sa账号,check_login.asp用字符串拼接 SQL。生产环境必须升级,但不能推倒重来。我的渐进式改造路径:

风险点毕业设计现状生产环境改造方案验证方式
数据库账号sa明文密码创建专用账号tour_app,仅授SELECT/INSERT/UPDATE权限EXEC sp_helplogins 'tour_app'查权限
SQL 注入Replace(..., "'", "''")改用ADODB.Command对象参数化查询构造' OR '1'='1输入,检查是否仍能登录
密码存储明文存users.password改用SHA1(用户名+密码+盐),字段扩为VARCHAR(40)新注册用户密码长度变为 40 位
Session 安全无超时设置在global.asa中Session.Timeout = 20(分钟)登录后闲置 21 分钟,再访问提示重新登录

关键代码(参数化查询示例):

' 替换 check_login.asp 中的拼接 SQL Set cmd = Server.CreateObject("ADODB.Command") cmd.ActiveConnection = conn cmd.CommandText = "SELECT * FROM users WHERE username = ? AND password = ?" cmd.Parameters.Append cmd.CreateParameter("@username", 200, 1, 50, username) ' 200=adVarChar cmd.Parameters.Append cmd.CreateParameter("@password", 200, 1, 50, password) Set rs = cmd.Execute

从那以后我每次接手 Classic ASP 项目,都强制走一遍「连接字符串脱敏 → Session 超时设置 → 关键 SQL 参数化」三步检查清单,哪怕只是临时修个 Bug。老系统不是技术债,是业务连续性的锚点;能跑通、能改、能守得住,才是真功夫。希望帮到你。

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

返回列表