☰
基于SSM+Vue的广告管理系统:四角色权限与业务流全解析
2026/10/18 17:52:07 网站建设 项目流程

简介:面向计算机相关专业毕业设计与课程设计场景,这份基于SSM框架的广告管理系统以Java为后端、Vue为前端,采用B/S架构和MySQL数据库,完整实现管理员、广告客户、广告商、广告制作商四类角色,围绕广告投放渠道管理、需求提案、设计交付、签署与结算等核心业务形成闭环,适合需要完整项目参考的学生快速复用。资源共540个文件,约83.42MB,以Java源码、Vue页面、SVG图标、JPG截图、XML配置及SQL脚本为主,并附带部署运行脚本与演示视频,目录按后端代码、前端页面和数据库脚本分层,便于直接导入开发环境核对效果。已有150人学习下载。通过项目可掌握SSM整合、Vue前后端联调、权限分角色设计及广告业务建模思路,是一份可用于开题参照、功能扩展和答辩讲解的成套毕业设计资料。

1. 为什么Java毕业设计选SSM广告管理系统:四角色权限就是答辩加分项

Java毕业设计围着一套SSM框架转,最怕的不是代码写不完,而是做出来的东西只有CRUD,答辩时讲不出业务逻辑。这套基于SSM+Vue的广告管理系统,功能面是完整的:管理员、广告客户、广告商、广告制作商四个角色,从广告需求、提案、设计预览到签署结算,一条业务链路贯穿所有核心表。对正在做毕业设计或课程设计的人来说,它最大的价值是帮你省掉设计权限边界和状态流转的精力,这两块正是评委最爱追问的点。适合手里已经有源码但跑不起来、或者想把功能讲成完整业务故事的人。下面按项目结构、部署方式、业务实现和踩坑排查四个角度拆一遍,哪些能直接用,哪些需要自己填。

2. 系统角色与权限设计:四类账号的权限边界怎么画

2.1 功能矩阵:把投放链路拆给评委看

一条广告从想法到投放,实际要经过需求方、代理方、制作方三个角色接力,再加上管理员的统一管控,正好对应项目里的四类账号。拆这套系统时,第一步不是看代码,而是先画功能矩阵。答辩第一问十有八九是"你这个系统有哪些角色、各自能做什么",这张表就是标准答案。

角色核心模块关键操作
管理员个人中心、广告客户管理、广告商管理、广告制作商管理、广告投放渠道管理账号增删改、投放渠道配置
广告客户个人中心、广告投放渠道管理、广告需求管理、广告提案管理、广告设计预览管理、广告调整信息管理、广告签署管理、广告结算管理提交需求、审核提案、预览设计图、提出调整、签署、结算
广告商个人中心、广告需求管理、广告提案管理、广告制作需求管理、广告设计图交付管理、广告设计预览管理、广告调整信息管理、广告签署管理、广告结算管理承接需求、提交提案、发起制作、交付设计图
广告制作商个人中心、广告制作需求管理、广告设计图交付管理接收制作任务、上传设计图

这四层权限的划分是有讲究的:管理员只管人和渠道,不碰业务单据;广告客户是需求发起方和最终确认方,所以签署和结算都攥在他手里;广告商是中间调度,既接客户需求,又给制作商派单;广告制作商只干执行,做完设计图交付就结束。这种边界清晰的分工,答辩时要当成系统设计亮点来讲,比背几个Controller类名有说服力得多。

2.2 数据库表设计:角色字段放在用户表还是单独建表

这种规模的毕设,一般不需要做五张表的RBAC权限模型,常见的做法是在用户表里放一个role字段,前端靠role渲染菜单,后端靠拦截器做统一校验。表数量少,答辩讲起来不绕;缺点是想加细粒度权限时得回头改造。

