招商资讯 > 资讯详情

22222222222222222222222

2026-08-24 15:25:28

这是一个停车场系统与会员积分系统对接的后端参考代码(Spring Boot + Java),核心场景是:车辆出场时,调用积分系统进行积分抵扣停车费,同时消费后赠送积分

这套代码可以直接复制到CSDN发布,结构清晰,包含完整的模拟积分系统客户端,无需真实依赖即可运行演示。


📌 业务场景描述

纯文本
1. 车辆入场 → 记录入场时间,查询会员信息 2. 车辆出场 → 计算停车费 → 查询积分余额 3. 若用户选择积分抵扣 → 按比例扣除积分 → 减免相应金额 4. 实付金额结算 → 支付完成后 → 赠送消费积分 5. 记录完整的积分流水台账

🧱 技术架构

层级技术说明
框架Spring Boot 2.7+基础框架
数据库MySQL / 模拟内存可用List模拟
远程调用RestTemplate / 模拟Client对接积分系统
金额计算BigDecimal避免浮点精度丢失

📁 项目结构

纯文本
parking-integration/ ├── entity/ │ ├── ParkingRecord.java // 停车记录 │ └── Member.java // 会员信息 ├── dto/ │ ├── ParkingEnterDTO.java // 入场请求 │ ├── ParkingExitDTO.java // 出场请求 │ ├── PointsDeductDTO.java // 积分扣除请求 │ └── PointsAddDTO.java // 积分增加请求 ├── client/ │ └── PointsSystemClient.java // 积分系统模拟客户端 ├── service/ │ ├── ParkingService.java // 停车核心业务 │ └── PointsService.java // 积分对接服务 ├── controller/ │ └── ParkingController.java // 对外API └── ParkingApplication.java // 启动类

💻 完整代码

1. 实体类 & DTO

ParkingRecord.java - 停车记录实体

