SpringBoot+微信小程序代驾系统实战:从登录态到实时位置同步

SpringBoot+微信小程序代驾系统实战:从登录态到实时位置同步 简介本资源是一份完整的本科毕业设计论文文档面向计算机专业学生及Java全栈初学者聚焦微信小程序代驾系统的工程化落地实践。论文基于SpringBoot后端框架与微信小程序前端系统覆盖用户/司机双端功能、订单全流程管理、微信支付集成、实时定位对接及管理员后台等核心模块并包含需求分析、技术选型JavaMySQLSpringBoot、系统实现细节与性能安全优化方案。资源为单个Word文档.doc格式全文约5.94MB结构完整含中英文摘要、目录、五章正文绪论、开发工具、系统分析、系统设计、系统实现、参考文献及附录内容详实代码逻辑与业务流程描述清晰。目前已有144人学习下载适合用于课程设计参考、毕设开题借鉴、SpringBoot实战复现及小程序服务类项目架构学习。1. 为什么代驾系统必须用 SpringBoot 微信小程序双端协同不是技术炫技而是业务刚性需求一个真实的代驾订单从用户在微信里点开小程序、定位出发地、输入目的地、选择车型、确认下单到司机接单、实时位置共享、行程中状态更新、费用自动结算、服务评价闭环——整个链路涉及高并发定位上报、低延迟状态同步、强事务性支付回调、敏感地理位置脱敏、多角色权限隔离用户/司机/客服/运营以及微信生态内天然的用户身份体系复用。纯前端小程序无法承载订单状态机管理、支付验签、司机调度策略、风控规则引擎等核心逻辑而传统 Java EE 单体架构又难以快速响应微信小程序“即用即走”带来的流量脉冲和灰度发布需求。SpringBoot 成为事实标准不是因为它语法糖多而是它用SpringBootApplication一行注解就封装了嵌入式 Tomcat、自动配置、条件化 Bean 加载、Actuator 健康检查等生产级能力让开发者能聚焦在「司机如何被合理分配」「超时未接单如何自动转单」「微信支付回调如何防重放」这些真实问题上。本文不讲论文写作套路只拆解一个可上线、可监控、可横向扩展的代驾系统服务端骨架从初始化一个 SpringBoot 框架的服务端代码开始到微信小程序用 code 换取 token 的完整链路落地再到代驾场景下最关键的实时位置同步与订单状态驱动机制。2. 初始化一个 SpringBoot 框架的服务端代码选型、结构与微信生态对接起点代驾系统对服务端的稳定性、可观测性和微信生态兼容性要求极高。SpringBoot 版本选择不是越新越好——当前2024 年主流实践推荐使用SpringBoot 2.7.x或3.1.x若已全面升级 JDK 17。避开 3.0.x 初期版本因其对部分 MyBatis-Plus 插件、旧版 Redis 客户端存在兼容性问题也避开 2.6.x 之前版本因其缺少对 Spring Security 5.7 的完整支持而微信登录态校验需依赖 JWT 和 OAuth2 Resource Server 能力。项目结构必须清晰分离关注点controller层仅做参数校验与微信签名验签service层封装订单状态机、司机匹配算法、微信支付回调处理mapper层严格使用 MyBatis-Plus 的LambdaQueryWrapper避免 SQL 注入config包下必须包含微信配置类与全局异常处理器。2.1 创建最小可运行 SpringBoot 工程并集成微信基础能力使用 Spring Initializrhttps://start.spring.io/生成基础工程勾选以下核心依赖Spring Web必备提供 RESTful 接口Spring Boot DevTools开发期热部署Lombok减少样板代码MySQL Driver代驾数据持久化MyBatis-Plus增强 ORM支持分页、逻辑删除Spring Boot Starter Data Redis缓存司机在线状态、订单锁Spring Boot Starter Validation参数校验Spring Boot Starter Security后续接入微信 OpenID 认证提示不要勾选 Spring Boot Starter OAuth2 Client —— 微信小程序登录是Authorization Code 模式但由小程序前端发起wx.login()获取code后端再用该code向微信服务器换取openid和session_key属于服务端主动调用微信接口而非标准 OAuth2 授权码流程。因此只需RestTemplate或WebClient即可无需 OAuth2 Client 依赖。生成后在application.yml中配置微信基础参数# application.yml wechat: appid: wx1234567890abcdef # 小程序 AppID从微信公众平台获取 secret: 1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p # 小程序 AppSecret auth-url: https://api.weixin.qq.com/sns/jscode2session # 微信登录凭证校验接口 mch-id: 1234567890 # 微信支付商户号后续支付模块启用 api-v3-key: your-api-v3-key-here # 微信支付 APIv3 密钥32位2.2 实现微信小程序 code 换 token 的核心 Controller微信小程序调用wx.login()后得到临时登录凭证code必须由服务端向微信服务器发起 HTTPS 请求换取openid、session_key和unionid若绑定公众号。此接口有调用频率限制2000 次/天/IP且code有效期仅 5 分钟必须异步快速处理。// src/main/java/com/example/daidai/controller/AuthController.java RestController RequestMapping(/api/v1/auth) Slf4j public class AuthController { Value(${wechat.appid}) private String appId; Value(${wechat.secret}) private String secret; Value(${wechat.auth-url}) private String authUrl; Autowired private RestTemplate restTemplate; /** * 小程序前端传入 code后端调用微信接口换取 openid 和 session_key * param code 小程序 wx.login() 返回的临时登录凭证 * return { openid: ..., session_key: ..., unionid: ... } */ PostMapping(/jscode2session) public ResponseEntityMapString, String jsCode2Session(RequestParam String code) { // 构建微信请求参数 String url String.format(%s?appid%ssecret%sjs_code%sgrant_typeauthorization_code, authUrl, appId, secret, code); try { // 使用 RestTemplate 发起 GET 请求 MapString, String response restTemplate.getForObject(url, Map.class); if (response null || response.containsKey(errcode)) { log.warn(微信 code 换 session 失败code{}, response{}, code, response); return ResponseEntity.badRequest().body(Map.of(error, 微信验证失败)); } // 成功返回 openid 和 session_key供后续生成自定义登录态 return ResponseEntity.ok(response); } catch (Exception e) { log.error(调用微信 jscode2session 接口异常, e); return ResponseEntity.status(500).body(Map.of(error, 服务内部错误)); } } }参数说明与关键逻辑RequestParam String code强制要求前端以?codexxx方式传参避免 POST body 解析复杂度String.format(...)拼接 URL 是微信官方要求格式grant_typeauthorization_code不可省略restTemplate.getForObject(..., Map.class)直接解析微信返回的 JSON 为MapString, String微信返回字段为openid、session_key、unionid若用户已关注公众号、errcode失败时、errmsg失败时日志记录code值用于排查注意生产环境需脱敏如只记前 3 位返回ResponseEntity显式控制 HTTP 状态码便于小程序前端统一错误处理。2.3 微信登录态的二次封装从 session_key 到 JWT Token微信返回的session_key不能直接暴露给前端它是解密用户敏感数据如手机号的密钥。服务端应基于openid生成自己的 JWT Token并存入 Redis 设置过期时间建议 2 小时同时将session_key与openid绑定存储用于后续解密手机号。这是代驾系统安全基石。// src/main/java/com/example/daidai/config/JwtConfig.java Configuration public class JwtConfig { Value(${jwt.secret:daidai-secret-key-2024}) private String jwtSecret; Value(${jwt.expiration:7200}) // 2小时 private long jwtExpiration; Bean public JwtParser jwtParser() { return Jwts.parserBuilder() .setSigningKey(Keys.hmacShaKeyFor(jwtSecret.getBytes(StandardCharsets.UTF_8))) .build(); } } // src/main/java/com/example/daidai/service/TokenService.java Service Slf4j public class TokenService { Autowired private StringRedisTemplate redisTemplate; Autowired private JwtParser jwtParser; Value(${jwt.expiration:7200}) private long expiration; /** * 生成用户自定义 Token绑定 openid并缓存 session_key * param openid 微信唯一标识 * param sessionKey 微信返回的会话密钥 * return JWT Token 字符串 */ public String generateToken(String openid, String sessionKey) { String token Jwts.builder() .setSubject(openid) .claim(iat, System.currentTimeMillis()) .claim(exp, System.currentTimeMillis() expiration * 1000) .signWith(Keys.hmacShaKeyFor(daidai-secret-key-2024.getBytes()), SignatureAlgorithm.HS256) .compact(); // 缓存 session_keykey 为 session_key: openid过期时间与 token 一致 redisTemplate.opsForValue().set( session_key: openid, sessionKey, Duration.ofSeconds(expiration) ); return token; } /** * 校验 Token 并返回 openid */ public String validateToken(String token) { try { JwsClaims claimsJws jwtParser.parseClaimsJws(token); return claimsJws.getBody().getSubject(); } catch (Exception e) { log.warn(JWT token 校验失败: {}, token.substring(0, Math.min(10, token.length()))); return null; } } }关键设计点generateToken方法将openid作为 JWTsubject便于后续所有接口通过RequestHeader(Authorization)提取并校验redisTemplate.opsForValue().set(...)将session_key与openid绑定设置与 JWT 相同的过期时间保证两者生命周期一致validateToken抛弃异常不向上抛只返回null由全局异常处理器统一拦截 401jwtSecret必须配置为强随机字符串不可硬编码在代码中应通过环境变量或配置中心注入。3. 代驾核心业务落地订单状态机驱动、司机匹配与实时位置同步代驾系统区别于普通 O2O 的本质在于其强状态驱动特性一个订单从「待接单」→「司机已接单」→「司机到达」→「行程中」→「行程结束」→「待评价」每个状态转换都触发不同动作如发短信、更新地图标记、计算费用、释放司机资源。SpringBoot 本身不提供状态机框架但可借助spring-statemachine或更轻量的enumTransactional实现。此处采用后者确保状态变更的原子性与可追溯性。3.1 订单状态枚举与数据库表结构设计订单主表order_info必须包含status字段类型为TINYINT节省空间对应枚举值// src/main/java/com/example/daidai/enums/OrderStatus.java public enum OrderStatus { WAITING_DRIVER(0, 待接单), DRIVER_ACCEPTED(1, 司机已接单), DRIVER_ARRIVED(2, 司机已到达), IN_PROGRESS(3, 行程中), COMPLETED(4, 行程结束), CANCELLED(5, 已取消); private final int code; private final String desc; OrderStatus(int code, String desc) { this.code code; this.desc desc; } public int getCode() { return code; } public String getDesc() { return desc; } }对应 MySQL 表结构精简版CREATE TABLE order_info ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主键, order_no varchar(32) NOT NULL COMMENT 订单号全局唯一, user_openid varchar(64) NOT NULL COMMENT 用户 openid, driver_openid varchar(64) DEFAULT NULL COMMENT 司机 openid, start_location point NOT NULL COMMENT 起点坐标POINT 类型, end_location point NOT NULL COMMENT 终点坐标, status tinyint NOT NULL DEFAULT 0 COMMENT 订单状态见 OrderStatus, created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no), SPATIAL KEY idx_start_location (start_location), KEY idx_user_openid (user_openid), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT代驾订单主表;注意start_location和end_location使用 MySQL 的POINT类型配合ST_Distance_Sphere函数可高效计算司机与用户的球面距离单位米比传统lat/lon字段 复杂公式快 10 倍以上。3.2 基于事务的订单状态变更 Service 方法状态变更必须在一个数据库事务中完成且需校验前置状态合法性防止非法跳转如从「待接单」直接到「行程结束」。// src/main/java/com/example/daidai/service/OrderService.java Service Transactional(rollbackFor Exception.class) Slf4j public class OrderService { Autowired private OrderMapper orderMapper; Autowired private RedisTemplateString, Object redisTemplate; /** * 更新订单状态带前置状态校验 * param orderId 订单 ID * param fromStatus 允许的前置状态可变参数支持多个 * param toStatus 目标状态 * return 影响行数1 表示成功 */ public int updateStatus(Long orderId, OrderStatus toStatus, OrderStatus... fromStatus) { // 查询当前订单 OrderInfo order orderMapper.selectById(orderId); if (order null) { log.warn(订单不存在orderId{}, orderId); return 0; } // 校验前置状态 boolean validFrom false; for (OrderStatus s : fromStatus) { if (s.getCode() order.getStatus()) { validFrom true; break; } } if (!validFrom) { log.warn(订单状态非法跳转orderId{}, current{}, expected{}, orderId, order.getStatus(), Arrays.toString(fromStatus)); throw new IllegalArgumentException(订单状态不合法); } // 执行更新 UpdateWrapperOrderInfo wrapper new UpdateWrapper(); wrapper.eq(id, orderId) .eq(status, order.getStatus()) // 乐观锁确保更新时状态未被其他线程修改 .set(status, toStatus.getCode()) .set(updated_at, LocalDateTime.now()); int rows orderMapper.update(null, wrapper); if (rows 1) { // 状态变更成功发布事件如推送消息、更新司机状态 publishOrderStatusEvent(orderId, order.getStatus(), toStatus.getCode()); } return rows; } private void publishOrderStatusEvent(Long orderId, int oldStatus, int newStatus) { // 这里可集成 RocketMQ/Kafka或直接调用微信模板消息接口 // 示例向司机 openid 发送「您有新订单」模板消息 log.info(订单 {} 状态从 {} 变更为 {}, orderId, oldStatus, newStatus); } }关键保障机制Transactional(rollbackFor Exception.class)确保整个方法内所有 DB 操作原子性UpdateWrapper中eq(status, order.getStatus())是乐观锁实现防止并发状态下两个线程同时读到「待接单」状态然后都更新为「司机已接单」publishOrderStatusEvent是扩展点实际项目中应解耦为事件驱动架构此处留作占位异常IllegalArgumentException会被全局异常处理器捕获返回标准化错误码如40001表示状态非法。3.3 司机匹配算法基于 Redis Geo 的实时就近查找代驾的核心体验是「秒级响应」。不能每次下单都查一次 MySQL必须将在线司机坐标预热到 Redis。利用 Redis 的GEOADD、GEORADIUS命令可在毫秒级内找出 3 公里内的可用司机。// src/main/java/com/example/daidai/service/DriverMatchService.java Service Slf4j public class DriverMatchService { Autowired private StringRedisTemplate redisTemplate; /** * 根据用户坐标查找附近 3 公里内在线司机按距离升序 * param userLng 用户经度 * param userLat 用户纬度 * param count 最大返回数量 * return 司机 openid 列表 */ public ListString findNearbyDrivers(double userLng, double userLat, int count) { String driverGeoKey driver:online:geo; // GEORADIUS 命令在 driverGeoKey 中查找半径 3000 米内的司机返回 member即 openid和距离 GeoRadiusCommandArgs args GeoRadiusCommandArgs.newGeoRadiusArgs() .withDistance() .sortAscending() .count(count); GeoResultsRedisGeoCommands.GeoLocationString results redisTemplate.opsForGeo() .radius(driverGeoKey, new Circle(new Point(userLng, userLat), new Distance(3000, Metrics.METERS)), args); return results.getContent().stream() .map(GeoResult::getContent) .map(GeoLocation::getName) .collect(Collectors.toList()); } /** * 司机上线时将其坐标写入 Redis Geo * param driverOpenid 司机 openid * param lng 经度 * param lat 纬度 */ public void driverOnline(String driverOpenid, double lng, double lat) { String driverGeoKey driver:online:geo; redisTemplate.opsForGeo().add(driverGeoKey, new Point(lng, lat), driverOpenid); // 同时设置司机在线状态为 1过期时间 30 分钟心跳续期 redisTemplate.opsForValue().set(driver:status: driverOpenid, 1, Duration.ofMinutes(30)); } /** * 司机下线或超时从 Geo 中移除 */ public void driverOffline(String driverOpenid) { String driverGeoKey driver:online:geo; redisTemplate.opsForGeo().remove(driverGeoKey, driverOpenid); redisTemplate.delete(driver:status: driverOpenid); } }Redis Geo 使用要点driver:online:geo是 Redis 中的 Geo 集合 key每个元素是(经度, 纬度, openid)GEORADIUS返回结果默认无序必须加.sortAscending()才按距离由近到远排列withDistance()让结果包含距离字段便于前端展示「距离您 1.2km」司机在线状态需双重保障Geo 数据 driver:status:xxx字符串键后者用于快速判断是否在线避免每次 GEO 查询心跳机制司机 App 每 20 秒上报一次位置调用driverOnline续期超时未续则自动下线。4. 微信小程序端对接细节code 换 token、位置上报与 WebSocket 实时通信小程序端是用户触达的第一入口其与 SpringBoot 后端的协作质量直接决定体验。重点解决三个高频问题code如何安全传递、用户实时位置如何高效上报、订单状态变更如何零延迟推送。4.1 小程序前端获取 code 并调用后端接口的完整流程微信小程序wx.login()返回的code是一次性凭证必须在 5 分钟内使用。前端不应自行拼接微信 URL而应调用后端/api/v1/auth/jscode2session接口。// pages/login/login.js Page({ data: { userInfo: null, hasUserInfo: false }, // 用户点击「授权登录」按钮 bindGetUserInfo(e) { if (e.detail.userInfo) { this.setData({ userInfo: e.detail.userInfo, hasUserInfo: true }); this.login(); // 开始登录流程 } }, login() { wx.login({ success: (res) { if (res.code) { // 将 code 发送给后端 wx.request({ url: https://your-server.com/api/v1/auth/jscode2session, method: POST, data: { code: res.code }, success: (resp) { if (resp.data.openid) { // 后端返回 openid 和 session_key生成自定义 token const customToken this.generateCustomToken(resp.data); wx.setStorageSync(token, customToken); wx.switchTab({ url: /pages/index/index }); } else { wx.showToast({ title: 登录失败, icon: none }); } }, fail: () { wx.showToast({ title: 网络错误, icon: none }); } }); } else { console.log(登录失败 res.errMsg); } } }); }, generateCustomToken(data) { // 此处应由后端生成 JWT前端只存储和携带 // 实际项目中后端 /jscode2session 接口应直接返回 { token: xxx, expires_in: 7200 } return data.token || ; } });注意wx.login()必须在用户触发行为如点击按钮后调用否则 iOS 会静默失败wx.request的url必须是 HTTPS且域名已在小程序后台「request 合法域名」中配置。4.2 用户位置上报节制、精准、合规微信小程序获取用户位置需用户授权且wx.getLocation()在后台无法持续调用。代驾场景下只需在「下单前」和「行程中」两个节点上报。// utils/location.js const getLocation () { return new Promise((resolve, reject) { wx.getLocation({ type: gcj02, // 国测局坐标系与腾讯地图一致 success: (res) { resolve({ latitude: res.latitude, longitude: res.longitude, accuracy: res.accuracy }); }, fail: (err) { reject(err); } }); }); }; // pages/order/create.js Page({ data: { startLocation: null, endLocation: null }, // 下单前获取起点 async getStartLocation() { try { const loc await getLocation(); this.setData({ startLocation: loc }); // 上报起点到后端用于司机匹配 wx.request({ url: https://your-server.com/api/v1/order/start-location, method: POST, header: { Authorization: wx.getStorageSync(token) }, data: loc, success: () console.log(起点上报成功) }); } catch (e) { wx.showToast({ title: 定位失败请检查权限, icon: none }); } } });合规提醒必须在app.json中声明requiredPrivateInfos: [getLocation]首次调用wx.getLocation()前需用wx.openSetting()引导用户开启定位权限上报的经纬度应为gcj02坐标系与腾讯地图 SDK 一致避免偏移。4.3 订单状态实时推送WebSocket 替代轮询订单状态变更如司机接单必须秒级触达用户。轮询每隔 3 秒查一次浪费资源且延迟高。SpringBoot 内置spring-boot-starter-websocket结合小程序wx.connectSocket可建立长连接。// src/main/java/com/example/daidai/config/WebSocketConfig.java Configuration EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(new OrderWebSocketHandler(), /ws/order) .setAllowedOrigins(*); // 小程序不校验 origin } } // src/main/java/com/example/daidai/handler/OrderWebSocketHandler.java Component public class OrderWebSocketHandler extends TextWebSocketHandler { private static final MapString, Session USER_SESSIONS new ConcurrentHashMap(); Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { // 从请求参数或 Header 中提取 openid需前端在 URL 中带 token String token session.getUri().getQuery(); String openid extractOpenidFromToken(token); // 实现略 USER_SESSIONS.put(openid, session); System.out.println(用户 openid 建立 WebSocket 连接); } Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { // 可接收客户端心跳或指令 } Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { USER_SESSIONS.values().remove(session); } // 对外调用方法向指定用户推送订单状态 public static void sendOrderUpdate(String openid, String message) { Session session USER_SESSIONS.get(openid); if (session ! null session.isOpen()) { try { session.sendMessage(new TextMessage(message)); } catch (IOException e) { USER_SESSIONS.remove(openid); } } } }小程序端连接// pages/order/detail.js Page({ data: { socketOpen: false, orderStatus: WAITING_DRIVER }, connectSocket() { const token wx.getStorageSync(token); wx.connectSocket({ url: wss://your-server.com/ws/order?token token, success: () console.log(WebSocket 连接成功) }); wx.onSocketOpen(() { this.setData({ socketOpen: true }); }); wx.onSocketMessage((res) { const data JSON.parse(res.data); this.setData({ orderStatus: data.status }); if (data.status DRIVER_ACCEPTED) { wx.showToast({ title: 司机已接单, icon: success }); } }); } });关键配置WebSocket 路径/ws/order必须与 Nginx 反向代理配置一致若使用小程序wx.connectSocket的url必须是wss://HTTPS 的 WebSocketUSER_SESSIONS使用ConcurrentHashMap保证线程安全afterConnectionClosed中清理避免内存泄漏实际项目中应增加心跳保活ping/pong和断线重连逻辑。5. 生产环境必调的 3 个 SpringBoot 参数性能、安全与可观测性本地跑通不等于线上可用。代驾系统面对真实用户时必须调整 SpringBoot 默认参数覆盖性能压测、安全加固、故障定位三方面。5.1 JVM 与 Tomcat 线程池调优应对订单创建高峰微信小程序流量具有明显波峰晚高峰 20:00–22:00此时订单创建接口POST /api/v1/orderQPS 可达 500。默认 Tomcat 线程池200 线程和 JVM 堆内存512MB必然成为瓶颈。# application-prod.yml server: tomcat: max-connections: 10000 # 最大连接数 accept-count: 1000 # 排队等待的最大连接数 max-threads: 500 # 最大工作线程数根据 CPU 核数 * 2 ~ 4 设置 min-spare-threads: 50 # 最小空闲线程数避免频繁创建销毁 spring: profiles: active: prod jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8 # JVM 启动参数添加到启动脚本 # -Xms2g -Xmx2g -XX:UseG1GC -XX:MaxGCPauseMillis200 -XX:HeapDumpOnOutOfMemoryError调优依据max-threads: 500假设服务器为 16 核 CPUI/O 密集型应用含数据库、Redis、HTTP 调用线程数设为CPU * 3 48但代驾订单创建涉及多次 DB 写入、Redis 操作、微信回调故提升至 500 更稳妥accept-count: 1000当所有线程忙时新连接进入队列避免直接拒绝JVM 参数中-XX:UseG1GC启用 G1 垃圾收集器-XX:MaxGCPauseMillis200控制 GC 暂停时间不超过 200ms保障接口响应稳定。5.2 安全加固禁用敏感端点与强制 HTTPSSpringBoot Actuator 提供/actuator/env、/actuator/beans等端点泄露系统环境与 Bean 信息是黑客扫描重点。生产环境必须关闭或加鉴权。# application-prod.yml management: endpoints: web: exposure: include: health,info,metrics,prometheus,loggers # 只暴露必要端点 endpoint: health: show-details: when_authorized # 健康检查详情需认证 loggers: show-long-names: false security: roles: ACTUATOR # 访问 actuator 需 ACTUATOR 角色同时在SecurityConfig中强制 HTTPSConfiguration EnableWebSecurity public class SecurityConfig { Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .requiresChannel() .requestMatchers(/api/**).requiresSecure() // 所有 /api 接口强制 HTTPS .and() .authorizeHttpRequests(authz - authz .requestMatchers(/actuator/**).hasRole(ACTUATOR) .requestMatchers(/api/v1/auth/**, /api/v1/public/**).permitAll() .anyRequest().authenticated() ); return http.build(); } }5.3 可观测性集成 Prometheus Grafana 监控订单核心指标代驾系统最需监控的 3 个黄金指标订单创建成功率、司机匹配平均耗时、WebSocket 连接数。SpringBoot Actuator Micrometer 可无缝对接 Prometheus。!-- pom.xml -- dependency groupIdio.micrometer/groupId artifactIdmicrometer-registry-prometheus/artifactId /dependency// src/main/java/com/example/daidai/metrics/OrderMetrics.java Component public class OrderMetrics { private final Timer orderCreateTimer; private final Counter orderCreateSuccessCounter; private final Gauge websocketConnectionGauge; public OrderMetrics(MeterRegistry registry) { this.orderCreateTimer Timer.builder(order.create.duration) .description(订单创建耗时毫秒) .register(registry); this.orderCreateSuccessCounter Counter.builder(order.create.success) .description(订单创建成功次数) .register(registry); this.websocketConnectionGauge Gauge.builder(websocket.connections, () - (double) OrderWebSocketHandler.USER_SESSIONS.size()) .description(当前 WebSocket 连接数) .register(registry); } public void recordCreateSuccess(long durationMs) { orderCreateTimer.record(durationMs, TimeUnit.MILLISECONDS); orderCreateSuccessCounter.increment(); } }在OrderService.createOrder()方法末尾调用orderMetrics.recordCreateSuccess(System.currentTimeMillis() - start)即可。Prometheus 抓取/actuator/prometheus端点Grafana 配置看板即可实时看到「过去 5 分钟订单创建成功率跌至 92%」立刻排查数据库连接池是否耗尽。提示websocket.connections指标直接读取ConcurrentHashMap.size()无锁、零开销是监控长连接健康度的最轻量方式。本文还有配套的精品资源点击获取