ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

智慧通讯业务3D可视化平台:SpringBoot+Three.js实战

智慧通讯业务3D可视化平台:SpringBoot+Three.js实战 这个项目是我带学生做毕业设计时一眼相中的题目基于JavaSpringBoot的智慧通讯业务办理3D可视化平台。先别被“智慧通讯”四个字唬住拆开来看就是两件事一是用SpringBoot做一套能跑通的通讯业务办理后台二是用Three.js这类3D渲染技术把业务数据、网点设备状态变成可视化大屏。市面上很多课程设计还停留在“表格增删改查”而3D可视化能明显拉开档次不管是答辩演示还是写进简历都更有说服力。如果你正在找Java课程设计选题或者毕业设计想做一个“既有业务功能又有视觉效果”的项目这篇内容可以给你一个完整的落地思路。我会从技术选型、数据库设计、后端接口、3D场景实现、部署排坑几个维度逐个拆所有代码都是真实项目中写过的直接踩过的坑也会一并标出来。开门见山地说这套平台的定位是“一个能展示的完整闭环系统”不是单纯的大屏demo也不只是一个CRUD报表。业务端要支持客户信息管理、业务办理状态流转、订单追踪展示端要能用一个3D营业厅场景实时反映设备在线状态、业务办理量、工单分布。两者通过SpringBoot接口串起来数据库用MySQL前端可视化为单独静态页面最终也可以打包进SpringBoot统一部署。1. 项目定位与技术选型为什么是 SpringBoot Three.js1.1 课程设计/毕业设计需求拆解很多同学拿到“智慧通讯业务办理3D可视化平台”这个题第一反应是又要做管理系统又要做3D是不是太大了其实题目本身已经暗示了层次“智慧通讯业务办理”是业务底座“3D可视化”是表达形式。所以设计的时候不能把两者割裂更不能偏科——只做后台管理没有可视化题目后半句不达标只画一个3D场景不给操作入口前半句又不达标。我通常建议把项目拆成三个模块来规划用户与权限模块登录认证、角色区分管理员/业务员/普通用户这是SpringBoot项目的基本盘。业务办理模块客户档案维护、通讯业务类型定义开户、过户、改套餐、缴费、停机等、办理单提交、状态流转。可视化展示模块通过3D场景展示网点分布、设备状态、今日办理量、工单进度等数据来源于后端接口。这样拆完以后工作量分配就很清晰前两个模块是“能跑通的业务系统”第三个模块是“能出彩的亮点”。毕业设计答辩时老师最常问的也是“你这个可视化数据从哪来”如果你能答出“后端统计接口返回的JSON前端渲染”再配合数据库表结构说明基本就能稳过。1.2 后端技术选型SpringBoot MyBatis-Plus MySQL 的理由后端我选的是SpringBoot 2.7.x MyBatis-Plus MySQL 8.0没有用微服务那套。原因很简单课程设计阶段单体应用完全够用代码少、部署简单、排错容易。SpringBoot的核心价值在于自动配置和生态成熟写一个Controller再加一个Service不需要繁琐的XML配置就能跑起来对不熟悉底层原理的同学非常友好。MyBatis-Plus和Spring Data JPA之间我推荐前者。JPA虽然封装程度高但写复杂统计查询时反而要绕弯子MyBatis-Plus的BaseMapper自带增删改查需要自定义SQL时又保留了SQL的可控性同时它还有分页插件、代码生成器能大幅缩短开发时间。课程设计时间紧张代码生成器直接把实体类、Mapper、Service、Controller都生成好你只需要改业务逻辑效率能翻一倍。MySQL 8.0目前是主流选择支持窗口函数写排名、环比统计这类SQL比较方便。这里顺便建议连接池用Druid或者HikariCP都行SpringBoot默认HikariCP其实很稳定没必要额外引入。我实际用下来Druid的监控页面在答辩时也是一个可展示的小亮点所以选了Druid加了druid-spring-boot-starter依赖。1.3 3D可视化选型Three.js 与 ECharts GL 的取舍3D可视化是个容易踩坑的环节。市面上可视化方案很多最常见的是ECharts和ECharts GL它擅长画3D柱状图、3D地图、3D散点这类“数据图表”。但如果要展示一个营业厅内部空间里面有柜台、机柜设备、用户轨迹ECharts就显得力不从心了。我最终选择的是Three.js。Three.js是基于WebGL的3D渲染引擎可以创建一个完整的3D场景地面、墙壁、设备模型、标签、光效、动画。它学习门槛比ECharts高但只做“简模状态映射点击交互”其实并不难。用立方体模拟机柜用球体模拟设备节点用CSS2DRenderer显示标签这些在Three.js官网都有现成例子可参考。而且Three.js做出来的效果“很3D”答辩时视觉冲击力明显更强。如果你时间实在不够也可以考虑折中用ECharts GL做3D地图/柱状图配合普通ECharts做趋势曲线这样也能满足“3D可视化”的字眼要求。但既然题目写的是“3D可视化平台”我个人建议上Three.js至少做一个小场景价值感瞬间不一样。2. 数据库设计与业务模型2.1 核心表结构设计数据库是整个平台的基座设计的好坏直接影响开发进度。我不建议一上来就建表先画一张简单的ER图明确实体关系用户属于角色角色有权限客户有多个业务办理单业务办理单引用业务类型网点包含设备设备与状态记录关联。基于这些关系我的核心表设计如下表名用途关键字段sys_user用户表id, username, password, real_name, role_id, branch_id, statussys_role角色表id, role_name, role_code, descriptioncustomer客户表id, customer_no, name, phone, id_card, address, created_timebiz_type业务类型表id, type_name, type_code, fee, need_audit, statusbiz_order业务办理单表id, order_no, customer_id, biz_type_id, user_id, branch_id, status, handle_time, remarkorder_status_log订单状态日志表id, order_id, from_status, to_status, change_time, operator_idbranch网点表id, branch_name, address, longitude, latitude, manager_iddevice设备表id, device_no, device_name, device_type, branch_id, status, last_online_timedevice_status_log设备状态日志表id, device_id, status, report_time每个表都建议加上create_time和update_time字段后面做可视化统计时非常有用。比如要统计“今日各网点办理量”只需要按biz_order.create_time和branch_id分组汇总。设备状态表用status字段区分在线/离线/故障3D大屏上不同颜色就是从这里来。2.2 业务办理流程的状态机设计业务办理模块最容易被做成“一个表一个状态字段update一下就完事”。但如果只是这样答辩时老师追问“一个单子从提交到完成经历了哪些环节”你就很难解释清楚。所以我建议引入状态机日志表biz_order.status记录当前状态0待受理、1办理中、2已完成、3已取消、4异常。order_status_log记录每一次状态变化从什么状态变成什么状态谁操作的什么时候变的。这样做有两个好处第一业务可追溯任何一张单子的历史流程都能查出来第二可视化大屏上可以展示“各状态订单占比”还能画漏斗图。SpringBoot里使用Transactional保证状态更新的原子性先更新biz_order.status再插入order_status_log一旦插入失败要回滚否则状态和日志对不上。状态流转的规则也要写在Service层里比如“已取消”和“已完成”不能跳到“办理中”前端下拉框根据当前状态动态过滤。这样逻辑集中在后端前端展示和交互就不会乱。2.3 可视化数据从哪来统计查询与缓存设计3D大屏上的数据不是凭空生成的来源就是数据库里的业务表。但你不能让前端直接查表必须提供后端接口返回聚合好的JSON。以“当日业务办理量按网点分布”为例SQL可以写成SELECT b.branch_name, COUNT(o.id) AS total FROM biz_order o LEFT JOIN branch b ON o.branch_id b.id WHERE o.create_time CURDATE() GROUP BY o.branch_id, b.branch_name;这种统计接口通常会被前端大屏高频调用如果每次都实时查全表数据量一上来MySQL会忙不过来。我的做法是热点接口加Redis缓存比如/api/visual/overview这个接口汇总了总用户数、今日办理量、设备在线率、工单趋势数据变化没那么频繁缓存30秒完全够用。SpringBoot里用Cacheable或者手动RedisTemplate都行课程设计里手工写RedisTemplate反而更容易讲清楚。另外要注意3D大屏展示的空间数据和设备状态不要在主线程里查询。我写了一个Scheduled定时任务每10秒去数据库拉一次设备状态写入Redis Hash结构接口再从Redis读取。这样既保证数据时效性也避免频繁查表。3. 核心功能实战从后端接口到3D场景3.1 SpringBoot 项目搭建与配置项目骨架我推荐用Spring Initializr生成依赖选择Spring Web、MySQL Driver、MyBatis-Plus、Redis、Lombok、Validation。如果你用IDEA可以直接在New Project里选Spring Initializr填好Group和Artifact就行。pom.xml关键依赖示例dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependencyapplication.yml里的几个关键配置注意一下spring: datasource: url: jdbc:mysql://localhost:3306/smart_tel?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver redis: host: localhost port: 6379 mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl注意serverTimezone必须设置我因为没加这一项连接数据库时报过The server time zone value Öйú±ê׼ʱ¼ä is unrecognized这是最常见的坑之一。MyBatis-Plus的驼峰映射默认开启所以数据库字段下划线会自动映射到实体类驼峰属性命名就按create_time这种方式来。3.2 业务办理后端接口实现业务办理的接口设计要遵守“一单一状态”的原则。核心接口如下POST /api/order/create提交业务办理单POST /api/order/handle受理/处理业务POST /api/order/complete完成业务POST /api/order/cancel取消业务GET /api/order/page分页查询办理单GET /api/order/status/{orderNo}查询办理进度以创建办理单为例Service层的关键逻辑是生成唯一订单号、保存客户关联、插入初始状态、写入日志。订单号不要用数据库自增ID太容易被猜我用的是“日期业务类型码随机数”比如20250612153000TX0001。生成代码String orderNo D LocalDateTime.now().format(DateTimeFormatter.ofPattern(yyyyMMddHHmmss)) RandomStringUtils.randomNumeric(4);提交办理单时前端提交customerId和bizTypeId后端校验客户是否存在、业务类型是否启用。校验通过后用事务插入biz_order和order_status_logTransactional(rollbackFor Exception.class) public BizOrder createOrder(OrderCreateRequest request) { Customer customer customerMapper.selectById(request.getCustomerId()); if (customer null) throw new BusinessException(客户不存在); BizType bizType bizTypeMapper.selectById(request.getBizTypeId()); if (bizType null || bizType.getStatus() ! 1) throw new BusinessException(业务类型不可用); BizOrder order new BizOrder(); order.setOrderNo(orderNo); order.setCustomerId(customer.getId()); order.setBizTypeId(bizType.getId()); order.setStatus(0); order.setCreateTime(LocalDateTime.now()); orderMapper.insert(order); OrderStatusLog log new OrderStatusLog(); log.setOrderId(order.getId()); log.setFromStatus(-1); log.setToStatus(0); log.setChangeTime(LocalDateTime.now()); log.setOperatorId(getCurrentUserId()); orderStatusLogMapper.insert(log); return order; }这里有一个容易忽略的点Transactional默认只回滚RuntimeException和Error你自己定义的BusinessException要继承RuntimeException否则事务不会生效。如果验证失败还想回滚就要抛出运行时异常。我学生在这里栽过好几次跟头数据库里经常出现状态变了但日志没插入的情况。3.3 3D可视化数据接口设计为了前端的3D场景能直接使用接口返回结构要统一且规范。我建议所有接口返回统一包装类{ code: 200, message: success, data: {} }3D大屏主要用这几个接口接口路径返回内容用途/api/visual/overview总客户数、今日办理量、设备在线率、工单分布大屏顶部统计卡/api/visual/branches所有网点坐标、名称、实时办理量3D场景中的网点标记/api/visual/devices设备编号、所属网点、状态、温度3D场景中的机柜设备状态/api/visual/trend近7日业务办理趋势ECharts折线图/api/visual/bizTypes各业务类型办理量占比ECharts饼图/api/visual/devices返回数据示例{ code: 200, message: success, data: [ { deviceNo: DEV-001, deviceName: 服务器A-01, deviceType: server, branchId: 1, branchName: 中心营业厅, status: online, temperature: 45.6, lastOnlineTime: 2025-06-12 15:30:00 } ] }前端拿到这批数据后根据status字段映射设备材质颜色online绿色、offline灰色、error红色。这个设计没什么高级技术但非常实用可视化大屏的“3D感”就是靠这种颜色映射和几何体位置来体现的。3.4 前端3D场景搭建Three.js 实战前端我不建议上太重的框架直接用HTML Three.js ECharts通过fetch或axios调用后端接口就行。如果你用Vue也可以把Three.js封装成组件但课程设计阶段纯静态页面反而简单。最简单的Three.js场景至少需要这几步场景Scene、相机Camera、渲染器Renderer、几何体Mesh、光源Light。以模拟营业厅机柜设备为例const scene new THREE.Scene(); const camera new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.set(20, 15, 20); camera.lookAt(0, 0, 0); const renderer new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); document.getElementById(container).appendChild(renderer.domElement); // 地面 const planeGeometry new THREE.PlaneGeometry(20, 20); const planeMaterial new THREE.MeshStandardMaterial({ color: 0x1a1a2e }); const plane new THREE.Mesh(planeGeometry, planeMaterial); plane.rotation.x -Math.PI / 2; scene.add(plane); // 机柜设备 function createCabinet(x, z, status) { const geometry new THREE.BoxGeometry(1.2, 2, 1); const material new THREE.MeshStandardMaterial({ color: status online ? 0x00d26a : status error ? 0xff4d4f : 0x999999 }); const cabinet new THREE.Mesh(geometry, material); cabinet.position.set(x, 1, z); scene.add(cabinet); return cabinet; } // 根据接口数据生建设备 fetch(/api/visual/devices) .then(res res.json()) .then(data { data.data.forEach((device, index) { const x (index % 5) * 2 - 4; const z Math.floor(index / 5) * 2 - 2; createCabinet(x, z, device.status); }); });上面这段是精简版实际代码还要加入OrbitControls实现鼠标旋转缩放、CSS2DRenderer显示设备名称标签、requestAnimationFrame循环渲染。为了演示设备状态会动态变化我加了一个定时器setInterval(() { fetch(/api/visual/devices) .then(res res.json()) .then(data { // 遍历场景中的机柜物体根据状态更新颜色 }); }, 10000);这个轮询方式最简单。如果你想更高级一点后端用WebSocket推送状态变化前端监听消息实时更新材质颜色。我两个方案都做过如果设备和网点数量不多轮询完全够用如果刷新频率要求高再上WebSocket。课程设计用轮询Scheduled写演示代码逻辑更清晰老师看得懂。3.5 可视化大屏组合ECharts 和 Three.js 同屏展示完整的大屏不能只有3D场景还得有图表数据支撑。推荐布局中间是Three.js的3D营业厅场景两侧或上下放ECharts图表。页面用Flex或Grid布局背景深色标题文字用亮色。ECharts和Three.js之间不存在冲突它们各自占用一个Canvas或DOM容器。只要注意在页面加载时先初始化Three.js场景再初始化ECharts图表不会相互干扰。比如右侧放一个“业务类型占比”饼图const chart echarts.init(document.getElementById(chart1)); fetch(/api/visual/bizTypes) .then(res res.json()) .then(data { chart.setOption({ tooltip: { trigger: item }, series: [{ type: pie, radius: [40%, 70%], data: data.data.map(item ({ name: item.typeName, value: item.total })) }] }); });大屏的数据刷新策略要避免整屏重刷。我之前写了个粗糙版本每10秒重新加载所有图表和Three.js数据结果页面肉眼可见地闪烁。后来改成ECharts实例只更新series.dataThree.js只更新材质颜色。这样刷新过程平滑得多。操作很简单的拿到新的数据后单独调用chart.setOption({ series: [{ data: newData }] })Three.js则根据设备ID找到对应物体更新材质。4. 部署、联调与文档整理4.1 本地联调时经常踩的坑前后端分离开发时最典型的问题是跨域。SpringBoot后端接口跑在8080/Vite前端跑在5173如果不配置跨域浏览器的fetch请求会被拦截。最简单的跨域配置是在Controller类上加CrossOrigin但更标准的做法是配置全局CORSConfiguration 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); } }另一个坑是数据库连接池报错。如果MySQL服务没有启动或者密码不对SpringBoot启动时就会报Failed to configure a DataSource。这个错误很好排查但很多同学会把数据库连接串里的时区漏掉报了错还一脸懵。我建议在application.yml里把driver-class-name显式写出来MySQL 8一定写com.mysql.cj.jdbc.Driver别用老版的com.mysql.jdbc.Driver。4.2 项目打包和统一部署课程设计要交源码也要能跑起来演示。如果前端是独立的HTML JS没有工程化你可以直接把前端静态文件放在SpringBoot的src/main/resources/static目录下然后访问http://localhost:8080/index.html就能看到大屏页面。这样Maven打包后一个jar包就包含了前端部署非常方便。打包命令mvn clean package -DskipTests打完的jar包在target目录运行java -jar smart-tel-0.0.1.jar如果Redis没有启动而你又强制在启动类里初始化客户端那么应用可能启动失败。建议代码里对Redis连接做容错或者演示的时候先启动Redis。很多同学没有装Redis可视化接口返回不了数据这就很尴尬。我的经验是演示前先把Redis和MySQL都启动好并给visualService加一个降级逻辑如果Redis挂了就去查数据库。4.3 万字文档怎么写结构、图和答辩准备项目附带“万字文档”最终成果物不只是代码更是一份能支撑答辩的文档。我的文档结构一般这样安排绪论背景与意义、国内外现状、论文结构。需求分析功能性需求、非功能性需求、用例图。系统设计总体架构图、功能模块图、数据库ER图、时序图。系统实现核心功能页面截图、核心代码逻辑说明、3D可视化实现原理。系统测试测试用例表、测试结果、异常处理分析。总结与展望。写文档的时候有两个容易让老师加分的点一是数据库ER图要详细到每个表字段用PowerDesigner或draw.io画二是3D可视化的实现要写清楚“数据如何从数据库流到前端渲染”的完整链路最好配一张自己画的时序图。这块能用文字描述清楚答辩时基本不会被问倒。答辩高频问题我也列一下“3D可视化用的是什么技术为什么选Three.js”回答WebGL渲染引擎支持真实3D场景比单纯图表更直观。“状态日志表有什么作用”回答记录流转历史便于追溯和统计分析。“如果设备数量很多3D场景会不会卡”回答会卡需要优化我们用了合并几何体、降低粒子数量、按需渲染等方式。“大屏数据是实时更新的吗”回答后端定时任务轮询数据库前端定时拉取接口属于准实时也可以改造为WebSocket推送。5. 完整排坑实录与性能优化建议5.1 高频问题速查表问题现象原因解决方法启动报时区错误jdbc连接串未设置serverTimezone加上serverTimezoneAsia/Shanghai前端请求接口跨域未配置CORS全局配置WebMvcConfigurer事务不生效自定义异常未继承RuntimeException继承RuntimeException或指定rollbackFor3D场景黑屏未加光源或相机位置不在物体前面添加环境光和方向光调整相机位置模型颜色不更新修改了材质的一个副本而未更新材质数组确保通过mesh.material.color.set更新原材质大屏数据刷新闪烁整个页面重新render只更新材质和图表data不重建dom数据库中文乱码连接串未设置characterEncoding加characterEncodingutf8Redis连接不上导致启动失败未做容错演示前先启动Redis或代码降级查库这串问题基本都是我实际开发中踩过的。特别是Three.js里更新材质颜色看起来很简单但其实有个细节如果你在创建Mesh时给material传了一个数组或者在多Mesh共用材质时直接赋值new THREE.MeshStandardMaterial()会导致之前所有Mesh不受影响必须用mesh.material.color.set(newColor)这种原地修改方法。5.2 性能优化让3D场景更顺滑Three.js绘制大量设备时每增加一个Mesh就意味着多一次Draw Call。课程设计里设备数量不会太多但为了让演示不卡顿可以做几个小优化合并静态几何体地面、墙壁这些不变的元素用BufferGeometryUtils.mergeBufferGeometries合成一个几何体只保留一个Mesh。限制光源数量Scene里不要放十几个点光源。一个AmbientLight加一个DirectionalLight就够了。开启渲染器性能设置renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))避免高DPI屏幕下拉满分辨率导致卡顿。减少粒子特效如果做了粒子系统粒子数量控制在5000以下。后端性能也有优化空间。统计接口不要动不动SELECT *只查需要的字段大屏接口加Redis缓存定时任务错峰执行不要所有任务都卡到同一秒启动。这些点不只写在技术文档里答辩时说出来老师会觉得你有实战经验。5.3 一些独门经验机构和设备怎么建模才“好看”Three.js项目说到底逃不过“美观”二字。很多同学学了一堆API做出来的东西还是像“方盒子堆砌”。我的经验是与其追求复杂模型不如把灯光和配色做好。大屏背景用深蓝色渐变搭配浅蓝色轮廓线设备用高明度色彩逐个区分状态。地面可以用THREE.GridHelper画网格线科技感瞬间上来。还可以在场景里加一个THREE.AxesHelper答辩时说是开发辅助工具方便理解坐标方向。设备位置不要手动一个个填坐标定义一个配置数组跟后端设备列表一一对应。设备坐标可以用相对坐标比如机柜按“排、列、层”规则生成这样后面加设备也方便。我用一个简单函数生成坐标function calcDevicePosition(row, column, layer) { return { x: column * 2 - 6, y: layer * 2.5 1.5, z: row * 2 - 4 }; }这个思路是从“真实机房有列、排、层”这个场景里来的效果比乱摆好很多。总的来说这套智慧通讯业务办理3D可视化平台真正做完之后你会收获三层能力一是SpringBoot搭建CRUD后端的能力包括事务、接口、缓存二是Three.js从零搭建3D场景并动态更新数据的经验三是把一个综合项目从设计到部署再到写成文档的全过程认知。这些能力对课程设计展示、找工作面试帮忙都很大。如果你正在计划做一个类似的Java毕业设计可以从业务办理这个模块先开始等跑通了再加3D场景千万不要一上来就啃Three.js容易失去信心。另外给你一个小建议从第一天就把SQL脚本和初始数据准备好每次改表结构都要同步更新init.sql。这样做的好处是后期演示时直接执行这个脚本就能生成一套能看的数据不用临时造数。很多项目的文档写得很好但一运行发现数据库是空的大屏上啥都看不出来这种翻车现场我见过太多次了。
返回列表