新闻详情

新闻详情

首页 / 资讯中心 / 详情

Spring Boot+小程序实现社区新生儿疫苗预约系统设计与实战

发布时间:2026/10/1 21:57:01来源:尧图网络
Spring Boot+小程序实现社区新生儿疫苗预约系统设计与实战
做社区新生儿疫苗预约这个小程序项目前后花了大概三周时间。核心需求很简单社区医院或卫生服务中心的儿保科每天要接待大量新生儿接种疫苗电话预约、纸质登记、到现场排队整个流程混乱且容易出错。家长们不知道什么时候有苗、没苗只能一趟趟跑护士们忙着登记、查记录、打电话通知工作效率极低。最终我基于Spring Boot搭后端小程序做家长端实现了排期展示、在线预约、疫苗库存管理、接种提醒这一整套闭环流程——也就是标题里那个“springboot社区新生儿疫苗预约小程序”附带完整源码编号26885。这篇就把整个项目的设计思路、核心模块、关键代码和踩坑实录完整梳理一遍给正在做同类需求或者想拿Java后端练手完整项目的朋友一个可直接参考的模板。先说清楚这个项目到底解决了什么问题它把“家长-宝宝-疫苗-预约-接种”这条链路全部线上化。家长在小程序端绑定宝宝信息查看未来7天的疫苗排期和剩余号源选择合适时段一键预约后台自动锁定库存、生成预约单并通过微信订阅消息提醒家长按时到站。管理员在PC端维护疫苗品种、批号、库存量和每日可预约人数随时查看预约数据。整个流程省掉了大量人工沟通成本“有没有苗”这种问题再也不需要打电话问。先交代一下项目的整体技术选型后端用的是Spring Boot 2.7.xJDK 1.8完全够用如果想体验新特性也可以上Spring Boot 3.x持久层框架用的MyBatis-Plus数据库MySQL 8.0缓存用的Redis文件存储这一块我直接用MinIO做本地化部署用来存宝宝的接种本照片、家长头像等附件。小程序端用原生微信小程序开发没有引入复杂的UI框架——因为预约类小程序页面结构相对固定原生语法完全够用而且原生写出来的包体积更小冷启动更快。如果你问我为什么后端一定要用Spring Boot而不是别的我的答案很直接生态成熟、上手门槛低、社区问题沉淀多。这个项目里涉及的微信登录、定时任务、Redis缓存、接口鉴权Spring Boot全都有现成的starter或者成熟整合方案。更重要的是对于社区医院这类场景后续可能需要对接医保、his系统Spring Boot在传统行业信息化的接受度远超Node.js或Go后续维护交接也更容易找到人。编码时我没有把接口直接“裸奔”给小程序而是统一做了Token鉴权这个后面细说。关于“附源码26885”这串编号其实它就是资料打包时的一个代号方便归档索引。源码里包含两个部分springboot-server后端工程和miniapp-client小程序前端工程外加一份部署说明文档。下载解压后可以看到整体目录结构如下springboot-server/ ├── src/main/java/com/community/vaccine/ │ ├── controller/ // 接口层小程序端所有请求入口 │ ├── service/ // 业务逻辑层 │ ├── mapper/ // MyBatis-Plus数据访问层 │ ├── entity/ // 数据库实体 │ ├── config/ // 全局配置Redis、WebMvc、微信参数 │ ├── common/ // 统一返回结构、异常处理、工具类 │ └── task/ // 定时任务过期订单处理、库存回补 ├── src/main/resources/ │ ├── mapper/ // MyBatis XML文件 │ ├── application.yml // 核心配置 │ └── sql/ // 建表脚本 miniapp-client/ ├── pages/ │ ├── index/ // 首页排期 │ ├── reserve/ // 预约流程 │ ├── order/ // 订单列表 │ ├── profile/ // 个人中心 │ └── baby/ // 宝宝管理 ├── utils/ // 请求封装、工具方法 └── app.js拿到源码建议先别急着跑一定要先看SQL脚本里的建表语句把表结构理解一遍所有的业务逻辑都是围绕这些表展开的。1. 项目整体设计与方案选型说实话拿到这个需求的第一反应并不是写代码而是先想清楚一个核心问题预约系统的“库存”到底该怎么定义。疫苗和普通商品不一样它有严格的批号管理、效期管理和冷链要求。同一个疫苗品种可能同时存在多个批号不同批号的库存数量不同、效期不同甚至接种年龄要求也不同。如果简单粗暴地把库存设计成一个总数后续对账会很痛苦。最终方案是“疫苗品种-批号-排期”三层结构每一条排期记录关联到具体批号预约时锁定的是某个排期下该批号的剩余可约数量。1.1 为什么是Spring Boot 小程序这套组合对社区级预约系统来说选型的第一原则不是“炫技”而是“稳妥”。Spring Boot 小程序的组合有几层考量。第一微信小程序是家长端最自然的存在形态不用下载App微信里搜一下或者扫个码就能打开对中老年带娃群体极其友好第二Spring Boot后端能同时兼顾预约接口、管理后台接口、定时任务一个工程搞定全部避免“一个项目拆三个服务”的过度设计第三这套组合网上案例极多社区医院的信息科真要去改代码遇到问题搜得到解决方案。第四微信生态自带订阅消息能力预约成功后由后端主动推送提醒不需要家长装额外App或者关注公众号体验和触达效率都很好。1.2 核心功能模块拆解整个系统按角色分为两类家长端的“预约使用者”和管理端的“机构管理员”。家长端小程序聚焦四个页面能力首页排期展示未来7天可预约的疫苗列表按日期分组显示每个时段的剩余号源。预约提交选择一个排期时段确认宝宝信息提交预约。订单列表查看待接种、已完成、已取消状态的预约单支持取消预约操作。宝宝管理新增/编辑宝宝档案包括姓名、出生日期、疫苗本编号。管理端实际做成了一个轻量的Web页面Spring Boot的Thymeleaf模板集中处理疫苗品种维护疫苗名称、适用月龄、剂次说明。批号与库存批号、生产日期、效期、入库数量、剩余数量。排期管理每天每个疫苗品种开放多少个号、具体时段如09:00-10:00、10:00-11:00。预约查询按日期、疫苗、状态筛选导出Excel。还有一个容易被忽略但非常关键的能力——定时任务。每天凌晨扫描预约单把前一天“已预约但未到站接种”的订单自动标记为过期并回补对应时段的号源。这个逻辑不写的话放鸽子的人一多号源就慢慢被僵尸订单占满护士得手动清理特别痛苦。1.3 技术栈与性能预期整体技术栈列一张表方便对照层面技术选型选型理由后端框架Spring Boot 2.7.x稳定、资料多、适合快速交付ORMMyBatis-Plus单表CRUD不用写SQL复杂查询走XML数据库MySQL 8.0存储事务性数据预约扣库存必须有事务保证缓存Redis 5.x号源扣减、token缓存、热点数据缓存文件存储MinIO部署在局域网不依赖公网OSS数据自主可控定时任务Spring Scheduled单机部署足够不需要引入XXL-Job这类重组件小程序端原生微信小程序包体小、启动快、页面定制灵活性能预期其实没必要做太高社区医院一个接种点的日活预约量通常只有几百单按每天500单、峰值QPS约20来估算单机部署、MySQL连接池给到20Redis做缓存扛住首页排期查询的读压力完全绰绰有余。真正需要关注的是并发扣库存时的数据一致性而不是无脑上微服务。2. 核心业务逻辑与数据模型设计预约系统的本质是“在有限资源下做资源分配”所以数据模型设计必须围绕资源来展开。我这里最核心的几张表是疫苗品种表vaccine、批号表vaccine_batch、排期表vaccine_schedule、预约单表appointment和宝宝表baby。排期表承担着“号源”这个核心角色它决定了某一天、某个时段、某个疫苗批号最多可以预约多少人。排期表的字段大致如下CREATE TABLE vaccine_schedule ( id bigint NOT NULL AUTO_INCREMENT, vaccine_id bigint NOT NULL COMMENT 疫苗品种ID, batch_id bigint NOT NULL COMMENT 疫苗批号ID, schedule_date date NOT NULL COMMENT 接种日期, time_slot varchar(32) NOT NULL COMMENT 时间段 如09:00-10:00, total_quota int NOT NULL COMMENT 总号源数, remain_quota int NOT NULL COMMENT 剩余号源数, status tinyint NOT NULL DEFAULT 1 COMMENT 1启用 0停用, create_time datetime DEFAULT NULL, PRIMARY KEY (id), KEY idx_date_vaccine (schedule_date, vaccine_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;表建完之后业务上最关键的就是预约流程的状态管理。我把预约单一共设计了五个状态流转关系必须清晰待接种用户提交预约成功号源已锁定等待用户到站。已完成用户到站接种管理员在后台确认完成。已取消用户主动取消号源立即回补。已过期预约日期过了用户没有到站定时任务批量处理号源回补。已作废管理员因突发情况作废预约单比如疫苗临时缺货。这里有一个容易搞错的细节用户取消后号源回补是“立即”的但是“过期回补”是“延迟”到第二天凌晨才执行的。为什么因为当天还有现场接种的可能护士现场登记时如果发现这个宝宝来了可以直接把订单标记为已完成而不是先让定时任务把号源回收掉造成库存和实际到站人数对不上。2.1 号源扣减的并发控制方案预约场景的并发控制是核心中的核心。两个家长同时点击同一个时段的最后一个号系统必须保证只有一个人能预约成功。最朴素的做法是直接对数据库行加锁也就是SELECT ... FOR UPDATE把那一行排期记录锁住然后判断剩余号源并更新。这个方案正确性没问题但锁表行期间其他预约请求全部阻塞在高并发下性能不好看。而且社区接种点经常出现“某个时段一放号几秒钟被抢完”的情况数据库行锁撑得住但体验一般。我最终采用的是“Redis预扣 数据库兜底”的双层方案第一步预约请求进来先操作Redis用DECR命令扣减该时段剩余号源的计数器。如果返回结果小于0说明没号了直接返回“已约满”同时INCR把计数器加回来保证计数器不为负。第二步Redis扣减成功后再走数据库事务。事务里先查排期记录带悲观锁确认数据库里的剩余号源确实大于0然后插入预约单、更新剩余号源事务提交。如果事务失败必须补偿性把Redis计数器加回来。核心代码Transactional(rollbackFor Exception.class) public AppointResult createAppointment(Long scheduleId, Long babyId) { String redisKey vaccine:quota: scheduleId; long remain redisTemplate.opsForValue().decrement(redisKey); if (remain 0) { redisTemplate.opsForValue().increment(redisKey); return AppointResult.fail(该时段已约满); } try { VaccineSchedule schedule scheduleMapper.selectForUpdate(scheduleId); if (schedule null || schedule.getRemainQuota() 0) { throw new BusinessException(该时段已约满); } Appointment appointment new Appointment(); appointment.setScheduleId(scheduleId); appointment.setBabyId(babyId); appointment.setVaccineId(schedule.getVaccineId()); appointment.setAppointDate(schedule.getScheduleDate()); appointment.setTimeSlot(schedule.getTimeSlot()); appointment.setStatus(AppointmentStatus.PENDING); appointmentMapper.insert(appointment); schedule.setRemainQuota(schedule.getRemainQuota() - 1); scheduleMapper.updateById(schedule); return AppointResult.ok(appointment); } catch (Exception e) { redisTemplate.opsForValue().increment(redisKey); throw e; } }这个方案的好处是Redis扣减扛住大部分并发流量数据库悲观锁只是兜底不会出现超卖。注意一点Redis计数器初始化的时机是管理员发布排期时顺便写入并且要设置过期时间避免排期数据长期占用Redis内存。2.2 库存回补的幂等性设计号源回补最怕的是“重复回补”。设想一下用户先取消预约随后定时任务又扫描到这张“已取消”的订单把它当做过期单再回补一次号源就多了实际库存对不上账。解决办法是给回补逻辑加状态前置条件。不管是用户取消还是定时任务过期处理执行前都必须用UPDATE appointment SET status 新状态 WHERE id ? AND status 原状态这种条件更新语句只有更新影响行数为1时才执行号源回补。比如取消预约int updated appointmentMapper.cancelIfPending(appointmentId); if (updated 1) { scheduleMapper.increaseRemainQuota(scheduleId, 1); redisTemplate.opsForValue().increment(vaccine:quota: scheduleId); }cancelIfPending对应的SQL是UPDATE appointment SET status已取消 WHERE id#{id} AND status待接种。这个条件更新天然保证了幂等哪怕同一个取消请求被重复提交第二次因为状态已经不是待接种更新行数为0不会重复回补库存。2.3 排期发布与库存初始化的联动后台管理员发布排期时要同时做三件事插入排期记录、初始化Redis计数器、检查排期日期是否在疫苗效期内。这里还有一个业务细节容易被忽略——疫苗批次的效期。疫苗批次有生产日期和有效期发布排期的时候如果没做效期校验可能出现排期日期已经超过疫苗效期的情况预约倒是成功了但苗根本不能打。我在排期发布接口里加了一层校验scheduleDate必须在批次效期之前否则直接拒绝发布。另外同一个疫苗品种在同一天不能重复创建相同时间段的排期否则页面展示会出现两个重复入口家长根本不知道选哪个。3. 核心模块实现与关键代码解析整个后端工程里接口层其实很薄真正的复杂度集中在微信登录、预约下单、消息通知三个地方。下面按模块拆开讲。3.1 微信登录与Token鉴权小程序端用户点击“微信一键登录”前端调用wx.login()拿到临时凭证code传给后端/api/auth/login接口。后端用这个code去向微信接口换取openid和session_keypublic WxLoginResult wxLogin(String code) { String url https://api.weixin.qq.com/sns/jscode2session?appid appId secret appSecret js_code code grant_typeauthorization_code; String resp restTemplate.getForObject(url, String.class); JSONObject json JSON.parseObject(resp); String openid json.getString(openid); String sessionKey json.getString(session_key); // 查库没有openid就自动注册有就直接登录 User user userMapper.selectByOpenid(openid); if (user null) { user new User(); user.setOpenid(openid); userMapper.insert(user); } // 生成自己的token写入Redis有效期7天 String token UUID.randomUUID().toString().replace(-, ); redisTemplate.opsForValue().set(login:token: token, openid, 7, TimeUnit.DAYS); return new WxLoginResult(token); }这里要特别提醒session_key不要返回给前端也不要自己存库。它是微信会话密钥理论上只能保存在服务端用于解密手机号或者某些敏感数据。实际开发中不少人把session_key直接塞到数据库里这是不必要的安全风险。我们的token是自己生成的UUID和微信的session_key完全隔离后续所有接口只需要校验这个token对应的openid即可。登录之后所有请求统一走一个拦截器AuthInterceptorpublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String token request.getHeader(Authorization); if (StringUtils.isBlank(token)) { throw new BusinessException(401, 未登录); } String openid redisTemplate.opsForValue().get(login:token: token); if (StringUtils.isBlank(openid)) { throw new BusinessException(401, 登录已过期); } request.setAttribute(openid, openid); return true; }把openid放到request的attribute里后续controller通过(String) request.getAttribute(openid)就能拿到当前用户省去每个接口重新解析token的重复代码。这里有个细节用Redis存token的好处是可以随时踢人下线比如用户更换手机登录或者后台封禁账号删掉Redis key就能让旧token立即失效。如果用无状态JWT做到这步就得维护黑名单反而更麻烦。3.2 预约下单接口的完整流程预约接口是整个项目里最核心的一个接口它的完整流程是这样的前端传scheduleId和babyId后端先校验宝宝是否属于当前openid下的用户。检查排期状态是否为启用status1日期不能是过去的日期。走Redis预扣号源。数据库事务里创建预约单、扣减库存。事务提交后异步发送微信订阅消息告诉家长预约成功。返回预约单详情给前端。这里有一个RESTful接口前后端联调容易踩的坑预约接口必须校验宝宝归属权。很多项目上线后才发现只要知道babyId就能给别人的宝宝预约这是严重越权漏洞。我用了最简单的办法babyMapper.selectByIdAndOpenid(babyId, openid)一个条件SQL就把问题堵住了。3.3 微信订阅消息的发送实现微信订阅消息是预约系统的最佳“自动通知”手段。用户预约时小程序端需要先调用wx.requestSubscribeMessage让用户确认授权接收通知拿到一个requestId然后把requestId传给后端。后端拿到这个凭证后再结合预约单数据去调微信接口发送订阅消息。后端发送的核心逻辑public void sendAppointmentNotify(Long appointmentId) { // 查预约单 宝宝信息 排期信息 Appointment appointment appointmentMapper.selectDetailById(appointmentId); JSONObject data new JSONObject(); data.put(thing1, new JSONObject().put(value, appointment.getVaccineName())); data.put(date2, new JSONObject().put(value, appointment.getAppointDate() appointment.getTimeSlot())); data.put(thing3, new JSONObject().put(value, 请按时携带接种本到社区中心)); JSONObject body new JSONObject(); body.put(touser, appointment.getOpenid()); body.put(template_id, WxConfig.subscribeTemplateId); body.put(page, pages/order/order); body.put(data, data); // 请求微信接口发送 wxApiClient.sendSubscribeMessage(body); }注意value字段有字数限制比如thing类型字段最长20个字符超出会被微信接口拒绝。疫苗名称加上“请按时携带接种本到社区中心”这串文字一定要提前做截断处理不然线上会莫名其妙报43004错误——这个坑我踩过一次排查了很久才发现是某个疫苗名称长了两个字。3.4 定时任务处理过期预约单定时任务用Spring自带的Scheduled注解就够用了。我配置成每天凌晨1点执行Scheduled(cron 0 0 1 * * ?) public void handleExpiredAppointments() { // 查询所有预约日期小于今天的待接种订单 ListAppointment expiredList appointmentMapper.selectExpiredPending(); for (Appointment item : expiredList) { int updated appointmentMapper.markExpiredIfPending(item.getId()); if (updated 1) { scheduleMapper.increaseRemainQuota(item.getScheduleId(), 1); redisTemplate.opsForValue().increment(vaccine:quota: item.getScheduleId()); } } }这一步最关键的是“只处理预约日期早于今天的订单”。千万别用“状态为待接种且当前时间超过预约时段”这种条件因为如果当天临时停电、停诊所有当天订单都会在第二天被误判为过期家长预约单明明还有效号源却被回收了。我的方案是统一按“日期”维度处理过期的定义是“预约日期 今天”不是“预约时段已过”。一个月的运行下来这个口径没有出过问题。4. 小程序端实现要点与常见坑小程序端页面不多但每个页面都有自己的注意点。我按实际开发的顺序来说方便对照源码看。4.1 登录态维护和小程序冷启动小程序的登录态维护不能只靠wx.login()因为wx.login()拿到的code是一次性的后端换的token虽然能存7天但小程序本身可能被用户杀掉重开、或者7天没打开过token早过期了。我在app.js的onLaunch里做了静默登录逻辑先读本地storage里的token带着token调/api/auth/check接口返回有效就直接用返回401就重新调wx.login()换取新token然后继续跑业务。// utils/request.js function ensureLogin() { return new Promise((resolve, reject) { const token wx.getStorageSync(token); if (token) { request(/api/auth/check, { token }, { noAuth: true }) .then(() resolve(token)) .catch(() doWxLogin(resolve, reject)); } else { doWxLogin(resolve, reject); } }); }这里有个体验细节登录操作不能阻塞首屏渲染。首页排期数据加载和登录流程是并行触发的后端接口对于“未登录”的请求只返回401前端拦截器捕获到401后执行登录登录成功再重新发起原请求而不是让用户白屏等待。具体实现是在request方法里加一层“401时自动重试一次”的逻辑。4.2 首页排期的加载更多与下拉刷新首页排期列表是“按日期分组展示疫苗排期”一个月下来可能有几百条排期记录不可能一次性全部加载。我用了分页加载首次加载当前日期之后7天的排期每次上拉触底时加载后续7天的数据并把日期范围往后平移。页面结构上一个scroll-view包住整个列表用bindscrolltolower触发加载更多。这里有一个小程序原生开发的经典坑——scroll-view的lower-threshold设得太大会导致连续触发多次加载我设成50px并且在数据加载中加一个loadingMore标志位防止重复请求onReachBottom() { if (this.data.loadingMore || this.data.noMore) return; this.loadMoreSchedules(); }onReachBottom是页面级滚动触底事件直接用就好不需要手动绑定scroll-view的低滚动事件。另外列表数据更新必须用setData替换整个数组而不是push之后单独set某一条否则视图层性能会明显变差。4.3 日期选择器的坑不能用picker的modedate直接限制范围预约页需要选择一个接种日期很多人的第一反应是用微信原生picker的modedate。但这个组件默认只能限制start和end字符串并不能按“排期是否开放”来禁用日期。实际开发你会发现用户随便选一个没排期的日子也能进页面最后提交预约时才提示“该日期无排期”体验非常差。我的做法是在预约页用一个自定义的星期条横向滚动的7天日期条只展示后端接口返回的有排期的日期。这样用户能选的就一定是有效日期后端接口只做兜底校验前端展示层面就已经把无效日期过滤掉了。这个交互改动虽然增加了少量开发量但对用户体验的提升非常明显。4.4 订单状态的展示与主动刷新订单列表页按状态Tab展示待接种、已完成、已取消。这里又一个容易踩的坑用户从首页预约成功后跳转到订单列表列表页的数据可能是旧数据因为页面在tab切换时不会重新触发onLoad。解决办法是在onShow生命周期里主动刷新当前tab的数据onShow() { if (this.data.currentTab) { this.loadOrders(this.data.currentTab); } }onShow每次页面显示都会触发比onLoad更可靠。如果担心频繁请求可以加一个“距上次刷新超过30秒才重新拉取”的节流逻辑。5. 常见问题与排查技巧实录这个项目从开发到上线我收集了一堆实际踩过的问题不少是网上搜不到的细节。整理成表格方便直接对照排查。症状根因解决方法小程序请求后端全部404后端端口用了8080但小程序配置了80端口访问确认application.yml的server.port把小程序request的baseUrl改为http://IP:8080预约提交时“已约满”但页面显示还有号Redis计数器与数据库不一致发布排期后强制初始化Redis计数器每次库存变更后同步更新两个数据源订阅消息发送报41030template_id错误或与小程序AppID不匹配检查微信公众平台里选用的模板ID必须是当前小程序账号添加的模板订阅消息报43101用户未授权订阅消息前端必须调用wx.requestSubscribeMessage且用户点击允许一次授权只能发送一次消息开发者工具能调通真机不行局域网IP问题或未配置合法域名真机调试时后端地址不能写localhost要用局域网IP上线必须配置https域名数据库连接超时连接池太小或MySQL线程数打满调整spring.datasource连接池参数初始5、最大20即可定时任务执行了两次没加分布式锁多实例部署导致单机部署加Scheduled即可多实例需引入Redis分布式锁5.1 微信开发者工具看网络请求的技巧联调阶段很多人习惯在开发者工具里点“Network”面板但有时候小程序发起的请求在Network面板里根本看不到尤其是wx.request封装了promise且调用链比较深的情况。我的经验是不要过度依赖Network面板直接在request.js统一封装的函数里打console.log输出请求路径、参数、返回数据。这种做法虽然“土”但对调试后端接口最直观还能顺手把请求耗时打出来console.log([API] ${url} params, data); console.time(url); wx.request({ ... success: (res) { console.timeEnd(url); console.log([API] ${url} resp, res.data); }});如果你需要看真实的请求和响应体也可以打开开发者工具的“调试器→Network”但前提是勾选了“不校验合法域名”并且在真机调试时把域名校验关掉。线上环境建议把这类日志从代码里移除避免敏感信息打到生产日志里。5.2 数据库和Redis数据一致性排查方法环境跑一段时间后我最担心的是“页面展示余号数量”和“数据库实际库存”对不上。这个问题基本都出在异常链路Redis扣减成功了但数据库事务回滚了没有把计数器加回来。排查思路是先对比两个数据源查数据库SELECT schedule_id, remain_quota FROM vaccine_schedule WHERE schedule_date CURDATE()查Redis用redis-cli批量查vaccine:quota:*的值如果发现不一致优先检查代码里所有decrement之后是否都有对应的increment补偿。这里给一个建议把Redis扣减和数据库事务放在同一个方法里并且补偿逻辑写在使用try-catch包裹的同一段代码中不要分到不同方法否则容易出现漏补偿。5.3 部署上线后的域名与HTTPS问题小程序正式上线要求后端接口必须是HTTPS而且域名必须在小程序后台配置到request合法域名里。社区医院这种场景一般没有现成的HTTPS域名和证书建议用Nginx做反向代理把证书配置在Nginx层后端Spring Boot仍然跑HTTP端口通过proxy_pass转发。Nginx核心配置片段server { listen 443 ssl; server_name vaccine.example.com; ssl_certificate /etc/nginx/cert/fullchain.pem; ssl_certificate_key /etc/nginx/cert/privkey.pem; location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }这个方案的好处是后端代码不需要做任何改动证书到期只需要在Nginx层替换。还有一点小程序的request域名不能带端口所以就算后端端口是8080Nginx也必须监听443并且不带端口转发否则真机请求会被微信拦截。5.4 高并发瞬间的数据库连接打满处理有一次实测早上8点放号瞬间涌入几百个预约请求。数据库连接池默认配置往往是10直接被打满部分请求超时。排查后发现是连接池太小但更根本的原因是每个预约请求都要先查排期、再插预约单、再更新库存总共3次数据库交互加上登录校验等一次完整的预约流程要占用数据库连接接近1秒。解决方式有两个一是调连接池maximum-pool-size设置到20二是把校验类逻辑尽量往Redis层挪减少数据库交互次数。实测调整后500个并发请求全部在2秒内处理完成没有出现连接池耗尽。6. 部署流程与后续可扩展方向项目跑通只是第一步能不能稳定跑起来、后续能不能扩展才见真功夫。这段就讲清楚从源码到线上部署的完整链路。6.1 后端打包与启动后端是标准Maven工程打包mvn clean package -DskipTests产物在target/springboot-vaccine-server.jar。部署到服务器上用nohup启动nohup java -jar springboot-vaccine-server.jar \ --spring.profiles.activeprod \ --server.port8080 \ app.log 21 注意--spring.profiles.activeprod生产环境的数据库连接、Redis地址、微信AppSecret这些配置别放在application.yml里明文写死用环境变量或者--参数覆盖。我一般是维护一个application-prod.yml其中数据库密码和AppSecret用${DB_PASSWORD}这类占位符启动脚本从环境变量里注入避免把密钥跟着源码一起传出去。6.2 小程序端构建与发布小程序端源码在miniapp-client目录直接用微信开发者工具打开填好自己的AppID修改utils/config.js里的baseUrl然后点击“上传”在微信公众平台提交审核。审核通过后发布上线即可。需要提醒的是线上版的baseUrl必须是HTTPS域名不能是http://服务器IP:8080这样的局域网地址。所以小程序的baseUrl配置要区分环境开发版用http://192.168.x.x:8080体验版和正式版用https://vaccine.example.com。我通常写一个环境判断const env prod; const baseUrl env prod ? https://vaccine.example.com/api : http://192.168.1.10:8080/api;6.3 扩展方向从预约到接种闭环当前版本聚焦预约但社区医院其实还有更多可以深挖的场景。第一个方向是“接种前自助建档”。家长在小程序里提前填写宝宝健康状况、过敏史、既往接种反应护士到站后直接打印知情同意书节省现场问询时间。这个需要后端增加一个健康档案表并和预约单关联。第二个方向是“电子接种证”。虽然疫苗本还是实体为主但可以在小程序里展示宝宝的接种历史每次接种完成后自动更新小程序里的“接种记录”页面相当于电子版接种卡。这样做的好处是对家长透明不容易漏种、错种。第三个方向是“多社区入驻”。当前版本是单点部署如果后续社区卫生服务中心有多个站点可以在排期表和预约单里增加stationId字段把系统升级成多站点共享模式。这块改动不大但能显著提升系统的复用价值。第四个方向是“疫苗库存预警”。管理员后台可以增加一个预警规则比如某批次库存低于20%时自动提醒采购或者某疫苗连续3天约满率超过90%时提醒增加放号量。这种数据分析功能实现难度不大但对实际运营的帮助很大。这些方向不需要推翻现有架构都是在现有表结构上做加法。这也是我当时刻意控制代码耦合的原因——业务逻辑全部在service层新增功能时基本不需要动controller和mapper的既有接口。7. 源码使用说明与实际运行建议最后把源码的打开方式和运行步骤完整列一遍。源码里我会放一个README.md但很多细节只有真跑一遍才会发现。7.1 本地开发环境要求需要的软件版本JDK 1.8 或以上我用的JDK 1.8Spring Boot 2.7.x兼容Maven 3.6MySQL 8.05.7也行但字符集建议utf8mb4Redis 5.x微信开发者工具最新稳定版先把SQL脚本springboot-server/src/main/resources/sql/init.sql导入MySQL然后修改application.yml里的数据库连接、Redis连接、微信AppID和AppSecret。这里提醒AppID和AppSecret必须是你自己的小程序账号下的不能用别人的否则微信登录接口会报40013或40125。7.2 启动顺序建议按以下顺序启动启动MySQL和Redis确认端口可访问。启动Spring Boot服务看控制台日志是否输出“Vaccine Server Started”。用Postman或浏览器直接访问http://localhost:8080/api/health确认返回{ status: UP }。打开微信开发者工具导入miniapp-client目录修改utils/config.js里的baseUrl为http://localhost:8080/api。在开发者工具里点“编译”看首页是否正常加载排期数据。如果你用的是微信开发者工具的“不校验合法域名”模式本地联调时baseUrl直接写http://localhost:8080/api就能跑通。真机预览的话手机和电脑必须在同一局域网并且baseUrl要改成电脑的局域网IP。7.3 运行一周后需要重点检查的指标系统跑起来不是终点稳定运行才是。我建议上线后第一周重点看四类数据每天预约成功数、约满时段数量评估号源配置是否合理过期未到站的订单数如果占比超过10%说明家长对接种时间提醒不够敏感建议增加预约日前一天的二次提醒接口平均响应时间和错误率重点关注首次发放号源时段的P95耗时数据库库存和页面展示是否一致每天做一次对账。这套检查方法帮我发现过一个真实问题某疫苗连续三天预约率不足30%原因是放号时段只设置了上午9点到10点很多家长上班不方便。后来在后台把时段扩展到下午预约率立刻上来了。这类调优只有系统真正跑起来之后才能发现。写到这里我个人最大的体会是社区级预约系统技术难度并不高真正难的是对业务场景的理解。疫苗预约不是“抢票”它要考虑库存的批次账、号源的有效期、家长不到站的情况、护士现场登记的灵活性这些隐含约束在需求文档里往往只有一句话但落到数据模型和接口设计上每一个都可能是坑。如果你正在做或者准备做类似的小程序预约项目建议先把每一张表、每一个状态流转画清楚再动手写代码这比任何框架选型都重要。源码里我已经把完整的表结构和状态机都整理好了照着跑一遍再改成自己的业务会比从零开始顺畅很多。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

