前置准备
硬件要求
最低配置即可流畅运行:CPU i5-10400及以上、内存8G及以上、显卡GTX1650及以上,无特殊硬件要求。
软件环境与依赖下载
以下所有依赖均为开源免费版本,直接点击链接下载即可,无需额外付费:
- 兰台开源档案管理系统v2.3下载地址:https://gitee.com/lantaioffical/lantai-open/releases/download/v2.3/lantai-server.zip
- Three.js r158开发包下载地址:https://github.com/mrdoob/three.js/archive/refs/tags/r158.zip
- Node.js 16.20.0下载地址:https://nodejs.org/download/release/v16.20.0/node-v16.20.0-x64.msi
- MySQL 8.0下载地址:https://cdn.mysql.com/Downloads/MySQL-8.0/mysql-8.0.36-winx64.msi
安装Node.js和MySQL时全程默认下一步即可,安装完成后在cmd执行node -v和mysql -V,输出版本号即为安装成功。
步骤1:档案元数据标准化采集与入库
本步骤核心是将现有档案数据统一格式,方便后续映射到数字孪生场景,必须将档案位置字段统一为「库房号-货架号-层号-盒号」的格式,例如KF01-HJ03-C02-B05对应1号库房3号货架2层5号档案盒。
首先在MySQL中执行以下建表语句,创建标准化元数据表:
```sql
CREATE TABLE archive_metadata (
id INT PRIMARY KEY AUTO_INCREMENT,
archive_no VARCHAR(32) NOT NULL UNIQUE COMMENT '档案编号',
location VARCHAR(32) NOT NULL COMMENT '存放位置',
type VARCHAR(16) NOT NULL COMMENT '档案类型',
store_time DATETIME NOT NULL COMMENT '入库时间',
borrow_status TINYINT DEFAULT 0 COMMENT '0在库1借出2待归档',
position_x FLOAT DEFAULT 0 COMMENT '孪生场景X坐标',
position_y FLOAT DEFAULT 0 COMMENT '孪生场景Y坐标',
position_z FLOAT DEFAULT 0 COMMENT '孪生场景Z坐标'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```

建表完成后,将现有档案数据按照字段要求导入表中,若使用兰台系统自带的导入功能,直接在后台【档案管理】-【批量导入】中上传Excel即可,系统会自动同步数据到上述表中。
步骤2:数字孪生场景基础建模
无需专业3D建模能力,直接使用Three.js内置的库房模板生成场景即可,操作步骤如下:
- 解压Three.js r158压缩包,进入
examples/archive_warehouse_template目录
- 在当前目录打开cmd,执行
npm install安装依赖,等待2-3分钟安装完成
- 打开目录下的
template_config.json,替换为你所在库房的实际参数,完整可复制配置如下:
```json
{
"warehouse_width": 1200,
"warehouse_depth": 800,
"warehouse_height": 350,
"shelf_count": 16,
"shelf_layer_count": 6,
"shelf_box_count_per_layer": 10,
"unit": "cm",
"auto_map_position": true
}
```
配置完成后执行npm run build-template,系统会自动生成对应尺寸的3D库房场景,生成的场景文件保存在dist/warehouse.glb。
步骤3:档案软件与孪生场景数据打通
本步骤核心是将档案元数据的状态、位置映射到3D场景的点位上,首先配置兰台系统的开放接口权限:
- 解压兰台系统压缩包,执行
java -jar lantai-server.jar启动服务,访问http://localhost:8080,用默认账号admin/123456登录后台
- 进入【系统设置】-【开放接口】-【新增密钥】,勾选「档案查询、档案状态同步」权限,复制生成的256位密钥
- 打开Three.js模板目录下的
src/main.js,替换为以下完整代码,将代码内的IP和密钥替换为你自己的内容:
```javascript
import as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
// 初始化场景
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xf0f0f0);
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 2000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 1.5));
document.body.appendChild(renderer.domElement);
// 加载预生成的库房3D模型
const loader = new GLTFLoader();
loader.load('../dist/warehouse.glb', (gltf) => {
scene.add(gltf.scene);
renderArchivePoints();
});
// 拉取档案元数据
async function getArchiveData() {
const res = await fetch('http://你的服务器IP:8080/lantai/api/open/archive/list', {
headers: { 'Authorization': 'Bearer 你刚才生成的开放接口密钥' }
});
return await res.json();
}
// 位置字段映射为3D场景坐标
function mapLocationToPosition(location) {
const [kf, hj, c, b] = location.split('-');
const shelfIndex = parseInt(hj.replace('HJ','')) - 1;
const layerIndex = parseInt(c.replace('C','')) - 1;
const boxIndex = parseInt(b.replace('B','')) - 1;
return {
x: shelfIndex 80 - 560,
y: layerIndex 50 + 30,
z: boxIndex 30 - 120
}
}
// 渲染档案点位,不同状态显示不同颜色
async function renderArchivePoints() {
const archiveList = await getArchiveData();
archiveList.forEach(item => {
const pos = mapLocationToPosition(item.location);
const geometry = new THREE.BoxGeometry(20, 15, 25);
const color = item.borrow_status === 0 ? 0x00ff00 : item.borrow_status === 1 ? 0xff0000 : 0xffff00;
const material = new THREE.MeshBasicMaterial({ color, transparent: true, opacity: 0.8 });
const cube = new THREE.Mesh(geometry, material);
cube.position.set(pos.x, pos.y, pos.z);
cube.userData = item;
// 点击点位弹出档案详情
cube.addEventListener('click', () => {
alert(`档案编号:${item.archive_no}\n类型:${item.type}\n入库时间:${item.store_time}`);
});
scene.add(cube);
})
}
// 初始化控制器,支持拖拽旋转缩放
const controls = new OrbitControls(camera, renderer.domElement);
camera.position.z = 1200;
camera.position.y = 600;
// 自动同步数据,每10秒拉取一次
setInterval(renderArchivePoints, 10000);
// 渲染循环
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
animate();
```
必须配置nginx跨域规则,否则前端无法拉取接口数据,在nginx.conf中添加以下配置后重启nginx:
```nginx
location /lantai/api/open/ {
add_header Access-Control-Allow-Origin ;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'Authorization,Content-Type';
if ($request_method = OPTIONS) {
return 204;
}
proxy_pass http://127.0.0.1:8080;
}
```
步骤4:功能调试与上线
- 在Three.js模板目录执行
npm run dev启动前端服务,访问http://localhost:5173即可看到数字孪生场景
- 验证核心功能:输入档案编号可自动定位到对应3D位置、点位颜色和档案借阅状态一致、点击点位可查看详情
- 验证完成后执行
npm run build打包前端代码,将打包后的dist目录部署到nginx即可对外提供服务
常见问题排查
- 场景加载卡顿:将renderer.setPixelRatio设置为1,降低渲染精度即可解决
- 点位不显示:打开浏览器控制台检查是否有跨域报错,确认nginx跨域配置是否生效,档案location字段是否符合要求
- 数据不同步:检查兰台系统服务是否正常运行,开放接口密钥是否过期
效果验证标准
满足以下3个条件即为搭建成功:1.档案状态更新后,孪生场景点位10秒内自动同步;2.输入档案编号定位到对应位置的误差不超过5厘米;3.场景拖拽、缩放操作延迟低于200ms。