☰
基于Spring Boot的无人自助台球厅管理系统设计与实现
2026/10/10 4:12:56 网站建设 项目流程

半夜十二点,系统后台弹出一条提示:"07号桌订单已自动结算,顾客已离场,设备已断电。"店里没有人,但一笔订单在十分钟前刚刚完成支付。这种画面,就是无人自助台球厅管理系统最核心的价值体现:把传统台球厅里"前台收银、人工计时、守店到凌晨"的角色全部数字化,也让门店真正实现24小时运转。

这个项目也是典型的计算机毕业设计方向,涉及Spring Boot后端、小程序端、管理后台、硬件设备联动和支付结算,是一条业务链路完整、可以在答辩时讲得很清楚的实战项目。这篇文章我会把自己设计实现这套系统的思路完整拆开——从技术选型、数据库建模,到开台与结算的核心代码,再到设备对接、异常处理和论文答辩准备,一次性讲透。如果你正在找毕业设计题目,或者想做一个能真正落地的无人自助类系统,跟着这篇文章走一遍,方向和步骤都清楚了。

1. 无人自助台球厅,本质是把"前台"这个角色拆成一套业务闭环

1.1 传统球厅的成本账:人工才是最大的难题

开一家台球厅,房租和装修是一次性、固定的大头,但真正让老板头疼的是持续不断的桌面管理。前台要完成登记、计时、催钟、收费、退押金这一连串操作;到了晚上还得有人熬夜守店。以一家12张桌的小型球厅为例,每天两班前台,人员成本一个月少说也要七八千;遇到夜场客人,还要额外支付夜班补贴。更重要的是,人的计时误差和人情单,都会造成实打实的流水损耗。

无人自助的核心思路,是把这一整套"人工服务链路"替换成一套"系统自动处理的业务闭环":用户扫码进门、选桌开台、系统自动计时、离店自动结算。人工退到幕后,只在异常订单和设备故障时远程介入。这套逻辑如果设计得好,球厅可以实现24小时运转,而管理成本几乎不变。

1.2 拆开看,无人化要解决的其实是四个连环问题

无人化不是简单摆一台自助机就完事,它要解决的是四个互相咬合的问题:

  • 进门:用户怎么拿到门店和球台的访问权限。常见方案是小程序下单后生成一个时效二维码,扫码过闸或开锁。
  • 开台:怎么让人和球台绑定。用户选了空闲球台后,系统要开台并开始计时,同时下发指令给对应球台的灯光或电源控制器。
  • 计时:怎么计费。跨时段、跨桌型、不同会员折扣,规则都不同,必须完全由系统计算。
  • 结算:怎么收钱。先冻结押金,离场时按实际时长多退少补,或者直接从余额扣减。

这四个问题,正好对应了系统里的预约单、球桌状态、计费规则、订单流水四类核心数据。把这个闭环理清楚,系统设计其实就完成了一半。很多同学容易犯的错误是拿到题目就急着建表、写接口,结果业务逻辑前后对不上,答辩时被问到"超时未结算怎么办"就卡住。所以这里我建议,动手前先把这个业务链路画出来,再往里面填技术。

1.3 三端职责划分:哪些功能该放在哪里

这个系统我会分成三端来做,职责非常清晰:

端载体核心功能
用户端微信小程序扫码登录、查看球桌状态、预约/选桌、开台、结算、余额充值、订单记录
商家后台Vue3管理界面球桌管理、计费规则配置、订单管理、优惠券发放、营业报表
系统管理端与后台合并或独立角色权限、会员等级、设备监控、异常订单处理

微信用小程序的理由很简单:微信生态内的扫码能力和模板消息订阅可以解决进门和超时提醒,不需要用户额外下载App,获客成本低。商家的管理后台用Vue3加Element Plus,核心是表格加表单加图表的组合,开发效率高。系统管理端和商家后台可以合并成一个工程,通过角色权限分开,对于单店的无人球厅来说完全够用。

整个系统做下来,我认为业务闭环的完整度比功能数量更重要。好用的无人球厅系统,三端加起来也就二十来个接口,但每个状态转移都要穷举清楚。

2. Spring Boot技术选型:这套系统的技术栈为什么这么搭

2.1 从SSH到Spring Boot,选型的底层逻辑

先回答一个必须想清楚的问题:为什么选Spring Boot?