强化学习训练成本解剖:GAR/GRS/OCS/RCS四维账本体系 2026/10/1 23:50:34

强化学习训练成本解剖:GAR/GRS/OCS/RCS四维账本体系

1. 这不是技术报告,是一份RL训练成本解剖图“一份 RL 训练账单里的生意”——这个标题一上来就撕掉了AI行业常见的技术滤镜。它不谈算法收敛性、不炫模型参数量、不堆砌SOTA指标,而是把显卡小时、token吞吐、人工标注工时、grader响应延迟这些藏在论文附…

阅读更多 →
Linux服务器中文字体缺失导致方块乱码?安装配置与验证全攻略 2026/10/1 23:50:33

Linux服务器中文字体缺失导致方块乱码?安装配置与验证全攻略

先问一个场景:你是不是也遇到过这种情况——后端接口、数据库、日志文件里中文都是好的,一跑到导出的PDF里就成了□□□□,或者Java程序生成的报表图片里全是方块,排查了半天编码、UTF-8、数据库连接池都没用。这时候大多数人会去…

阅读更多 →
2024年TensorFlow生产实践:从安装部署到Keras核心用法 2026/10/1 23:50:33

2024年TensorFlow生产实践:从安装部署到Keras核心用法

2024年还有人纠结要不要学TensorFlow,这个问题我太熟了。年初组里接新项目,刚入职的同事张口就是"现在谁还用TF啊,PyTorch不香吗",结果一看线上仓库,一堆TF Serving压着几年前的SavedModel模型,天…

