SSM框架实战:警务辅助人员管理系统的搭建、配置与部署
简介这是一套基于微信小程序与SSM框架的警务辅助人员管理系统毕业设计源码包。系统前端采用WXML/WXSS/JS微信原生开发技术后端基于SSM框架并使用MySQL数据库同时配有Vue后台管理页面实现警务辅助人员管理场景中的核心业务流程。压缩包共包含1024个文件以png/svg图标、js脚本、vue页面、java源码、wxml/wxss模板、json配置及jpg图片等为主整体约18.18MB目录结构清晰便于按功能模块查阅。目前已有122人学习下载适合计算机相关专业学生或初级开发者用于毕业设计参考、二次开发与项目实战练习。资料内含项目前后端代码、数据库SQL脚本、开发配置文件及说明文档可帮助快速理解系统架构与开发思路节省环境搭建和基础编码时间。1. 辅警管理这个场景为什么最后都会落在 SSM 上警务辅助人员的管理和普通企业的员工管理系统看着相似实际差很多。考勤、排班、绩效考核、装备领用、培训记录这些数据每一类都要跟编制外的“警号”体系挂钩而且同一人可能同时出现在多个执勤序列里。这类系统的技术选型通常不会去搞微服务Spring、SpringMVC、MyBatis 三件套依然是在这个领域最常见的做法zip 包解压之后的工程结构基本也就是按这套思路组织。SSM 适合这个项目的原因有两个。一是它足够“模块化”Spring 管对象生命周期SpringMVC 管请求分发MyBatis 管 SQL 和结果映射每一层都可以单独替换、单独测试。二是它对中小型后台系统非常匹配一个 Tomcat 实例加一个 MySQL 库就能把人员档案、排班、考核全部跑起来不需要引入太重的基础设施。这篇文章就围绕“weixin085警务辅助人员管理系统ssm”这套典型工程把框架组织方式、核心模块的代码写法、部署参数和常见坑点讲透。2. SSM 在警务辅助人员系统里的三层分工与配置写法2.1 Spring IoC 容器在这个项目中管理什么 Bean警务辅助人员系统里最频繁创建的类基本上都是 Service 和 Mapper 接口的实现。比如辅警信息维护、班次排定、考核结果录入这些 Service 在代码里如果是用new关键字直接创建的事务控制和依赖注入都会乱套。Spring IoC 容器在这里统一管理这些对象的生命周期使得 Controller 层拿到的 Service 都是同一个实例且事务代理能被正确织入。常见的做法是使用注解扫描。Configuration ComponentScan(basePackages {com.weixin085.police.service, com.weixin085.police.dao}) EnableTransactionManagement public class SpringRootConfig { Bean public DataSource dataSource() { DruidDataSource ds new DruidDataSource(); ds.setDriverClassName(com.mysql.jdbc.Driver); ds.setUrl(jdbc:mysql://localhost:3306/police_assist?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai); ds.setUsername(root); ds.setPassword(root); ds.setInitialSize(5); ds.setMaxActive(20); return ds; } Bean public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) { SqlSessionFactoryBean bean new SqlSessionFactoryBean(); bean.setDataSource(dataSource); bean.setTypeAliasesPackage(com.weixin085.police.entity); bean.setMapperLocations(new PathMatchingResourcePatternResolver() .getResources(classpath*:mapper/*.xml)); return bean; } }这段配置里有几个关键参数。ComponentScan扫描的是 service 和 dao 包路径实际项目里如果包名不同需要改成自己工程的包前缀否则会出现Autowired注入失败。dataSource()里使用了 Druid 连接池的DruidDataSourceinitialSize和maxActive要按并发量调辅警管理系统通常同时在线人数在几十到几百之间初始 5、最大 20 已经够用。数据库连接串里characterEncodingutf8必须加否则中文字段会出现乱码serverTimezoneAsia/Shanghai是 MySQL 8.x 版本的硬性要求5.7 可以不加但建议统一加上。2.2 SpringMVC 的请求分发落在哪些 Controller 上SpringMVC 在辅警管理系统里主要承担 URL 到处理方法的映射。以“辅警档案管理”为例前端页面会向后台发送/assistant/list、/assistant/add、/assistant/edit、/assistant/delete这样的请求Controller 层负责接收这些请求调用 Service 层完成业务后封装结果交给视图层渲染。Controller RequestMapping(/assistant) public class AssistantController { Autowired private AssistantService assistantService; GetMapping(/list) public String list(RequestParam(defaultValue 1) int pageNum, RequestParam(defaultValue 10) int pageSize, Model model) { PageInfoAssistant pageInfo assistantService.findByPage(pageNum, pageSize); model.addAttribute(pageInfo, pageInfo); return assistant/list; } PostMapping(/add) ResponseBody public Result add(RequestBody Assistant assistant) { boolean flag assistantService.insertAssistant(assistant); return flag ? Result.success() : Result.fail(添加失败); } }这个 Controller 展示了两个关键点。list方法返回的是视图名前端用 JSP 或者 FreeMarker 渲染而add方法加了ResponseBody把返回的Result对象直接序列化成 JSON给前端 Ajax 调用。很多初学者会在这两个返回方式上搞混导致页面白屏或者 404。需要注意RequestBody接收的是 JSON 字符串前端 Ajax 调用时contentType要设置为application/json如果前端是表单提交的urlencoded格式这里要改成方法参数直接接收对象。SpringMVC 的配置文件里还有几个必调参数。视图解析器的前缀后缀一般设置为/WEB-INF/views/和.jsp这样可以强制 JSP 页面放在 WEB-INF 下外部不能直接访问。静态资源的放行配置也要跟上CSS、JS、图片如果不放行页面会渲染得很难看。mvc:resources mapping/static/** location/static// mvc:annotation-driven/2.3 MyBatis 的 Mapper 与动态 SQL 处理复杂查询辅警管理系统的查询条件往往不是固定的管理人员可能会同时按姓名、所属部门、入职时间范围、警号来组合过滤。这种多条件查询如果每个组合都写一个 SQL代码量会成倍增加而且很难维护。MyBatis 针对这种情况提供了动态 SQL在 XML 里用where、if标签来拼接查询条件。select idselectByCondition resultTypecom.weixin085.police.entity.Assistant SELECT * FROM assistant where if testname ! null and name ! AND name LIKE CONCAT(%, #{name}, %) /if if testpoliceNo ! null and policeNo ! AND police_no #{policeNo} /if if testdepartmentId ! null AND department_id #{departmentId} /if if testentryDateStart ! null AND entry_date #{entryDateStart} /if /where ORDER BY create_time DESC /select这里有一个常见的坑在 MySQL 里LIKE CONCAT(%, #{name}, %)的写法是正确的但有些人会直接写成LIKE %${name}%这样虽然能查出数据却存在 SQL 注入风险而且$符号是字符串拼接不是预编译参数。#{...}是 JDBC 的PreparedStatement占位符$是直接拼接务必使用#{}来传值。where标签会自动处理第一个if前面多余的关键字。如果所有条件都不成立它不会生成WHERE子句如果有条件成立它会自动去掉第一个条件前面的AND。动态 SQL 是 SSM 项目里最高频使用的功能之一。3. 警务辅助人员管理系统的核心业务模块与代码实现3.1 辅警档案管理模块的增删改查路径辅警档案管理是整个系统的数据基础。一个标准的辅警档案表至少包含姓名、性别、身份证号、警号、所属部门、岗位、入职日期、联系电话、政治面貌等字段。其中警号是业务主键它会关联到薪资、排班、考核等所有后续操作。在 Service 层档案的删除通常不用物理删除而是做逻辑删除。这是因为辅警的历史数据涉及审计和追溯需求一旦物理删掉后续查不到记录会比较麻烦。Service public class AssistantServiceImpl implements AssistantService { Autowired private AssistantMapper assistantMapper; Override public boolean updateStatus(Long id, Integer status) { Assistant assistant new Assistant(); assistant.setId(id); assistant.setStatus(status); return assistantMapper.updateByPrimaryKeySelective(assistant) 0; } }updateByPrimaryKeySelective是 MyBatis 生成器内置方法它只更新非 null 字段。在“在职/离职”切换这个场景里前端只需要传 id 和 status 两个字段其余字段不会被动修改。这个细节很重要如果前端把整个对象传过来而对象里有些字段是空的用updateByPrimaryKey全字段更新就会把数据库里的原有数据覆盖成 null造成数据丢失。3.2 排班管理中日期与班次的重叠校验排班管理是警务辅助系统区别于普通员工系统的核心功能。辅警排班通常分为白班、夜班、备勤三种班次一个辅警在同一个日期不能同时安排两个班次而且一个班次下的人数不能超过岗位上限。这些约束要在 Service 层写校验逻辑。public boolean assignShift(ShiftAssignment assignment) { // 查询同一辅警在同一天是否已有排班 int count shiftMapper.countByAssistantAndDate( assignment.getAssistantId(), assignment.getWorkDate() ); if (count 0) { throw new BizException(该辅警在所选日期已有排班); } // 查询该班次是否已满员 Integer capacity shiftMapper.getShiftCapacity(assignment.getShiftId()); Integer assigned shiftMapper.countByShiftAndDate( assignment.getShiftId(), assignment.getWorkDate() ); if (assigned ! null capacity ! null assigned capacity) { throw new BizException(该班次人数已满); } return shiftMapper.insert(assignment) 0; }这里要注意事务的隔离级别。count校验和insert操作之间存在时间窗口如果两个人同时提交同一个辅警的排班两个线程都能通过 count 校验最后都会 insert 成功产生重复排班。一个更稳妥的做法是在assistant_id和work_date上建联合唯一索引把并发问题交给数据库层兜底。在 MySQL 中使用ALTER TABLE shift_assignment ADD UNIQUE INDEX uk_assistant_date (assistant_id, work_date);来解决。系统层面的校验解决的是用户体验问题数据库约束解决的是数据一致性问题两者缺一不可。3.3 考核评分中的权限与审核流程辅警考核通常是由带班领导录入评分然后由上级审核。如果系统不做权限控制任何一个登录用户都能修改考核结果那么数据的可信度就得不到保证。传统的 SSM 系统通常用拦截器或者自定义注解来实现权限判断。Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface RequireRole { String value(); }然后在拦截器中校验当前登录用户是否具有角色。public class RoleInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (handler instanceof HandlerMethod) { HandlerMethod method (HandlerMethod) handler; RequireRole requireRole method.getMethodAnnotation(RequireRole.class); if (requireRole ! null) { User currentUser (User) request.getSession().getAttribute(currentUser); if (currentUser null || !requireRole.value().equals(currentUser.getRole())) { response.sendError(HttpServletResponse.SC_FORBIDDEN); return false; } } } return true; } }这种方式比在每个 Controller 方法里手动判断要干净得多。角色注解加在方法上就能直观地从代码上看出该接口的操作权限。审核流程里的另一个常见需求是操作留痕可以在考核表里加上create_by、create_time、audit_by、audit_time四个字段来记录完整链路。4. 从 zip 工程到本机跑起来配置、部署与验证4.1 解压后的工程结构与数据库初始化拿到“weixin085警务辅助人员管理系统ssm.zip”之后解压出来的目录结构一般长这样src/main/java存放 Java 源码src/main/resources存放配置文件src/main/webapp是前端页面。如果你的 JDK 版本高于 1.8建议先确认项目的编译级别很多 SSM 项目是基于 JDK 1.8 编写的直接用 JDK 17 打开会出现一系列兼容问题。数据库初始化是部署的第一步。通常在工程里自带一个sql目录里面有一个police_assist.sql或类似名字的脚本文件。如果没有需要根据实体类自己建表。导入命令如下。mysql -u root -p police_assist police_assist.sql如果 MySQL 还没有创建police_assist数据库需要先执行CREATE DATABASE police_assist DEFAULT CHARACTER SET utf8mb4;。注意字符集要使用utf8mb4而不是utf8因为辅警档案里可能包含生僻字或特殊符号utf8mb4能完整支持。4.2 修改 jdbc 配置文件中的关键连接参数数据库脚本导入之后需要修改工程里的db.properties或jdbc.properties。这里是对着代码理解参数的最直接入口。jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/police_assist?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue jdbc.usernameroot jdbc.password123456 jdbc.initialSize5 jdbc.maxActive20 jdbc.maxWait30000driver的值要和你下载的 MySQL 驱动版本对应。MySQL 5.7 及以前用com.mysql.jdbc.DriverMySQL 8.0 以后要用com.mysql.cj.jdbc.Driver不然会报ClassNotFoundException或者Loading class is forbidden的异常。useSSLfalse也很重要本地开发环境没有配置 SSL 证书如果不关闭 SSL 连接MySQL 8.0 启动时会出现大量告警。allowPublicKeyRetrievaltrue同样是针对 MySQL 8.0 的配置使用caching_sha2_password认证方式的用户首次连接时没有这个参数会报错。4.3 Tomcat 部署的两种方式与项目访问路径配置改好之后就是部署。常见的做法有两种第一种是用 IDEA 集成的 Tomcat 启动第二种是打 war 包放到 Tomcat 的webapps目录下。在 IDEA 里部署时配置 Tomcat Server然后设置Deployment里的Application context。如果工程名是police-assist那么访问地址就是http://localhost:8080/police-assist/。这里有一个容易忽略的细节如果你的项目里配置了server.servlet.context-pathSpring Boot 项目习惯在传统 SSM 中是通过applicationContext.xml里的context:component-scan来扫描的没有这个配置项。传统 SSM 的上下文路径就是 war 包的名字用 IDEA 运行时就是Deployment里的 Application context。手动部署的命令如下。mvn clean package cp target/police-assist.war /usr/local/tomcat/webapps/ /bin/bash /usr/local/tomcat/bin/startup.sh tail -f /usr/local/tomcat/logs/catalina.out启动之后查看日志是排错的关键。如果看到Cause: java.sql.SQLException: Access denied for user rootlocalhost说明用户名或密码错了如果看到Table police_assist.sys_user doesnt exist说明数据库脚本没有导入或者连接的库名不对。4.4 启动过程中最常见的三类报错第一类是 jar 包冲突。SSM 项目里pom.xml中同时引用了spring-webmvc和spring-web的不同版本启动时会报NoSuchMethodError。排查方式是执行mvn dependency:tree查看依赖树然后统一 Spring 版本。第二类是注解扫描不到。Controller报了 404但Service层没有被注入通常是因为spring-mvc.xml里扫描的是controller包而applicationContext.xml扫描的是service和dao包两边有遗漏。SpringMVC 的配置文件只管 Controller根配置管 Service 和 Dao两个扫描区间不能混。第三类是中文字符乱码。请求参数乱码是 Tomcat 的server.xml里没有加URIEncodingUTF-8数据库存进去乱码是jdbc.url少了characterEncodingutf8。这两处都查一遍基本能解决。Tomcat 8 及以上版本的URIEncoding默认是 UTF-8如果是 Tomcat 7 就需要手动加。5. 一个稳准狠的排查技巧把慢查询和对账挂在 MyBatis 的拦截器上SSM 项目上线一段时间之后最让人头疼的不是功能报错而是页面变慢、数据对不上。这类问题往往要到业务数据积累到一定量才暴露出来到时再靠肉眼去翻日志会很被动。常见做法是在 MyBatis 里加一个拦截器把每次 SQL 执行的耗时和参数自动打出来这样慢查询的定位就从“猜”变成了“直接看”。Component Intercepts({ Signature(type Executor.class, method update, args {MappedStatement.class, Object.class}), Signature(type Executor.class, method query, args {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}) }) public class SqlLogInterceptor implements Interceptor { private static final Logger log LoggerFactory.getLogger(SqlLogInterceptor.class); Override public Object intercept(Invocation invocation) throws Throwable { long start System.currentTimeMillis(); Object result invocation.proceed(); long cost System.currentTimeMillis() - start; MappedStatement ms (MappedStatement) invocation.getArgs()[0]; String sqlId ms.getId(); if (cost 500) { log.warn(slow sql detected. id{}, cost{}ms, sqlId, cost); } else { log.debug(sql executed. id{}, cost{}ms, sqlId, cost); } return result; } Override public Object plugin(Object target) { return Plugin.wrap(target, this); } }这个拦截器可以做成两种用法。平时log.debug输出到日志文件里发现性能问题直接搜索cost300以上的记录就能快速锁定是哪条 Mapper 定义的 SQL。想要更细的 SQL 文本可以继续从MappedStatement里拿BoundSql来拼完整语句。在实际的辅警管理项目里最耗时的操作往往集中在排班查询和考核统计报表。排班表的数据量可能不大但多次关联sys_user、assistant、department表之后没有索引的关联字段会成为瓶颈。用这个拦截器看到具体 SQL 后在对应的where条件字段上补ALTER TABLE shift_assignment ADD INDEX idx_work_date (work_date);问题就直接解决了。拦截器的实现验证起来也不难。启动项目后随便进入一个列表页观察控制台或日志文件。能打出 SQL 日志说明拦截器生效打不出说明Intercepts注解里的Executor签名不对或者 Spring 容器里SqlSessionFactory的插件配置覆盖了注解方式。如果用 XML 配置applicationContext.xml需要在sqlSessionFactory的 Bean 中加入property namepluginsarraybean classcom.weixin085.police.interceptor.SqlLogInterceptor//array/property才能让它真正接到 MyBatis 的执行链上。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →