资讯动态

旅游网站设计与开发:从数据模型到开题答辩的工程化实践

发布时间:2026/10/27 5:53:26 来源:尧图企业网站定制
开头这个题目我见过太多次了——“基于XX的旅游网站的设计与开发源码开题”每年选题季都会有一批人拿着几乎一样的标题来找我聊。说实话旅游网站这个题目本身不难难的是大部分人把它做成了“能跑起来的增删改查”然后在开题答辩和最终验收的时候被问得哑口无言。旅游网站的设计与开发本质上是把一个真实的业务场景用户找目的地、看线路、比价格、下单、支付、评价、售后压缩到一个可交付的工程量里同时还要配上开题报告说清楚“为什么这么做”。配套的源码不是越多越好而是要能讲清楚每一块的取舍开题也不是套模板填空它决定了你后面三个月是顺着走还是反复返工。我把这篇东西写给三类人看第一类是刚定下题目、还在纠结技术栈和功能清单的第二类是代码写了一部分发现订单、库存、图片这些地方越写越乱的第三类是代码差不多能跑了但开题报告和技术文档写不出来、答辩心里没底的。全文围绕一个通用技术路线展开前端框架 后端服务 关系型数据库 对象存储具体技术名词你按自己熟悉的替换即可思路是通用的。里面会有表结构、代码片段、参数计算也会有一些只有真正动手做过才会知道的坑——比如为什么你的线路详情页加载要三秒、为什么并发下单会超卖、为什么图片上传在本地好好的、一部署就 404。1. 先把题目拆开旅游网站到底要做成什么样1.1 “基于XX”这四个字决定了你一半的技术路线很多人写题目的时候随手填个技术栈等到中期检查才发现自己根本讲不清为什么选它。题目里的“基于XX”不是装饰它是你整篇文档的技术主线你后面所有的架构图、模块划分、部署方案都要能回溯到这四个字上。我的建议是把“基于”拆成三层来理解。第一层是运行形态——是单体应用还是前后端分离是本地部署还是容器化部署。第二层是核心框架——后端用什么语言和框架前端用什么框架这决定了你的代码风格和目录结构。第三层是数据与中间件——数据库选关系型还是文档型缓存要不要上搜索用数据库模糊查询还是独立检索引擎。对旅游网站这个场景我给一个相对稳妥的组合前后端分离的单体后端 关系型数据库 本地或对象存储放图片 一个轻量缓存。理由很实在——旅游网站的核心数据用户、线路、订单、库存、评价之间的关系非常强线路属于某个目的地、订单关联用户和线路、评价关联订单这种强关联用关系型数据库表达最自然写 SQL 联表查询、做事务控制都顺手。如果你的题目写的是文档型数据库那你得额外解释清楚“订单一致性怎么保证”这是个给自己找麻烦的选择。至于微服务我劝退。一个课程级或毕业设计级的旅游网站硬拆成五六个服务带来的不是加分而是负担服务注册发现、链路追踪、分布式事务每一样都要写文档、都要在答辩时被追问。单体分模块按 controller / service / mapper 分层按业务域分包已经足够体现工程能力而且你能把精力放在业务逻辑上。提示题目里的技术栈一旦写进开题报告后面改起来很麻烦。定之前先问自己三个问题我能不能用它写出一个完整的增删改查我能不能解释它在这个场景下的优势我本地环境能不能稳定跑起来三个都是“能”再定。1.2 功能清单应该怎么砍三类角色、五个闭环旅游网站的功能可以列出一百项但你的时间只够做二十项。砍功能的标准不是“看起来酷不酷”而是“能不能形成闭环”。我一般把功能按角色切三刀游客/普通用户、注册用户、后台管理员。普通用户侧需要形成的最小闭环是浏览 → 检索 → 详情 → 下单 → 支付模拟→ 查看订单 → 评价。后台侧需要形成的最小闭环是登录 → 线路/景点管理 → 订单管理 → 用户管理 → 内容轮播、公告管理。这两个闭环走通了你的系统就是“完整”的而不是“半截”的。具体到功能模块我整理成下面这张表你可以直接拿去当开题报告的功能清单初稿模块面向角色核心功能是否必做用户模块全部注册、登录、找回密码、个人资料、头像上传必做目的地/景点模块全部分类浏览、列表、详情、图文介绍、交通与贴士必做旅游线路模块全部线路列表、筛选天数/价格/主题、详情、行程安排必做检索模块全部关键词搜索、条件筛选、排序、分页必做订单模块注册用户下单、订单状态流转、取消、订单详情必做支付模块注册用户模拟支付、支付回调、支付状态同步必做模拟即可评价模块注册用户订单完成后评价、评分、图片评价、展示建议做收藏/足迹注册用户收藏线路、浏览记录选做后台内容管理管理员轮播图、公告、攻略文章建议做后台业务管理管理员线路 CRUD、库存维护、订单处理、退款必做数据统计管理员订单量、销售额、热门线路 Top N选做加分项客服/留言全部留言板、常见问题选做砍功能的时候有个技巧选做项不要删掉写进“后续可扩展”里。开题报告的评审看的不是你做了多少而是你有没有想清楚范围。你在文档里写“本期聚焦交易主链路收藏与社交分享作为二期扩展”比硬做十个半成品要专业得多。1.3 先写文档还是先写代码一个被低估的顺序问题我的经验是先画数据模型再写开题最后写代码。原因很朴素——开题报告里最难写的部分是“系统设计”而系统设计的核心就是数据模型和模块划分。你如果先把表设计出来开题报告的设计章节基本就是“把图翻译成文字”半小时能写完反过来你先写一堆技术名词堆砌的文档等真开始写代码会发现表结构跟文档对不上来回改文档和代码两边都烂。具体操作上我会先在一张纸上列出所有实体用户、目的地、景点、线路、行程日、订单、订单明细、支付记录、评价、轮播图、管理员然后画连线标关系一对多、多对多。多对多的地方一定要有中间表。这一步花两个小时能省掉后面两周的返工。2. 数据模型旅游网站真正的地基2.1 核心表结构怎么设计才不别扭旅游网站的数据模型有两个特点一是层级多目的地 → 景点 → 线路 → 行程日 → 行程项二是状态多订单有状态、支付有状态、线路有上架状态。层级多容易设计成“一张表塞所有字段”状态多容易设计成“一个字段存所有状态”。先说层级。目的地和景点是一对多景点和线路是多对多一条线路会串多个景点线路和行程日是一对多行程日和行程项是一对多。这个结构不要偷懒合并尤其是行程日单独一张表——因为详情页要按“第1天、第2天”渲染时间轴你如果把它塞成一个 JSON 字段后面想按天查、想排序就很痛苦。下面是核心表的一个简化版本字段名用下划线风格你可以按自己的规范调整-- 用户表 CREATE TABLE t_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(32) NOT NULL UNIQUE COMMENT 登录名, password VARCHAR(128) NOT NULL COMMENT 加盐哈希后的密码, salt VARCHAR(32) NOT NULL COMMENT 盐值, nickname VARCHAR(32) DEFAULT NULL, avatar VARCHAR(255) DEFAULT NULL COMMENT 头像地址, phone VARCHAR(20) DEFAULT NULL, role TINYINT NOT NULL DEFAULT 0 COMMENT 0普通用户 1管理员, status TINYINT NOT NULL DEFAULT 1 COMMENT 1正常 0禁用, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; -- 线路表核心商品 CREATE TABLE t_route ( id BIGINT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(120) NOT NULL COMMENT 线路标题, dest_id BIGINT NOT NULL COMMENT 目的地ID, cover_img VARCHAR(255) NOT NULL COMMENT 封面图, days INT NOT NULL COMMENT 行程天数, price DECIMAL(10,2) NOT NULL COMMENT 成人价, child_price DECIMAL(10,2) DEFAULT NULL COMMENT 儿童价, stock INT NOT NULL DEFAULT 0 COMMENT 可售库存, sold INT NOT NULL DEFAULT 0 COMMENT 已售数量, themes VARCHAR(100) DEFAULT NULL COMMENT 主题标签,逗号分隔, status TINYINT NOT NULL DEFAULT 0 COMMENT 0下架 1上架, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_dest (dest_id), KEY idx_status_price (status, price) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT旅游线路表;这里有几个点值得单独说。价格用 DECIMAL 而不是 DOUBLE浮点数做金额计算会出现 199.99999999 这种事等你做对账的时候会疯。库存和已售分成两个字段而不是只存一个库存然后减掉——因为“已售”是要展示给用户看的“已有 328 人预订”也是后台统计要用的两个字段各自维护比反推要清晰。主题标签用逗号分隔的字符串在课程级项目里是可以接受的别硬上标签表除非你确实要做标签筛选的多条件组合。2.2 订单表整个系统里最容易出事的一张表订单表的坑主要在状态设计。我见过太多人把订单状态做成一堆布尔字段is_paid、is_cancelled、is_refunded、is_finished。结果是“已支付且已取消”这种非法状态也能被组合出来日志一看全是脏数据。正确做法是单一状态字段 明确定义的流转图。我把状态定义成这样0 待支付、1 已支付待出行、2 已完成、3 已取消、4 已退款。流转规则写死在服务层任何非法流转直接抛异常。下面这张表就是我常用来做状态校验的当前状态允许动作目标状态触发条件0 待支付支付1 已支付支付回调成功0 待支付取消3 已取消用户主动取消或超时未支付1 已支付出行完成2 已完成管理员确认或到期自动1 已支付退款4 已退款管理员审核通过2 已完成评价2 已完成生成评价记录3 已取消——终态4 已退款——终态另外订单主表和订单明细表一定要拆开。主表存订单号、用户、总额、状态、联系人信息明细表存“这条订单买了哪条线路、几个人、单价、小计”。为什么因为后面你很可能要做“一单多线路”购物车式下单主表结构不用动明细表加行就行。订单号别用自增 ID 直接暴露用“日期 随机数 自增”拼一个业务单号既好查又不泄露业务量。2.3 索引与查询让详情页从三秒变三百毫秒索引这件事不写不觉得一写就知道疼。旅游网站最典型的查询是三种按目的地 状态 价格排序的列表查询、按关键词的模糊搜索、按用户查订单。第一种查询索引建成(dest_id, status, price)这样的组合索引注意顺序区分度高的、等值条件在前范围条件在后。你把 price 放前面MySQL 用不上后面的 dest_id这是很常见的失误。第二种模糊搜索LIKE %关键词%是走不了索引的数据量小的时候无所谓数据量上万就开始卡。课程级项目里我的处理方式是标题和简介拼接成一个search_text字段用LIKE 关键词%做前缀匹配能走索引同时对搜索结果按“标题命中优先、简介命中其次”排序。第三种订单查询建(user_id, status, create_time)组合索引用户中心的订单列表基本就是这个查询模式。还有一个容易被忽略的点详情页的 N1 查询。线路详情要展示目的地名、行程日列表、每天的景点、评价列表、评价人昵称——如果你一个线路查一次然后循环里再查五次查询变五十次。解决办法是用联表查询一次拿到主数据再用IN批量查关联数据在服务层组装。我实测过一条线路详情改成批量查询之后接口耗时从 800ms 降到 90ms 左右差距全在往返次数上。3. 后端核心功能把主链路一条条打通3.1 用户认证与权限别自己发明加密方案用户模块看着简单但它是安全问题的重灾区。我见过最离谱的做法是数据库里存明文密码稍微好一点的是 MD5 直接存。这两种都不行。正确做法是每个用户一个随机盐 慢哈希。注册时生成 16 位随机盐密码和盐一起做哈希比如 BCrypt 或者 PBKDF2迭代次数别太低数据库只存盐和哈希值。登录时用同样的方式算一遍对比全程不落明文。这里我要强调一句不要自己写加密算法也不要用简单的“MD5(密码 固定字符串)”固定盐等于没盐撞库一撞一个准。登录态用 Token 方案比较省事登录成功后服务端签发一个带过期时间的 Token前端存起来之后每次请求放在请求头里服务端拦截器统一校验。Token 里放用户 ID 和角色不要放敏感信息因为它是可解码的。拦截器里顺便把“哪些接口需要登录、哪些接口需要管理员角色”配置成白名单/黑名单比在每个接口里写判断要干净// 拦截器中的权限判断示意 Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String uri request.getRequestURI(); // 白名单直接放行 if (WHITE_LIST.contains(uri)) { return true; } String token request.getHeader(Authorization); Claims claims tokenService.parse(token); // 解析失败会抛异常 if (claims null) { throw new BizException(401, 登录已过期请重新登录); } Integer role claims.get(role, Integer.class); // 后台接口统一前缀 /admin要求管理员角色 if (uri.startsWith(/admin) (role null || role ! 1)) { throw new BizException(403, 无权限访问); } request.setAttribute(userId, claims.get(uid, Long.class)); return true; }这里有个实操细节Token 过期时间别设太长也别太短。设 30 分钟用户下单填一半被踢出去体验极差设 30 天安全性又成问题。我的做法是访问 Token 设 2 小时同时前端在快过期时静默刷新一次用户几乎感知不到。另外退出登录不是把前端存的 Token 删掉就完事——最好在服务端维护一个短期的失效名单比如存到缓存里键是 Token 的哈希过期时间和 Token 一致否则被截获的 Token 在过期前一直有效。3.2 线路检索与分页排序规则的细节决定体验列表页是旅游网站的门面用户在这里做决策。排序、筛选、分页三件事必须做好。分页我建议服务端分页别图省事一次查出全部然后前端切页。线路数据上万条的时候一次全查内存直接爆。服务端分页的关键是“查询总数 查询当页数据”两条 SQL且两条 SQL 的 where 条件必须完全一致——我遇到过很多人写的时候count 语句漏了一个筛选条件导致分页器显示 20 页点到第 8 页就没数据了。排序规则要写清楚优先级。默认排序我一般这么定上架状态优先 是否有库存 销量降序 价格升序 上架时间降序。为什么要加“是否有库存”这一层因为用户看到一个卖完的线路排第一点击进去发现不能买体验很糟。库存为 0 的线路应该沉底或者打上“已售罄”标签。筛选条件里价格区间的处理要注意用户传进来的是“最低价-最高价”但你的数据库存的是单价。这时候不能简单price BETWEEN min AND max因为线路可能有成人价和儿童价用户期望的是“这条线路有任何一种价格落在区间内”。这是个业务理解问题不是技术问题但很多人想不起来。我的处理是给线路冗余一个min_price字段下单时按最便宜的算筛选只查这一个字段。3.3 下单与库存并发超卖的三种解法这是整个项目最有技术含量的一块也是答辩时最容易被追问的地方。“如果两个人同时买最后一张票会怎么样”——这个问题你答不好前面的功能做得再花哨也会被扣分。问题本质是UPDATE t_route SET stock stock - 1 WHERE id ?这条语句在并发下会读到同一个库存值然后各自减一库存变成 -1也就是超卖。解法一悲观锁在事务里SELECT ... FOR UPDATE锁住这行其他人排队等。简单可靠但并发一高就排队响应变慢。解法二乐观锁表里加个version字段更新时带WHERE version 旧值影响行数为 0 就说明被改过重试或报错。适合冲突不频繁的场景。解法三条件更新直接UPDATE t_route SET stock stock - 1, sold sold 1 WHERE id ? AND stock 1看影响行数判断是否成功。这是我最推荐的方式一条 SQL 搞定不需要额外字段也不会有长时间锁等待。Transactional(rollbackFor Exception.class) public Long createOrder(Long userId, Long routeId, int quantity) { // 1. 条件更新扣减库存影响行数 0 说明库存不足 int affected routeMapper.deductStock(routeId, quantity); if (affected 0) { throw new BizException(该线路库存不足请选择其他日期或线路); } // 2. 生成订单主表 Order order new Order(); order.setOrderNo(orderNoGenerator.next()); order.setUserId(userId); order.setStatus(OrderStatus.UNPAID.getCode()); orderMapper.insert(order); // 3. 生成订单明细价格从数据库重新读取绝不信任前端传值 Route route routeMapper.selectById(routeId); OrderItem item new OrderItem(); item.setOrderId(order.getId()); item.setRouteId(routeId); item.setPrice(route.getPrice()); item.setQuantity(quantity); orderItemMapper.insert(item); return order.getId(); }对应的 Mapper SQLupdate iddeductStock UPDATE t_route SET stock stock - #{quantity}, sold sold #{quantity} WHERE id #{routeId} AND status 1 AND stock gt; #{quantity} /update这段代码里有两个细节值得强调。第一价格必须从数据库重新查前端传来的价格一律不信否则改个请求参数就能一元游全国。第二扣库存和建订单必须在同一个事务里否则扣了库存订单没生成库存就白白少了一个。第三如果后面加了“超时未支付自动取消”取消时要反向补库存而且要用“只补一次”的幂等控制比如状态更新带WHERE status 0不然重复回调会把库存补多。注意下单接口一定要做防重复提交。用户在手机上网络卡连点两下“提交订单”就生成两笔订单。做法很简单前端提交后按钮置灰后端对一个用户短时间内的重复请求做拦截比如 3 秒内的相同参数只处理一次。3.4 图片上传旅游网站的“半条命”旅游网站是重图片的封面图、轮播图、行程图、评价图全是图。图片这块如果处理不好会出现三个典型问题上传后本地能看、部署后 404图片太大导致列表页加载慢换台电脑跑起来图片全丢了。第一个问题根源在存储路径。很多人把上传目录写成D:/upload/这种绝对路径本地跑得好好的部署到服务器上当然找不到。正确做法是配置一个可外部化的根路径代码里拼相对路径存库访问时通过静态资源映射或者统一的文件访问接口输出。第二个问题上传时必须压缩。手机拍的照片动辄三五兆一个列表页二十张图就是上百兆。我一般在上传接口里做两件事限制大小比如单张不超过 5MB、按最长边等比缩放到 1600px 以内再存。缩略图另外生成一份 400px 的列表页用缩略图详情页用大图。这一套做下来列表页的加载时间能砍掉一大半。第三个问题本地存储换成对象存储。课程级项目用本地目录完全够用但你要在文档里写清楚“如果上生产建议替换为对象存储只需改配置不用改业务代码”——这句话体现的是架构意识评审老师很吃这一套。实现的技巧是把文件上传抽成一个接口FileStorageService本地实现和对象存储实现都实现这个接口通过配置切换。4. 前端页面与交互让系统看起来像那么回事4.1 页面结构规划先画路由再写组件前端最容易失控的地方是页面散、组件乱、接口调用到处写。开工前先把路由表列出来我一般这么分路由页面是否需要登录/首页轮播、热门目的地、推荐线路否/destinations目的地列表否/destinations/:id目的地详情否/routes线路列表筛选、排序、分页否/routes/:id线路详情行程、评价、下单入口否/search搜索结果否/login、/register登录注册否/user/orders我的订单是/user/orders/:id订单详情是/user/profile个人中心是/admin/**后台管理是管理员路由规划清楚之后组件拆分就有依据了列表页的筛选栏、线路卡片、分页器详情页的图片轮播、行程时间轴、评价列表、下单弹层——这些都是可复用的独立组件。新手常犯的错误是把所有东西写在一个页面文件里一个.vue文件写到两千行改一个地方要滚半天。4.2 列表页与详情页的关键实现列表页的核心是状态与 URL 同步。筛选条件、排序方式、页码都应该反映在 URL 上这样用户刷新页面筛选不丢、分享链接别人能打开同样的结果。实现上就是监听路由参数变化参数变了重新请求。很多人把筛选条件放在组件内部变量里用户一刷新回到初始状态这是个很小但很影响体验的细节。详情页的核心是首屏速度。图片懒加载必须做行程时间轴的数据可以随主接口一起返回数据量不大但评价列表单独分页请求不要塞进主接口。下单弹层里的日期选择要限制“只能选今天之后”数量要限制“不超过剩余库存”这些约束前端要做后端也要再做一遍——前端是体验后端是底线。4.3 接口封装与错误处理统一比聪明重要前端的接口调用一定要统一封装。我的做法是建一个请求工具文件统一处理四件事基础地址拼接、Token 自动携带、响应码统一判断、异常统一提示。// 统一的请求封装示意 async function request(url, options {}) { const token storage.get(token); const res await fetch(API_BASE url, { ...options, headers: { Content-Type: application/json, ...(token ? { Authorization: token } : {}), ...(options.headers || {}) } }); const data await res.json(); if (data.code 401) { storage.remove(token); router.push(/login); throw new Error(请先登录); } if (data.code ! 0) { // 统一弹提示业务层就不用每处都写 toast(data.message || 请求失败); throw new Error(data.message); } return data.data; }这样写的好处是后端改返回结构你只改一处登录过期统一跳转不用每个页面都判断。坏处是容易把“业务错误”和“系统错误”混在一起所以我在封装里区分了code的含义——401走登录跳转403走无权限提示其他业务错误统一 toast。5. 开题报告怎么写才不像模板填空5.1 开题的标准结构与本项目的对应关系开题报告的结构基本是固定的选题背景与意义、国内外研究现状、研究内容与目标、技术路线与方案、进度安排、参考文献。真正拉开差距的是背景意义和技术路线两节其他部分只要信息准确、格式规范就行。背景意义不要写成“旅游业发展迅速互联网普及率高”这种空话。要落到具体问题上用户在做旅游决策时面临信息分散、比价困难、行程规划成本高中小型旅游服务商缺乏低成本的自有展示与预订渠道。你的系统解决的是“把分散的目的地信息、线路产品、用户评价整合到一个可检索、可预订的入口”这就是意义。前后呼应评审一看就知道你想过。技术路线要写选型理由而不是罗列名词。写“后端采用某主流开发框架前端采用某主流前端框架数据库采用关系型数据库”是零分的写法。要写“后端选择该框架是因为其内置的依赖注入与事务管理能简化订单这类强一致性业务前端选择该框架是因为组件化开发便于线路卡片、评价列表等模块复用数据库选关系型是因为用户-订单-线路之间关联紧密需要事务保证”。同样的技术不同的表述分数不一样。5.2 创新点怎么写才不尴尬“创新点”这个词对本科生项目很不友好因为大部分项目确实没有学术创新。我的建议是不硬造创新写“设计特点”把你在工程上做的取舍讲出来这本身就是价值。比如这几条就很实在一是库存扣减采用条件更新避免超卖在高并发下单场景下保证数据一致二是图片分级存储与压缩通过缩略图与原图分离降低列表页加载压力三是订单状态机设计用单一状态字段配合流转校验避免非法状态组合四是前后端分离 统一响应封装降低接口维护成本。这些话术不夸张但每一条都能对应到你代码里的具体位置答辩时被问到能立刻打开文件指给老师看这就是底气。5.3 进度安排和参考文献的两个小技巧进度安排要倒推。别从今天往后排从交付日期往前倒最后两周留给论文撰写和修改倒数第三周留给系统测试和演示准备再往前是联调和补功能。这样排出来的计划才现实不会出现“第 10 周完成所有功能第 11 周写论文”这种一看就不可能完成的安排。参考文献别凑数。三五个英文文献加十个中文文献是常规配置但每一篇最好都能在你的正文里被引用到——比如讲前后端分离的时候引用一篇架构相关的讲库存并发的时候引用一篇数据库事务相关的。参考文献和你正文对不上是最容易被挑出来的问题。6. 部署、演示与验收最后一公里6.1 本地环境与打包注意事项打包之前先做一次全量自测清单注册登录、搜索筛选、详情浏览、下单、支付模拟、取消订单、后台增删改查、图片上传。每一项都点一遍别只测自己刚改的那个。打包时最容易忽略的是环境配置分离。数据库地址、账号密码、文件存储路径、跨域配置这些都不能写死在代码里。用配置文件加环境区分开发/生产两套配置占位符里填真实值提交到仓库前把真实密码去掉换成示例值——这是个很小的动作但在评审眼里是专业度的体现。数据库脚本也要整理出来建表语句按依赖顺序排先用户、目的地再线路最后订单、初始化数据管理员账号、几条示例线路、目的地。这样别人拿到你的源码能一把跑起来这是“可复现”的关键。6.2 演示环节的几个实用细节演示最怕的不是功能少是演示中途崩了。我的经验是准备三层保险第一层演示用的数据提前录好别现场输第二层把演示流程写成一个清单按顺序点不即兴发挥第三层准备一个录屏网络或环境出问题时直接放录屏讲的时候心里不慌。时间分配上二十分钟的演示五分钟讲背景和功能概览十分钟走主流程浏览 → 检索 → 详情 → 下单 → 后台处理订单五分钟讲技术亮点库存并发、图片处理、状态机。技术亮点一定要主动讲不要让老师去问你主动讲出来是加分被问出来是应试。7. 常见问题与排查实录7.1 高频报错速查表下面这张表是我这些年被问得最多的问题基本覆盖了八成新手卡点现象大概率原因排查与解决前端请求报跨域错误后端未配置跨域或预检请求被拦截后端统一配置允许的来源与方法检查拦截器是否放行了 OPTIONS 请求登录后刷新页面就退出了Token 存在内存或会话里刷新丢失改为持久化存储检查请求头是否每次携带图片上传成功但访问 404存储路径是绝对路径或静态资源未映射改为相对路径存库配置静态资源目录映射检查访问前缀列表分页点第二页没数据分页参数从 0 开始还是从 1 开始不一致统一约定后端接收时做一次转换并写注释下单后库存没变或减多了扣减与建单不在同一事务或重复提交加事务注解加防重检查条件更新的影响行数判断详情页加载特别慢N1 查询或大图未压缩批量查询替代循环查图片压缩 缩略图 懒加载后台编辑线路后前台不更新缓存未失效更新操作后主动清理对应缓存键或设置较短过期时间中文存入数据库变成乱码连接串或表字符集不是 utf8mb4连接串加字符集参数建表指定 utf8mb4字段长度留余量7.2 几个只有踩过才知道的坑第一订单号重复。用“时间戳 随机数”生成订单号的在压测或者快速连点时会撞。要么加机器标识要么用数据库自增做后缀要么直接用雪花算法类的方案。别觉得概率低就不管撞一次排查半天。第二金额计算的分歧。前端算一遍总价用于展示后端算一遍总价用于入库两边算法不一致就会出现“显示 998实付 1000”。解决办法是以后端为准前端展示的价格只做参考提交后以后端返回的订单金额为准刷新页面。第三删除操作的连锁反应。后台删除一条线路如果已经有订单关联直接物理删除会导致订单详情查不到线路信息页面报空指针。正确做法是逻辑删除加is_deleted字段或者下架处理同时对已关联订单做保护有未完成订单的线路不允许删除。第四时间与时区。订单创建时间、支付时间、出行日期这三个时间类型不一样。创建和支付是时间点用 DATETIME出行日期是日期用 DATE。混用会导致“出行日期显示成前一天 23:59:59”这种问题。这一点在跨时区部署时更明显课程项目里统一用服务器本地时区就行但要在文档里写清楚约定。第五测试数据太假。演示的时候线路全是“测试线路1、测试线路2”封面图是一张灰色占位图整个系统看起来就像没做完。花两个小时录入十条真实感强的数据目的地介绍、行程安排、几张好看的风景图观感提升是数量级的。这件事投入产出比极高但大部分人最后才想起来。最后分享一个我在做这类项目时一直坚持的习惯每完成一个模块就把这个模块的表结构、接口、关键代码位置记在一个小本子上。等到写论文、做答辩 PPT、回答老师提问的时候这个本子就是你的索引。项目做完了你未必记得每个类在哪但你一定记得“库存扣减在订单服务里用的是条件更新”。这个习惯看着笨但它能让你在整个交付周期里始终保持对项目的掌控感而不是到最后两周疯狂翻代码找自己在哪写过什么。

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

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

免费获取报价 →
↑