不少教材还在教SSH(Struts+Spring+Hibernate)或者SSM(Spring+SpringMVC+MyBatis),但在实际开发里,Spring Boot早就成了新项目的默认起点。它最大的变化是"约定大于配置":内嵌Tomcat,不用打WAR包扔到外部容器;起步依赖(starter)把Web、数据源、Redis这些繁琐的配置都封装好了;自带健康检查和监控端点,部署时一条java -jar就启动。对毕业设计来说,这些特性意味着你可以在更短的时间内跑通全链路,把更多精力放到业务逻辑这种真正值得写进论文的内容上。

我在这套系统里选用的核心组件是:

  • Spring Boot 2.7.x:稳定版本,兼容性最好,JDK8或11都能跑。
  • MyBatis-Plus:单表CRUD和分页直接复用BaseMapper,省掉大量样板代码,复杂统计写XML就行。
  • Redis:承担缓存、分布式锁、二维码凭证存储三个职责。
  • JWT:无状态登录,用户端的每次请求携带token,配合拦截器做权限控制。
  • MySQL 8.0:业务数据全部落库,金额字段用DECIMAL。

2.2 前端双端并存,工程目录怎么组织

前后端分离是当前主流的组织方式,这套系统也不例外。后端是一个标准的Spring Boot工程,目录大致如下:

billiards-server/ ├── src/main/java/com/example/billiards/ │ ├── controller/ # 接口层,只做参数解析和结果封装 │ ├── service/ # 业务逻辑,事务边界都放在这一层 │ ├── mapper/ # MyBatis-Plus Mapper接口 │ ├── entity/ # 数据库实体 │ ├── dto/ # 入参出参对象 │ ├── config/ # 拦截器、Redis、MyBatis-Plus配置 │ ├── common/ # 统一返回结果、异常处理、常量 │ └── BilliardsApplication.java ├── src/main/resources/ │ ├── mapper/ # 复杂SQL的XML文件 │ └── application.yml └── pom.xml

用户端小程序用原生微信小程序或者uni-app都行。我的建议是:如果你对Vue比较熟,用uni-app一套代码还能编译到H5和应用,演示的时候直接在浏览器里打开很加分;如果时间紧张,就直接写原生小程序,不需要额外的编译层。

商家后台用Vue3 + Vite + Element Plus,接口对接的是同一套后端RESTful API,通过JWT的role字段区分用户类型。这里要特别注意:管理端的接口和用户端接口要分权限体系,比如商家能查看营收报表,但普通用户绝对不能访问。

2.3 为什么不做微服务,以及哪些中间件是必须的

有人会问:项目要不要拆成多个服务,显得更"高级"?

我的回答是:不要。无人台球厅单店场景,哪怕高峰期同时在线打球也就几十人,一张表几十个并发请求的规模,单体架构完全扛得住。拆微服务会引入服务注册、配置中心、调用链追踪一整套复杂度,部署和演示都会更难,答辩时还容易被追问"你如何解决分布式事务"。真正的加分项是把你用到的Redis、定时任务讲清楚是为了解决什么问题,而不是堆砌技术名词。

但这不意味着什么中间件都不用。后面章节你会看到,Redis在防并发抢台、二维码凭证TTL、超时异步提醒这三个场景里是刚需;而如果没有一个定时任务框架,深夜异常订单就只能等着人工发现。我用的Spring自带的@Scheduled,简单可靠,足够支撑这类低频定时任务。

3. 数据库设计:状态、费率、会员三类建模是核心难点

3.1 核心表一览:七张表撑起整个业务闭环

这套系统的数据模型,我最终收敛成七张核心表。不要贪多,每张表都有清晰的职责,多一张表就是多一份维护成本:

表名职责关键字段
member用户openid、nickname、phone、balance、frozen_balance、level_id
ball_table球桌table_no、table_type、rate_group_id、status、device_id
rate_rule计费规则table_type、weekday、time_start、time_end、price_per_half_hour、enabled
reservation预约单member_id、table_id、start_time、end_time、status
billing_order开台订单order_no、member_id、table_id、checkin_time、checkout_time、total_amount、status
payment_log支付流水pay_no、order_id、amount、type、out_trade_no、status
device设备device_sn、type、bind_table_id、online_status

