☰
Spring Boot乡村养老服务管理系统开发实战:从源码到上线全解析
2026/10/7 10:53:03 网站建设 项目流程

简介:这是一套基于SpringBoot框架构建的乡村养老服务管理系统完整源码,面向乡村养老场景,覆盖管理员、老人、医疗人员与志愿者四类用户。系统模块包括账户管理、养老服务申请与记录、老人健康信息维护、志愿者陪伴照料等流程,适合作为毕业设计、课程项目或乡村养老信息化改造的参考基线。压缩包共926个文件、33.73MB,核心文件包括145个Java后端代码、71个Vue页面、161个JavaScript脚本、64个HTML页面、52个样式文件,并附带SQL数据库脚本与项目启动脚本。服务申请、健康档案、志愿活动记录等业务流程完整,前后端结构清晰,便于按模块阅读和二次开发。目前已有43人在线学习浏览。借助这套源码,可以直观理解SpringBoot的分层架构、前后端接口配合方式、健康数据管理流程以及乡村养老服务业务闭环,对想落地JavaWeb项目或扩展养老管理功能的学习者具有明确参考价值。

1. 乡村养老服务管理系统到底在做什么:先想清楚业务模型再写代码

“乡村养老服务管理系统”这个标题听起来很大,但落到代码里,核心就三件事:老人档案建卡、上门服务留痕、补贴发放有据。Spring Boot 框架在其中管的是接口、事务和权限,真正难的是业务模型能不能贴合村一级的真实操作习惯。

如果你手里拿到的正是这样一个 zip 源码包,最忌讳上手就改代码。先看 README、SQL 脚本和实体类,把“乡–村–服务人员”三级角色和一张工单从创建到结算的状态流转挖出来,再决定改哪里。这套逻辑顺了,后面跑通、改造、交差都顺。

这个方向适合三类人:做毕业设计或课程项目的在校生、接乡镇零散信息化项目的独立开发者、以及想把 Excel 台账换成系统的基层信息员。我会按“解压源码 → 改配置 → 跑通业务 → 排查坑 → 准备上线”的顺序,把常见做法和血泪经验说清楚。

2. 用 Spring Boot 搭出可扩展的项目骨架:从 zip 源码包到本地能跑

2.1 解压源码后先认清目录结构:Maven 工程与 SQL 脚本

拿到 zip 后第一步不是急着点 IDE 运行,而是先解压看结构。一个规范的 Spring Boot 后端工程,通常长下面这样:

rural-eldercare/ ├── pom.xml ├── sql/ │ ├── 1_schema.sql # 建表脚本 │ └── 2_data.sql # 演示数据 ├── src/main/java/com/rural/eldercare/ │ ├── RuralElcareApplication.java │ ├── controller/ # 接口层 │ ├── service/ # 业务层 │ ├── mapper/ # MyBatis 数据访问接口 │ ├── entity/ # 实体类 │ ├── dto/ # 入参/出参对象 │ └── config/ # 安全、Web 配置 └── src/main/resources/ ├── application.yml ├── mapper/ # Mapper XML └── static/ # 静态资源/前端页面

如果你拿到的包和我这里不完全一样,命名不同很正常;重点看三个入口:pom.xml、application.yml、XXXApplication.java。pom.xml决定你用的 Spring Boot 版本和依赖,application.yml决定连哪个库、端口号,启动类里的main方法是整个 jar 的入口。

这里有个识别技巧:看pom.xml里<parent>中的版本。Spring Boot 2.7.x 和 3.x 的写法差别很大,比如WebSecurityConfigurerAdapter在 3.x 被移除了,javax.*包名也换成了jakarta.*。先确认版本再查资料,能省下一晚上的折腾。如果发现是 2.3.x、2.6.x 这种老版本,也别急着升级,乡镇项目稳定优先,只要没有严重安全漏洞,保持原版跑得转就是胜利。

sql目录是最容易忽略却最要命的部分。很多源码包单独提供 SQL 脚本,而不是用 JPA 自动建表。这时你必须在跑应用前把库建好,否则启动会报找不到表。拿到的 SQL 文件一般有两个:一个建表、一个灌数据。我习惯先把2_data.sql导入,看看到底有哪些演示数据,这样后面调试接口时心里有数。

2.2 改三个必改配置:数据源、端口、MyBatis 映射路径

要让项目在本地跑起来,优先动application.yml里这三个位置。下面是一份常见配置骨架:

server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/rural_elder?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 换成你自己的密码 driver-class-name: com.mysql.cj.jdbc.Driver sql: init: platform: mysql mode: always mybatis: type-aliases-package: com.rural.eldercare.entity mapper-locations: classpath:mapper/*.xml

url里的characterEncoding=utf8保证中文不乱码;serverTimezone=Asia/Shanghai保证日期计算不偏 8 小时,这一点后面避坑章节还会专门说。password不要硬编码在 yml 里,生产环境推荐用环境变量替换,例如${DB_PASSWORD}。

mybatis.mapper-locations告诉框架去哪找 XML 文件,type-aliases-package让你在 XML 里可以直接写resultType="Elder"而不是一长串全限定类名。如果启动时报Invalid bound statement (not found),八成就是这个路径和src/main/resources/mapper/实际目录对不上。先检查这两个路径,比重新导入项目快得多。

然后初始化数据库:

mysql -uroot -p -e "create database rural_elder default charset utf8mb4;" mysql -uroot -p rural_elder < sql/1_schema.sql mysql -uroot -p rural_elder < sql/2_data.sql

这三行分别是建库、建表、灌演示数据。别小看第三步,没有演示数据,列表接口全是空的,你根本没法判断 SQL 写得对不对。如果要换 MySQL 8.0,driver-class-name用com.mysql.cj.jdbc.Driver;如果是 5.7,可能得回退到旧驱动类,但新驱动向下兼容,默认用新的即可。

2.3 为什么选 MyBatis 而不是 JPA:乡镇项目改表频繁的务实选择

很多教程默认推 JPA,但在这种有大量报表、多表联查、临时加筛选条件的场景里,我一般选 MyBatis。原因很朴素:SQL 可控。村信息员今天打电话说“能不能加一个只看五保户的选项”,你直接进 XML 改一条 SQL 就能交付;用 JPA 的 Specification 绕来绕去,反而难调。

如果你看到源码里既有Mapper接口又有 XML,说明查询都放在 XML 里。去找src/main/resources/mapper/ElderMapper.xml,大概率能看到类似这样的统计语句:

<select id="countByHealthLevel" resultType="int"> SELECT COUNT(*) FROM elder WHERE village_id = #{villageId} GROUP BY health_level </select>

#{villageId}是预编译参数,框架会帮你转义;而${villageId}是字符串拼接,哪怕这个值来自后端常量,也不要出现在接受用户输入的地方,否则就是 SQL 注入。这个原则在民政系统的安全审计里是必查项,如果你要交付给政府类客户,这点不能含糊。

如果整个工程还出现了“若依框架”的痕迹(比如SysUser、RuoYiApplication这类命名),说明它是在若依脚手架上改的业务。这种源码的权限和用户管理不用你费心,但你的业务代码要放独立模块里,别混进框架自带的system模块。否则后面若依升级,你的改动全会被冲突淹没。

2.4 第一次启动:从 jar 包到接口冒出 JSON 的标准动作

配置改完后,有两种启动方式。在 IDE 里直接跑RuralElcareApplication.java最简单,但想验证打包能不能用,我更建议先跑一遍 Maven 打包:

mvn clean package -DskipTests java -jar target/rural-eldercare-0.0.1-SNAPSHOT.jar

-DskipTests是跳过单元测试,如果你拿到包的测试类里配了无法访问的数据库地址,这一步能少踩一次坑。注意看启动日志里Tomcat started和Started RuralElcareApplication这两行,出现它们才算没白跑。

启动成功后,用浏览器访问登录接口或者url里的某个页面。如果项目有 Spring Boot 自带的前端静态页,打开http://localhost:8080应该能看到登录页面。如果是纯后端接口项目,没有页面很正常,用 curl 验证一下健康检查:

curl http://localhost:8080/actuator/health

返回{"status":"UP"}就说明应用活了。这条命令在接下来所有排错里都是第一道照妖镜:先看应用本身活着没,再谈接口逻辑。

3. 核心模块落地:老人档案、服务工单与补贴发放的代码实现

3.1 老人档案建模:容易漏掉的冗余字段与字典设计

先看elder表的建表语句。这是整个系统的心脏,字段设计直接决定后续有多少补丁。

CREATE TABLE elder ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, id_card VARCHAR(18) NOT NULL UNIQUE, gender TINYINT COMMENT '0-女 1-男', birth_date DATE, village_id BIGINT NOT NULL COMMENT '所属行政村', health_level TINYINT COMMENT '1-自理 2-半失能 3-失能', poverty_flag TINYINT DEFAULT 0 COMMENT '1-低保/五保', contact_phone VARCHAR(20), address VARCHAR(255), create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, KEY idx_village (village_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

我把birth_date和gender从身份证号里“冗余”了一份。有人觉得多余,但乡村录入场景里,很多老人第一次建档时身份证号没带在身上,信息员填的是出生年份和性别。等后来补录身份证号,再回填。冗余字段不是白存,它是给不完美的现实留的口子。

health_level和poverty_flag这类状态字段,建议专门建一张字典表,或者在 Java 里用枚举兜底。不要散写在业务代码的数字魔数里,否则别人接手时根本不知道health_level=2到底是“半失能”还是“失能”。如果源码里已经有了dictionary表,就优先用它。

另外village_id不要只建普通索引,建议建联合索引,比如KEY idx_village_level (village_id, health_level)。因为后期报表全是“某个村有几个半失能老人”,这种联合索引能让统计 SQL 走覆盖索引,省很多时间。

3.2 服务工单的状态机:从派单到结单的流转与防篡改

养老服务最怕“假服务”。护工没上门,系统里却多了一条服务记录,这不仅是管理问题,还是资金问题。所以工单不能随便增删改,必须走状态机。下面是核心的创建工单方法:

public ServiceOrder createOrder(OrderCreateDTO dto) { Elder elder = elderMapper.findById(dto.getElderId()); if (elder == null) { throw new ServiceException("老人档案不存在"); } String today = LocalDate.now().toString(); int done = orderMapper.countTodayOrders(dto.getWorkerId(), today); if (done >= 8) { throw new ServiceException("该护工今日工单已达上限,不能派单"); } ServiceOrder order = ServiceOrder.of(dto.getElderId(), dto.getWorkerId(), OrderState.CREATED); orderMapper.insert(order); return order; }

先校验老人存在,再校验护工当日负荷。countTodayOrders统计的是“已派 + 进行中”的工单,防止把护工排到一天十几个单,服务质量崩掉。我见过某些项目把限制写成 999,等于没有限制,最后被审计问得话都说不出来。

真正要紧的是状态流转。我会把“结单”单独放在一个 service 方法里,不交给 Controller 直接改字段:

public void completeOrder(Long orderId, Long workerId, CompleteOrderDTO dto) { ServiceOrder order = orderMapper.findById(orderId); if (order == null || order.getState() != OrderState.IN_PROGRESS) { throw new ServiceException("当前状态不允许结单"); } if (!order.getWorkerId().equals(workerId)) { throw new ServiceException("只能结自己的单"); } order.setState(OrderState.COMPLETED); order.setEndTime(LocalDateTime.now()); order.setRemark(dto.getRemark()); orderMapper.updateState(order); }

CREATED → IN_PROGRESS → COMPLETED → CONFIRMED,每一步都校验“谁在操作、当前状态合法吗”。假如需要“取消”,只能管理员取消,护工自己不能撤销,因为撤销和补单都是一条后门。审计时数据库里的state_change_log比任何解释都有说服力。

为什么不在 Controller 里写这套逻辑?因为层与层各管一件事。Controller 只收参数、返回 JSON,Service层承载业务规则。这样以后加一条“周六不能结单”的规则,你只改 service 不碰接口定义,前端字段也不用动。

3.3 补贴计算:金额用 BigDecimal,单价从参数表来

补贴是乡村养老系统的资金命脉,算错一分钱都会被查。常见错误是用double存单价,或者把单价硬编码在代码里。正确做法是:

public BigDecimal calcSubsidy(Long orderId) { ServiceOrder order = orderMapper.findById(orderId); if (order.getState() != OrderState.CONFIRMED) { throw new ServiceException("只有确认后的订单才能结算"); } SubsidyRule rule = subsidyRuleMapper.findByServiceTypeAndLevel( order.getServiceType(), order.getHealthLevel()); if (rule == null) { throw new ServiceException("未配置该服务项目的补贴标准"); } BigDecimal amount = rule.getUnitPrice(); if (order.getServiceType() == ServiceType.HOMECARE.getCode()) { amount = amount.multiply(BigDecimal.valueOf(order.getWorkMinutes().doubleValue() / 60.0)) .setScale(2, RoundingMode.HALF_UP); } return amount; }

金额运算必须用BigDecimal.multiply,并指定舍入模式;divide如果要除,紧跟精度,否则直接抛ArithmeticException。补贴单价放在subsidy_rule表里,由乡镇管理员按季度调整,别换一次政策就发一次包。

我在实际项目里见过洗浴项目按 22 分钟计费,double累计 20 条后差出两毛钱,对账对到半夜。那之后我给自己定了条死规矩:凡是金额、单价、补贴,一律BigDecimal,且精度统一两位,舍入用HALF_UP。HELP这个词在这里不是求助,而是“向上取一半”的舍入模式,中文叫四舍五入。

3.4 统计报表:用一条 SQL 代替几十行 Java 循环

乡镇系统逃不开月报、季报。别在 Java 里 for 循环每个村统计,直接在 Mapper XML 里写聚合 SQL,既清晰又省内存。常见的补贴汇总:

<select id="sumSubsidyByVillage" resultType="java.util.Map"> SELECT v.name AS village_name, COALESCE(SUM(s.amount), 0) AS subsidy_total, COUNT(DISTINCT s.elder_id) AS elder_count FROM village v LEFT JOIN service_order s ON s.village_id = v.id WHERE s.state = 4 AND s.confirm_time BETWEEN #{start} AND #{end} GROUP BY v.id, v.name ORDER BY subsidy_total DESC </select>

COALESCE(SUM(...), 0)处理没有服务记录的村,避免返回null导致前端显示空白。COUNT(DISTINCT s.elder_id)算受益人次,不是服务单数,因为一个老人可能一月服务了 10 次,口径要清楚。如果你拿到源码里的统计 SQL 没有加state = 4(即 CONFIRMED),赶紧补上,否则会把未确认的草稿单子算进补贴,这是财务事故。

4. 权限与监控:Spring Security 和 Spring Boot Admin 的集成要点

4.1 登录认证:为什么选 JWT 而不是 Session

乡村网络环境差,服务人员可能上午在村西头,下午去另一个村,服务站点也常换机器。Session 存在内存里,应用一重启全掉线。更现实的理由是,现在很多项目接的是手机 H5,前后端分离已经是主流。JWT 无状态、方便多端验证,但要注意密钥管理和过期策略。

常见做法是写一个过滤器,解析请求头里的Authorization: Bearer xxx:

@Component public class JwtAuthenticationFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { String auth = request.getHeader("Authorization"); if (auth != null && auth.startsWith("Bearer ")) { String token = auth.substring(7); try { Claims claims = Jwts.parser() .setSigningKey(secretKey()) .parseClaimsJws(token).getBody(); request.setAttribute("uid", claims.get("uid")); request.setAttribute("role", claims.get("role")); } catch (JwtException | IllegalArgumentException e) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "登录已过期"); return; } } chain.doFilter(request, response); } }

密钥别写死在代码里,放到配置文件或者环境变量里,每次启动从配置读取,这样就算代码传到 git 上也不会泄露。JWT 的优势是“天然分布式”,但secretKey()要让别人知道你是读的哪个配置,不然新人接手时到处找密钥,最后把写死的密钥翻出来了,等于白设计。

4.2 角色与数据权限:乡、村、服务人员三种视角

数据权限比角色更头疼。同是“管理员”,乡级管理员能看全乡,村级管理员只能看自己村。在表上加village_id还不够,查询时要把当前用户的villageId强制注入进去,而不是信任前端传的参数。

@PreAuthorize("hasAnyRole('TOWN_ADMIN','VILLAGE_ADMIN','WORKER')") public PageResult<ElderVO> pageElders(ElderQueryDTO dto) { Long userId = SecurityUtil.getUserId(); UserInfo user = userMapper.findRole(userId); if (user.getRole().equals("VILLAGE_ADMIN")) { dto.setVillageId(user.getVillageId()); } return elderMapper.page(dto); }

关键点是dto.setVillageId(user.getVillageId()),它发生在你校验完角色之后。如果只靠前端传villageId,别人调接口传villageId=1就能看别的村。这是安全审计里最容易挨刀的地方。我在审查别人代码时,第一件事就是搜Controller里有没有直接把参数透传给 Mapper 的接口,十有八九能查出越权漏洞。

在 Spring Boot 3.x 中,WebSecurityConfigurerAdapter已被移除,要改用SecurityFilterChain新版写法。如果你拿到的是 2.x 源码,看到extends WebSecurityConfigurerAdapter会有“已弃用”的提示,不用慌,功能一样,只是换了个写法。除非你有强制升级需求,否则不要顺手把 Spring Boot 版本改了,否则大量依赖都要跟着动。

4.3 用 Spring Boot Admin 盯住运行健康度

乡村项目通常没有专职运维,出问题要能被提前发现。我习惯在服务里加spring-boot-admin-client,连接一个内网跑的 admin server。配置只需要几行:

spring: boot: admin: client: url: http://192.168.10.10:9000 instance: prefer-ip: true service-url: http://192.168.10.20:8080 management: endpoints: web: exposure: include: health,info,metrics

prefer-ip: true的意思是让 admin server 用内网 IP 去访问这个服务,而不是机器名。management.endpoints.web.exposure.include只暴露health,info,metrics,不要图方便放*,尤其不能暴露shutdown和env。env会回显数据库密码,相当于把保险柜钥匙挂在门口。

admin server 本身最好也加server.address=127.0.0.1或者白名单,别绑0.0.0.0。监控面板这种东西,只有运维和开发能看,绑到公网等于把系统内部结构昭告天下。

4.4 操作日志:审计现场的第一手证据

政务类项目最怕“死无对证”。谁在几点把某位老人的补贴从 200 改成了 300,必须有记录。最省事的方案是用 AOP 切面统一记录写操作。

在需要留痕的地方,我给方法加一个自定义注解@AuditLog("修改补贴"),切面里统一记录入参、操作人、IP 和结果。下面是一个极简切入点:

@Around("@annotation(auditLog)") public Object log(ProceedingJoinPoint pjp, AuditLog auditLog) throws Throwable { String operator = SecurityUtil.getUserId().toString(); String args = Arrays.toString(pjp.getArgs()); Object result; try { result = pjp.proceed(); saveLog(auditLog.value(), operator, args, "SUCCESS"); } catch (Exception e) { saveLog(auditLog.value(), operator, args, "FAIL:" + e.getMessage()); throw e; } return result; }

把日志写进数据库专门的一张表operate_log,比翻文件日志高效得多。时间久了文件的日志会被滚动删除,数据库表却能一直留着。审计时工作人员问你“为什么这条补贴取消了”,你把operate_log里那行数据拍在桌上,这是会计凭证级别的底气。

5. 避坑指南:乡村场景下最容易翻车的五个常见问题

5.1 时区与日期:补贴结算差一天等于差一路钱

现象:系统白天录入的服务工单,凌晨查看时create_time比真实时间少了 8 小时;日结报表把当天最后一批单算到了前一天,补贴汇总对不上账。

原因:MySQL 连接串里没传serverTimezone,Spring Boot 默认用 JVM 时区,而部署服务器可能是 UTC。字段类型用了DATETIME本来没问题,但 JDBC 读取时发生了时区转换偏移。

解决:连接串统一加serverTimezone=Asia/Shanghai;实体类时间字段用LocalDateTime,不要用java.util.Date;部署机器时执行timedatectl set-timezone Asia/Shanghai。这三件事缺一不可,体检裤带系一道可能不够。

5.2 身份证校验:格式对不代表号码真

现象:能输入 18 位身份证号,但随便改一个数字也能通过,到民政核对名单时整批被退回。

原因:只写了正则\d{17}[\dXx],没有校验最后一位校验码。身份证第 18 位是根据前 17 位按 GB 11643 规则算出来的,改一个数字,校验码就对不上。

解决:写一个公共校验工具,入库前调用。核心是加权求和后对 11 取模:

public static boolean isValidIdCard(String idCard) { if (idCard == null || idCard.length() != 18) return false; String regex = "^\\d{17}[0-9Xx]$"; if (!idCard.matches(regex)) return false; char[] chars = idCard.toCharArray(); int[] w = {7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2}; String check = "10X98765432"; int sum = 0; for (int i = 0; i < 17; i++) { sum += (chars[i] - '0') * w[i]; } return check.charAt(sum % 11) == Character.toUpperCase(chars[17]); }

注意一定要Character.toUpperCase(chars[17]),否则用户输入小写x会误判。这套校验逻辑不复杂,但很多人死活踩坑,就是因为把自己写的正则当成了万能钥匙。

5.3 MyBatis 批量插入:一条巨大 SQL 拖垮数据库

现象:从 Excel 导入五百位老人档案时,程序卡死或耗时十多分钟,数据库 CPU 飙高。

原因:一个<foreach>拼了几千条 INSERT 组成的超大语句,同时没调整 MySQL 的max_allowed_packet,或者干脆用循环单条插入。

解决:用 MyBatis 的批量插入 SQL,并且分批提交,每批 200 条:

<insert id="batchInsertElder"> INSERT INTO elder (name, id_card, gender, birth_date, village_id, health_level) VALUES <foreach collection="list" item="e" separator=","> (#{e.name}, #{e.idCard}, #{e.gender}, #{e.birthDate}, #{e.villageId}, #{e.healthLevel}) </foreach> </insert>

调用时按 200 条一组循环,并且不要和整个导入流程挤在同一个大事务里。正确姿势是“每批一个事务”,中途失败只回滚这一批,已提交的保留,同时给导入接口一个断点续传的入口,而不是让信息员从头再导一遍。

5.4 文件上传:图片存在数据库是最典型的灾难

现象:护工用手机拍照上传服务证明,数据库.ibd文件几天涨了几个 GB,备份越来越慢。

原因:有人图省事,把图片转 Base64 字符串直接塞进TEXT字段。Base64 比原始字节多 33% 体积,一张 1MB 照片变成 1.37MB 字符串存在库里,再来一万张就是灾难。

解决:图片落在服务器或 OSS,数据库只存 URL 和大小。本地存储就单独建/data/uploads目录,通过路径访问,不经过业务应用。上传接口加大小限制:

spring: servlet: multipart: max-file-size: 5MB

还要在 Nginx 配置里对上传文件做后缀白名单,只允许.jpg.jpeg.png,否则有人传个.jsp上去就有被解析的风险。这个坑看似普通,但几乎所有电子档案系统最后都栽在存储规划上。

5.5 源码包跑不起来:先怪环境还是先怪代码

现象:拿到 zip 解压后,mvn spring-boot:run报一堆错,第一反应是源码有问题。

原因:多半是 JDK 版本、Maven 镜像、MySQL 版本和代码不匹配。Spring Boot 2.x 用 JDK8/11,Spring Boot 3.x 必须 JDK17;MySQL 5.7 和 8.0 的驱动类名、认证方式都不一样。

解决:先看 README,没有就自己查pom.xml和application.yml,把 JDK 切到匹配版本,Maven 换成国内镜像;MySQL 建库用utf8mb4。如果连com.mysql.cj.jdbc.Driver都报找不到,说明 pom 里没引驱动或驱动版本太低。最后看sql目录,一般源码自带的 SQL 一定能跑通,先别去动它。等应用起来了,再根据你的业务需求去改表,这样定位报错时你心里有明确的先后顺序。

6. 从能跑到能用:轻量压测、日志与上线前检查清单

系统跑通只是第一步。要真敢让它在乡里部署,得先过一遍弹量测试。乡镇系统不需要扛高并发,一般几十个管理员同时用就撑死了,但也不能几个请求就把线程池打满。

先测登录接口和核心列表接口。用ab做简单压测:

ab -n 200 -c 20 -p login.json -T application/json http://localhost:8080/api/login

-n 200是总请求数 200,-c 20是并发数 20,login.json是提前写好的请求体。如果错误率不是 0%,先看接口返回的报错码。出现500打开应用日志,看到SQLException就去查连接池配置;看到OutOfMemoryError就把jvm的-Xmx调一下。压测不是为了好看,是为了逼出那些只会在并发下现形的初始化 bug。

日志配置要分层:

logging: file: name: logs/rural-elder.log level: com.rural.eldercare.mapper: debug

mapper包用debug能打印 SQL 语句,排查“查不到数据”很管用;生产环境记得调回info,否则一个高光时刻的村子能打出几个小时不重复的日志。日志文件建议按天滚动,避免单文件越来越大,找个日志文件时翻到怀疑人生。

上线前检查清单,整理成一张表存在项目根目录:

检查项正确姿势常见错误
数据库连接串带serverTimezone=Asia/Shanghai使用 UTC 导致时间错 8 小时
密码配置环境变量注入明文写在application.yml提交到仓库
管理端端口内网隔离或白名单绑定公网0.0.0.0
文件上传目录独立目录加 Nginx 映射图片 Base64 存库
多环境配置至少dev和prod分离只有一份开发配置直接上线

我自己在上一个乡村项目里吃过时区的亏,后来每次交付前都会把“当前时间 + 数据库时间 + 业务时间”三处对一遍。代码能跑只是开始,能经得起查才是能用。希望这个清单能帮你少熬几个夜,也希望你交付的每个系统都不被“返工”二字追上。

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

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

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

立即咨询