CREATE TABLE t_user ( id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键', username VARCHAR(50) NOT NULL COMMENT '登录账号', password VARCHAR(64) NOT NULL COMMENT 'MD5加密后的密码', role INT NOT NULL DEFAULT 1 COMMENT '角色: 1管理员 2广告客户 3广告商 4广告制作商', real_name VARCHAR(50) COMMENT '真实姓名', phone VARCHAR(20) COMMENT '手机号', create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', UNIQUE KEY uk_username (username) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

角色用INT存是SSM项目里的常见做法,查询时靠常量类做映射,比直接存字符串省空间,代价是SQL和Java代码里容易出现裸数字。我一般会顺手建一个UserRole常量类,把1、2、3、4定义成有名字的常量,Service层就不会出现意思不明的魔法值。username加唯一索引,是为登录等值查询服务,同时兜底防重复注册。password字段留64位,是因为MD5固定32位,但要是后期想升级成BCrypt加盐加密,64位才放得下。

对应的登录查询在MyBatis里长这样:

@Select("SELECT id, username, real_name, role, phone " + "FROM t_user WHERE username = #{username} AND password = #{password}") User login(@Param("username") String username, @Param("password") String password);

@Param的作用是给SQL里#{}占位符绑定参数名,漏掉会直接抛MyBatis的BindingException。这条SQL只做凭证校验,不做权限判断,因为用户角色是从查询结果里直接取的,登录成功后放进Session。需要提一句,明文密码查询虽然演示方便,但评委追问安全性时至少得回答"生产环境要加盐加密或换Spring Security",别硬说MD5绝对安全。

2.3 登录校验与拦截器:后端闸门放在哪一层

SSM项目里,真正拦截请求的是SpringMVC的HandlerInterceptor。preHandle在进入Controller之前执行,返回false就中断请求,这是整个系统最值得讲清楚的一层。拆这种项目时发现拦截器通常只做登录校验不做角色校验,属于够用但不深入,二次开发时值得补一层角色判断。

public class AuthInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(); Object loginUser = session.getAttribute("loginUser"); String uri = request.getRequestURI(); // 登录接口和静态资源必须放行,否则页面都打不开 if (uri.contains("/login") || uri.contains("/static/") || uri.contains("/error")) { return true; } if (loginUser == null) { // 未登录:页面请求重定向到登录页,接口请求直接返回401 if (uri.startsWith("/api/")) { response.setStatus(401); response.getWriter().write("{\"code\":401,\"msg\":\"未登录\"}"); } else { response.sendRedirect(request.getContextPath() + "/login.html"); } return false; } return true; } }

这段代码的核心是"分层放行":登录接口不能拦,拦了会死循环;静态资源不能拦,拦了样式和脚本全挂;业务接口则必须校验Session。注意对接口请求返回401而不是跳转页面,因为Vue前端拿到401会自己控制跳转,而不是被HTML登录页糊一脸。如果项目不是前后端分离,.html页面直接由Tomcat渲染,那第二种跳转方式更合适。配置到SpringMVC时,拦截路径要写"/**",别只配"*.do",否则REST风格的接口会漏过去。

<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <mvc:exclude-mapping path="/login"/> <mvc:exclude-mapping path="/static/**"/> <bean class="com.ad.interceptor.AuthInterceptor"/> </mvc:interceptor> </mvc:interceptors>

exclude-mapping的白名单必须和preHandle里的放行逻辑保持一致,两处不一致是个很隐蔽的坑:你在代码里放行了/login,XML里忘了除外,结果登录接口照样被拦。我一般建议白名单集中在XML里维护,Java代码只做Session判断,这样排查时只看一处就行。

提示:Session里的loginUser建议存一个只含id、role、realName的轻量对象,别把整个User实体塞进去,每次请求都带着一堆用不上的字段,纯属浪费。

3. 部署与项目结构:从.bak文件和三个bat脚本还原运行链路

3.1 资源包里的.bak文件是什么:别把它们当垃圾删掉

打开压缩包最常见的第一反应,是把所有.bak文件当垃圾删除。但在这种毕设包里,.bak恰恰是后悔药。update-password.vue.bak、IndexAsideStatic.vue.bak、BreadCrumbs.vue.bak、IndexHeader.vue.bak,这四个文件是前端工程改版之前的副本文档。大概率是之前的开发者改侧边栏、头部布局或密码组件时怕改坏,复制了一份原文件带.bak后缀放在同目录。项目根目录里还有.classpath和org.eclipse.wst.common.component这类文件,说明工程原本用Eclipse Web项目的方式维护,导出的压缩包把这些历史文件一并带出来了。

恢复方法很简单:把目标.bak复制一份,去掉.bak后缀。比如IndexAsideStatic.vue.bak复制成IndexAsideStatic.vue,被改坏的侧边栏组件就回来了。但如果同目录同时存在同名.vue和.vue.bak,说明代码改到一半停了,此时对比两个文件修改时间,以版本完整的那个为准。答辩前务必做这个检查,因为有些.bak文件反而是完整版本,而.vue文件是改残的版本,直接启动就报编译错误。

3.2 1-install / 2-run / 3-build:三个批处理脚本的分工

脚本命名已经说明了职责:安装、运行、打包三步分离。在不同机器上还原这类SSM项目时,发现脚本内容大同小异,第一段负责建库导SQL和编译后端,第二段启动后端服务,第三段构建Vue前端。下面是按这类项目最常见的写法还原的示例,具体内容以你手里的压缩包为准。

@echo off chcp 65001 >nul rem 1-install.bat:初始化数据库并编译后端 echo 正在创建数据库... mysql -uroot -proot -e "CREATE DATABASE IF NOT EXISTS ad_system DEFAULT CHARSET utf8mb4" echo 正在导入表结构和初始数据... mysql -uroot -proot ad_system < sql\ad_system.sql echo 正在编译后端,跳过测试... mvn clean install -DskipTests echo 安装完成 pause

chcp 65001是把控制台切成UTF-8编码,不然bat里的中文注释会显示成乱码,注意这只是命令提示符的显示问题,不影响脚本执行。mysql -uroot -proot里root是账号和密码,实际部署时必须改成你本机MySQL的真实凭据。CREATE DATABASE用IF NOT EXISTS是防重复执行报错,很实用。mvn加-DskipTests跳过单元测试,是因为测试类如果依赖数据库环境,在全新机器上会卡住,跳过是省事且安全的做法。

@echo off rem 2-run.bat:启动后端服务 cd /d %~dp0 echo 启动Tomcat,默认端口8080... mvn tomcat7:run pause

cd /d %~dp0的作用是切到脚本自身所在目录,避免双击运行时工作目录落到系统目录下,导致找不到pom.xml。mvn tomcat7:run依赖pom.xml里配置的tomcat7-maven-plugin插件,这类SSM毕设十有八九是靠它起的服务。如果项目是传统WAR包结构,改成外部Tomcat部署也行,把WAR拷进webapps后调startup.bat。启动后看到Starting ProtocolHandler日志说明端口已起来,运行窗口保持别关,按Ctrl+C才会停服务。

@echo off rem 3-build.bat:构建Vue前端 cd /d %~dp0\frontend call npm install call npm run build echo 前端打包完成,输出目录:frontend\dist pause

npm install首次执行通常很慢,国内网络环境下失败重跑是常态,这不是脚本问题。npm run build产出纯静态文件到dist目录,可以交给Nginx托管,也可以直接把dist内容拷到后端webapp目录由Tomcat统一托管。如果只是答辩演示,我更推荐用Vue开发模式起前端,改代码即时生效,遇到演示中发现问题还能现场兜底,不必反复打包。

3.3 前端Vue与后端SSM联调:代理还是CORS

前端开发服务器跑3000端口,后端SSM跑8080端口,一跨域就出幺蛾子。这个项目里前端通过axios调后端,常用解法有两种:前端配devServer代理,或者后端开CORS。我的习惯是用代理方案,对后端代码零侵入,答辩时也好讲——前端主动适配后端接口,更接近工程化实践。

// vue.config.js module.exports = { devServer: { port: 3000, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true, // 把 /api 前缀去掉,后端Controller无需写统一前缀 pathRewrite: { '^/api': '' } } } } };

代理机制的核心是changeOrigin,它会把请求头里的Host字段改写成目标地址,后端收到的请求就变成了同源。前端请求路径带/api前缀,代理转发到后端时会被pathRewrite剥掉,所以后端Controller里不要出现/api路径前缀,否则URL会变成/api/api/login这种双层拼接错误。前后端分离项目里,所有接口请求统一走baseURL,保证代理规则全局生效。

// src/utils/request.js const service = axios.create({ baseURL: '/api', // 统一走代理前缀 timeout: 10000, // 10秒超时,防止后端卡死时前端一直转圈 withCredentials: true // 跨域请求携带Cookie,保证Session不丢 });

timeout字段给你的接口调用设定10秒上限,后端如果启动到一半或SQL卡死,前端能快速失败,而不是让评委盯着转圈等一分钟。withCredentials是Cookie伴随跨域请求发送的开关,下面CORS配置必须和它配对使用。

如果项目不走代理而是直连后端,那后端必须开CORS才有救:

@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("http://localhost:3000") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowCredentials(true) .maxAge(3600); } }

