
广州榄雕非遗文化展示和体验预约系统本质上是一个带有内容展示、预约交易和后台管理的综合型业务系统。它既不是单纯的非遗官网也不是只做报名登记的表单工具而是把“文化内容展示、体验活动发布、用户预约、后台审核、名额管理”串成了一条完整业务链路。这个项目适合两类人一类是计算机相关专业的学生拿它当毕业设计或课程设计另一类是刚学会 PHP 和 Vue 基础、想用一个完整项目把前后端串起来练手的新手。这个项目最值得关注的点不是界面做得有多炫而是预约名额怎么防止超卖、活动状态怎么流转、前台和后台怎么通过接口联动。把这些东西想明白了比堆叠功能更有价值。下面按实际开发顺序把这个系统的构建过程拆一遍。我会把业务模型、技术选型、数据库设计、接口实现、前端联调、测试排查和答辩重点都放到具体场景里讲。1. 先抓住核心这个系统做的是展示、预约还是内容管理1.1 业务拆分文化展示、体验预约、后台管理广州榄雕是传统美术类非遗项目以乌榄核为材料进行雕刻通常需要展示作品、介绍传承人、讲述工艺技法。体验预约则要解决“线下传习体验活动怎么报名”的问题。一个完整系统里至少要拆成三个模块。第一个是文化展示模块。它负责管理非遗项目介绍、榄雕作品、传承人信息、新闻动态。这些内容不是死的管理员要能增删改查用户在前台能看到受控的内容列表。第二个是体验活动模块。主办方发布体验课程设置时间、地点、名额、费用、状态用户在前台浏览活动选择适合自己的场次提交预约。第三个是预约管理模块。管理员或传承人要对预约记录进行审核、确认、取消、核销同时要保证每个场次的剩余名额是准确的。这三个模块不是互相独立的。用户预约一个活动会影响活动名额管理员审核一个预约会改变预约状态前台展示的内容来自管理员录入的数据。所以数据库表之间必须有一对多、多对一这种明确的关联关系。1.2 什么人适合拿这个项目练手或当毕业设计如果你是临近毕业的学生选这个题目的好处是功能边界清晰、业务场景真实。非遗展示不是虚构需求预约系统也有明显的状态逻辑和并发问题适合写进论文的“核心难点”部分。如果你是刚入门的前后端学习者我建议把它当成一个“完整闭环”的练习项目。先不要追求复杂的权限框架也不要一上来就做分布式、微服务。把用户登录、内容发布、预约下单、后台审核四条基础链路做通就已经覆盖了大部分企业级项目的常见操作。做这个项目时最容易出现的问题是业务没理清就开始写表结构。比如预约记录和体验活动的关系、用户允许预约几次、取消后名额是否归还这些都要在开发前确定否则后面改接口和前端都会很痛苦。2. 技术栈怎么选PHP做主后端Vue做前台Python做辅助2.1 PHP后端选型原生还是框架从标题里的“B54PHP”可以判断这组项目主题是以 PHP 为后端主体的。实际开发时有两种选择。第一种是原生 PHP。优点是部署简单一个 PHP 文件就能跑通接口适合快速理解 HTTP 请求和响应过程。缺点是项目结构一复杂代码会散落在一起维护和答辩都不好讲清楚。第二种是使用 ThinkPHP 或 Laravel 这类框架。框架自带路由、模型、验证器和模板引擎能帮你把控制器、模型、中间件分层管理。毕业设计项目我一般推荐 ThinkPHP因为中文资料多、文档完整、学习曲线比 Laravel 平缓部署到本地 Apache 或 Nginx 也容易。无论选哪种后端要做的事情是一样的处理用户注册登录、对接口做权限控制、读写数据库、校验预约逻辑、返回 JSON 给前端。建议使用 PHP 8.0 以上版本至少也要保证 7.4 以上否则部分依赖兼容性会很麻烦。2.2 Vue 3加Element Plus做前后端分离前端我建议直接用 Vue 3 加 Vite 构建再配上 Element Plus 组件库。Vite 开发环境启动快组件按需引入后打包体积也可控。Element Plus 的表单、表格、弹窗、分页组件能极大减少后台管理页的开发量。前后端分离后项目会分成两个目录后端只提供接口前端只做界面和请求交互。前端通过 Axios 调用后端的/api接口拿到 JSON 数据后渲染页面。这样做的好处是前台展示页和后台管理页可以共用一个前端工程通过路由和登录状态限制管理员入口即可。2.3 Python脚本有哪些合理的辅助位置系统主体不建议用 Python 重写。如果只做演示项目Python 更适合放在辅助脚本的位置。比如开发阶段用 Python 脚本批量生成测试数据、把 Excel 里的活动信息导入数据库、批量压缩或裁剪作品图片。还有一种常见做法是写一个 Python 爬虫或数据清洗脚本从非遗相关的公开资料里整理文本内容。这里要特别注意数据来源的合规性不要使用来源不明的资料也不要批量采集与应用脱节的敏感信息。实际项目中Python 脚本的价值在于“减少重复劳动”而不是替代 PHP 后端。3. 数据库设计从非遗作品表到预约名额约束3.1 核心表结构用户、文化展示、体验活动、预约记录数据库设计是这个项目的核心环节。很多初学者喜欢把所有字段塞进一张大表这是最需要避免的。用户表users存储注册用户和管理员信息至少包含用户名、密码、角色、状态。密码不要明文存放用 PHP 的password_hash()处理后写库。体验活动表activities存储活动标题、开始时间、结束时间、地点、总名额、剩余名额、状态。预约记录表reservations存储用户ID、活动ID、联系人、手机号、参与人数、预约状态、核销码。文化展示部分可以拆成多张表非遗项目分类表、文化资讯表、作品表和传承人表。作品表关联传承人表文化资讯表关联分类表。这样前台展示时可以通过关联关系一次性取出“作者名称”“分类名称”而不是在前端拼字符串。下面是一份简化的表结构示例表名核心字段说明usersid, username, password, role, status角色区分普通用户和管理员categoriesid, name, typetype 区分非遗项目、作品分类、资讯分类articlesid, category_id, title, cover, content文化展示内容关联分类worksid, artisan_id, title, image, intro榄雕作品关联传承人activitiesid, title, start_time, end_time, total_quota, remaining_quota体验活动场次reservationsid, activity_id, user_id, status, verify_code预约记录活动与用户多对一3.2 名额防超卖的数据库关键约束预约类系统最怕的就是名额超卖。一个体验活动只有 20 个名额结果 25 个人预约成功现场就乱了。防超卖不能只靠代码层级先 SELECT 再 UPDATE。因为在并发请求下两个请求可能同时读到剩余名额为 1然后都执行插入最终名额变成负数。最稳妥的方式是使用数据库条件更新语句在更新时同时判断剩余名额是否大于 0。关键约束还有两点。第一预约记录表中要给activity_id和user_id建立唯一索引防止同一个用户重复预约同一场活动如果业务允许一个家庭代报多人则至少要在接口层做查重。第二活动状态要参与判断只有状态为“报名中”的活动才允许创建预约已结束、已取消或被关闭的活动要直接返回错误。4. 后端接口与预约流程先跑通单条再考虑批量4.1 接口划分和登录鉴权后端接口要按模块划分不要一个接口做所有事情。用户模块提供注册、登录、获取当前用户信息接口内容模块提供文化资讯列表、详情、作品列表、传承人列表活动模块提供活动列表、活动详情预约模块提供创建预约、我的预约、取消预约后台管理模块提供活动发布、预约审核、核销等接口。登录鉴权建议用最简单的 JWT 方式。用户登录成功后后端生成一个 token 返回给前端前端在请求头里携带 token后端通过中间件校验。管理员接口还需要校验用户角色普通用户不能调用活动发布和预约审核接口。不要把所有接口都放在未登录可访问状态。前台浏览内容可以公开但创建预约、查看我的预约、取消预约必须登录。管理端接口一律要校验管理员角色。4.2 预约创建的核心实现事务与条件更新创建预约是整个系统里最值得认真写的接口。核心逻辑不是简单 INSERT 一条预约记录而是“扣减名额”和“插入记录”必须作为一个整体完成。推荐做法是开启事务然后执行条件更新语句// 示例代码具体实现以框架为准 $db-beginTransaction(); try { $affected $db-execute( UPDATE activities SET remaining_quota remaining_quota - 1 WHERE id ? AND remaining_quota 0, [$activityId] ); if ($affected 0) { $db-rollBack(); return [code 400, msg 该场次名额已满或活动状态不允许预约]; } $db-execute( INSERT INTO reservations (activity_id, user_id, status, verify_code, created_at) VALUES (?, ?, ?, ?, ?), [$activityId, $userId, $status, $verifyCode, $now] ); $db-commit(); return [code 200, msg 预约成功]; } catch (\Exception $e) { $db-rollBack(); return [code 500, msg 预约失败请稍后重试]; }这段代码里最核心的是WHERE remaining_quota 0。数据库在更新时会锁定对应行两个并发请求同时到达时只有一个请求能成功扣减另一个请求受影响行数为 0从而被正确拦截。取消预约时也要用事务先检查预约是否存在且属于当前用户然后删除或更新预约状态同时把活动的剩余名额加回去。这里要注意只有“待确认”或“已确认”状态的预约才能取消已完成和已取消的记录不能重复操作。4.3 单条预约正确要看的几个判断点接口写完以后不要直接拿并发工具去测。建议先用单个请求跑通一次然后检查这几个判断点活动名额是否从 20 变成 19。预约记录是否插入成功。同一个用户再次预约同一场活动是否被唯一索引拦截。活动状态不是报名中时接口是否返回明确错误。数据库中的remaining_quota是否出现负数。如果这些都没问题再考虑用并发脚本压测。压测并不是为了看性能而是验证名额扣减逻辑不会出错。5. 前端页面和联动展示页、管理页、预约表单5.1 前台页面怎么拆前台页面通常拆成首页、文化展示列表、作品展示、传承人介绍、体验活动列表、活动详情、用户中心。首页建议放推荐活动、热门资讯和代表性作品数据来源是后端的展示接口。文化展示列表要支持翻页列表项包含封面图、标题和简短描述。活动详情页要展示活动开始时间、地点、剩余名额、费用然后用一个按钮引导用户登录预约。预约表单不要设计得太复杂。用户需要填写的内容一般只有联系人、手机号、参与人数和备注。活动ID可以从路由参数或页面详情数据里获取。提交前前端要校验手机号格式、参与人数是否大于 0并显示当前场次剩余名额。5.2 管理端页面怎么拆管理端做到够用即可。核心页面是活动管理、预约审核、内容管理、用户管理。活动管理页面使用表格展示活动列表支持新增、编辑、上下架操作。新增活动时需要设置时间、地点、总名额剩余名额默认等于总名额。预约审核页面展示所有预约记录管理员可以点击“确认”或“拒绝”确认后不影响名额拒绝后应把名额归还给活动。内容管理页面负责资讯分类、作品、传承人信息的维护。管理端和后端接口的关系实际上是同一套接口的两种调用方式。前端根据用户角色显示不同菜单普通用户进入前台管理员进入后台。实现方案可以是用 Vue Router 的由前一环节决定的守卫也可以在路由元信息中设置requiresAdmin: true由前端控制页面级权限。5.3 页面联调时最容易错的地方联调阶段报错最多的通常不是页面本身而是接口调用时的参数名不一致。比如后端接口要求的字段是contact_name前端表单字段却叫name就会导致预约提交后联系人信息为空。要解决这个问题最直接的方法是在 Axios 请求封装里统一使用后端接口约定的字段名或者在提交前做一次明确的字段映射。另一个容易错的地方是跨域配置。开发环境下前端跑在 5173 端口后端跑在 8080 端口后端必须允许跨域否则浏览器会拦截请求。生产部署时则建议通过 Nginx 把前端静态文件和后端接口反代到同一个域名下彻底避免跨域问题。6. 常见报错与排查顺序别一上来就改代码6.1 按现象分类排查接口404、中文乱码、白屏、预约失败接口返回 404先确认后端路由是否配置正确再看 Nginx 或 Apache 的伪静态规则有没有指向正确的入口文件。ThinkPHP 这类框架需要把请求重写到public/index.php缺少 rewrite 规则时访问/api/activity/list就可能直接 404。中文乱码通常集中在两个位置。一是数据库连接字符集不是 utf8mb4二是接口返回的 JSON 没有设置 UTF-8 响应头。检查方式很简单用命令行直接查询数据库看是否正常再通过浏览器看接口返回是否正常逐步隔离出问题在哪一层。前端页面白屏时先打开浏览器控制台看有没有报错。Vue 项目里常见的白屏原因是路由路径错误、组件加载失败、接口返回的数据结构不符合页面预期。不要先怀疑后端要看控制台报错信息指向哪个文件。预约失败时先看返回提示。如果提示“名额已满”就确认活动剩余名额是否真的为 0如果提示“重复预约”就检查唯一索引是否生效如果提示“请先登录”就检查 token 是否过期或请求头是否传对。6.2 环境、依赖、权限、输入顺序排查遇到系统性问题时建议按固定顺序排查看具体现象是整个页面加载失败还是某个接口报错还是单条数据异常。看环境PHP 版本、MySQL 版本、Node 版本是否在要求范围内。看依赖后端是否缺少 PHP 扩展前端是否缺少 packagePython 脚本是否缺少第三方库。看权限数据库账号是否有建表和写入权限后端日志目录是否可写。看输入前端提交的数据格式、参数名、文件编码是否符合后端预期。不要一上来就改代码。很多问题改完代码也没用根源只是.env配置里的数据库密码错了或插件版本不对。7. 从演示项目到完整交付数据准备、测试思路和答辩重点7.1 用Python脚本准备演示数据和测试数据一个空白的预约系统很难演示最好准备一批看起来真实的数据。传统做法是手动往数据库里录入但效率很低。这时候可以用 Python 脚本快速完成。例如把体验活动信息整理到 Excel 表格中然后用 pandas 读取表格数据再通过后端接口批量创建活动# 示例脚本仅用于开发阶段批量导入演示数据 import pandas as pd import requests df pd.read_excel(activities.xlsx) for _, row in df.iterrows(): payload { title: row[标题], start_time: str(row[开始时间]), end_time: str(row[结束时间]), location: row[地点], total_quota: int(row[总名额]), fee: 0, status: 1, } resp requests.post(http://localhost:8080/api/admin/activity/store, jsonpayload, headersheaders) print(row[标题], resp.status_code)注意Python 脚本只是辅助工具不需要进入系统核心链路。这样做的好处是答辩时你可以清晰地说出“哪部分是 PHP 负责、哪部分是 Vue 负责、哪部分是 Python 辅助脚本负责”技术边界很明确。7.2 并发预约测试怎么验证不超卖预约系统的核心难点是并发场景下的名额一致性。很多人会用 JMeter 或 Postman 的 Runner 功能发送多次请求但压测前一定要把环境参数设置清楚。假设某个活动只有 5 个名额你可以准备 20 个不同账号并发提交预约请求。测试结束后检查两件事第一预约记录最多只有 5 条状态为“已确认”或“待确认”的记录第二活动的remaining_quota等于 0且不会出现负数。如果出现超卖首先要检查的代码就是那条条件更新语句。一个很常见的错误是先用 SELECT 查出剩余名额再通过代码里的 if 判断决定是否插入。这种写法在并发下几乎必然超卖因为两个请求可能同时读到了剩余名额为 1。还有一点要提醒如果数据库用的是 MyISAM 引擎事务和行锁是不支持的必须改成 InnoDB。7.3 毕业设计答辩时怎么讲出重点答辩时不用把所有代码都讲一遍评委更关心的是你有没有理解系统和项目里的难点。你可以按这个顺序来讲业务背景广州榄雕非遗文化展示和体验预约的需求→ 技术选型为什么用 PHP 加 Vue而不是其他方案→ 数据库设计核心表有哪些关联关系怎么设计→ 核心功能实现预约创建时如何通过事务和条件更新防止超卖→ 测试验证单接口测试、并发测试、异常场景测试→ 部署方式。如果被问到系统哪些地方可以改进可以提三点预约通知做成短信或邮件提醒活动签到增加二维码核销管理端增加数据报表展示每天预约量、活动热门度。这些话说明你考虑过真实场景比硬背源码更有说服力。这个系统真正落地时最该盯住的不是功能列表而是业务状态、名额一致性和前后端参数约定。先跑通单条预约再试并发先把手动录入跑稳再用 Python 脚本批量导入数据先把数据库和接口边界整理清楚再去做视觉美化。等你把这条链路完整走通几次再回头扩展收藏、留言、轮播图这些功能就会快很多。