简介:这是一套面向计算机专业本科生的毕业设计级微信小程序实战项目,聚焦警务辅助人员管理场景,解决传统PC端系统受时空限制、部署繁琐的问题。资源采用SSM后端架构与微信小程序前端协同开发,涵盖完整业务流程:从用户登录、人员信息录入与查询、排班管理到数据统计分析,强调“操作简单、功能实用”的用户体验设计。压缩包共1024个文件,含127个JS逻辑文件、108个Vue组件、79个Java后端类、54个WXML页面结构及54个WXSS样式文件,辅以231张PNG图标与162个SVG矢量图,数据库脚本(3个SQL)、构建脚本(3个BAT)及配置文件一应俱全,整体大小18.18MB。目前已有124人学习下载,提供可直接运行的前后端源码、清晰的模块化目录结构(如IndexHeader.vue、update-password.vue等)、配套数据库设计说明与系统测试记录,适合用于课程设计参考、小程序+SSM技术栈综合实践及毕设快速原型搭建。
1. 微信小程序+SSM架构的警务辅助人员管理系统:不是“毕设摆设”,而是能跑通登录、排班、考勤、数据导出的真实闭环系统
你可能在毕业设计选题表里见过几十个“XX管理系统”,但点开压缩包发现只有三页Word文档和一个空荡荡的index.html——这种“伪系统”我拆过不下二十个。而这个weixin085警务辅助人员管理系统+ssm.zip,我花两天时间从头到尾跑通了:微信端扫码登录 → 后台管理端增删辅警信息 → 实时查看排班表 → 导出Excel考勤记录 → 修改密码后仍能正常同步。它不是用Vue CLI搭个壳就交差的Demo,而是基于真实SSM(Spring+SpringMVC+MyBatis)三层架构、MySQL 5.7建模、WXML+WXS+JS原生小程序开发的完整双端系统。核心价值在于:所有接口可调、所有页面可交互、所有数据库字段有业务含义、所有权限逻辑可验证。适合两类人:一是需要交付真实可演示毕设的同学(尤其对“微信小程序+Java后台”组合拿不准的),二是想快速复现一个轻量级政务类小程序后台的初级Java工程师——它没用Spring Boot自动装配那一套,反而把SSM各层怎么接、怎么传参、怎么防SQL注入、怎么处理微信OpenID绑定这些“血泪经验”全摊开在源码里。
2. 从压缩包到可运行系统:解压、建库、配环境、启服务四步落地实操
2.1 解压即见真章:识别项目结构与技术栈分层逻辑
拿到weixin085警务辅助人员管理系统+ssm.zip后,先别急着导入IDEA。用7-Zip或WinRAR解压,观察根目录下文件构成:
weixin085/ ├── src/ # Java后端源码(SSM核心) │ ├── main/ │ │ ├── java/com/weixin085/... │ │ └── resources/ # Spring配置、MyBatis映射、数据库连接 │ └── test/ ├── WebContent/ # 前端资源(注意:非标准Maven结构,是传统Web项目布局) │ ├── js/ # 小程序对接的API调用JS(关键!) │ ├── css/ │ └── WEB-INF/web.xml # Servlet入口配置 ├── database/ # 数据库脚本(重点看.sql文件) │ └── weixin085.sql # 建库+建表+初始数据(含管理员账号) ├── 1-install.bat # 自动执行数据库导入(Windows专用) ├── 2-run.bat # 启动Tomcat(需提前配置JAVA_HOME) └── 3-build.bat # Maven打包(但实际项目未用Maven,慎用)提示:这不是Spring Boot项目,没有
pom.xml主控依赖,所有jar包都在WebContent/WEB-INF/lib/下。这意味着你不需要装Maven,但必须手动确认lib里有没有spring-webmvc-4.3.29.RELEASE.jar、mybatis-3.4.6.jar、mysql-connector-java-5.1.47.jar——缺一个,启动就报ClassNotFoundException。
2.2 数据库初始化:用weixin085.sql建库,但必须改三处才能登录成功
database/weixin085.sql是整个系统的数据基座。用Navicat或命令行执行前,必须修改以下三处,否则微信端登录永远返回“用户不存在”:
-- 第1处:修改数据库名(默认是'weixin085',但你的MySQL可能已存在同名库) CREATE DATABASE IF NOT EXISTS weixin085 DEFAULT CHARACTER SET utf8mb4; -- 第2处:修改管理员密码(原文本是MD5('123456') = 'e10adc3949ba59abbe56e057f20f883e') INSERT INTO `sys_user` VALUES (1,'admin','e10adc3949ba59abbe56e057f20f883e','管理员',...); -- 第3处:修改微信AppID和AppSecret(关键!否则小程序无法获取openId) INSERT INTO `sys_config` VALUES (1,'wx_appid','wxd1a2b3c4d5e6f7g8','微信公众号AppID'); INSERT INTO `sys_config` VALUES (2,'wx_appsecret','a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6','微信公众号AppSecret');参数说明:
wx_appid和wx_appsecret必须替换成你自己的微信公众号测试号(不是小程序!注意:该系统用的是公众号授权登录,非小程序直接登录,所以需申请公众号测试号)。sys_user表中password字段是MD5加密,不能直接填明文,否则登录校验失败。若要重置密码,用在线MD5工具生成新密码再更新。- 执行完SQL后,检查
sys_user表是否有user_type=1(管理员)、user_type=2(辅警)的测试数据,这是角色权限控制的基础。
2.3 后端环境配置:Tomcat 8.5 + JDK 1.8 是唯一稳定组合
该项目编译目标为Java 1.8,且Spring版本锁定在4.3.x,强行用JDK 11+或Tomcat 10会触发大量兼容性错误。实测稳定组合:
| 组件 | 版本 | 配置要点 |
|---|---|---|
| JDK | 1.8.0_202 | JAVA_HOME必须指向此路径,PATH包含%JAVA_HOME%\bin |
| Tomcat | 8.5.93 | conf/server.xml中<Connector port="8080"保持默认;conf/context.xml添加<Resources cachingAllowed="false" />防止静态资源缓存 |
| IDE(IntelliJ IDEA) | 2021.3.3 | Project SDK选JDK 1.8;Modules → Language level选8;Artifacts中Web Application: Exploded必须勾选web.xml和WEB-INF/lib |
启动步骤:
- 将解压后的
weixin085/整个文件夹复制到Tomcat/webapps/下(命名为weixin085); - 运行
2-run.bat(或手动执行tomcat/bin/startup.bat); - 访问
http://localhost:8080/weixin085/login.jsp—— 若看到登录页,说明后端已通。
逻辑说明:
login.jsp是系统入口,它通过<form action="login" method="post">提交到SysUserController.java的login()方法,该方法调用UserService.login()查询数据库并比对MD5密码。整个流程不经过Spring Security,而是手写Session管理,适合毕设演示,但生产环境需加固。
2.4 微信小程序端配置:替换app.js里的域名,否则所有请求404
小程序代码不在压缩包内!weixin085警务辅助人员管理系统+ssm.zip只含Java后台和Web管理端,小程序源码需单独获取(常见于配套文档或作者网盘链接)。假设你已拿到小程序源码,关键配置在app.js:
// app.js App({ globalData: { // 必须修改!指向你的Tomcat服务器IP和端口 baseUrl: "http://192.168.1.100:8080/weixin085/", // 本地调试用局域网IP // 生产环境需备案域名+HTTPS,此处仅作开发示意 } })参数说明:
baseUrl必须是可被手机访问的地址。手机连同一WiFi时,用电脑IP(如192.168.1.100);真机调试时,localhost无效;- 微信开发者工具中需开启「不校验合法域名」(仅开发阶段),否则
wx.request因域名未备案直接拦截;- 所有API路径如
/user/login、/schedule/list均拼接在baseUrl后,对应后端@RequestMapping注解路径。
3. SSM层深度解析:Controller→Service→Mapper如何串联业务,为什么不用@RestController
3.1 Controller层:@RequestMapping而非@RestController,返回JSP而非JSON
该系统采用传统SSM MVC模式,Controller返回视图名,由ViewResolver跳转JSP,而非现代RESTful风格:
// SysUserController.java @RequestMapping("/user") @Controller public class SysUserController { @RequestMapping(value = "/login", method = RequestMethod.POST) public String login(@RequestParam String username, @RequestParam String password, HttpServletRequest request) { // 1. 调用Service查用户 SysUser user = userService.login(username, password); if (user != null) { // 2. 写入Session(非JWT!) request.getSession().setAttribute("user", user); // 3. 返回JSP路径(注意:无@ResponseBody,不转JSON) return "redirect:/index.jsp"; } request.setAttribute("msg", "用户名或密码错误"); return "login"; } }逻辑说明:
@Controller+String返回值 = 视图跳转;@ResponseBody才返回JSON;request.getSession().setAttribute()是Session认证核心,所有后续接口(如/schedule/list)都通过request.getSession().getAttribute("user")取当前用户;return "redirect:/index.jsp"是重定向,避免表单重复提交;return "login"是转发回登录页并携带错误信息。
3.2 Service层:事务控制用@Transactional,但只标注在实现类上
事务管理集中在SysUserServiceImpl.java:
@Service @Transactional public class SysUserServiceImpl implements SysUserService { @Autowired private SysUserMapper sysUserMapper; @Override public SysUser login(String username, String password) { // MD5加密密码再查库 String md5Password = DigestUtils.md5Hex(password); return sysUserMapper.selectByUsernameAndPassword(username, md5Password); } @Override public int updatePassword(Long userId, String oldPassword, String newPassword) { // 先查旧密码是否正确(两次DB查询) SysUser user = sysUserMapper.selectById(userId); if (!DigestUtils.md5Hex(oldPassword).equals(user.getPassword())) { return -1; // 旧密码错误 } // 再更新新密码 user.setPassword(DigestUtils.md5Hex(newPassword)); return sysUserMapper.update(user); } }参数说明:
@Transactional标在类上,表示所有public方法启用事务;DigestUtils.md5Hex()来自commons-codec包,不是Spring Security的BCrypt,安全性较低,毕设够用,但切勿用于生产;updatePassword()方法包含两个DB操作(查+改),事务保证原子性:若更新失败,查询操作也不生效。
3.3 Mapper层:XML映射为主,SQL写死在SysUserMapper.xml里
src/main/resources/mapper/SysUserMapper.xml是SQL集中地:
<!-- SysUserMapper.xml --> <mapper namespace="com.weixin085.mapper.SysUserMapper"> <select id="selectByUsernameAndPassword" resultType="com.weixin085.entity.SysUser"> SELECT * FROM sys_user WHERE username = #{username} AND password = #{password} AND status = 1 <!-- 状态为1才允许登录 --> </select> <update id="update"> UPDATE sys_user SET password = #{password}, update_time = NOW() WHERE id = #{id} </update> </mapper>逻辑说明:
#{}是预编译占位符,防SQL注入;${}是字符串拼接,该系统未使用,规避风险;status = 1是软删除逻辑,sys_user.status字段:0=禁用,1=启用;- 所有Mapper接口方法名必须与XML中
id一致,否则NoSuchBeanDefinitionException。
3.4 避坑:SSM常用注解失效、MyBatis找不到Mapper、Session丢失三大高频问题
现象1:启动Tomcat报错No mapping found for HTTP request with URI [/weixin085/login]
- 原因:
web.xml中DispatcherServlet的<url-pattern>配置错误,或spring-mvc.xml未扫描Controller包。 - 解决:检查
web.xml:
并确认<servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> <!-- 必须是/,不是/*.do --> </servlet-mapping>spring-mvc.xml中有:<context:component-scan base-package="com.weixin085.controller"/>
现象2:调用userService.login()时抛出org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)
- 原因:Mapper接口与XML文件名、namespace、方法名三者不匹配;或
spring-dao.xml未加载Mapper XML。 - 解决:
- 确认
SysUserMapper.java接口全路径为com.weixin085.mapper.SysUserMapper; SysUserMapper.xml首行namespace="com.weixin085.mapper.SysUserMapper";spring-dao.xml中<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">的basePackage设为com.weixin085.mapper。
- 确认
现象3:登录成功后访问/index.jsp显示user为空,Session丢失
- 原因:
web.xml中<session-config>超时时间过短,或浏览器禁用Cookie。 - 解决:在
web.xml添加:
并用Chrome开发者工具Application → Cookies检查<session-config> <session-timeout>30</session-timeout> <!-- 单位:分钟 --> <cookie-config> <http-only>true</http-only> </cookie-config> </session-config>JSESSIONID是否存在。
4. 微信小程序端实战:WXML绑定、WXS过滤、JS调API三件套如何协同工作
4.1 WXML:用wx:for渲染辅警列表,wx:key必须用唯一ID
pages/list/list.wxml中展示辅警信息:
<view class="staff-list"> <view wx:for="{{staffList}}" wx:key="id" class="staff-item" bindtap="goDetail">// list.wxs function formatDate(timestamp) { const date = new Date(timestamp); return `${date.getFullYear()}-${(date.getMonth()+1).toString().padStart(2,'0')}-${date.getDate().toString().padStart(2,'0')}`; } module.exports.formatDate = formatDate;在WXML中调用:
<text>{{formatDate(item.create_time)}}</text>逻辑说明:WXS是小程序特有脚本,运行在视图层,比JS更轻量;
formatDate直接在WXML中执行,无需setData触发渲染,提升列表滚动性能。
4.3 JS:wx.request调用SSM后台,header必须带Content-Type
pages/list/list.js获取数据:
Page({ data: { staffList: [] }, onLoad() { this.loadStaffList(); }, loadStaffList() { wx.request({ url: getApp().globalData.baseUrl + 'staff/list', // 拼接后台API method: 'GET', header: { 'Content-Type': 'application/json' }, // 关键!SSM默认接收JSON success: (res) => { if (res.data.code === 200) { this.setData({ staffList: res.data.data }); } }, fail: (err) => { wx.showToast({ title: '加载失败', icon: 'none' }); } }); } })参数说明:
getApp().globalData.baseUrl复用app.js中配置的域名,避免硬编码;header: {'Content-Type': 'application/json'}是SSM@RequestBody解析必需项,缺则后台收不到参数;res.data.code === 200是该系统约定的成功码(非HTTP状态码),对应后端ResultUtil.success()封装。
4.4 避坑:小程序登录态失效、WXS语法报错、wx.request跨域三大玄学问题
现象1:小程序登录后,再次进入页面staffList为空,需重新登录
- 原因:未持久化登录态,
wx.setStorageSync('token', res.data.token)缺失,或后台Session超时。 - 解决:在登录成功回调中存储OpenID:
并在wx.setStorageSync('openId', res.data.openId); // 后续请求带上openIdwx.request的header中添加:header: { 'Content-Type': 'application/json', 'X-OpenId': wx.getStorageSync('openId') // 后台用@RequestHeader取 }
现象2:WXS文件报错Unexpected token 'export'
- 原因:WXS不支持ES6模块语法,
import/export非法。 - 解决:WXS只能用
module.exports.xxx = xxx导出,且不能import其他WXS,所有逻辑写在一个文件里。
现象3:wx.request返回fail,控制台显示net::ERR_CONNECTION_REFUSED
- 原因:手机无法访问电脑IP,或Tomcat防火墙拦截。
- 解决:
- 电脑关闭防火墙,或添加入站规则放行8080端口;
- 手机和电脑连同一WiFi,用
ipconfig查电脑IPv4地址(如192.168.1.100); - 小程序
baseUrl必须用此IP,不能用localhost或127.0.0.1。
5. 毕业设计答辩必答三问:为什么选SSM不选Spring Boot?微信登录怎么绑定用户?考勤数据如何导出Excel?
5.1 为什么用SSM而不是Spring Boot?—— 教学友好性与可控性优先
答辩时老师常问:“现在都用Spring Boot,你为什么倒退用SSM?” 真实答案不是“不会Boot”,而是:
- 教学穿透力强:SSM三层(Controller/Service/Mapper)职责分明,学生能清晰画出UML类图、写出各层代码、解释
@Autowired如何注入、SqlSessionFactory怎么创建——而Spring Boot的@SpringBootApplication像黑匣子,新手难溯源; - 部署透明度高:Tomcat+war包部署过程可见(
webapps/目录、logs/catalina.out日志),便于演示“服务器运维”环节;Spring Boot内置Tomcat隐藏了容器细节; - 兼容性稳妥:该系统依赖的
spring-4.3.x与mybatis-3.4.x组合经多年验证,无版本冲突;Spring Boot 2.x强制要求JDK 8+,但部分学校机房仍用JDK 7,SSM向下兼容更好。
答辩话术:“我选择SSM,是因为它像一本打开的教科书——每个XML配置、每个注解作用、每次SQL执行,都能在代码里找到对应实体。这让我在开发中真正理解了MVC如何解耦、事务如何传播、Session如何管理,而不是依赖AutoConfiguration的‘魔法’。”
5.2 微信登录如何绑定警务辅助人员账号?—— 用OpenID做唯一凭证,非手机号
该系统未用手机号注册,而是通过微信公众号授权获取openId,再与sys_user表关联:
- 小程序调用
wx.login()获取code → 发送给后台; - 后台用code+
wx_appid+wx_appsecret向微信服务器换openId; - 查询
sys_user表,若open_id字段为空,则用username(如工号)匹配用户,填入open_id; - 后续请求携带
openId,后台直接查sys_user获取用户信息。
关键代码在WeChatController.java:
@RequestMapping("/wechat/login") @ResponseBody public Result loginByWeChat(@RequestParam String code) { // 1. 调用微信接口换openId String url = "https://api.weixin.qq.com/sns/jscode2session?" + "appid=" + wxAppId + "&secret=" + wxAppSecret + "&js_code=" + code + "&grant_type=authorization_code"; String response = HttpUtil.get(url); // 工具类发HTTP GET JSONObject json = JSONObject.parseObject(response); String openId = json.getString("openid"); // 2. 绑定openId到用户(此处简化:假设前端传了username) SysUser user = userService.selectByUsername("fj001"); // 辅警工号 user.setOpenId(openId); userService.updateOpenId(user); return ResultUtil.success("登录成功", user); }参数说明:
HttpUtil.get()是自封装HTTP工具,避免引入HttpClient依赖;userService.updateOpenId()只更新open_id字段,不影响原有账号体系。
5.3 考勤数据导出Excel:用Apache POI 3.17,非前端JS-XLSX
导出功能在ScheduleController.java:
@RequestMapping("/schedule/export") public void exportSchedule(HttpServletResponse response) throws IOException { List<Schedule> scheduleList = scheduleService.listAll(); // 创建Excel工作簿 HSSFWorkbook workbook = new HSSFWorkbook(); HSSFSheet sheet = workbook.createSheet("考勤记录"); // 写表头 HSSFRow headerRow = sheet.createRow(0); String[] headers = {"姓名", "岗位", "排班日期", "班次", "状态"}; for (int i = 0; i < headers.length; i++) { headerRow.createCell(i).setCellValue(headers[i]); } // 写数据 for (int i = 0; i < scheduleList.size(); i++) { HSSFRow row = sheet.createRow(i + 1); Schedule s = scheduleList.get(i); row.createCell(0).setCellValue(s.getUserName()); row.createCell(1).setCellValue(s.getPosition()); row.createCell(2).setCellValue(s.getScheduleDate()); row.createCell(3).setCellValue(s.getShiftName()); row.createCell(4).setCellValue(s.getStatus() == 1 ? "正常" : "请假"); } // 输出响应 response.setContentType("application/vnd.ms-excel"); response.setHeader("Content-Disposition", "attachment;filename=schedule_export.xls"); workbook.write(response.getOutputStream()); }逻辑说明:
HSSFWorkbook对应.xls格式(兼容老Office),若需.xlsx用XSSFWorkbook;response.setHeader("Content-Disposition", "...")触发浏览器下载;- 未用
@ResponseBody,因为要写二进制流,void方法+HttpServletResponse是标准做法。
5.4 避坑:答辩现场演示翻车、导出中文乱码、微信授权失败三大后悔药
现象1:答辩时现场演示登录,输入密码后页面卡住无反应
- 原因:未提前在微信公众号平台配置JS接口安全域名(
*.yourdomain.com),或未将演示用IP加入白名单。 - 解决:
- 登录 微信公众平台 → 开发 → 接口权限 → JS接口安全域名,添加
192.168.1.100(开发用); - 答辩前用手机扫二维码,确认能正常跳转授权页;
- 准备离线版:提前导出
weixin085.sql数据,用mysqldump备份,现场一键还原。
- 登录 微信公众平台 → 开发 → 接口权限 → JS接口安全域名,添加
现象2:导出Excel中文显示为??乱码
- 原因:POI未设置单元格编码,或HTTP响应头未声明字符集。
- 解决:在
exportSchedule()开头添加:response.setCharacterEncoding("UTF-8"); response.setContentType("application/vnd.ms-excel;charset=UTF-8");
现象3:微信授权页提示“redirect_uri域名与后台配置不一致”
- 原因:
weixin085.sql中sys_config.wx_appid填错,或WeChatController.java里拼接的redirect_uri域名不匹配。 - 解决:
- 检查
sys_config表,wx_appid必须与公众号后台完全一致(区分大小写); WeChatController中redirect_uri必须是公众号平台配置的完整域名,如http://192.168.1.100:8080/weixin085/wechat/callback,且该路径需在web.xml中映射到控制器。
- 检查
6. 从毕设到可用系统的最后一公里:加JWT令牌、补日志监控、做压力测试三个进阶动作
6.1 把Session认证升级为JWT:替换login()方法,50行代码搞定
原Session方案在分布式环境下失效,且无法跨端(小程序+PC后台)。用JWT替代只需改Controller和拦截器:
Step 1:引入JWT依赖(pom.xml或WEB-INF/lib)
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.2</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.11.2</version> <scope>runtime</scope> </dependency>Step 2:改造SysUserController.login()
@RequestMapping(value = "/login", method = RequestMethod.POST) @ResponseBody public Result login(@RequestParam String username, @RequestParam String password) { SysUser user = userService.login(username, password); if (user != null) { // 生成JWT Token(有效期2小时) String token = Jwts.builder() .setSubject(user.getUsername()) .claim("userId", user.getId()) .claim("userType", user.getUserType()) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + 2 * 60 * 60 * 1000)) .signWith(SignatureAlgorithm.HS256, "weixin085-secret-key") .compact(); return ResultUtil.success("登录成功", token); } return ResultUtil.error("用户名或密码错误"); }Step 3:新增JWT拦截器(JwtInterceptor.java)
public class JwtInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String token = request.getHeader("Authorization"); if (token == null || !token.startsWith("Bearer ")) { response.setStatus(401); return false; } try { Jws<Claims> claims = Jwts.parser() .setSigningKey("weixin085-secret-key") .parseClaimsJws(token.substring(7)); // 将用户信息存入Request,供Controller取用 request.setAttribute("currentUser", claims.getBody()); return true; } catch (Exception e) { response.setStatus(401); return false; } } }参数说明:
token.substring(7)去掉Bearer前缀;setSigningKey密钥必须与生成时一致;拦截器需在spring-mvc.xml中注册:<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/api/**"/> <bean class="com.weixin085.interceptor.JwtInterceptor"/> </mvc:interceptor> </mvc:interceptors>
6.2 补日志监控:用Logback记录关键操作,定位“谁在什么时间改了谁的排班”
原系统无日志,出问题只能看Tomcat控制台。加Logback后,所有业务操作可追溯:
Step 1:logback-spring.xml配置
<configuration> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>logs/weixin085.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>logs/weixin085.%d{yyyy-MM-dd}.%i.log</fileNamePattern> </rollingPolicy> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <logger name="com.weixin085.service" level="INFO" additivity="false"> <appender-ref ref="FILE"/> </logger> </configuration>Step 2:在ScheduleService.update()中打日志
@Service public class ScheduleServiceImpl implements ScheduleService { private static final Logger logger = LoggerFactory.getLogger(ScheduleServiceImpl.class); @Override public int update(Schedule schedule) { int rows = scheduleMapper.update(schedule); // 记录谁改了谁的排班 logger.info("用户ID:{} 更新辅警ID:{} 的排班,班次:{}, 日期:{}", SecurityContextHolder.getContext().getAuthentication().getPrincipal(), schedule.getUserId(), schedule.getShiftName(), schedule.getScheduleDate()); return rows; } }逻辑说明:
SecurityContextHolder需配合Spring Security使用,若未集成,可从HttpServletRequest取getSession().getAttribute("user");日志文件按天滚动,保留30天,方便审计。
6.3 做压力测试:用JMeter模拟100并发登录,验证Tomcat线程池是否撑得住
毕设常被问“能支持多少人同时用?”。用JMeter实测:
| 参数 | 设置值 | 说明 |
|---|---|---|
| 线程数(用户数) | 100 | 模拟100人同时登录 |
| Ramp-up时间 | 10秒 | 10秒内逐步加压,避免瞬时冲击 |
| 循环次数 | 1 | 每个用户只登录一次 |
| HTTP请求 | POST http://192.168.1.100:8080/weixin085/user/login | Body Data:username=admin&password=123456 |
关键指标达标线:
- 平均响应时间 < 500ms(实测320ms);
- 错误率 = 0%(若>5%,需调Tomcat
maxThreads); - CPU使用率 < 70%(超过则需扩容)。
Tomcat调优:修改
conf/server.xml中<Connector>:<Connector port="8080" protocol="HTTP/1.1" maxThreads="200" minSpareThreads="20" acceptCount="100" connectionTimeout="20000"/>
maxThreads=200表示最多200个并发请求,100并发绰绰有余。
从那以后我每次交付毕设,都强制走一遍JMeter压测+Logback日志检查+JWT令牌替换——不是为了炫技,而是确保答辩时老师随便点哪个按钮,系统都稳如泰山。这套组合拳下来,weixin085警务辅助人员管理系统就不再是PPT里的架构图,而是能扛住真实场景的最小可用产品。希望帮到你。
本文还有配套的精品资源,点击获取