简介:这份代驾平台源码包面向微信小程序开发者与后端工程师,提供代驾业务从用户下单、司机接单到订单结算的完整实现,适合有一定小程序或Java基础、希望快速搭建代驾系统或进行二次开发的技术人员。压缩包共约2000个文件,整体7.63MB,以934个js、221个vue、158个ts等前端脚本为主,配合110个java后端服务代码、118个json配置、53个wxss与51个wxml页面结构,另有png图标、yaml部署配置、less与scss样式及少量mp3音频资源,前后端结构清晰。目前已有972人学习下载。源码涵盖司机服务、地图定位、订单处理等核心模块,可直接运行体验完整流程,也便于在此基础上扩展支付、计费与调度逻辑,是研究代驾类小程序全栈实现与二次开发的实用参考。
1. 代驾平台源码拆开看:小程序和后端到底怎么配合
代驾这个场景,用户下单、司机接单、行程计费、订单结算,每一步都卡在实时性上。你拿到一份「代驾小程序和后端共同开发的代驾平台源码.zip」,第一反应可能是先跑起来看看,但真正决定这套源码能不能用的,是前后端分离架构下小程序端和后端服务之间的接口契约、状态同步和计费逻辑。这份源码解决的核心问题是:把乘客叫代驾、司机抢单、行程追踪、费用结算这条链路用一套可运行的代码串起来。适合谁?适合想拿一套完整代驾业务练手全栈开发的人,也适合需要快速搭一个代驾平台原型去验证商业模式的小团队。但要注意,源码能跑通不等于能上线,计费规则、司机调度、支付回调这些地方,每一处都藏着需要你按自己业务改写的逻辑。下面我按实际拆解顺序,把这份代驾平台源码从结构到落地讲清楚。
2. 代驾平台源码的模块拆解与运行环境搭建
拿到一个代驾平台源码压缩包,别急着双击运行。先搞清楚它由哪几块组成,每块用什么技术栈,依赖什么外部服务。代驾平台典型的前后端分离项目实战结构,一般分三部分:微信小程序端(乘客端+司机端)、后端 API 服务、管理后台。有些源码还会带一个 WebSocket 服务专门推行程位置。
2.1 先看目录结构,判断技术栈和依赖
解压之后,根目录通常长这样:
daijia-platform/ ├── miniprogram/ # 微信小程序端(乘客+司机) ├── server/ # 后端服务 │ ├── src/ │ │ ├── controller/ # 接口层 │ │ ├── service/ # 业务逻辑 │ │ ├── mapper/ # 数据访问 │ │ └── entity/ # 数据库实体 │ ├── pom.xml # Maven 依赖(Java 后端常见) │ └── application.yml # 配置文件 ├── admin/ # 管理后台前端 └── sql/ # 数据库初始化脚本看到pom.xml基本可以确定后端是 Java 技术栈,常见组合是 Spring Boot + MyBatis + MySQL + Redis。如果根目录是package.json加app.js,那后端可能是 Node.js。代驾平台源码里 Java 后端占比很高,因为订单状态流转、计费规则这类逻辑用 Java 写起来结构清晰,后期也好维护。
先确认三件事:后端语言和框架版本、数据库类型和版本、小程序基础库版本要求。这三个对不上,后面全是坑。
2.2 数据库初始化:别直接导入,先改字符集和引擎
sql/目录下一般有一个init.sql或者按模块拆分的多个 sql 文件。导入之前先做两件事:
# 1. 创建数据库,指定字符集 mysql -u root -p -e "CREATE DATABASE daijia DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;" # 2. 导入前检查 sql 文件里有没有硬编码的数据库名 grep -i "CREATE DATABASE\|USE " sql/init.sql如果 sql 文件里写了CREATE DATABASE daijia,而你本地数据库名不一样,导入会报错。更稳妥的做法是手动建库,然后只导入表结构和数据:
mysql -u root -p daijia < sql/init.sql导入完成后检查关键表是否存在:
-- 代驾平台核心表一般包括这些 SHOW TABLES; -- 预期看到:user(用户)、driver(司机)、order(订单)、 -- driver_location(司机位置)、price_rule(计费规则)、 -- coupon(优惠券)、payment_record(支付记录)注意:如果导入时报
Specified key was too long,说明 sql 文件里用了 utf8 而不是 utf8mb4,或者索引字段长度超了。改字符集或者缩短索引字段。
2.3 后端配置:数据库连接、Redis、微信参数一个不能少
打开application.yml,需要改的配置项集中在这几块:
spring: datasource: url: jdbc:mysql://127.0.0.1:3306/daijia?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai username: root password: 你的密码 redis: host: 127.0.0.1 port: 6379 password: # 本地没设密码就留空 database: 0 wechat: appid: wx你的小程序appid secret: 你的小程序secret mch-id: 微信支付商户号 mch-key: 微信支付密钥serverTimezone=Asia/Shanghai这个参数必须加,否则订单创建时间会差 8 小时,计费时长直接算错。Redis 用来存司机位置和订单锁,代驾场景下司机位置更新频率高,用 Redis 的 GEO 结构比 MySQL 查得快。
微信相关的appid和secret去微信公众平台拿,mch-id和mch-key需要开通微信支付才有。本地开发阶段如果还没申请,先把支付相关的开关关掉,不然启动就报错。
2.4 启动后端:先看日志里有没有 Bean 加载失败
cd server # Maven 项目 mvn clean package -DskipTests java -jar target/daijia-server-1.0.0.jar # 或者直接跑 mvn spring-boot:run启动过程中重点看三行日志:Tomcat 端口是否被占用、数据源是否连接成功、MyBatis 的 mapper 是否加载。如果报Invalid bound statement,说明 mapper xml 文件没被扫描到,检查application.yml里mybatis.mapper-locations的路径。
后端起来之后,用 curl 测一个不需要登录的接口:
curl http://localhost:8080/api/common/price-rule返回计费规则 JSON 就说明后端基本通了。如果返回 404,检查 controller 的@RequestMapping路径和 context-path 配置。
2.5 小程序端:改请求地址,关掉域名校验
用微信开发者工具打开miniprogram/目录。第一件事是改app.js或者config.js里的后端地址:
// config.js const config = { baseUrl: 'http://localhost:8080/api', // 本地开发用这个 // baseUrl: 'https://你的域名/api', // 上线时换成这个 wsUrl: 'ws://localhost:8080/ws' // WebSocket 地址 }本地开发时localhost在微信开发者工具里能通,但真机调试不行。真机调试需要把后端部署到一台手机能访问的服务器上,或者用内网穿透工具把本地端口暴露出去。
开发者工具里勾上「不校验合法域名」,否则请求全被拦截。这个选项在「详情」→「本地设置」里。
小程序端有两个角色入口:乘客端和司机端。有些源码是两套小程序,有些是一套小程序里根据登录角色切换界面。打开app.json看 pages 列表,如果看到pages/passenger/和pages/driver/两个目录,就是一套代码两个角色。
3. 订单状态流转与司机调度的核心逻辑
代驾平台最核心的业务逻辑就两件事:订单状态怎么流转、司机怎么被调度。这两块写不好,平台要么订单卡死,要么司机接不到单。
3.1 订单状态机:从下单到完成的 7 个状态
代驾订单典型的状态流转是这样的:
| 状态值 | 状态名 | 触发动作 | 谁触发 |
|---|---|---|---|
| 0 | 待接单 | 乘客下单成功 | 乘客 |
| 1 | 已接单 | 司机抢单 | 司机 |
| 2 | 司机已到达 | 司机到达起点 | 司机 |
| 3 | 行程中 | 乘客上车,开始计费 | 司机 |
| 4 | 待支付 | 行程结束,生成账单 | 司机 |
| 5 | 已完成 | 支付成功 | 系统 |
| 6 | 已取消 | 乘客或司机取消 | 双方 |
后端代码里一般用一个枚举类管理这些状态:
public enum OrderStatus { WAITING(0, "待接单"), ACCEPTED(1, "已接单"), ARRIVED(2, "司机已到达"), IN_TRIP(3, "行程中"), UNPAID(4, "待支付"), COMPLETED(5, "已完成"), CANCELLED(6, "已取消"); 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; } }状态流转必须加校验,不能从「待接单」直接跳到「行程中」。常见做法是在 service 层写一个canTransfer(from, to)方法,每次更新状态前先判断。
public boolean canTransfer(OrderStatus from, OrderStatus to) { switch (from) { case WAITING: return to == ACCEPTED || to == CANCELLED; case ACCEPTED: return to == ARRIVED || to == CANCELLED; case ARRIVED: return to == IN_TRIP || to == CANCELLED; case IN_TRIP: return to == UNPAID; case UNPAID: return to == COMPLETED; default: return false; } }这个校验逻辑看着简单,但少了它,后面订单状态乱了根本查不出问题出在哪。
3.2 司机调度:Redis GEO 加抢单锁
司机调度有两种模式:派单和抢单。代驾平台源码里抢单模式更常见,实现也简单。核心逻辑是:乘客下单后,把订单推给附近一定范围内的司机,司机抢单。
附近司机怎么查?用 Redis 的 GEO 结构:
// 司机上报位置时写入 Redis GEO public void updateDriverLocation(Long driverId, double lng, double lat) { String key = "driver:location"; redisTemplate.opsForGeo().add(key, new Point(lng, lat), driverId.toString()); // 同时设置过期时间,避免离线司机一直留在集合里 redisTemplate.expire(key, 5, TimeUnit.MINUTES); } // 查询附近 3 公里的司机 public List<Long> findNearbyDrivers(double lng, double lat, double radiusKm) { String key = "driver:location"; Circle circle = new Circle(new Point(lng, lat), new Distance(radiusKm, Metrics.KILOMETERS)); GeoResults<RedisGeoCommands.GeoLocation<String>> results = redisTemplate.opsForGeo().radius(key, circle); return results.getContent().stream() .map(r -> Long.parseLong(r.getContent().getName())) .collect(Collectors.toList()); }司机位置上报频率建议 5 到 10 秒一次,太频繁 Redis 写入压力大,太慢附近司机查不准。
抢单环节必须加锁,否则两个司机同时抢同一单会出问题:
public boolean grabOrder(Long orderId, Long driverId) { String lockKey = "order:lock:" + orderId; // SETNX 加过期时间,保证原子性 Boolean locked = redisTemplate.opsForValue() .setIfAbsent(lockKey, driverId.toString(), 10, TimeUnit.SECONDS); if (Boolean.TRUE.equals(locked)) { try { // 再次检查订单状态,防止已被抢 Order order = orderMapper.selectById(orderId); if (order.getStatus() != OrderStatus.WAITING.getCode()) { return false; } order.setDriverId(driverId); order.setStatus(OrderStatus.ACCEPTED.getCode()); orderMapper.updateById(order); return true; } finally { redisTemplate.delete(lockKey); } } return false; }注意:锁的过期时间设 10 秒,是因为抢单操作本身很快,10 秒足够。但如果业务逻辑里有远程调用,要评估调用超时时间,锁过期时间必须大于业务执行时间,否则锁提前释放,并发问题又回来了。
3.3 计费规则:起步价、里程费、时长费怎么算
代驾计费一般由三部分组成:起步价(含一定公里数)、超出里程费、等待时长费。计费规则存在price_rule表里,按城市或时间段区分。
public BigDecimal calculateFee(Order order, PriceRule rule) { // 起步价 BigDecimal fee = rule.getStartPrice(); // 超出起步里程的部分 if (order.getDistance() > rule.getStartDistance()) { BigDecimal extraDistance = BigDecimal.valueOf(order.getDistance()) .subtract(BigDecimal.valueOf(rule.getStartDistance())); fee = fee.add(extraDistance.multiply(rule.getPerKmPrice())); } // 等待时长费,按分钟算 if (order.getWaitMinutes() > rule.getFreeWaitMinutes()) { int chargeMinutes = order.getWaitMinutes() - rule.getFreeWaitMinutes(); fee = fee.add(BigDecimal.valueOf(chargeMinutes) .multiply(rule.getPerMinutePrice())); } // 夜间加价 if (isNightTime(order.getCreateTime())) { fee = fee.multiply(rule.getNightMultiplier()); } return fee.setScale(2, RoundingMode.HALF_UP); }setScale(2, RoundingMode.HALF_UP)这行别省,金额保留两位小数,四舍五入。不处理的话,前端展示会出现23.999999这种数字。
计费规则表的关键字段:
| 字段名 | 类型 | 说明 |
|---|---|---|
| start_price | decimal(10,2) | 起步价 |
| start_distance | decimal(10,2) | 起步包含公里数 |
| per_km_price | decimal(10,2) | 超出每公里单价 |
| free_wait_minutes | int | 免费等待分钟数 |
| per_minute_price | decimal(10,2) | 超出后每分钟等待费 |
| night_multiplier | decimal(3,2) | 夜间加价系数,如 1.20 |
这些参数改一个,最终费用差很多。上线前一定要用真实场景跑一遍:3 公里白天、10 公里夜间、等待 20 分钟,分别算出来对不对。
4. 前后端联调与接口对接的避坑清单
前后端分离项目实战里,联调阶段出的问题比写代码还多。代驾平台源码涉及小程序端、后端、管理后台三端,接口对不上的情况太常见了。
4.1 跨域问题:后端加配置,别在前端瞎折腾
小程序端不存在浏览器跨域问题,但管理后台是 Web 端,调后端接口会跨域。后端加一个全局 CORS 配置:
@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); } }allowedOriginPatterns("*")和allowCredentials(true)同时用时,Spring Boot 2.4 以上版本必须用allowedOriginPatterns而不是allowedOrigins,否则启动报错。这个坑我踩过,日志里报When allowCredentials is true, allowedOrigins cannot contain "*",换成allowedOriginPatterns就好了。
4.2 按钮重复提交:前端防抖加后端幂等
乘客点「呼叫代驾」按钮,网络卡的时候用户会连点,结果生成两笔订单。前后端对于按钮重复提交校验方法,常见做法是双管齐下:
前端加防抖:
// 小程序端按钮点击防抖 let submitting = false; function callDriver() { if (submitting) return; submitting = true; wx.showLoading({ title: '正在呼叫...' }); wx.request({ url: config.baseUrl + '/order/create', method: 'POST', data: { ... }, complete: () => { submitting = false; wx.hideLoading(); } }); }后端加幂等:
// 用 Redis 做幂等,同一个用户 5 秒内只能下一单 public Result createOrder(Long userId, OrderDTO dto) { String idempotentKey = "order:create:" + userId; Boolean first = redisTemplate.opsForValue() .setIfAbsent(idempotentKey, "1", 5, TimeUnit.SECONDS); if (Boolean.FALSE.equals(first)) { return Result.error("操作太频繁,请稍后再试"); } // ... 创建订单逻辑 }前端防抖是体验优化,后端幂等才是兜底。只做前端不做后端,用户抓包重放照样能刷单。
4.3 时间格式:前后端统一用时间戳
小程序端new Date()和后端LocalDateTime直接传,时区一乱就出问题。统一做法是接口传输用时间戳(毫秒),前端展示时再格式化。
// 小程序端格式化时间戳 function formatTime(timestamp) { const date = new Date(timestamp); const y = date.getFullYear(); const m = String(date.getMonth() + 1).padStart(2, '0'); const d = String(date.getDate()).padStart(2, '0'); const h = String(date.getHours()).padStart(2, '0'); const min = String(date.getMinutes()).padStart(2, '0'); return `${y}-${m}-${d} ${h}:${min}`; }后端返回给前端的字段统一用Long类型的时间戳,不要返回格式化好的字符串。字符串格式一变,前端就得跟着改。
4.4 司机位置推送:WebSocket 断线重连
司机位置实时推送给乘客,用 WebSocket 比轮询省资源。但 WebSocket 会断,必须加重连机制。
// 小程序端 WebSocket 重连 let socketTask = null; let reconnectTimer = null; function connectWs() { socketTask = wx.connectSocket({ url: config.wsUrl + '?token=' + getToken() }); socketTask.onClose(() => { // 断线后 3 秒重连 reconnectTimer = setTimeout(connectWs, 3000); }); socketTask.onError(() => { socketTask.close(); }); } function closeWs() { clearTimeout(reconnectTimer); if (socketTask) socketTask.close(); }重连间隔别设太短,3 到 5 秒比较合适。太短了服务端压力大,太长了乘客看到司机位置半天不动。
5. 代驾平台源码上线前必须排查的 5 个坑
源码本地跑通只是第一步,上线前这几个坑不排掉,生产环境必翻车。
5.1 支付回调没验签,订单被伪造
现象:订单支付状态被随意改成「已支付」,但实际没收到钱。
原因:支付回调接口没有验证微信支付的签名,任何人构造一个 POST 请求就能把订单改成已支付。
解决:回调接口必须验签,用微信支付 SDK 提供的WXPayUtil.isSignatureValid()方法校验。验签通过后再更新订单状态,并且要校验回调里的订单金额和本地订单金额是否一致。
@PostMapping("/pay/callback") public String payCallback(@RequestBody String xmlData) { Map<String, String> result = WXPayUtil.xmlToMap(xmlData); // 验签 if (!WXPayUtil.isSignatureValid(result, mchKey)) { return "<xml><return_code><![CDATA[FAIL]]></return_code></xml>"; } // 校验金额 String orderId = result.get("out_trade_no"); BigDecimal callbackAmount = new BigDecimal(result.get("total_fee")) .divide(BigDecimal.valueOf(100)); // 微信金额单位是分 Order order = orderMapper.selectById(orderId); if (order.getAmount().compareTo(callbackAmount) != 0) { return "<xml><return_code><![CDATA[FAIL]]></return_code></xml>"; } // 更新订单状态 order.setStatus(OrderStatus.COMPLETED.getCode()); orderMapper.updateById(order); return "<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>"; }5.2 司机位置数据没清理,Redis 内存暴涨
现象:Redis 内存持续增长,几天后 OOM。
原因:司机每次上报位置都往 GEO 集合里写,但司机下线后没有清理,集合越来越大。
解决:两个措施。一是每次写入位置时给整个 GEO key 设过期时间(前面代码里已经加了expire)。二是司机主动下线时,调用redisTemplate.opsForGeo().remove()删掉该司机的位置记录。另外,定时任务每天凌晨清理一次超过 24 小时没更新的司机位置。
5.3 订单超时未支付,库存没释放
现象:乘客下单后不支付,订单一直挂在「待支付」状态,司机被占用无法接新单。
原因:没有超时取消机制。
解决:用 Redis 的过期键或者延迟队列实现。简单做法是下单时往 Redis 写一个 key,设置 15 分钟过期,同时起一个定时任务扫描过期 key,把对应订单取消。
// 下单时写入 redisTemplate.opsForValue().set("order:timeout:" + orderId, "1", 15, TimeUnit.MINUTES); // 定时任务每分钟扫描 @Scheduled(cron = "0 * * * * ?") public void cancelTimeoutOrders() { Set<String> keys = redisTemplate.keys("order:timeout:*"); for (String key : keys) { String orderId = key.replace("order:timeout:", ""); Order order = orderMapper.selectById(orderId); if (order != null && order.getStatus() == OrderStatus.UNPAID.getCode()) { order.setStatus(OrderStatus.CANCELLED.getCode()); orderMapper.updateById(order); } } }注意:
redisTemplate.keys()在生产环境慎用,数据量大时会阻塞 Redis。更好的做法是用 Redis 的过期事件通知,或者用专门的延迟队列。
5.4 小程序端请求没带 token,接口全返回 401
现象:登录后调其他接口全部返回 401。
原因:登录成功后 token 存了,但请求拦截器没把 token 加到 header 里。
解决:在小程序端封装统一的请求方法,自动带上 token。
function request(options) { const token = wx.getStorageSync('token'); return new Promise((resolve, reject) => { wx.request({ url: config.baseUrl + options.url, method: options.method || 'GET', data: options.data || {}, header: { 'Content-Type': 'application/json', 'Authorization': token ? 'Bearer ' + token : '' }, success: (res) => { if (res.statusCode === 401) { // token 过期,跳转登录 wx.redirectTo({ url: '/pages/login/login' }); reject(res); } else { resolve(res.data); } }, fail: reject }); }); }5.5 数据库连接池太小,高峰期接口超时
现象:白天低峰期正常,晚上代驾高峰期接口大量超时。
原因:默认连接池(HikariCP)最大连接数只有 10,高峰期并发上来后连接不够用。
解决:根据实际并发调整连接池参数。
spring: datasource: hikari: maximum-pool-size: 50 # 最大连接数 minimum-idle: 10 # 最小空闲连接 connection-timeout: 3000 # 获取连接超时时间(毫秒) idle-timeout: 600000 # 空闲连接超时时间 max-lifetime: 1800000 # 连接最大存活时间maximum-pool-size不是越大越好,一般设为 CPU 核数乘以 2 再加磁盘数。50 对于中小型代驾平台够用了。改完用SHOW PROCESSLIST看 MySQL 实际连接数,别超过数据库的max_connections。
6. 从源码到可运营平台:二次开发的关键改动点
源码能跑通、坑也排完了,但直接拿开源代驾平台源码上线运营,还差几步关键改动。这部分我按实际改造经验说几个必须动的地方。
6.1 计费规则要支持按城市配置
源码里计费规则通常只有一套全局配置。实际运营中,不同城市起步价、里程费都不一样。改造方式是在price_rule表加一个city_code字段,查询时根据订单所在城市匹配规则。
ALTER TABLE price_rule ADD COLUMN city_code VARCHAR(10) DEFAULT 'GLOBAL' COMMENT '城市编码'; CREATE INDEX idx_city_code ON price_rule(city_code);对应的查询逻辑改成:
public PriceRule getPriceRule(String cityCode) { // 先查城市专属规则,没有则用全局规则 PriceRule rule = priceRuleMapper.selectByCityCode(cityCode); if (rule == null) { rule = priceRuleMapper.selectByCityCode("GLOBAL"); } return rule; }6.2 司机端要加接单范围限制
源码里司机能看到所有待接单订单,实际运营中司机只能接自己服务范围内的单。改造方式是在司机表加service_radius字段,查询待接单列表时用 Redis GEO 过滤。
public List<Order> getAvailableOrders(Long driverId) { Driver driver = driverMapper.selectById(driverId); // 查司机当前位置 Point driverPoint = getDriverPoint(driverId); // 查司机服务半径内的待接单订单 return orderMapper.selectNearbyWaitingOrders( driverPoint.getX(), driverPoint.getY(), driver.getServiceRadius()); }对应的 SQL 用 MySQL 的空间函数或者手动算经纬度距离。数据量不大时手动算就行:
SELECT *, (6371 * ACOS( COS(RADIANS(#{lat})) * COS(RADIANS(start_lat)) * COS(RADIANS(start_lng) - RADIANS(#{lng})) + SIN(RADIANS(#{lat})) * SIN(RADIANS(start_lat)) )) AS distance FROM `order` WHERE status = 0 HAVING distance < #{radius} ORDER BY distance ASC;6.3 加一层风控:防止司机刷单
代驾平台最常见的作弊行为是司机自己下单自己接,刷平台补贴。风控逻辑不复杂,但必须有:
public boolean checkRisk(Order order) { // 1. 同一司机同一乘客短时间内多次订单 int recentCount = orderMapper.countRecentOrders( order.getDriverId(), order.getUserId(), 24); if (recentCount > 3) { return false; } // 2. 订单距离异常短但费用异常高 if (order.getDistance() < 0.5 && order.getAmount().compareTo( BigDecimal.valueOf(50)) > 0) { return false; } // 3. 司机和乘客设备指纹相同(需要前端上报设备信息) if (order.getDriverDeviceId() != null && order.getDriverDeviceId().equals(order.getUserDeviceId())) { return false; } return true; }风控规则不用一开始就写全,先上两三条最明显的,后面根据实际数据慢慢加。
6.4 日志和监控:出问题能查到原因
上线前把日志配好,不然出了问题只能靠猜。关键接口的入参、出参、耗时都打出来:
@Around("execution(* com.daijia.controller..*(..))") public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { long start = System.currentTimeMillis(); String method = joinPoint.getSignature().toShortString(); Object[] args = joinPoint.getArgs(); log.info("请求开始: {} 参数: {}", method, JSON.toJSONString(args)); try { Object result = joinPoint.proceed(); long cost = System.currentTimeMillis() - start; log.info("请求结束: {} 耗时: {}ms 返回: {}", method, cost, JSON.toJSONString(result)); return result; } catch (Exception e) { log.error("请求异常: {} 参数: {}", method, JSON.toJSONString(args), e); throw e; } }日志里别打密码和 token,用@JsonIgnore或者手动过滤。日志文件按天切割,保留 30 天就够了。
6.5 我踩过的一个坑:订单金额用 double 算,差了一分钱
最后说一个我自己的血泪教训。早期版本里订单金额用double类型计算,测试环境没问题,上线后财务对账发现每天差几毛钱。原因是double浮点运算有精度丢失,0.1 + 0.2不等于0.3。后来全部改成BigDecimal,并且所有金额字段在数据库里用decimal(10,2),问题才解决。如果你拿到的源码里金额字段是double或者float,别犹豫,全部改成BigDecimal。这个改动越早做越好,等订单数据多了再改,迁移成本翻倍。
代驾平台源码从跑通到上线,中间隔着的不是代码量,是对业务细节的理解。计费规则、状态流转、并发控制、风控,每一块都得按自己的运营场景调。我一般拿到一套新源码,先跑通主流程,然后花两天时间专门读订单和计费相关的代码,把每个参数的含义搞清楚,再动手改。希望帮到你。
本文还有配套的精品资源,点击获取