API名称 | 描述 |
---|---|
user/register | 用户注册 |
user/unregister | 用户注销 |
user/login | 用户登录 |
device/bind | 绑定设备 |
device/unbind | 解绑设备 |
device/getDeviceList | 获取设备列表 |
device/getLocationList | 获取设备的位置列表 |
device/refreshLocation | 刷新设备位置 |
环境 | HTTP请求地址 |
---|---|
正式环境 | device.vernal.ltd/tagapi |
终端用户注册接口
请求路径:/user/register
请求方法:POST
名称 | 位置 | 类型 | 必填 | 描述 |
---|---|---|---|---|
json
{
"cid": "WE23dT7PmGUrO2R+6Ch+GjMTmbwOqht6", //客户端id 必填
"name": "11111111", //用户名 必填
"pwd": "123456" //密码 必填
}
json
{
"code": "0000",
"items": null,
"message": "请求成功",
"status": 200
}
终端用户登录接口
请求路径:/user/login
请求方法:POST
名称 | 位置 | 类型 | 必填 | 描述 |
---|---|---|---|---|
json
{
"cid": "WE23dT7PmGUrO2R+6Ch+GjMTmbwOqht6", //客户端id 必填
"name": "111111", //用户名 必填
"pwd": "123456" //密码 必填
}
json
{
"code": "0000",
"items": {
"id": 26, //用户id
"name": "111111", //用户名
"token": "S4xte+ypz1AcXa4xW1FLTbdijU1D8oeC" //用于请求加密用的token
},
"message": "请求成功",
"status": 200
}
获取设备列表
请求路径:/device/getDeviceList
请求方法:POST
名称 | 位置 | 类型 | 必填 | 描述 |
---|---|---|---|---|
json
{
"userId": 26, //用户ID 必填
"data": "M/cnF490qMGdNzQNzIUfFI9iCe8FT0fhmKx+ZWWVNb8k5M7oa8dhL4FZzbE2ABeCL6zRxvrt5dFXNUileTuMu/ZJANdGQB2s2u2Z49dgZPE=" //加密后的用户数据 必填
}
{
"pageNo": 1, //页码
"pageSize": 20, //每一页数量
"queryFilter": {
"userId": 26 //用户id
}
}
json
{
"status": 200,
"code": "0000",
"message": "操作成功",
"items": "ctV99GK5rJHrdbLXN2laF3+BUaeYVA2c8/pMbEXsRLNTa80t3zSu7usoGKAnZKBevYSEQl6jbOouRWyQInOnTXufpQheNwTxADAuNpahu6hBc86mUsiZ4ccmctW/1rn6GlPzKJW5iWDatRaNE4+6wqVKVQALGMlngfAqUS9FEY5SC0xR/uj/pSobaQfsW981rz0zP8FM8LPpXC4658U5EfEYQogYHAvmfRQj3shh+NupwdYDjNg01fHBgEU8ykJI9vNpjrYY0vj28MHOKBLs/hDeOS634BCx5FWxzltxlxHkNnZhf7C3BIXV0S0iU+XnYRxs3yBoUhl1BAX9kVVliINkX2F+tG078GYybGj6CC9p0AJg7aBaKJOUdQDlQ5d7y2RD40aNvKwFwtE+0qe6G1edgSkOWxKB9vDBzigS7P4Q3jkut+AQseRVsc5bcZcR5DZ2YX+wtwRSaOnwGA18YVNhXaneBQFmdQQF/ZFVZYiDZF9hfrRtO0OffsJMST4z7Hu40/mySOAS02OoTMCjjstkQ+NGjbysBcLRPtKnuhuN5Es/Qz3PXfbwwc4oEuz+EN45LrfgELHkVbHOW3GXEeQ2dmF/sLcE5Mh33L1iUHKhB6EcEFl0fHUEBf2RVWWIg2RfYX60bTupVRCH0q623aKRokOnKjGC7t01tw2jm53LZEPjRo28rAXC0T7Sp7obgU8wpEGowDP28MHOKBLs/hDeOS634BCx5FWxzltxlxHkNnZhf7C3BCDxYgpIY6EZqNfGSHC/oVt1BAX9kVVliINkX2F+tG07M8f/iz6AX29ge5OMGyVIkKIvrhva6rzby2RD40aNvKwFwtE+0qe6G1IpWS0Amego9vDBzigS7P4Q3jkut+AQseRVsc5bcZcR/WyReaJ67kBBlCDcX5FYA5qlxK20JCLYdQQF/ZFVZYiDZF9hfrRtO7yoG7ne49wKvOmjmBqfT46VemY1WwjYtMtkQ+NGjbys8znFbezI+i81XlZvP337XgL23c/S1t4l3UVvujTKQSpjntj9x7/ozejXGWoMNJQbwfMCc5LPODUeGcn6q+ZqDA=="
}
{
"current": 1, //当前页码
"pages": 1, //总共页数
"records": [{
"deviceNum": "1712591093199087", //设备号
"location": { //设备最新一条数据
"latitude": "29.3646496", //纬度
"longitude": "105.9406226", //经度
"timestamp": 1742873587000, //时间
"battery": 100 //电量
},
"mac": "F1:1F:D9:16:D1:C2", //MAC 地址
"nickName": "bike", //设备名
"userId": 26 //用户id
}, {
"deviceNum": "1716953170364070",
"location": {},
"mac": "C1:F1:3C:13:BE:24",
"nickName": "方板子4号",
"userId": 26
}, {
"deviceNum": "1716953170801801",
"location": {},
"mac": "E0:14:E2:29:44:E5",
"nickName": "方板子2号",
"userId": 26
}, {
"deviceNum": "1716953172761163",
"location": {},
"mac": "E4:A9:31:08:A6:B7",
"nickName": "方板子3号",
"userId": 26
}, {
"deviceNum": "1716953173985356",
"location": {},
"mac": "FA:E7:03:8B:67:A1",
"nickName": "方板子1号",
"userId": 26
}, {
"deviceNum": "1726482692700617",
"location": {},
"mac": "F0:D6:01:D4:A8:F4",
"nickName": "MOTO",
"userId": 26
}],
"searchCount": true,
"size": 6,
"total": 6
}
获取定位列表
请求路径:/device/getLocationList
请求方法:POST
名称 | 位置 | 类型 | 必填 | 描述 |
---|---|---|---|---|
json
{
"userId": 26, //用户ID 必填
"data": "M/cnF490qMGdNzQNzIUfFI9iCe8FT0fhmKx+ZWWVNb8k5M7oa8dhL4FZzbE2ABeCL6zRxvrt5dFV6LMUzMhyXtFfL2K/TFzTvYSEQl6jbOouRWyQInOnTaK99OinqhLV9vDBzigS7P7THkIq9Y9vSL3P+JUCvCK/" //加密后的用户数据 必填
}
{
"pageNo": 1,
"pageSize": 20,
"queryFilter": {
"deviceNum": "1712591093199087",
"userId": 26
}
}
json
{
"status": 200,
"code": "0000",
"message": "操作成功",
"items": "ctV99GK5rJHrdbLXN2laF3+BUaeYVA2c8/pMbEXsRLPk81Ulmr76yfSBik7KIUNBpzQD0Li03VjYfyZnXp8k3/UEstvAaqkleXMonfeQc3aVb6xHzmBoJXBAkGUU1Wtauo96Ah02DX8J6Wla/ynQG1WuW0zEQ327vsx57FOxsrR+hDKF5eY64dM2WkLhmSWKvmq+oLtcH6DkVF/9rj/Ke+uTu/fpoX7n9V2gCPmSQcPtbgIgfpQdaKBVWI3gan0Qvmq+oLtcH6DvxM2ltwLRlPeOtekOVorNquMzI5O7VJE7bQ2OnXL4qrLndSIygwXU7PCCI1uIeUsjd3cUz/9kw/beeESuAQP7wS+6YvDoiWNWFPEM56Zex1rrCvjGp2sGIgX16D9fyYYNlrswMrvTMNh/JmdenyTf9QSy28BqqSXpsPC2LzmB4ZVvrEfOYGglcECQZRTVa1oJDBp+w3oTFgnpaVr/KdAbVa5bTMRDfbuKkW/QwuGf05d3PJcbal6sL+QV2dPZlAmgQ4SzyYnfkco0tHRkxcc4dRgaXmg3WW9y0nIkoeqRJX1svrsqkLa0gQTWJOxi1ud0az0vw0NK2Q=="
}
{
"current": 1,
"pages": 1,
"records": [{
"accuracy": "49", //精度
"datePublished": 1742873999000, //数据上报时间
"latitude": "29.3646496", //纬度
"longitude": "105.9406226", //经度
"timestamp": 1742873587000, //时间
"battery": 100 //电量
}, {
"accuracy": "48",
"datePublished": 1742873585000,
"latitude": "29.364612",
"longitude": "105.9401178",
"timestamp": 1742873584000,
"battery": 100 //电量
}, {
"accuracy": "131",
"datePublished": 1742871672000,
"latitude": "29.3647014",
"longitude": "105.940289",
"timestamp": 1742871119000,
"battery": 100 //电量
}],
"searchCount": true,
"size": 3,
"total": 3
}
请求刷新设备位置
请求路径:/device/refreshLocation
请求方法:POST
名称 | 位置 | 类型 | 必填 | 描述 |
---|---|---|---|---|
json
{
"userId": 3, //用户ID 必填
"data": "BIH80dqjWdLPxxnHm1mGawDtsryFV5XTUYQ3pqWodckxWIsrwFZW8V4JyPZRHHpxbaIX4jm1tATiGBdLiREvZB4/THcBsfdTQVsBU9CuVWp9dWJCUiw/9Ud8U9vJx89ZclfEvSI9+LM=" //加密后的用户数据 必填
}
{
"cid": "Gk7F6n92incFPoKRJCdpxK3YWgKiWw41",
"deviceNum": "1716991605662459",
"userId": 3
}
json
{
"status": 200,
"code": "0000",
"message": "操作成功",
"items": null
}
绑定设备
请求路径:/device/bind
请求方法:POST
名称 | 位置 | 类型 | 必填 | 描述 |
---|---|---|---|---|
json
{
"userId": 26, //用户ID 必填
"data": "dmfICksRMWRDLcW6AKV71Ojk8JIazaITIj6LjPrQF5Wazx1bM3VLWO3YnsLjPdTHR5J/LYX874cCTm7+IlR91A==" //加密后的用户数据 必填
}
{
"cid": "WE23dT7PmGUrO2R+6Ch+GjMTmbwOqht6", //客户端id 必填
"deviceNum": "Test-1726482692700617",
"userId": 26
}
json
{
"code": "0000",
"items": null,
"message": "请求成功",
"status": 200
}
解绑设备
请求路径:/device/unbind
请求方法:POST
名称 | 位置 | 类型 | 必填 | 描述 |
---|---|---|---|---|
json
{
"userId": 26, //用户ID 必填
"data": "dmfICksRMWRDLcW6AKV71G3bnB8mursMQZQg3F+RWAMiAsp8I33OGvbwwc4oEuz+AKbFa3jayFw=" //加密后的用户数据 必填
}
{
"cid": "WE23dT7PmGUrO2R+6Ch+GjMTmbwOqht6", //客户端id 必填
"deviceNum": "1726482692700617",
"userId": 26
}
json
{
"code": "0000",
"items": null,
"message": "请求成功",
"status": 200
}
终端用户注销接口
请求路径:/user/unregister
请求方法:POST
名称 | 位置 | 类型 | 必填 | 描述 |
---|---|---|---|---|
json
{
"userId": "1", //用户ID 必填
"data": "W9qwRTwEc4ZHkn8thfzvhwJObv4iVH3U" //加密后的用户数据 必填
}
{
"cid": "WE23dT7PmGUrO2R+6Ch+GjMTmbwOqht6", //客户端id 必填
"userId": 26
}
json
{
"code": "0000",
"items": null,
"message": "请求成功",
"status": 200
}
错误码 | 错误信息 | 描述 |
---|---|---|
8001 | 邮件地址不正确 | |
8002 | 验证码已过期 | |
8003 | 验证码错误 | |
8004 | 验证码已发送 | |
8005 | 请先获取验证码 | |
8006 | 用户已存在 | |
8007 | 用户注册失败 | |
8008 | 用户注册成功 | |
8009 | 用户不存在 | |
8010 | 密码错误 | |
8011 | 解密错误,请重新获取token | |
8012 | 设备绑定失败 | |
8013 | 设备不存在 | |
8014 | 绑定失败,设备已绑定 | |
8015 | 用户认证失败 | |
8016 | 请输入设备号 | |
8017 | 设备二维码无效 | |
8018 | 未知错误 | |
8019 | 客户端未授权 | |
8020 | 设备与客户端不匹配 |
public class DESUtils {
/**
* Triple DES 加密
*
* @param plainText 需要加密的明文
* @param key 加密密钥(必须是24字节)
* @return 返回 Base64 编码的加密结果
*/
public static String encrypt(String plainText, String key) throws Exception {
// 将密钥转换为字节数组
byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
// 创建 DESedeKeySpec
DESedeKeySpec spec = new DESedeKeySpec(keyBytes);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
SecretKey secretKey = keyFactory.generateSecret(spec);
// 初始化 Cipher 为加密模式
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
// 加密数据
byte[] encryptedBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
// 返回 Base64 编码的加密结果
return Base64.getEncoder().encodeToString(encryptedBytes);
}
/**
* Triple DES 解密
*
* @param encryptedText Base64 编码的加密字符串
* @param key 解密密钥(必须是24字节)
* @return 返回解密后的明文
*/
public static String decrypt(String encryptedText, String key) throws Exception {
// 将密钥转换为字节数组
byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
// 创建 DESedeKeySpec
DESedeKeySpec spec = new DESedeKeySpec(keyBytes);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
SecretKey secretKey = keyFactory.generateSecret(spec);
// 初始化 Cipher 为解密模式
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
// 解码 Base64 并解密数据
byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedText));
// 返回解密后的明文
return new String(decryptedBytes, StandardCharsets.UTF_8);
}
}
const crypto = require('crypto');
/**
* Triple DES 加密
* @param {string} plainText 需要加密的明文
* @param {string} key 加密密钥(必须是24字节)
* @returns {string} 返回 Base64 编码的加密结果
*/
function encrypt(plainText, key) {
// 检查密钥长度(3DES需要24字节)
if (Buffer.from(key, 'utf8').length !== 24) {
throw new Error('Key must be 24 bytes long for Triple DES');
}
// 创建cipher对象
const cipher = crypto.createCipheriv('des-ede3', Buffer.from(key, 'utf8'), null);
// 加密数据
let encrypted = cipher.update(plainText, 'utf8', 'base64');
encrypted += cipher.final('base64');
return encrypted;
}
/**
* Triple DES 解密
* @param {string} encryptedText Base64 编码的加密字符串
* @param {string} key 解密密钥(必须是24字节)
* @returns {string} 返回解密后的明文
*/
function decrypt(encryptedText, key) {
// 检查密钥长度(3DES需要24字节)
if (Buffer.from(key, 'utf8').length !== 24) {
throw new Error('Key must be 24 bytes long for Triple DES');
}
// 创建decipher对象
const decipher = crypto.createDecipheriv('des-ede3', Buffer.from(key, 'utf8'), null);
// 解密数据
let decrypted = decipher.update(encryptedText, 'base64', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
module.exports = { encrypt, decrypt };
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
<script>
/**
* Triple DES 加密
*/
function encrypt(plainText, key) {
const keyHex = CryptoJS.enc.Utf8.parse(key);
const encrypted = CryptoJS.TripleDES.encrypt(plainText, keyHex, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return encrypted.toString();
}
/**
* Triple DES 解密
*/
function decrypt(encryptedText, key) {
const keyHex = CryptoJS.enc.Utf8.parse(key);
const decrypted = CryptoJS.TripleDES.decrypt(encryptedText, keyHex, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return decrypted.toString(CryptoJS.enc.Utf8);
}
</script>