注意几个容易被忽视的设计细节:

  • 金额一律用DECIMAL(10,2),绝对不能存成FLOAT或DOUBLE。计费计算里的0.1加0.2误差在财务场景里是不可接受的。
  • 时间字段用DATETIME,不要用字符串。跨时段计费需要大量时间运算,字符串比较会带来各种隐藏问题。
  • 所有表保留deleted逻辑删除字段,避免直接物理删除破坏订单与球桌的历史关联。

3.2 球桌状态机:用int字段管住整张桌子的生命周期

ball_table.status是这套系统里最重要的字段之一,它的状态流转决定了开台、预约、结算的合法顺序:

状态值含义可流转到的状态
0空闲1(预约)、2(使用中)
1已预约0(取消)、2(到店开台)
2使用中0(结算完成)
3维护0(恢复)、2(试台)

所有涉及状态变更的Service方法,第一步都应该做状态校验。不要让任何一笔订单可以跨过合法状态直接跳变。我早期版本这里写得很松,只改了status字段没做校验,结果出现"预约状态直接变成已结算"的脏数据,排查了大半天。后来加了一个统一的StateValidator工具类,所有状态流转都走它,问题就再没出现过。

同样的状态机思想也用在billing_order.status上。我用0(已开台)、1(已结算)、2(已退款)、3(已取消),其中退款仅从已结算状态进入,防止出现"订单还没结算就先退款"这类不可能的业务。

3.3 计费规则怎么设计:高峰价、包段价、跨天价

计费规则是无人球厅系统里最容易设计烂掉的部分。如果只是简单地把单价写在球桌表里,那么"晚上10点以后计费不同""周末比工作日贵""斯诺克和美式价格不一样"这些需求就无法扩展。

我的做法是抽一张rate_rule规则表,把价格策略从球桌里解放出来:

CREATE TABLE rate_rule ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50), -- 规则名称,如"周末闲时" table_type INT, -- 球桌类型:1美式 2斯诺克 3VIP weekday VARCHAR(20), -- 适用星期:1,2,3,4,5(逗号分隔) time_start TIME, time_end TIME, price_per_half_hour DECIMAL(10,2), -- 每半小时价格 enabled TINYINT DEFAULT 1 );

比如一条规则可以是{table_type: 2, weekday: '6,7', time_start: '22:00', time_end: '02:00', price_per_half_hour: 35.00},表示周末晚上10点到凌晨2点,斯诺克每半小时35元。结算时先根据开台时间和离台时间,把时间切成若干段,每段匹配一条规则,然后逐段累加。

跨天场景是这里最容易翻车的。比如晚上23:30开台,凌晨01:30结算,时间范围跨越了午夜,如果只用"etime >= time_start"判断,凌晨0点到2点这段就匹配不上。正确做法是根据星期循环匹配,或者把时间区间拆成两段(23:30-24:00匹配前一天规则,00:00-01:30匹配当天规则)。这个问题我在下面第6章会有更详细的踩坑记录。

4. 核心链路拆解:从扫码进门到自动结算的完整实现

4.1 预约、押金冻结与进门凭证

先看用户侧最常用的一段流程:在小程序里查看空闲球桌列表,选一张桌,点击"立即开台"。前端调用后端接口时,传的是memberId和tableId,后端要做三件事:

第一,防并发抢台。同一张桌可能同时被两个用户看到空闲并点击开台,数据库的status字段在这两毫秒内还没有变成使用中,所以两个请求都能读到status=0。解决办法是在Redis里加一把短暂的分布式锁,key是lock:table:{tableId},然后数据库更新时再用一条UPDATE语句校验状态:

UPDATE ball_table SET status = 2 WHERE id = #{tableId} AND status = 0

如果影响行数为0,说明已经被抢,直接返回"球台已被预约"。

第二,冻结押金。用户余额里先冻结一部分(比如100元),不是直接扣款。我专门做了frozen_balance字段,开台时把可用余额转入冻结余额,结算时再解除或扣减。这一步保证了用户打了一半突然关掉小程序,系统也能在后台继续计时,等下次进来结算。

第三,生成进门凭证。开台成功后,后端生成一张带TTL(默认5分钟)的进门二维码,内容是加密后的订单号加桌面号,门禁扫码后调用后端verify接口完成开门。二维码存Redis,有效期内可重复使用,过期自动失效。