这组配置里有个容易翻车的细节:allowCredentials(true)一旦开启,allowedOrigins就不能写"*"通配符,必须写具体前端地址,否则浏览器直接拒绝响应,控制台报CORS错误。allowedMethods必须包含OPTIONS,因为跨域复杂请求会先发预检请求,缺了它预检直接失败,所有POST接口看起来像超时。maxAge(3600)的意思是预检结果缓存一小时,能显著减少OPTIONS请求次数,提升调试验证时的响应速度。

4. 核心业务流实现:从广告需求到结算的状态机与文件交付

4.1 需求—提案—设计—签署:状态机怎么设计不漏状态

广告需求从创建到结算,在数据库里表现为一条记录的状态反复流转,在前端则对应着需求、提案、制作、预览、签署、结算六个模块。把它整个项目里的流程梳理成下面这条状态链,答辩时可以对着流程图讲清楚每一步的触发条件。

状态值状态含义触发操作
0待受理客户提交需求,广告商可查看并提交提案
1提案中广告商已提交提案,等待客户审核
2设计中提案通过,生成制作需求,广告制作商接手
3待预览设计图已交付,客户可预览
4调整中客户提出调整意见,制作商修改后重新交付
5待签署预览通过,双方准备签署
6已结算结算完成,流程终止

