尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

街道办管理系统实战:Spring Boot全栈项目从零到部署完整指南

街道办管理系统实战:Spring Boot全栈项目从零到部署完整指南 街道办管理系统这个项目在Java毕设和课程设计里出镜率高到离谱。这不是夸张它几乎是Spring Boot全家桶的完美练兵场——后端有框架、数据库有设计、前端有交互加上“街道办”的业务场景足够接地气居民档案、审批流转、活动报名每一个功能点都能让看的人立刻明白这个系统在解决什么问题。我自己完整做下来这套系统之后最大的感受是它不复杂但足够全面做完一遍你对Spring Boot项目的理解会比啃十篇教程都深。这篇文章我就把整个过程掰开揉碎来讲从功能模块怎么拆、数据库表怎么设计、后端接口怎么规范到联调踩坑、部署打包、文档编写全部写清楚代码逻辑都是实际跑通的直接照着做完全可行。1. 街道办管理系统为什么它是Spring Boot实战的“黄金选题”1.1 一个贴近真实业务场景的完整闭环很多练手项目容易走入两个极端。一种是纯CRUD做一个“用户管理”就完了看似简单但完全没有业务流程学会的只是增删改查的机械操作。另一种是过度追求复杂一上来就分布式、微服务、消息队列结果被困在各种中间件的安装配置里根本写不了几行业务代码。街道办管理系统恰好踩在中间那条线上。街道办的日常工作大体可以分成四条线人口基础数据管理常住人口、流动人口的档案建立与更新、政务事项审批低保申请、居住证明、生育登记这类流程性事务、社区信息服务公告发布、社区活动组织、意见征集、以及综合统计查询人口结构分析、办事效率统计。这四条线放到系统里恰好覆盖了信息管理系统最典型的几个阶段数据从哪来居民信息录入、人口导入数据怎么管分类维护、条件检索、状态流转数据如何用审批单提交、公告触达、活动报名数据怎么呈现统计图表、导出报表这套业务闭环意味着你做完这个项目不只是会用几个注解和框架而是完整经历了一个信息管理系统从设计到落地的全部思考过程。1.2 这套系统适合谁来做如果说清楚它的适用人群大概是这三类第一类计算机相关专业的毕业生。街道办管理系统是各大高校毕设选题里的常客原因是它的功能规模刚好是“一个人在一学期内能高质量完成”的量级。你说它简单吧它有审批流、有角色权限、有统计分析你说它复杂吧它又不涉及高并发、分布式事务这些难以在毕设中充分展开的难题。用它来做毕设工作量、技术含量、答辩时的可讲性三者能达到一个比较理想的平衡。第二类正在学Spring Boot的初学者。如果你已经看完了Spring Boot的基础教程但对着“下一步学什么”感到迷茫那直接拿这个项目练手是最快的方式。因为它的业务足够具象你不会卡在“不知道这个接口的业务含义是什么”这种问题上而是可以把全部精力放在“技术如何服务于业务”这件事本身。第三类打算转行做Java开发的朋友。对于没有真实项目经验的人来说简历上最缺的往往就是一个“完整的、可演示的、能讲清楚设计思路”的项目。街道办管理系统就是这样一块很好的敲门砖它可以在面试时展示你对Spring Boot、MyBatis、MySQL、前端联调这些后端日常工作的熟练度。1.3 我做完这个项目的整体感受先交代一下我最终的实现方案后端选用Spring Boot 2.7.x MyBatis-Plus Spring Security JWT数据库用MySQL 8.x前端用的是Vue 2 Element UI。下面所有内容都是基于这套组合展开的但设计思路本身不绑定具体技术版本你换成Spring Boot 3.x或者其他前端框架核心逻辑依然成立。整个项目从零开始做到最终跑通我最深的体会是这个系统的复杂度正好能让你感受到“工程化”的份量。比如不是所有查询都适合用MyBatis-Plus的BaseMapper搞定有些多表关联的统计查询你还是得老老实实写XML里的自定义SQL不是所有状态变化都可以靠前端按钮控制审批单从一个状态流转到另一个状态必须有后端的状态校验兜底。这些“学校不教但工作必用”的经验恰恰是这个项目最大的价值来源。2. 需求分析与功能模块拆分从街道办日常工作提炼系统边界2.1 角色划分是一切设计的前提任何系统的功能设计都离不开对使用角色的分析。街道办管理系统里的用户如果从真实业务视角来分至少可以拆成四个角色系统管理员负责整个系统的全局配置。这个角色和具体街道办业务不直接相关但却是系统的“地基”主要包括工作人员账号的开通与禁用、角色权限的分配、基础数据字典的维护、系统运行日志的查看。没有管理员角色其他角色都无从谈起。街道办工作人员这是系统中业务操作量最大的角色。他们负责居民信息的录入与更新、政务事项的初审与复核、公告的编辑与发布、社区活动的创建与管理、投诉建议的受理与回复。换句话说所有和“办事”相关的功能都集中在他们身上。街道办领导/管理层他们的需求集中在“看”上。看审批进度、看办结率、看人口结构数据、看活动参与情况。这个角色一般不做具体的增删改操作而是通过统计分析模块来掌握全局并可以对下属提交的某些事项做终审。社区居民这是系统里角色最简单但数量最庞大的用户群。他们的核心诉求是查看社区发布的公告和活动、在线提交办事申请、查询申请进度、提交投诉建议。需要注意的是真实的街道办对外系统通常还会配合微信公众号或者小程序来使用但在一个单体管理系统中我们一般把居民访问端做成一个独立的Web入口逻辑上和服务端共同部署。2.2 六大核心功能模块拆解基于上面的角色我把系统拆成了六个核心模块每个模块对应标题里“街道办管理系统”所要承载的典型业务。居民信息管理模块这是整个系统的数据基础。核心对象是“居民”包含常住人口和流动人口。功能包括居民档案的登记、批量导入比如从Excel导入、条件查询按姓名、身份证号、户籍地址、年龄段、政治面貌等维度、档案修改、迁出注销等。身份证号在这里是唯一业务标识必须做唯一性校验这一点在后面数据库设计时会重点说。政务审批管理模块这是系统里最能体现“流程性”的模块。街道办的常见审批事项包括居住证明开具、低保申请初审、独生子女父母光荣证办理、生育登记等。我设计时没有为每种审批单独建表而是用一张“审批主表 审批类型字段”来统一承载不同审批类型只是表单字段不同核心流程一致——先由居民提交申请材料工作人员初审领导终审最后把办理结果反馈给居民。公告信息管理模块功能上比较简单工作人员发布公告、设置置顶、下架过期公告居民端按照时间倒序查看公告列表并且支持按标题关键词搜索。这个模块技术含量不高但它是整个系统的“门面”首页是否好看很大程度上取决于公告模块界面做得是否清爽。社区活动管理模块包含活动创建、活动发布、居民报名、报名名单导出、活动结束后的参与情况登记。这里有个细节要提前想好活动报名人数需要控制在“活动容量”范围内所以报名接口必须做并发控制否则两个人同时报名最后一个名额就会超员。技术上可以用数据库乐观锁加活动状态判断来解决这个我在后面代码部分会展开。投诉建议管理模块居民的投诉建议从提交、受理、处理中、已办结到用户确认评价是一个完整的生命周期管理。投诉建议往往涉及多个部门流转所以字段设计时要考虑“当前处理人”和“处理记录”这两个字段否则无法追踪问题到底卡在哪个环节。统计分析模块面向管理层包括人口结构统计按性别、年龄段、户籍类型分布、审批办结率统计、活动参与趋势、月度投诉分类汇总等。这部分数据一律从明细表里GROUP BY聚合出来重点是SQL的编写效率和数据的准确性。2.3 功能清单的优先级排序我在做需求分析时习惯把功能排成P0、P1、P2三个优先级。P0是系统能跑起来的基础包括登录认证、用户管理、角色权限、居民信息CRUD这四项缺一不可必须最早完成。P1是业务价值最高的功能包括审批流转、公告发布、活动报名、投诉处理这些决定了系统能不能被称作“管理系统”。P2是锦上添花的部分包括数据导入导出、统计分析图表、操作日志、个人中心完善等。把P0和P1做完系统就已经是一个完整可交付的状态了。P2的部分可以根据时间情况选择是否全部完成。很多同学做项目容易一上来就忙着写代码我建议反过来先在纸上把这六个模块的界面草图、字段清单、接口列表理清楚再动手。磨刀不误砍柴工这个习惯能让你后面写代码的速度至少快一倍。3. 数据库设计街道办业务的对象模型与字段取舍3.1 核心表结构设计思路数据库设计是这类管理系统项目的灵魂也是最容易被忽视的部分。经常会看到有人为了省事把所有字段都塞进一张大表里当时是方便了后面每个功能都写得非常别扭。我自己的设计原则是一个业务对象一张主表一个扩展概念一张关联表流程性数据单独建表字典类数据用字段冗余。基于这个原则我最终的数据库主要包含以下核心表表名业务含义核心字段说明sys_user系统用户工作人员登录账号username、passwordBCrypt加密存储、real_name、phone、status、dept_idsys_role角色role_name、role_key、descriptionsys_user_role用户角色关联user_id、role_idsys_menu菜单/权限parent_id、menu_name、path、permission_keysys_role_menu角色菜单关联role_id、menu_idresident居民档案name、id_card、gender、birth_date、nation、political_status、marital_status、education、household_address、live_address、phone、population_type、residence_status、checkin_dateapproval_form审批单主表form_no、resident_id、approval_type、content、attachment_urls、status、apply_user_id、approve_user_id、approve_time、approve_commentnotice公告title、content、is_top、status、publish_time、publish_user_idactivity社区活动title、content、start_time、end_time、location、max_people、signup_count、status、create_user_idactivity_signup活动报名记录activity_id、resident_id、signup_time、status、checkin_statuscomplaint投诉建议title、content、type、status、reply_content、reply_time、reply_user_id、evaluate_levelfile_info附件信息file_name、file_path、file_size、upload_user_id、upload_time、biz_type、biz_id你可能会注意到我没有设计“户籍成员表”或“家庭关系表”原因是街道办管理系统的核心诉求通常是“以人查档”而不是“以家庭为单位管理”家庭成员关系的应用场景相对较少。如果你做的毕设题目里明确要求“以家庭为单位”来管理那就需要额外增加一个family表然后用resident.family_id来关联。这里体现的就是一个非常重要的能力——根据需求灵活调整数据模型而不是机械地抄网上现成的表结构。3.2 关键字段设计的细节考量有几个字段的取舍很多人容易踩坑我这里单独说一下。身份证号必须唯一但也要考虑到特例。居民身份证号在业务上是唯一的所以数据库表里应该加唯一索引。但实际录入数据时可能遇到没有身份证号的流动儿童、或者极少数历史数据缺失的情况所以字段本身要允许为空NULL在业务层校验“身份证号为空时必须录入姓名和出生日期”这样可以避免因为数据质量问题直接把系统卡死。软删除字段deleted统一为逻辑删除。街道办的人口数据是长期沉淀的直接物理删除风险太大。比如某个居民被误删除日志还没审计数据就永久丢失了。所以所有核心表都加一个deleted字段MyBatis-Plus的TableLogic注解可以帮我们自动实现逻辑删除。这点对管理类系统来说是标配务必养成这个习惯。状态字段用tinyint类型枚举含义写在代码常量里。不要用字符串存状态比如审批状态“待审核”到底是pending还是0还是待审核不同人写的代码不一样后期联调就容易出问题。我统一用tinyint0表示待审核1表示通过2表示驳回3表示已撤销所有状态定义放在一个常量类里前端只传数字后端根据常量翻译成文字返回。时间字段统一使用datetime类型默认值设置为CURRENT_TIMESTAMP。在表定义时就需要把create_time、update_time这两个自动维护字段设计好一个是插入时自动写入一个是更新时自动刷新不要指望每次在代码里手动set。MyBatis-Plus的MetaObjectHandler可以自动填充尽量减少重复性代码。3.3 为什么不建议用物理外键很多初学阶段的数据库教程里都会教你在创建表的时候加上FOREIGN KEY来保证数据的引用完整性。但实际做企业级管理系统的开发时我和身边大多数同行都不太建议在业务表之间直接加物理外键原因有三性能问题每张表在插入和更新时都要额外检查外键约束数据量上去之后影响明显耦合问题系统的数据关系往往不是简单的父子关系物理外键会让表结构变得僵硬后续做分库分表或字段扩展都很麻烦实际业务需要管理系统中“逻辑删”“暂存”“草稿”等状态很常见如果外键约束强绑定很多中间状态就没法存所以我的做法是在表设计上用“逻辑外键”——也就是在子表中维护父表的主键ID字段但不建立数据库层面的物理约束。数据的一致性由应用层来保证比如新增审批单时先校验resident_id是否存在且状态正常。这样既保证了数据的业务正确性又保持了表结构的灵活性。3.4 初始化数据脚本的准备工作数据库设计完成后除了建表语句还要生成一份完整的初始化SQL脚本内容至少包括三类一是字典数据比如民族下拉框汉族、满族、回族、壮族等、政治面貌群众、中共党员、共青团员等、学历初中及以下、高中、大专、本科、硕士及以上、房屋类型自有住房、租赁住房、公租房等。这些数据用INSERT语句预置好前端下拉框直接查表或者走接口获取不要让用户手动录入。二是默认账号至少要有admin管理员账号和test工作人员测试账号密码统一用BCryptHash加密后的值写入并且在文档里注明明文密码。很多同学在交作业时忘了这茬结果老师拿到系统后登录不进去印象分直接掉一大截。三是演示数据每个核心表都要准备一批逼真的数据。居民表至少要准备20条以上的演示数据涵盖不同年龄、性别、户籍类型审批单要有不同状态的记录公告和活动也要有已经发布和尚未开始的。这么做的核心目的是你做演示时不至于因为库里空荡荡而尴尬同时也便于测试列表分页、状态筛选这些功能是否正常。4. 技术栈选型与核心功能实现审批、上传、权限这些硬骨头怎么啃4.1 Spring Boot版本与核心依赖选择技术选型这件事很多人会纠结“到底选什么版本最合适”。我的建议是如果不做毕业设计答辩展示新技术就选自己最熟悉、生态最稳定的版本组合。我这次用的是Spring Boot 2.7.18这是2.x系列的最终版本文档完善、第三方集成示例多、网上遇到问题基本都能搜到答案。Spring Boot 3.x虽然已经发布了很长时间但它基于Jakarta EE规范部分旧教程的包名不兼容对于时间和精力都有限的项目来说没有必要为了追新给自己增加排坑成本。核心依赖清单大致是这样的dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-security/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-api/artifactId version0.11.5/version /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependencyMyBatis-Plus在这里扮演的角色很关键。它内置的BaseMapper提供了单表CRUD、分页插件、条件构造器能省掉大量重复的Mapper XML编写工作。街道办管理系统的绝大部分列表查询都是单表条件查询用LambdaQueryWrapper就可以很优雅地实现。但是像统计分析模块里的多表聚合查询我还是建议老老实实写XML里的自定义SQL而不是硬用QueryWrapper拼出复杂的Join查询后面维护起来会非常痛苦。4.2 JWT认证与Spring Security整合管理系统的所有接口除了登录接口外都应该需要认证才能访问。这里我选的是Spring Security JWT的组合核心思路是用户登录成功后服务端签发一个JWT Token返回给前端前端每次请求在Header里带上Authorization: Bearer token后端通过一个Filter拦截请求解析Token后把用户信息放入SecurityContext。登录认证的完整流程大概是这样的前端提交用户名和密码后端用AuthenticationManager执行认证调用UserDetailsService.loadUserByUsername()加载用户并用BCryptPasswordEncoder.matches()校验密码认证成功后用当前用户名生成JWT同时将用户ID、角色编码放入Token的Claims里返回给前端{ token: xxx, userInfo: { ... } }前端将Token存储在Cookie或localStorage中后续请求自动携带对应的核心配置类需要继承WebSecurityConfigurerAdapter并重写configure(HttpSecurity http)方法大概长这样Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers(/api/auth/login, /api/upload/**).permitAll() .antMatchers(/api/admin/**).hasRole(ADMIN) .antMatchers(/api/staff/**).hasAnyRole(ADMIN, STAFF) .anyRequest().authenticated() .and() .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); }有一个细节非常容易踩坑Spring Security默认开启了CSRF防护若不做任何配置前端提交表单时POST请求会被403拦截。我第一版就在这上面折腾了很久。所以如果你在联调时发现“登录接口直接打不通返回403”大概率就是CSRF或者CORS配置的问题。4.3 行政审批模块的状态机设计审批模块是整个系统里业务流程最复杂的部分但本质上它就是一个有限状态机。我是这样定义状态流转的状态码状态名称可流转到的状态码0待审核1通过、2驳回、3已撤销1已通过无后续操作2已驳回0重新提交、3居民撤销3已撤销无后续操作在后端服务实现时我单独写了一个ApprovalStatusHandler工具类专门负责校验状态流转的合法性。比如工作人员点击“审核通过”按钮时后端会先判断当前状态是否为0如果不是直接抛出业务异常“该审批单已被处理请勿重复操作”。这个校验在前端也要做但后端必须兜底因为接口是可以被绕过前端页面直接调用的。状态流转的Service核心逻辑大概是这样Transactional(rollbackFor Exception.class) public void approve(Long formId, Integer targetStatus, String comment, Long userId) { ApprovalForm form approvalFormMapper.selectById(formId); if (form null) { throw new BusinessException(审批单不存在); } if (!ApprovalStatusHandler.canTransit(form.getStatus(), targetStatus)) { throw new BusinessException(非法的状态流转: form.getStatus() - targetStatus); } form.setStatus(targetStatus); form.setApproveUserId(userId); form.setApproveComment(comment); form.setApproveTime(LocalDateTime.now()); approvalFormMapper.updateById(form); // 记录审批流转日志 approvalLogMapper.insert(new ApprovalLog(form.getId(), form.getStatus(), comment, userId)); }这里用Transactional保证状态更新和日志记录要么同时成功要么同时失败。审批单的所有状态变化都落一条日志方便后期审计“这个单子是谁在什么时间处理的”这也是真实系统里非常看重的能力。4.4 活动报名的并发控制社区活动模块有个业务点容易被人忽略——报名超员。前端限制了活动报名按钮的展示和“名额已满”的提示但后端接口在被并发请求时是可能同时通过两个请求的导致报名人数超过活动容量。解决办法其实很经典在activity表上加一个signup_count字段然后在更新时使用乐观锁思路UPDATE activity SET signup_count signup_count 1 WHERE id #{activityId} AND signup_count max_people只有当该语句影响行数为1时才算报名成功否则说明名额已满回滚事务并提示用户“手慢了下次早点来”。用一条SQL直接搞定并发控制不用引入Redis分布式锁这在单体应用里是最经济可靠的方案。4.5 文件上传与本地存储街道办审批业务需要上传证明材料身份证照片、户口本扫描件等所以文件上传功能必不可少。我的做法是前端通过el-upload组件的action属性指向后端/api/upload/file接口后端用MultipartFile接收文件校验扩展名和大小限制单文件10MB以内存储路径使用“日期分目录”方式比如/data/uploads/2025/01/15/xxx.jpg避免单目录文件数量过多文件名使用UUID.randomUUID()重新生成防止中文文件名和重名问题文件信息写入file_info表通过biz_type和biz_id与业务数据关联这里有个体验上的细节审批单更新材料时前端应该能“预览已上传的附件”。所以文件接口除了上传还需要提供一个/api/file/list?bizTypeapprovalbizIdxxx的查询接口返回文件列表前端用el-image直接以接口前缀存储路径拼接出访问地址。5. 后端接口规范与前后端联调的工程化细节5.1 统一返回体与全局异常处理接口设计的第一条规则就是所有接口统一返回格式。我定义的统一返回体是Data public class ResultT { private Integer code; // 200成功其他失败 private String message; // 提示信息 private T data; // 返回数据 public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMessage(操作成功); result.setData(data); return result; } public static T ResultT error(String message) { ResultT result new Result(); result.setCode(500); result.setMessage(message); return result; } }配合全局异常处理器业务代码里只需要抛出异常不用到处写try-catch去拼装错误响应。比如“居民身份证号已存在”这种情况业务代码就一行throw new BusinessException(身份证号已存在)全局处理器捕获后自动返回{code:500, message:身份证号已存在}。这让Controller的代码干净很多。5.2 分页查询的参数设计与实现管理系统的列表页几乎都离不开分页。MyBatis-Plus自带分页插件使用起来很简单但参数命名和返回结构一定要提前约定好避免前端每次联调都来问“你返回的total到底叫什么字段”。我习惯的请求参数是page页码从1开始、size每页条数、keyword关键词、status状态筛选以及各业务模块自己的筛选条件。返回结构是{ code: 200, message: 操作成功, data: { records: [ { id: 1, name: 张三 } ], total: 100, page: 1, size: 10 } }分页查询的Service逻辑用一个居民列表的例子说明public IPageResidentVO pageResident(ResidentQuery query) { PageResident page new Page(query.getPage(), query.getSize()); LambdaQueryWrapperResident wrapper new LambdaQueryWrapper(); wrapper.like(StringUtils.isNotBlank(query.getKeyword()), Resident::getName, query.getKeyword()) .eq(query.getPopulationType() ! null, Resident::getPopulationType, query.getPopulationType()) .eq(query.getStatus() ! null, Resident::getStatus, query.getStatus()) .orderByDesc(Resident::getCreateTime); return residentMapper.selectPage(page, wrapper); }第一参数query.getPage()和第二个参数query.getSize()前端传什么就映射什么接口层不做额外解释第1页、每页10条这种约定用注释写清楚。5.3 联调中常见的问题与解决前后端联调是整个项目里最容易出问题也最耗费时间的环节。我把实际踩过的坑列出来大家可以直接避雷。第一个坑跨域问题。前端开发服务器跑在localhost:8080后端在localhost:9090两者端口不同浏览器默认是会拦截跨域请求的。解决办法是写一个CORS配置类在Spring Boot里显式放行前端地址。示例如下Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowCredentials(true) .maxAge(3600); } }注意allowedOriginPatterns(*)和allowCredentials(true)必须搭配使用旧的allowedOrigins(*)在带Cookie时会报错这是一个容易踩的雷。第二个坑日期格式的序列化问题。后端返回的LocalDateTime默认序列化成2025-01-15T10:30:00前端直接用不友好。解决办法是在统一返回体上或者配置文件里指定格式spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8前端组件接收到的就是2025-01-15 10:30:00这种可读格式Element UI的el-date-picker也能直接绑定解析。第三个坑登录Token失效后的跳转逻辑。前端在请求遇到401状态码时应该自动跳转回登录页并清除本地存储的用户信息。很多人容易忘了封装统一的axios拦截器导致每个页面都要单独处理“未登录”的情况代码冗余且容易漏。正确做法是在axios的response.interceptors.response.use()里统一拦截401。5.4 Controller层的瘦身实践很多初学者会把大量业务逻辑堆在Controller里几百行的Controller比比皆是。我的习惯是Controller只做三件事接收参数、调用Service、返回统一结果。所有业务校验、数据组装、事务控制在Service层完成。这么做的另一个好处是写单元测试的时候可以直接测Service不用启动Web容器效率高很多。6. 前端页面搭建与核心交互逻辑6.1 为什么选择Vue 2 Element UI前端部分我用了Vue 2 Element UI。可能有人会问现在Vue 3和Element Plus都已经很成熟了为什么还用Vue 2答案很务实网上关于街道办管理系统的参考项目大多基于Vue 2 Element UI遇到问题搜索解决方案最快而且对这个项目的体量来说Vue 2和Vue 3的差别对最终体验和使用者来说几乎没有影响。如果是我自己开源给社区的人使用我会建议升级到Vue 3但对于毕设和练手而言稳定和资料完善才是第一位的。6.2 典型页面的设计思路系统一共包含20个左右的页面核心页面有这些登录页全屏背景图加居中的登录卡片用户名、密码两个输入框带验证码验证码后端生成图片返回可以增加系统的真实感。登录成功后根据角色跳转到不同的首页。系统首页/工作台放一些统计卡片比如今日待办审批数、本月活动数、人口总数、待处理投诉数下方放最近公告列表和最近活动列表。这些数据专门写一个统计接口一次性返回。居民信息管理页左侧可以是树形列表按社区/网格分组如果有这个需求的话右侧是表格。表格上方是筛选区关键词、人口类型、状态下方是分页条。导航栏有新增、导入、导出三个按钮。点击某行记录可以弹出侧滑详情面板展示居民完整档案。审批办理页用Tab页签区分不同审批类型每个Tab里是表格表格按状态筛选。点击“处理”按钮会弹出审核对话框显示申请材料列表工作人员填写审核意见点“通过”或“驳回”。活动管理页卡片式布局展示活动列表每张卡片上有活动封面、标题、报名时间、已报名/总量进度条。点击卡片进入详情页详情页里有活动介绍和报名名单。6.3 踩过的一个性能优化的坑做完第一版之后我发现居民列表页在数据量到1万条左右时打开页面要将近三秒。排查下来发现前端一次性调了三个接口居民列表、下拉字典民族、学历等、统计信息而且下拉字典接口返回了全表数据前端渲染时又做了很多不必要的计算。优化方案很简单字典数据单独缓存到前端Vuex里只在系统启动时请求一次后续字典下拉框全部走本地缓存列表接口和统计接口并行调用减少串行等待居民列表后端加索引身份证号、姓名、电话字段全部建立普通索引优化完之后页面加载速度降到了三四百毫秒。这个案例说明大部分管理系统性能问题不是框架层的问题而是接口设计不合理和数据库索引缺失导致的这一块在答辩时展开讲讲也是加分项。7. 部署交付与文档编写的完整指南7.1 从开发环境到部署上线的完整流程项目做完之后就该考虑怎么把它交付出去让别人能在自己电脑上跑起来。这一步如果处理不好再好的代码也可能因为“环境搭不起来”而被打折扣。我的交付环境说明文档里包含以下几个部分第一部分环境要求JDK 1.8及以上、Maven 3.6、MySQL 8.x、Node.js 14前端构建用。每项都给出具体的安装建议比如MySQL用Docker安装还是本机安装官方安装包下载地址等。第二部分数据库准备执行sql/init.sql脚本脚本里包含建库、建表、初始化数据三个部分。要注意编码问题脚本文件必须以UTF-8格式保存且在连接URL里指定characterEncodingutf8useSSLfalse否则中文数据会出现乱码。第三部分后端配置与启动修改application.yml里的数据库账号密码然后执行mvn spring-boot:run或者打包成JAR后运行java -jar xxx.jar。默认端口设为9090避免和前端开发服务器的8080起冲突。第四部分前端构建与启动进入前端目录先执行npm install安装依赖开发环境执行npm run serve生产环境执行npm run build然后把dist目录里的文件部署到Nginx的HTML目录下。7.2 Nginx部署时的反向代理配置如果采用前后端分离部署Nginx配置一定要注意“前端路由刷新404”的问题。因为Vue是单页应用前端路由由history模式接管刷新某个子路由时Nginx会按照文件路径去找找不到就报404。解决办法是配置try_files回退到index.htmlserver { listen 80; server_name localhost; root /opt/street-office/dist; index index.html; location / { try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:9090; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }这段配置同时解决了另一个问题前端请求/api/开头的接口会被Nginx反向代理到后端的9090端口前端开发时那套跨域配置在部署阶段也可以不用了。7.3 文档要写什么、怎么组织标题里明确提到了“文档”说明一份合格的项目文档是交付物的重要组成部分。很多同学对写文档有畏难情绪总觉得写文档比写代码难。其实项目文档不需要长篇大论重点是让人能看懂、能复现、能维护。我的文档结构是这样项目概述项目背景、系统定位、核心功能一句话描述技术架构用文字和表格说明前后端技术栈、核心依赖版本、系统架构图用文本描述不要画复杂架构图功能模块说明每个模块的输入、输出、核心流程配合截图说明数据库设计说明每张表的作用和关键字段含义表间关联关系描述接口文档按模块列出核心接口注明请求方式、参数、返回示例。这里可以直接用Swagger/knife4j生成省时省力部署手册环境准备、数据库初始化、后端启动、前端构建、Nginx配置等完整步骤测试报告测试环境、测试用例、测试结果汇总项目总结项目的亮点、遇到的问题与解决方案、后续优化方向最后这部分我尤其推荐大家认真写因为答辩时老师特别爱问“你在项目中遇到的最大难点是什么是怎么解决的”如果你在文档里已经做好了铺垫答辩的时候就可以讲得很有底气。7.4 答辩演示时不要踩的坑演示环节常见的翻车现场包括没提前插好数据库导致页面白屏、测试数据太少导致列表空荡荡、演示时网络波动导致依赖的CDN资源加载不出来、切页面时忘记已登录状态导致跳出登录页。我的建议是正式答辩前至少完整走三遍演示流程每一步该点哪里都心中有数。尤其要注意演示中需要展示的数据提前准备一组能突出系统亮点的高质量测试数据比如一条从“待审核”到“已通过”完整走完流程的审批记录两次不同状态的活动报名等。数据就是演示的“脚本”数据准备得好宣讲自然流畅。8. 复盘与经验总结做完这个项目之后的几点真实体会项目做完之后我最大的感触是管理系统这个分类看上去到处都是增删改查但真正把它做得顺手、成熟、有工程感里面藏着大量细节。第一个细节是要对“通用功能”有很强的敏感度。比如分页、字典、文件上传、日志记录这些功能在多个模块里反复出现单纯的复制粘贴是低效的我选择把它们抽象成公共组件。前端封装成全局组件后端拆成公共Service每个模块只需传参调用后续维护和扩展都轻松很多。第二个细节是不要迷信“只要会用框架就行”。MyBatis-Plus确实能省掉很多简单的SQL但在统计模块需要做多表聚合时还是得亲手写SQL。刚开始我写的统计SQL效率很低在一次几万条数据的聚合查询上耗时好几秒后来加索引、优化查询条件才把时间降下来。框架帮你解决了80%的基础问题剩下的20%恰恰是值钱的部分。第三个细节是代码规范要早点定下来。我之前有一阵子Controller、Service、Mapper命名比较随意后来项目越写越大找文件都快找疯了。在一个完整项目里包结构、命名规范、注释习惯这些软实力和代码功能实现的硬实力一样重要。比如我后来统一固定了controller、service、mapper、entity、vo、dto这批分层目录每个类名都遵循“业务模块角色”的命名方式光这一条就让联调痛苦指数直线下降。最后再说一个小技巧做这类项目时建议从一开始就用Git做版本管理每一个功能模块完成就提交一个commit消息写清楚“完成居民模块”还是“修复审批状态流转bug”。这个习惯在项目出问题回溯时真的能救命答辩时老师如果想看你的开发过程你也能拿出一个清晰的提交历史来支撑你的工作量说明。
返回列表