4.2 开台核心代码:事务、锁、设备指令的顺序很重要

开台接口最核心的代码如下,注意动作顺序:先抢锁,再校验状态,再冻结金额,最后更新桌台状态、建订单、下发设备指令。

@Transactional public String openTable(OpenTableDTO dto) { // 1. 防并发 String lockKey = "lock:table:" + dto.getTableId(); Boolean locked = redisTemplate.opsForValue() .setIfAbsent(lockKey, "1", Duration.ofSeconds(10)); if (Boolean.FALSE.equals(locked)) { throw new BizException("该球台正在开台中,请稍后重试"); } try { // 2. 状态校验 BallTable table = tableMapper.selectById(dto.getTableId()); if (table == null || table.getStatus() != TABLE_FREE) { throw new BizException("球台当前不可开台"); } // 3. 冻结押金 int updated = memberMapper.freezeBalance(dto.getMemberId(), DEFAUT_DEPOSIT); if (updated == 0) { throw new BizException("余额不足,无法开台"); } // 4. 创建订单 BillingOrder order = new BillingOrder(); order.setOrderNo(genOrderNo("OPEN")); order.setMemberId(dto.getMemberId()); order.setTableId(table.getId()); order.setCheckinTime(LocalDateTime.now()); order.setFrozenAmount(DEFAUT_DEPOSIT); order.setStatus(ORDER_PLAYING); orderMapper.insert(order); // 5. 更新桌台状态 tableMapper.updateStatusById(table.getId(), TABLE_USING); // 6. 下发开灯/解锁指令 deviceService.sendCommand(table.getDeviceId(), "POWER_ON"); return order.getOrderNo(); } finally { redisTemplate.delete(lockKey); } }

有个细节值得讲:设备指令放在事务提交之前还是之后?我的做法是在事务里调用设备服务,但如果设备指令失败,整体事务回滚,订单不成立。这保证了用户看到的结果不会出现"订单创建成功但灯没亮"。如果设备接口不稳定,可以改造成事务提交后异步重试,但作为毕设,前置调用的方案更简单、逻辑也更清晰。

4.3 结算:时长计算、跨时段价格与退款

结算发生在用户点击"结束计费"或者后台检测到用户离场(比如门禁再次开门)时。核心是把checkin_time到checkout_time这段时间拆成费率段,每段按半小时向上取整计费,然后累加:

public BigDecimal settleOrder(String orderNo) { BillingOrder order = orderMapper.selectByOrderNo(orderNo); if (order == null || order.getStatus() != ORDER_PLAYING) { throw new BizException("订单状态不正确"); } LocalDateTime start = order.getCheckinTime(); LocalDateTime end = LocalDateTime.now(); long totalMinutes = Duration.between(start, end).toMinutes(); BigDecimal amount = calculateAmount(start, end, order.getTableId()); // 冻结金额抵扣 BigDecimal diff = amount.subtract(order.getFrozenAmount()); if (diff.compareTo(BigDecimal.ZERO) > 0) { memberMapper.deductBalance(order.getMemberId(), diff); } else { memberMapper.unfreezeBalance(order.getMemberId(), diff.abs()); } // 收尾状态与退款流水 order.setCheckoutTime(end); order.setTotalMinutes(totalMinutes); order.setTotalAmount(amount); order.setStatus(ORDER_SETTLED); orderMapper.updateById(order); tableMapper.updateStatusById(order.getTableId(), TABLE_FREE); return amount; } private BigDecimal calculateAmount(LocalDateTime start, LocalDateTime end, Long tableId) { List<RateRule> rules = rateRuleMapper.selectEnabledRulesByTable(tableId); BigDecimal total = BigDecimal.ZERO; LocalDateTime cursor = start; while (cursor.isBefore(end)) { RateRule rule = matchRule(rules, cursor, end); LocalDateTime segmentEnd = computeSegmentEnd(cursor, end, rule); long minutes = Duration.between(cursor, segmentEnd).toMinutes(); long halfHours = (long) Math.ceil(minutes / 30.0); total = total.add(rule.getPricePerHalfHour().multiply(BigDecimal.valueOf(halfHours))); cursor = segmentEnd; } return total; }

这里的matchRule方法要处理跨天:先尝试匹配当天规则,如果当前时间在凌晨,要回退到前一天规则。computeSegmentEnd则返回当前规则的有效结束时间,保证不越过费率切换点。加上这两个方法后,无论从几点开台、跨不跨天,计费结果都是稳的。

5. 无人化运营的关键点:设备联动、异常单处理与数据看板

5.1 硬件设备接入:抽象接口是第一位

无人球厅一定少不了硬件:智能门锁或门禁、桌面电源控制器、以及可选的自助售货机。不同品牌设备开放接口各异,但业务层需要的动作其实就几个:开门、断电、通电、查状态。

所以我在系统里定义了一个设备服务抽象接口:

public interface DeviceService { boolean sendCommand(String deviceSn, String action); Map<String, Object> queryStatus(String deviceSn); }

Spring Boot里通过一个DeviceFactory根据device.type字段分发到对应品牌的实现类。这样就算后期换了门锁品牌,只需要新增一个实现类,业务代码完全不用动。对接方式常见有两种:HTTP接口(设备商提供POST地址)和MQTT协议(适合大批量设备)。如果是毕设演示,建议直接用HTTP模拟设备,或者用一个简易的HTTP模拟器,先把链路串起来,不用真的买硬件。

设备表里要记录online_status,用一个定时任务每30秒轮询一次设备心跳。任何设备离线,后台立刻标红并向商户推送提醒,这是无人门店最基础的监控能力。设备指令的失败重试同样重要:指令发出去没有回执,不要马上标记为失败,而是先查询设备状态确认;如果确实失败,重试最多3次,仍然失败就生成一条运维工单。

5.2 超时提醒、异常订单与防逃费策略

无人门店最怕的是"用户打完球直接走人,订单不结算"。为此我设计了三级兜底:

第一级,提前提醒。用户开台本身就订阅了小程序消息,我会在计费到订单预设时长(比如剩15分钟)时推送一条"您的台费即将进入下一时段,请及时结算"。

第二级,超时自动计费。用户不做任何操作,系统不会停表,而是继续按规则计费。这个设计非常关键,它保证球台使用费永远和实际使用时长挂钩。

第三级,余额不足的兜底。如果押金不够覆盖新增费用,系统会先尝试从可用余额扣,扣到负数会记录一笔负余额并限制该用户下一次开台。这套策略虽然不优雅,但在无人场景下是必要的防逃费"刹车"。

除了防逃费,还要处理各种异常订单:用户预约了但20分钟没到店,预约单自动释放,球台回到空闲并记一次爽约;设备启动失败导致用户无法开灯,系统要支持管理员后台手动修正订单、直接结算。每一类异常都写成枚举和独立处理类,不要用一堆if else堆在Service里,这样才能保证代码能讲清楚。

5.3 数据看板:老板真正关心的是哪几个数字

管理后台的数据看板不需要做成大屏,实用最重要。我做的时候只保留了四个核心指标:今日营收、今日订单量、当前在打球桌数、设备离线数。下面加两个趋势图:近7天营收趋势,和按小时统计的桌台利用率。这些数据都来自同一个报表SQL:

SELECT DATE_FORMAT(checkin_time, '%Y-%m-%d') AS day, COUNT(*) AS order_count, SUM(total_amount) AS revenue FROM billing_order WHERE status = 1 AND checkin_time >= DATE_SUB(CURDATE(), INTERVAL 6 DAY) GROUP BY day ORDER BY day;

按小时利用率要复杂一点,需要把每笔订单的起止时间拆到每个小时再聚合,这类统计放在SQL里写会很长,我选择在Java里循环重算。因为数据量不大(单店一天最多上百笔订单),计算完全够快,没必要引入复杂的实时数仓方案。数据看板的价值不在于图表多炫,而在于让老板一眼看出"今晚8点到10点是高峰,应该把优惠时段向前挪一小时"这种可执行的判断。

6. 毕设落地实战:从零复现这个项目的步骤与避坑记录

6.1 环境准备与项目初始化

要复现这个项目,先准备好环境:JDK 8或17、Maven 3.6+、MySQL 5.7以上、Redis 6.x、IDEA或Eclipse,前端还需要Node.js 16+。版本尽量不要用太新的,Spring Boot 2.7.x对JDK17已经兼容得很好,但JDK21可能会遇到Lombok和MyBatis-Plus的兼容问题,不值得为了"新"而折腾。

后端项目用Spring Initializr创建,选择Spring Web、MySQL Driver、MyBatis-Plus(手动引入)、Spring Data Redis、Validation、Lombok这几个依赖。创建后的第一件事不是写接口,而是把application.yml配置好:数据源地址、Redis地址、MyBatis-Plus的驼峰映射、日志级别。然后写一个最简单的health接口,确认能启动再继续。

我建议的主链路开发顺序是:登录 -> 球桌列表 -> 预约/开台 -> 结算 -> 订单列表。先把这条链路跑通,再补管理后台、统计报表和设备对接。避免一上来就想把所有表都填满数据,先把状态流转验证了,后面加功能只会越来越快。

6.2 我实际踩过的坑:六个值得记录的现场

第一,金额用Float导致计算误差。打球的时长费用会出现34.799999这种数字,前端展示很难看,数据库对账也对不上。后来把所有的金额字段和计算都改成BigDecimal,并且在数据库层用DECIMAL存储,问题彻底解决。

第二,并发抢台超卖。两个用户几乎同时点击同一张桌,后端都执行了selectById,发现状态都是0,然后都开了台。修复用了两把锁:Redis的临时锁防止并发进入,数据库的UPDATE带条件status=0保证最终只有一条生效。双保险之后再也没有出现过一张桌开两单的情况。

第三,跨天计费错位。晚上23:30开台、凌晨00:30结算,按"当前小时匹配当天费率"的简单写法,凌晨第一段匹配不到晚间规则,直接按默认价格算,账单少了钱。改成按费率时段切片、跨凌晨时自动回退到前一天的规则表后,这个问题才解决。

第四,微信登录在本地联调失败。code2session接口需要配置合法域名,本地跑后端时小程序访问不到,一开始以为是后端代码问题。后来用内网映射工具把本地端口暴露出来,并在小程序后台配置了临时域名才通过。这个坑对毕设演示尤其致命,一定要提前验证。

第五,MyBatis-Plus自动填充失效。created_time、updated_time打算用@TableField(fill = FieldFill.INSERT)自动生成,结果插入数据时字段一直是null。原因是没有写MyMetaObjectHandler实现类,我补上之后就正常了。这是MyBatis-Plus很常见的坑,一定要记得配置元对象处理器。

第六,部署后MySQL时区报错。服务器上连接数据库报错提示Server returns invalid timezone,在JDBC连接URL上加serverTimezone=Asia/Shanghai后恢复。这也是云服务器部署时的必备经验。

6.3 论文怎么写、答辩问什么:提前准备这几张图

毕设论文的章节套路相对固定,但内容必须和你的系统对得上。我推荐的目录是:绪论(背景与意义)-> 相关技术介绍 -> 需求分析(功能需求+非功能需求+用例图)-> 系统设计(总体架构+功能模块划分)-> 数据库设计(ER图+核心表说明)-> 系统实现(核心功能截图+关键代码讲解)-> 系统测试(功能测试用例+结果)-> 总结与展望。

论文里必须出现的图有四张:总体架构图、系统用例图、数据库ER图、核心业务时序图。其中时序图画的就是第4章的完整流程:用户->前端->后端->数据库->设备,每一步的调用关系标清楚。构图时注意层次,别把三端的功能都堆在一张图里。

答辩时被问到最多的问题,我归纳一下:为什么用Redis解决并发?押金冻结和结算的流程是什么?跨时段费率怎么算?设备指令失败如何处理?以及最常见的"你这个系统怎么防止用户逃单"。这些问题在本文第4、5章都有对应答案,提前用自己的话练习一遍,重点讲清楚状态流转和数据一致性,基本就能过关。

最后提醒一点:答辩演示不要现场开台又现场结算,这两步之间如果间隔太短,结算金额会是0,画面很尴尬。预置一条真实历史的订单数据,演示时直接看这条订单的完整链路,反而更能体现系统的严谨性。

这套系统做完回头看,我最深的体会是:无人自助类系统的技术栈都非常常规,难点全在业务状态流转和异常兜底上。你不需要写出多炫的算法,但必须把每一种"用户不按套路操作"的可能性都想到,这才是这种系统真正的价值。如果准备动手,我建议先从那个状态机图开始,再把费率表的几条测试数据造好,剩下的就是按部就班把这个闭环填完整。

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

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

立即咨询