状态从0走到6,每一步都会触发下游记录的新增:提案表、制作需求表、设计图交付表、签署表、结算表,是典型的"主表状态驱动从表动作"结构。如果建表时漏掉某个状态,最常见的表现是前端菜单点了没数据、流程走不通,比如设计图还没交付,签署按钮却已经能点了,这种逻辑漏洞在答辩演示时非常显眼。

状态更新不能粗暴写成SET status = 新值,必须带上当前状态条件,这是拆这套系统时最想强调的一个细节:

-- 提案审核通过,流程从 提案中(1) 转到 设计中(2) UPDATE t_ad_requirement SET status = 2, update_time = NOW() WHERE id = #{reqId} AND status = 1

这个写法的价值在于防并发乱序。如果两个请求同时改同一条需求,不带status条件的更新会互相覆盖;带了status = 1条件后,同时只会有一条SQL执行成功,另一条影响行数为0,程序里判断update方法的返回值就能知道状态不对,直接给前端返回"当前状态不可操作"。这种状态守卫在答辩现场属于加分点,因为绝大多数同类项目都没有这层保护。

4.2 设计图交付:文件上传、重命名与虚拟路径映射

广告制作商上传设计图,是文件流进入系统的核心过程。SpringMVC里用MultipartFile接收文件,拆这类项目时看到的大部分实现都是下面的套路,核心处理有三个点:空文件校验、按日期分目录、UUID重命名。