阅读更多 →
TensorFlow 2024实战指南:从环境安装到模型部署完整链路 2026/10/1 23:50:32

TensorFlow 2024实战指南:从环境安装到模型部署完整链路

TensorFlow是我最早接触的深度学习框架,算下来前前后后也用了好几年。最近后台总有人问“2024年了还该不该学TF”、“装TF老是出问题怎么办”,索性把这几年积累的实操经验整理成一篇,覆盖从安装到跑通模型的完整链路,也聊聊我对Te…

阅读更多 →
MiMo-V2.6:大模型RLHF训练成本解剖与工程落地指南 2026/10/1 23:50:32

MiMo-V2.6:大模型RLHF训练成本解剖与工程落地指南

1. 这不是一份技术报告,而是一张训练成本的解剖图“一份 RL 训练账单里的生意”——这个标题乍看像财经专栏,实则直击当前大模型强化学习(RL)落地最痛的神经:谁在付钱?钱花在哪?花得值不值&…

阅读更多 →
Linux云计算+AIOps大模型:云原生基础设施与智能运维一体化实战 2026/10/1 23:50:26

Linux云计算+AIOps大模型:云原生基础设施与智能运维一体化实战

1. 从“会敲命令”到“能扛故障”:这套一体化实战到底在解决什么问题很多人学 Linux 的路径都差不多:装个虚拟机,跟着教程敲ls、cd、grep,背一堆常用命令,然后去面试被问到“线上 CPU 飙高怎么排查”就卡壳。问题不在于…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