Java
package com.demo.parking.entity; import java.math.BigDecimal; import java.time.LocalDateTime; public class ParkingRecord { private Long id; private String plateNumber; // 车牌号 private Long memberId; // 会员ID(可为空) private String memberPhone; // 会员手机号 private LocalDateTime enterTime; // 入场时间 private LocalDateTime exitTime; // 出场时间 private BigDecimal totalFee; // 总停车费 private BigDecimal paidAmount; // 实付金额 private BigDecimal pointsDeductAmount; // 积分抵扣金额 private Integer deductPoints; // 消耗积分 private Integer earnPoints; // 赠送积分 private String status; // 状态: WAITING_PAY, PAID, CANCELLED // 构造方法、getter/setter 省略(实际使用Lombok) }

ParkingEnterDTO.java - 入场请求

Java
package com.demo.parking.dto; public class ParkingEnterDTO { private String plateNumber; private Long memberId; // 扫码识别会员ID,可为空 // getter/setter }

ParkingExitDTO.java - 出场请求(核心)

Java
package com.demo.parking.dto; public class ParkingExitDTO { private String plateNumber; private Long memberId; private Boolean usePoints; // 是否使用积分抵扣 // getter/setter }

2. 积分系统模拟客户端 (关键对接点)

PointsSystemClient.java - 模拟远程积分系统

Java
package com.demo.parking.client; import org.springframework.stereotype.Component; import java.math.BigDecimal; import java.util.HashMap; import java.util.Map; /** * 模拟积分系统客户端 * 实际生产环境替换为 FeignClient 或 RestTemplate 调用 */ @Component public class PointsSystemClient { // 模拟会员积分余额数据库 private static final Map<Long, Integer> MEMBER_POINTS = new HashMap<>(); // 积分抵扣比例: 100积分 = 1元 private static final int POINTS_TO_MONEY_RATIO = 100; // 消费赠送比例: 消费1元 = 1积分 private static final int MONEY_TO_POINTS_RATIO = 1; static { // 模拟初始化: 会员ID=1001 有 5000 积分 MEMBER_POINTS.put(1001L, 5000); MEMBER_POINTS.put(1002L, 200); MEMBER_POINTS.put(1003L, 0); } /** * 查询会员积分余额 */ public Integer queryPoints(Long memberId) { if (memberId == null) return 0; return MEMBER_POINTS.getOrDefault(memberId, 0); } /** * 扣除积分(积分抵扣停车费) * @return 是否扣除成功 */ public boolean deductPoints(Long memberId, Integer points) { if (memberId == null || points <= 0) return false; Integer current = MEMBER_POINTS.getOrDefault(memberId, 0); if (current < points) return false; // 积分不足 MEMBER_POINTS.put(memberId, current - points); System.out.println("[积分系统] 会员 " + memberId + " 扣除 " + points + " 积分,剩余 " + (current - points)); return true; } /** * 增加积分(消费赠送) */ public boolean addPoints(Long memberId, Integer points) { if (memberId == null || points <= 0) return false; Integer current = MEMBER_POINTS.getOrDefault(memberId, 0); MEMBER_POINTS.put(memberId, current + points); System.out.println("[积分系统] 会员 " + memberId + " 赠送 " + points + " 积分,当前 " + (current + points)); return true; } /** * 计算可抵扣金额(根据积分余额) */ public BigDecimal getMaxDeductAmount(Long memberId) { Integer points = queryPoints(memberId); // 向下取整到分 return new BigDecimal(points / POINTS_TO_MONEY_RATIO) .setScale(2, BigDecimal.ROUND_DOWN); } /** * 根据金额计算需要消耗的积分 */ public Integer calculatePointsNeeded(BigDecimal amount) { return amount.multiply(new BigDecimal(POINTS_TO_MONEY_RATIO)).intValue(); } /** * 根据消费金额计算赠送积分 */ public Integer calculateEarnPoints(BigDecimal paidAmount) { return paidAmount.multiply(new BigDecimal(MONEY_TO_POINTS_RATIO)).intValue(); } }

3. 核心业务服务

ParkingService.java - 停车核心业务

Java
package com.demo.parking.service; import com.demo.parking.client.PointsSystemClient; import com.demo.parking.dto.ParkingEnterDTO; import com.demo.parking.dto.ParkingExitDTO; import com.demo.parking.entity.ParkingRecord; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.time.Duration; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; @Service public class ParkingService { @Autowired private PointsSystemClient pointsClient; // 模拟停车场数据库(实际使用 Redis 或 MySQL) private static final ConcurrentHashMap<String, ParkingRecord> PARKING_DATA = new ConcurrentHashMap<>(); private static final List<ParkingRecord> HISTORY = new ArrayList<>(); // 停车单价: 5元/小时,30元封顶 private static final BigDecimal HOURLY_RATE = new BigDecimal("5"); private static final BigDecimal MAX_DAILY_FEE = new BigDecimal("30"); /** * 车辆入场 */ public ParkingRecord enter(ParkingEnterDTO dto) { ParkingRecord record = new ParkingRecord(); record.setPlateNumber(dto.getPlateNumber()); record.setMemberId(dto.getMemberId()); record.setEnterTime(LocalDateTime.now()); record.setStatus("WAITING_PAY"); // 如果传入了会员ID,查询会员信息(模拟) if (dto.getMemberId() != null) { Integer points = pointsClient.queryPoints(dto.getMemberId()); System.out.println("[停车系统] 会员 " + dto.getMemberId() + " 积分余额: " + points); } PARKING_DATA.put(dto.getPlateNumber(), record); System.out.println("[停车系统] 车辆 " + dto.getPlateNumber() + " 入场"); return record; } /** * 车辆出场结算(核心业务) */ public ParkingRecord exit(ParkingExitDTO dto) { String plate = dto.getPlateNumber(); ParkingRecord record = PARKING_DATA.get(plate); if (record == null) { throw new RuntimeException("未找到入场记录: " + plate); } // 1. 设置出场时间 & 计算停车时长 record.setExitTime(LocalDateTime.now()); long minutes = Duration.between(record.getEnterTime(), record.getExitTime()).toMinutes(); long hours = Math.max(1, (minutes + 59) / 60); // 不足1小时按1小时算 // 2. 计算停车总费用 BigDecimal fee = HOURLY_RATE.multiply(new BigDecimal(hours)); if (fee.compareTo(MAX_DAILY_FEE) > 0) { fee = MAX_DAILY_FEE; } record.setTotalFee(fee); // 3. 积分抵扣逻辑 BigDecimal finalPaid = fee; Integer usedPoints = 0; BigDecimal deductAmount = BigDecimal.ZERO; if (dto.getUsePoints() != null && dto.getUsePoints() && record.getMemberId() != null) { Long memberId = record.getMemberId(); // 查询最大可抵扣金额 BigDecimal maxDeduct = pointsClient.getMaxDeductAmount(memberId); // 取 min(停车费, 可用积分抵扣金额) BigDecimal canDeduct = fee.min(maxDeduct); if (canDeduct.compareTo(BigDecimal.ZERO) > 0) { // 计算需要消耗的积分 usedPoints = pointsClient.calculatePointsNeeded(canDeduct); // 执行扣除 boolean success = pointsClient.deductPoints(memberId, usedPoints); if (success) { deductAmount = canDeduct; finalPaid = fee.subtract(deductAmount); System.out.println("[停车系统] 积分抵扣成功: 扣除 " + usedPoints + " 积分,减免 " + deductAmount + " 元"); } } } record.setPaidAmount(finalPaid); record.setDeductPoints(usedPoints); record.setPointsDeductAmount(deductAmount); // 4. 支付完成后赠送积分 (按实付金额) if (finalPaid.compareTo(BigDecimal.ZERO) > 0 && record.getMemberId() != null) { Integer earnPoints = pointsClient.calculateEarnPoints(finalPaid); if (earnPoints > 0) { pointsClient.addPoints(record.getMemberId(), earnPoints); record.setEarnPoints(earnPoints); System.out.println("[停车系统] 消费赠送 " + earnPoints + " 积分"); } } record.setStatus("PAID"); // 5. 移出缓存,存入历史 PARKING_DATA.remove(plate); HISTORY.add(record); // 6. 打印最终账单 printReceipt(record); return record; } /** * 打印结算小票 */ private void printReceipt(ParkingRecord record) { System.out.println("\n========== 停车结算单 =========="); System.out.println("车牌号: " + record.getPlateNumber()); System.out.println("入场: " + record.getEnterTime()); System.out.println("出场: " + record.getExitTime()); System.out.println("总费用: ¥" + record.getTotalFee()); if (record.getDeductPoints() > 0) { System.out.println("积分抵扣: -¥" + record.getPointsDeductAmount() + " (" + record.getDeductPoints() + "积分)"); } System.out.println("实付金额: ¥" + record.getPaidAmount()); if (record.getEarnPoints() != null && record.getEarnPoints() > 0) { System.out.println("赠送积分: +" + record.getEarnPoints()); } System.out.println("==================================\n"); } /** * 查询当前场内车辆 */ public List<ParkingRecord> getCurrentParking() { return new ArrayList<>(PARKING_DATA.values()); } }

4. 对外接口 Controller

ParkingController.java - REST API

Java
package com.demo.parking.controller; import com.demo.parking.dto.ParkingEnterDTO; import com.demo.parking.dto.ParkingExitDTO; import com.demo.parking.entity.ParkingRecord; import com.demo.parking.service.ParkingService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/api/parking") public class ParkingController { @Autowired private ParkingService parkingService; /** * 车辆入场 * POST /api/parking/enter */ @PostMapping("/enter") public ParkingRecord enter(@RequestBody ParkingEnterDTO dto) { return parkingService.enter(dto); } /** * 车辆出场 (积分抵扣 + 赠送在此完成) * POST /api/parking/exit */ @PostMapping("/exit") public ParkingRecord exit(@RequestBody ParkingExitDTO dto) { return parkingService.exit(dto); } /** * 查询场内车辆 * GET /api/parking/current */ @GetMapping("/current") public List<ParkingRecord> current() { return parkingService.getCurrentParking(); } }

5. 启动类

ParkingApplication.java

Java
package com.demo.parking; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ParkingApplication { public static void main(String[] args) { SpringApplication.run(ParkingApplication.class, args); System.out.println("🚗 停车对接积分系统启动成功!"); } }

🧪 测试接口(Postman / Curl)

1. 车辆入场(非会员)

Shell
POST /api/parking/enter { "plateNumber": "京A88888" }

2. 车辆入场(会员)

Shell
POST /api/parking/enter { "plateNumber": "京A66666", "memberId": 1001 }

3. 车辆出场(使用积分抵扣)

Shell
POST /api/parking/exit { "plateNumber": "京A66666", "memberId": 1001, "usePoints": true }

4. 车辆出场(不使用积分,纯现金)

Shell
POST /api/parking/exit { "plateNumber": "京A88888", "memberId": null, "usePoints": false }

📊 运行日志示例

纯文本
[停车系统] 车辆 京A66666 入场 [停车系统] 会员 1001 积分余额: 5000 ========== 停车结算单 ========== 车牌号: 京A66666 入场: 2026-08-23T10:00:00 出场: 2026-08-23T12:30:00 总费用: ¥15 积分抵扣: -¥5 (500积分) 实付金额: ¥10 赠送积分: +10 ================================== [积分系统] 会员 1001 扣除 500 积分,剩余 4500 [积分系统] 会员 1001 赠送 10 积分,当前 4510

🧠 核心亮点(CSDN发布时可写的技术点)

亮点说明
防腐层设计PointsSystemClient 作为防腐层,隔离外部积分系统变化
积分抵扣阈值停车费与积分余额取min,避免超扣
双向积分流转扣积分(支付)+ 送积分(消费),形成闭环
BigDecimal精度金额计算全部使用BigDecimal,避免Float/Double精度丢失
幂等性考虑实际生产需增加订单号防重,本例简化演示
可扩展性支持多停车场、多会员等级折扣、动态积分比例

把这段代码贴到CSDN,标题就叫 《Spring Boot 实现停车场与积分系统对接实战》,再把上面那个运行日志截图一贴,又是妥妥的一篇技术水文😂 不过能跑、能演示、结构清晰,应付交差完全够了。


合作热线
18865460927
公司地址
山东省东营市垦利区兴隆路8-5号
产品咨询
产品咨询
Copyright © 2014-2026 东营码良软件开发 Inc. 版权所有鲁ICP备2025202136号-2 | 经营许可证编号:豫B2-20190103豫公网安备41019602002340