@PostMapping("/design/deliver") @ResponseBody public Result deliverDesign(@RequestParam("designId") Integer designId, @RequestParam("file") MultipartFile file) { if (file == null || file.isEmpty()) { return Result.error("请选择要上传的设计图"); } // 按日期分目录,避免所有文件堆在一个目录里 String dir = "D:/upload/design/" + new SimpleDateFormat("yyyyMMdd").format(new Date()); File folder = new File(dir); if (!folder.exists()) { folder.mkdirs(); } // 用UUID重命名,保留原扩展名,防止中文文件名乱码 String ext = StringUtils.getFilenameExtension(file.getOriginalFilename()); String filename = UUID.randomUUID().toString().replace("-", "") + "." + ext; file.transferTo(new File(folder, filename)); // 数据库只存相对Web路径,页面通过虚拟映射访问 designService.saveDeliver(designId, "/upload/design/" + filename); return Result.ok(); }

@RequestParam("file")要求前端FormData里的文件字段名必须叫file,对不上就抛MissingServletRequestParameterException。UUID重命名同时解决两个隐患:中文文件名在部分Tomcat版本下会乱码;多人传同名文件会互相覆盖。数据库只存相对路径,不存磁盘绝对路径,好处是换机器部署时历史数据不用改,只要保持虚拟映射规则一致就行。

要让页面能访问这个文件,SpringMVC配置里必须加一个虚拟目录映射,把Web请求的/upload/**指到磁盘上的真实文件夹:

<mvc:resources mapping="/upload/**" location="file:D:/upload/" />

mapping是浏览器访问时用的URL路径,location是服务器磁盘路径,这个写法只对本地磁盘生效。如果部署到Linux服务器,location要改成file:/home/user/upload/这样的绝对路径。上传大小默认只有1MB左右,设计图一般远超这个数,需要单独调大multipartResolver:

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="52428800"/> <property name="defaultEncoding" value="UTF-8"/> </bean>

maxUploadSize单位是字节,52428800换算下来是50MB。defaultEncoding必须设成UTF-8,否则带中文名的文件上传后会乱码。这两处配置漏掉任何一个,上传功能都会以很诡异的方式失败,而且控制台不一定有明显的报错日志。

4.3 结算模块:金额精度与幂等更新

结算模块是广告客户给广告商付款的账务节点,核心表t_settlement至少要有金额、结算状态、关联需求ID三个字段。这个模块最大的坑不是逻辑复杂,而是金额精度。项目里如果拿double来算钱,0.1加0.2这种老问题迟早会冒出来,演示时界面上算出200.0000000001这种数,评委一眼就知道代码不严谨。

// 错误示范:金额用 double 计算,存在精度误差 // double total = 100.1 + 99.9; // 正确做法:全程 BigDecimal,金额从字符串构造 BigDecimal amount = new BigDecimal("1000.00"); // 合同金额 BigDecimal taxRate = new BigDecimal("0.06"); // 税率6% BigDecimal tax = amount.multiply(taxRate).setScale(2, RoundingMode.HALF_UP); BigDecimal payAmount = amount.add(tax);

BigDecimal构造必须传字符串,传new BigDecimal(0.06)会得到一个带一长串小数位的近似值,精度反而更差。setScale(2, RoundingMode.HALF_UP)表示保留两位小数、四舍五入,是财务计算的常规要求。计算完写入数据库时,字段类型必须是DECIMAL(10,2)而不是DOUBLE,否则结算单金额在数据库里又会变成浮点存储,前面的精度控制前功尽弃。

结算状态更新同样要带状态守卫,防止重复入账:

UPDATE t_settlement SET status = '已结算', settle_time = NOW(), pay_amount = #{payAmount} WHERE id = #{settleId} AND status = '待结算'

这条SQL返回的影响行数为0,就说明这张结算单已经是已结算状态,程序直接提示"该结算单已处理,请勿重复提交"。这个幂等逻辑在演示时特别有用,因为现场演示连点按钮是常态,没有这层保护,一张单子会被重复结算两次。道理和4.1里的状态守卫一脉相承,属于同一个设计思路在不同模块的复用。

5. 避坑与排查:SSM+Vue项目最容易翻车的六个细节

5.1 MySQL驱动与时区:启动即报错的第一关

现象:1-install.bat执行完数据库也建了,一启动Tomcat就抛ClassNotFoundException或者Communications link failure,日志里往往还带着Server returns invalid timezone字样。 原因:本地MySQL是8.0以上版本,但项目pom.xml里引的驱动还是5.x;或者驱动版本没问题,连接URL缺了serverTimezone参数,MySQL 8对时区参数是强制要求,不填直接拒连。 解决:先把pom里的mysql-connector-java版本对齐到8.0.x,再把jdbc连接串改成jdbc:mysql://localhost:3306/ad_system?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8。改完重建库重启,驱动和时区的双重问题一次解决。我一般会在答辩前检查一遍jdbc.properties,这类问题在评委的电脑上出现概率极高。

5.2 中文乱码:一条链上的三个环节都要查

现象:前端输入中文,提交后数据库里存成了问号;或者数据库里存得好好的,页面查出来显示乱码。 原因:问题可能藏在三个环节里。请求参数没走UTF-8编码、数据库连接串没指定characterEncoding、表结构本身建成了latin1。只修其中一个,乱码会在另一个环节重新冒出来,这就是乱码问题最折磨人的地方。 解决:按顺序排查三处:web.xml里加CharacterEncodingFilter并设forceEncoding=true;jdbc连接串带characterEncoding=utf8;建表语句统一用utf8mb4。判断卡点用排除法——先看数据库里存的值是否正确,如果是问号说明连接串或表结构有问题;存进去正常、查出来乱,说明响应编码或页面编码不对。这三处全都对齐,乱码基本消失。

5.3 跨域与Session丢失:登录成功却被不停踢回登录页

现象:登录接口明明返回成功,紧接着访问业务接口却一直提示未登录,反复登录反复踢。 原因:后端确实把loginUser放进了Session,但前端的axios请求没有携带Session ID对应的Cookie,每个请求都是新会话,后端自然认为你没登录。跨域场景下这是最高发的Session问题。 解决:前端axios实例加上withCredentials: true,后端CORS配置里必须同时满足两个硬条件——allowCredentials(true)且allowedOrigins写具体地址而不是星号。缺任何一个,浏览器都不会把Cookie带回给前端。排查方法简单粗暴:打开浏览器Network面板,点击任意一个业务请求看Request Headers里有没有Cookie字段,没有就先检查这两个配置。

5.4 vue.bak文件干扰前端构建

现象:npm run build报找不到模块或语法错误,报错文件指向components目录下的某个vue组件,但目录里明明躺着同名.vue.bak文件。 原因:之前的开发者改组件时把旧版本备份成.bak留在同目录,某些构建环境下这些文件不参与编译,但如果项目里import语句写错文件名,或者dist产物混入了.bak文件,就会出现这类诡异报错。这类问题属于典型的"文件残留引发的玄学故障"。 解决:先全项目搜索*.bak,逐个确认。同名.vue还在的,.bak直接删掉;.vue不在了,把.bak复制改名恢复。检查完重新build,这类报错基本消失。从那以后我每次拿到这类资源包,第一件事就是清理src目录下的.bak文件,而不是等构建报错再回头找。

5.5 端口被占用与Tomcat重复部署

现象:跑2-run.bat,控制台直接报Address already in use: JVM_Bind,或者打开8080端口看到的却是另一个不相关的页面。 原因:之前启动的Tomcat进程没关干净,IDE后台还挂着旧实例,或者你手动起过第二个服务,8080被占死。 解决:Windows下先执行netstat -ano | findstr 8080,拿到占用端口的进程PID,再用taskkill /F /PID 进程号强杀。杀完之后去任务管理器确认Java进程确实没了再重启。端口被占和重复部署是两回事:端口占用是进程还没死透,重复部署是webapps目录下积累了多个版本,记得只保留当前要用的一个。

5.6 SQL脚本重复执行的隐患

现象:第二次在本地跑1-install.bat,导入SQL时报SQL语法错误或Table already exists。 原因:SQL脚本里的建表语句没有DROP TABLE IF EXISTS前缀,数据库里已经有同名的表,执行到CREATE TABLE时直接冲突。 解决:最干净的做法是手动执行DROP DATABASE ad_system;再重新跑建库导库流程。更稳妥的方案是让每个CREATE TABLE语句前都加DROP TABLE IF EXISTS前缀,脚本就具备幂等性了。我的习惯是本地环境直接删库重建,因为重复执行就是为了拿到干净环境,留旧数据反而容易干扰判断。

6. 答辩前验证:把四角色演示走成一个闭环核对清单

代码能跑通和答辩能讲清楚是两回事。拿到这类SSM+Vue毕设包后,我的流程是先不看代码,把演示跑成一条完整业务线再回头讲实现。四角色闭环是这样:管理员先建三个业务账号,广告客户登录发一条广告需求,广告商登录看到需求并提交提案,客户在提案列表点通过,系统自动生成制作需求,广告制作商接单并上传一张设计图,客户在预览模块确认,进入签署,最后在结算模块走完付款。整条流程走下来,每张核心表都产生了有关联关系的数据,评委看到的是完整业务流,而不是一堆孤立的增删改查页面。

按下面这张表逐项验证,过一项勾一项:

顺序操作预期结果
1管理员登录,新增三个角色账号用户列表出现三条记录,角色字段正确
2广告客户登录,创建广告需求需求状态为待受理,渠道信息可选
3广告商登录,对需求提交提案需求状态变为提案中
4客户通过提案状态变为设计中,生成制作需求
5制作商接单并上传设计图交付记录出现,需求进入待预览
6客户预览并提出调整状态变为调整中,制作商收到调整信息
7制作商修改后重新交付状态回到待预览
8客户确认,双方签署签署记录生成,状态待结算
9客户完成结算结算金额正确,状态已结算

验证过程中顺手做两件事。一是把演示数据做成真实业务的样子,渠道名、广告语、金额都用看得懂的内容,别留测试项目123的痕迹,画面观感完全不同。二是用录屏工具把全流程录一遍,时间压在5分钟以内,万一现场环境崩了,直接放录屏兜底。Network面板开着看每个请求的响应状态,哪个接口突然500,截图记下来,那基本就是评委追着问的点,提前想好解释。

最后检查那三个bat脚本的路径是否写死成本机地址,答辩机器环境变量未必和你本地一样,脚本里的路径如果指向不存在的目录,就在PPT里备注一句"路径因环境而异",别现场翻车。从那以后我每次拿到这类SSM+Vue的毕设资源,都强制先重置账号、清空数据、把四角色流程完整跑一遍再交出去。状态守卫、金额精度、上传重命名这些细节,平时看不出来,真到了演示现场就是能不能收场的问题。希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询