docs: 移动端开发文档扩展 + 附录术语表/错误码

05-移动端开发:
- 01-移动端工程结构与页面分域.md
  - 补充 UniApp 分包策略(主包 2MB 限制)
  - 页面路由配置与生命周期规范
  - Token 双 Token 无感刷新逻辑
  - 内存泄漏防护清单

- 02-硬件交互与弱网离线策略.md
  - 智能工牌蓝牙通信协议(BadgeStatus/NotifyType)
  - iBeacon 协议格式与信标配置
  - 蓝牙扫描 Hook 实现(useBluetoothScan)
  - 权限处理与超时机制

08-附录:
- 01-术语表.md
  - IoT 设备术语(工牌、信标、RSSI)
  - 工单状态机(Mermaid 状态图)
  - 优先级与触发来源
  - 移动端 BLE 术语

- 02-错误码清单.md
  - 后端统一错误码(全局 + OPS 模块)
  - 前端 ResultEnum 状态码
  - 蓝牙扫描状态与错误处理
  - 错误码对照速查表
This commit is contained in:
lzh
2026-04-07 13:45:03 +08:00
parent 7fb3aea295
commit b61896d3ba
4 changed files with 1165 additions and 952 deletions

View File

@@ -38,33 +38,71 @@ aiot-uniapp/
│ │ └── helpers.ts # 通用辅助函数
│ │
│ ├── pages/ # 【主包】核心页面
│ │ ├── login/
│ │ │ ├── index.vue # 登录页
│ │ │ └── auth.vue # 静默授权回调
│ │ ├── index/
│ │ │ └── index.vue # 工作台首页Tab Bar
│ │ ├── scan/
│ │ │ ├── index.vue # 扫码入口
│ │ │ └── bluetooth-debug/ # 蓝牙调试页
│ │ ├── user/
│ │ │ └── index.vue # 个人中心
│ │ └── contact/
│ │ └── index.vue # 通讯录
│ │
│ ├── pages-core/ # 【分包】核心功能(登录、设置)
│ │ ├── auth/
│ │ │ ├── login.vue # 账号密码登录
│ │ │ ├── code-login.vue # 验证码登录
│ │ │ └── forget-password.vue
│ │ └── user/
│ │ ── index.vue # 个人中心
│ │ ── profile/
│ │ ├── settings/
│ │ └── security/
│ │
│ ├── pages-ops/ # 【分包】Ops 现场作业
│ │ ├── work-order/
│ │ │ ├── list.vue # 工单列表
│ │ │ ├── index.vue # 工单列表
│ │ │ ├── detail.vue # 工单详情
│ │ │ ├── arrival.vue # 到岗打卡(信标扫描)
│ │ │ └── complete.vue # 完工提交
│ │ │ ├── create.vue # 创建工单
│ │ │ └── stats.vue # 工单统计
│ │ ├── inspection/
│ │ │ ├── list.vue # 巡检任务列表
│ │ │ ├── scan.vue # 扫码巡检
│ │ │ └── record.vue # 巡检记录填报
│ │ └── queue/
│ │ └── index.vue # 我的队列
│ │ │ ├── index.vue # 巡检任务列表
│ │ │ ├── detail.vue # 巡检详情
│ │ │ └── components/
│ │ │ └── bluetooth-verify.vue # 蓝牙信标验证
│ │ └── inspection/composables/
│ │ └── use-bluetooth-scan.ts # 蓝牙扫描 Hook
│ │
│ ├── pages-iot/ # 【分包】IoT 设备管理
│ │ ├── device/
│ │ │ ├── list.vue
│ │ │ └── detail.vue
│ │ └── alarm/
│ │ └── index.vue
│ ├── pages-system/ # 【分包】系统管理
│ │ ├── area/
│ │ ├── dept/
│ │ ├── user/
│ │ └── dict/
│ │
│ ├── pages-bpm/ # 【分包】工作流
│ │
│ ├── pages-infra/ # 【分包】基础设施
│ │
│ ├── api/ # API 接口定义
│ │ ├── ops/ # Ops 业务 API
│ │ │ ├── order-center/ # 工单中心
│ │ │ ├── cleaning/ # 保洁业务
│ │ │ ├── security/ # 安保业务
│ │ │ └── inspection/ # 巡检业务
│ │ ├── iot/ # IoT 设备 API
│ │ ├── system/ # 系统管理 API
│ │ └── login.ts # 登录相关 API
│ │
│ ├── store/ # Pinia 状态管理
│ │ ├── token.ts # Token 管理(双 Token 刷新)
│ │ ├── user.ts # 用户信息
│ │ ├── dict.ts # 字典缓存
│ │ └── theme.ts # 主题配置
│ │
│ ├── http/ # HTTP 请求封装
│ │ ├── http.ts # 核心 HTTP 方法
│ │ ├── types.ts # 类型定义
│ │ └── tools/
│ │ └── enum.ts # ResultEnum 状态码
│ │
│ ├── static/ # 静态资源
│ │ ├── images/
@@ -94,9 +132,10 @@ aiot-uniapp/
| 目录 | 说明 | 体积控制 |
|-----|------|---------|
| `/pages/login/` | 登录、验证码、静默授权页 | < 200KB |
| `/pages/index/` | 工作台首页Tab Bar | < 300KB |
| `/pages/user/` | 个人中心基础设置 | < 100KB |
| `/pages/scan/` | 扫码入口 | < 200KB |
| `/pages/user/` | 个人中心 | < 100KB |
| `/pages/contact/` | 通讯录 | < 150KB |
| `components/` | 全局共享的基础 UI 组件库 | < 200KB |
**严禁将具体的业务流程(如填写工单、扫描设备)塞入主包。**
@@ -109,27 +148,36 @@ aiot-uniapp/
// pages.json
{
"pages": [
{ "path": "pages/login/index" },
{ "path": "pages/index/index" },
{ "path": "pages/user/index" }
{ "path": "pages/scan/index" },
{ "path": "pages/user/index" },
{ "path": "pages/contact/index" }
],
"subPackages": [
{
"root": "pages-ops",
"root": "pages-core",
"pages": [
{ "path": "work-order/list" },
{ "path": "work-order/detail" },
{ "path": "work-order/arrival" },
{ "path": "inspection/list" },
{ "path": "inspection/scan" }
{ "path": "auth/login" },
{ "path": "auth/code-login" },
{ "path": "user/profile/index" }
]
},
{
"root": "pages-iot",
"root": "pages-ops",
"pages": [
{ "path": "device/list" },
{ "path": "device/detail" },
{ "path": "alarm/index" }
{ "path": "work-order/index" },
{ "path": "work-order/detail" },
{ "path": "work-order/create" },
{ "path": "inspection/index" },
{ "path": "inspection/detail" }
]
},
{
"root": "pages-system",
"pages": [
{ "path": "area/index" },
{ "path": "dept/index" },
{ "path": "user/index" }
]
}
],
@@ -150,21 +198,103 @@ aiot-uniapp/
---
## 三、页面状态与生命周期规约
## 三、页面路由与导航
移动端页面的生命周期`onLoad`, `onShow`, `onReady` Vue 组件`created`, `mounted`有重叠必须规范使用
### 3.1 路由配置pages.json
### 3.1 生命周期使用规范
```json
{
"pages": [
{
"path": "pages/index/index",
"type": "home",
"style": { "navigationStyle": "custom" }
},
{
"path": "pages/scan/index",
"style": { "navigationBarTitleText": "扫码巡检" }
}
],
"subPackages": [
{
"root": "pages-ops",
"pages": [
{ "path": "work-order/index", "style": { "navigationStyle": "custom" } },
{ "path": "work-order/detail", "style": { "navigationStyle": "custom" } },
{ "path": "inspection/index", "style": { "navigationStyle": "custom" } },
{ "path": "inspection/detail", "style": { "navigationStyle": "custom" } }
]
}
],
"tabBar": {
"custom": true,
"color": "#999999",
"selectedColor": "#f97316",
"list": [
{ "text": "工作台", "pagePath": "pages/index/index" },
{ "text": "我的", "pagePath": "pages/user/index" }
]
}
}
```
### 3.2 页面跳转方法
```typescript
// 基础导航
import { navigateBackPlus } from '@/utils'
// 跳转到工单详情(带参数)
function goToWorkOrderDetail(orderId: number) {
uni.navigateTo({
url: `/pages-ops/work-order/detail?id=${orderId}`
})
}
// 返回上一页(封装版,支持指定 fallback
navigateBackPlus('/pages-ops/work-order/index')
// 重定向(关闭当前页面)
uni.redirectTo({ url: '/pages/index/index' })
// 切换到 TabBar 页面
uni.switchTab({ url: '/pages/index/index' })
```
### 3.3 页面参数接收
```vue
<script setup lang="ts">
import { onLoad, onShow, onUnload } from '@dcloudio/uni-app';
import { ref, onMounted } from 'vue';
import WorkOrderService from '@/services/ops/WorkOrderService';
import { onLoad } from '@dcloudio/uni-app'
const orderId = ref('');
const orderDetail = ref<OrderDetail | null>(null);
const loading = ref(false);
const props = defineProps<{
id: string // URL 参数通过 defineProps 接收
}>()
onLoad((options) => {
// options 包含 URL 参数
console.log(options?.id)
})
</script>
```
---
## 四、页面状态与生命周期规约
移动端页面的生命周期`onLoad`, `onShow`, `onReady` Vue 组件`created`, `mounted`有重叠必须规范使用
### 4.1 生命周期使用规范
```vue
<script setup lang="ts">
import { onLoad, onShow, onUnload } from '@dcloudio/uni-app'
import { ref, onMounted } from 'vue'
import { getOrderDetail } from '@/api/ops/order-center'
const orderId = ref('')
const orderDetail = ref<OrderDetail | null>(null)
const loading = ref(false)
/**
* onLoad: 只做初始化
@@ -173,13 +303,13 @@ const loading = ref(false);
* - 绝不操作 DOM 节点
*/
onLoad((options) => {
orderId.value = options?.id || '';
orderId.value = options?.id || ''
if (!orderId.value) {
uni.showToast({ title: '参数错误', icon: 'none' });
return;
uni.showToast({ title: '参数错误', icon: 'none' })
return
}
fetchOrderDetail();
});
fetchOrderDetail()
})
/**
* onShow: 处理从后台切回前台的数据刷新
@@ -190,9 +320,9 @@ onShow(() => {
// 场景1从其他页面返回可能需要刷新状态
// 场景2从后台切回检查工单状态是否有变化
if (orderId.value && !loading.value) {
checkStatusUpdate();
checkStatusUpdate()
}
});
})
/**
* Vue onMounted: 可操作 DOM但小程序中受限
@@ -200,7 +330,7 @@ onShow(() => {
*/
onMounted(() => {
// 主要用于 Web 端逻辑
});
})
/**
* onUnload: 清理动作
@@ -210,124 +340,98 @@ onMounted(() => {
*/
onUnload(() => {
// 断开工牌蓝牙连接
BleManager.disconnect();
// BleManager.disconnect()
// 取消正在进行的请求
requestCancelToken.cancel('页面卸载');
});
// requestCancelToken.cancel('页面卸载')
})
async function fetchOrderDetail() {
loading.value = true;
loading.value = true
try {
orderDetail.value = await WorkOrderService.getDetail(orderId.value);
orderDetail.value = await getOrderDetail(Number(orderId.value))
} finally {
loading.value = false;
loading.value = false
}
}
async function checkStatusUpdate() {
// 静默检查状态变化
const latest = await WorkOrderService.getStatus(orderId.value);
const latest = await getOrderDetail(Number(orderId.value))
if (latest.status !== orderDetail.value?.status) {
// 状态变化,刷新详情
fetchOrderDetail();
fetchOrderDetail()
}
}
</script>
```
### 3.2 内存泄漏防护
### 4.2 内存泄漏防护
| 资源类型 | 注册位置 | 必须清理位置 | 清理方式 |
|---------|---------|-------------|---------|
| WebSocket | `onLoad` / `onShow` | `onUnload` / `onHide` | `ws.close()` |
| EventBus 监听 | `onLoad` | `onUnload` | `uni.$off()` |
| 蓝牙连接 | 用户操作后 | `onUnload` | `BleManager.disconnect()` |
| 蓝牙连接 | 用户操作后 | `onUnload` | `uni.closeBLEConnection()` |
| 定时器 | 任意 | `onUnload` | `clearInterval()` |
| 页面级 Store | `onLoad` | `onUnload` | `store.$reset()` |
---
## 、状态管理设计
## 、状态管理设计
### 4.1 分层状态管理
### 5.1 分层状态管理
```
全局状态 (Pinia Store)
├── userStore # 用户信息、登录态
├── configStore # 系统配置、字典缓存
── bleStore # 蓝牙连接状态、当前工牌
├── token.ts # Token 管理(双 Token 无感刷新)
├── user.ts # 用户信息、登录态
── dict.ts # 系统字典缓存
└── theme.ts # 主题配置
页面级状态 (Composable / setup)
├── useWorkOrder() # 单个工单页面的状态
├── useInspection() # 巡检页面的状态
└── useUpload() # 图片上传的状态
└── useBluetoothScan() # 蓝牙扫描状态
```
### 4.2 Store 示例
### 5.2 Token 管理(双 Token 无感刷新)
```typescript
// stores/ble.ts
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
import BleManager from '@/services/ble/BleManager';
// store/token.ts
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useBleStore = defineStore('ble', () => {
// State
const connectedDevice = ref<BadgeDevice | null>(null);
const connectionState = ref<'disconnected' | 'connecting' | 'connected'>('disconnected');
const lastHeartbeat = ref<number>(0);
interface TokenInfo {
accessToken: string
refreshToken: string
expiresTime: number
}
// Getters
const isConnected = computed(() => connectionState.value === 'connected');
const isDeviceOnline = computed(() => {
if (!lastHeartbeat.value) return false;
return Date.now() - lastHeartbeat.value < 5 * 60 * 1000; // 5分钟内有心跳
});
// Actions
async function connect(deviceId: string) {
connectionState.value = 'connecting';
try {
const success = await BleManager.connect(deviceId);
if (success) {
connectionState.value = 'connected';
// 监听心跳
BleManager.on('heartbeat', (data) => {
lastHeartbeat.value = data.timestamp;
});
} else {
connectionState.value = 'disconnected';
}
} catch (error) {
connectionState.value = 'disconnected';
throw error;
}
export const useTokenStore = defineStore('token', () => {
const tokenInfo = ref<TokenInfo | null>(null)
async function refreshToken() {
// 调用刷新接口,更新 accessToken
const res = await http.post('/system/auth/refresh-token', {
refreshToken: tokenInfo.value?.refreshToken
})
tokenInfo.value = res
}
function disconnect() {
BleManager.disconnect();
connectedDevice.value = null;
connectionState.value = 'disconnected';
function logout() {
tokenInfo.value = null
uni.removeStorageSync('token_info')
}
return {
connectedDevice,
connectionState,
lastHeartbeat,
isConnected,
isDeviceOnline,
connect,
disconnect
};
});
return { tokenInfo, refreshToken, logout }
})
```
---
## 、关键实现要点
## 、关键实现要点
### 5.1 工程结构红线
### 6.1 工程结构红线
| 红线项 | 说明 | 后果 |
|-------|------|------|
@@ -336,22 +440,23 @@ export const useBleStore = defineStore('ble', () => {
| **必须预加载** | 首页应预加载高频分包 | 用户点击后白屏时间过长 |
| **清理必须配对** | 有注册必须有卸载 | 内存泄漏页面异常 |
### 5.2 目录命名规范
### 6.2 目录命名规范
| 类型 | 命名规范 | 示例 |
|-----|---------|------|
| 页面目录 | kebab-case | `work-order/`, `inspection-scan/` |
| 组件文件 | kebab-case | `app-badge.vue`, `app-upload.vue` |
| 服务文件 | PascalCase | `BleManager.ts`, `WorkOrderService.ts` |
| 工具文件 | camelCase | `request.ts`, `helpers.ts` |
| 常量文件 | UPPER_SNAKE | `constants.ts` (内部常量) |
| 页面目录 | kebab-case | `work-order/`, `inspection/` |
| 组件文件 | kebab-case | `bluetooth-verify.vue` |
| 服务文件 | PascalCase | `BleManager.ts` |
| 工具文件 | camelCase | `use-bluetooth-scan.ts` |
| API 文件 | camelCase | `order-center/index.ts` |
### 5.3 相关代码入口
### 6.3 相关代码入口
| 模块 | 文件路径 | 说明 |
|-----|---------|------|
| 页面路由 | `src/pages.json` | 主包 + 分包配置 |
| 应用配置 | `src/manifest.json` | 小程序 AppID权限声明 |
| 请求封装 | `src/utils/request.ts` | 拦截器错误处理 |
| 蓝牙服务 | `src/services/ble/BleManager.ts` | BLE 连接管理 |
| 全局组件 | `src/components/` | 复用 UI 组件 |
| 请求封装 | `src/http/http.ts` | 拦截器Token 刷新错误处理 |
| 蓝牙扫描 | `src/pages-ops/inspection/composables/use-bluetooth-scan.ts` | iBeacon 扫描 Hook |
| 字典常量 | `src/utils/constants/dict-enum.ts` | DICT_TYPE 定义 |
| 状态码 | `src/http/tools/enum.ts` | ResultEnum 定义 |

View File

@@ -0,0 +1,200 @@
# 01-术语表
本文档汇总 AIOT 系统中使用的专业术语,按领域分类整理。
---
## 一、IoT 设备术语
### 1.1 工牌Badge
| 术语 | 英文 | 说明 |
|-----|------|------|
| 智能工牌 | Smart Badge | 保洁/安保人员佩戴的 BLE 设备,用于接收工单通知、上报位置 |
| 工牌状态 | Badge Status | IDLE(空闲) / BUSY(忙碌) / PAUSED(暂停) / OFFLINE(离线) |
| 工牌通知 | Badge Notify | 向工牌发送震动/语音通知 |
| 心跳 | Heartbeat | 工牌定期上报在线状态 |
| 电量 | Battery Level | 工牌剩余电量百分比 (0-100) |
| RSSI | Received Signal Strength Indicator | 信号强度指示,单位 dBm |
### 1.2 信标Beacon
| 术语 | 英文 | 说明 |
|-----|------|------|
| 蓝牙信标 | Bluetooth Beacon | 固定部署的 BLE 广播设备,用于定位 |
| iBeacon | Apple iBeacon | Apple 制定的 BLE 广播协议 |
| UUID | Universally Unique Identifier | 信标唯一标识16字节 |
| Major | Major ID | 主标识通常对应区域ID |
| Minor | Minor ID | 次标识通常对应信标ID |
| 信号阈值 | RSSI Threshold | 最小可接收信号强度,如 -75dBm |
### 1.3 设备状态
| 术语 | 英文 | 说明 |
|-----|------|------|
| 未激活 | Inactive | 设备未激活状态 |
| 在线 | Online | 设备正常连接 |
| 离线 | Offline | 设备断开连接 |
---
## 二、工单业务术语
### 2.1 工单类型
| 术语 | 英文 | 说明 |
|-----|------|------|
| 保洁工单 | Cleaning Order | 保洁作业任务 |
| 安保工单 | Security Order | 安保处理任务 |
| 维修工单 | Repair Order | 设备维修任务 |
| 客服工单 | Service Order | 客服服务任务 |
### 2.2 工单状态(状态机)
```mermaid
stateDiagram-v2
[*] --> PENDING: 创建
PENDING --> QUEUED: 进入队列
QUEUED --> DISPATCHED: 推送到工牌
DISPATCHED --> CONFIRMED: 保洁员确认
CONFIRMED --> ARRIVED: 到达现场
ARRIVED --> COMPLETED: 完成作业
ARRIVED --> PAUSED: 暂停
PAUSED --> ARRIVED: 恢复
PENDING --> CANCELLED: 取消
QUEUED --> CANCELLED: 取消
DISPATCHED --> CANCELLED: 取消
CONFIRMED --> CANCELLED: 取消
ARRIVED --> CANCELLED: 取消
```
| 状态 | 英文 | 说明 |
|-----|------|------|
| 待分配 | PENDING | 工单已创建,等待分配 |
| 排队中 | QUEUED | 已推荐保洁员,在队列中等待 |
| 已推送 | DISPATCHED | 已推送到工牌,等待确认 |
| 已确认 | CONFIRMED | 保洁员按下确认按钮 |
| 已到岗 | ARRIVED | 到达现场,开始作业 |
| 进行中 | IN_PROGRESS | 作业进行中 |
| 已暂停 | PAUSED | 临时暂停 |
| 已完成 | COMPLETED | 作业完成 |
| 已取消 | CANCELLED | 工单取消 |
### 2.3 优先级
| 优先级 | 英文 | 说明 |
|-------|------|------|
| P0 紧急 | P0 Urgent | 最高优先级,可打断当前任务 |
| P1 重要 | P1 High | 高优先级,优先处理 |
| P2 普通 | P2 Normal | 普通优先级 |
| P3 低优 | P3 Low | 低优先级 |
### 2.4 触发来源
| 来源 | 英文 | 说明 |
|-----|------|------|
| 蓝牙信标 | IOT_BEACON | 信标触发(如厕所溢水) |
| 客流阈值 | IOT_TRAFFIC | 客流统计触发 |
| 视频告警 | VIDEO_ALARM | AI 视频分析告警 |
| 门禁告警 | ACCESS_ALARM | 门禁系统告警 |
| 巡更告警 | PATROL_ALARM | 巡更异常告警 |
| 紧急按钮 | PANIC_BUTTON | 紧急求助按钮 |
| 手动创建 | MANUAL | 人工创建工单 |
---
## 三、巡检术语
### 3.1 巡检要素
| 术语 | 英文 | 说明 |
|-----|------|------|
| 巡检区域 | Inspection Area | 需要巡检的物理区域 |
| 巡检模板 | Inspection Template | 该区域检查项的配置 |
| 检查项 | Inspection Item | 具体的检查内容 |
| 巡检记录 | Inspection Record | 一次巡检的结果记录 |
### 3.2 巡检结果
| 术语 | 英文 | 说明 |
|-----|------|------|
| 合格 | Passed | 检查项通过 |
| 不合格 | Failed | 检查项未通过 |
| 位置异常 | Location Exception | 未在指定区域完成巡检 |
| 归属判定 | Attribution | 异常原因:个人责任/突发状况/正常 |
---
## 四、人员术语
### 4.1 角色
| 术语 | 英文 | 说明 |
|-----|------|------|
| 保洁员 | Cleaner | 执行保洁任务的人员 |
| 安保员 | Security Guard | 执行安保任务的人员 |
| 维修员 | Repairman | 执行维修任务的人员 |
| 巡检员 | Inspector | 执行巡检任务的人员 |
| 调度员 | Dispatcher | 负责工单分配的人员 |
### 4.2 人员状态
| 术语 | 英文 | 说明 |
|-----|------|------|
| 空闲 | IDLE | 可接受新任务 |
| 忙碌 | BUSY | 正在执行任务 |
| 暂停 | PAUSED | 临时离开 |
| 离线 | OFFLINE | 不在线 |
---
## 五、区域术语
| 术语 | 英文 | 说明 |
|-----|------|------|
| 区域 | Area | 物理空间划分 |
| 父区域 | Parent Area | 上级区域 |
| 子区域 | Child Area | 下级区域 |
| 功能类型 | Function Type | 区域功能:厕所/电梯/大堂等 |
| 地理围栏 | Geo-fence | 虚拟边界,用于位置校验 |
---
## 六、移动端术语
### 6.1 小程序
| 术语 | 英文 | 说明 |
|-----|------|------|
| 主包 | Main Package | 小程序主包2MB 限制 |
| 分包 | SubPackage | 业务分包2MB 限制 |
| 预加载 | Preload | 提前加载分包资源 |
| TabBar | Tab Bar | 底部导航栏 |
### 6.2 蓝牙
| 术语 | 英文 | 说明 |
|-----|------|------|
| BLE | Bluetooth Low Energy | 低功耗蓝牙 |
| GATT | Generic Attribute Profile | 通用属性配置文件 |
| MTU | Maximum Transmission Unit | 最大传输单元默认20字节 |
| Notify | Notification | BLE 通知机制 |
| 广播 | Advertising | BLE 设备广播数据 |
| 扫描 | Scanning | 搜索周围 BLE 设备 |
---
## 七、缩写对照
| 缩写 | 全称 | 中文 |
|-----|------|------|
| AIOT | Artificial Intelligence of Things | 智联网 |
| BLE | Bluetooth Low Energy | 低功耗蓝牙 |
| RSSI | Received Signal Strength Indicator | 接收信号强度指示 |
| UUID | Universally Unique Identifier | 通用唯一标识符 |
| API | Application Programming Interface | 应用程序接口 |
| HTTP | HyperText Transfer Protocol | 超文本传输协议 |
| JSON | JavaScript Object Notation | JavaScript 对象表示法 |
| DOM | Document Object Model | 文档对象模型 |
| Pinia | - | Vue 状态管理库 |
| UniApp | - | 跨端开发框架 |

View File

@@ -0,0 +1,242 @@
# 02-错误码清单
本文档汇总 AIOT 系统中使用的错误码,包括后端统一错误码和前端/移动端特定错误。
---
## 一、后端统一错误码
### 1.1 全局错误码0-999
| 错误码 | 错误信息 | 说明 |
|-------|---------|------|
| 0 | 成功 | 请求成功 |
| 400 | 请求参数不正确 | 参数校验失败 |
| 401 | 账号未登录 | Token 过期或无效 |
| 403 | 没有该操作权限 | 权限不足 |
| 404 | 请求未找到 | 资源不存在 |
| 405 | 请求方法不正确 | HTTP 方法错误 |
| 423 | 请求失败,请稍后重试 | 并发请求锁定 |
| 429 | 请求过于频繁,请稍后重试 | 限流触发 |
| 500 | 系统异常 | 服务器内部错误 |
| 501 | 功能未实现/未开启 | 功能未开放 |
| 502 | 错误的配置项 | 配置错误 |
| 900 | 重复请求,请稍后重试 | 重复提交 |
| 901 | 演示模式,禁止写操作 | Demo 模式限制 |
| 999 | 未知错误 | 未分类错误 |
### 1.2 OPS 模块错误码1-020-xxx-xxx
#### 区域管理1-020-001-xxx
| 错误码 | 错误信息 | 说明 |
|-------|---------|------|
| 1020001000 | 区域不存在 | AREA_NOT_FOUND |
| 1020001001 | 该区域下存在子区域,请先处理子区域 | AREA_HAS_CHILDREN |
| 1020001002 | 该区域已绑定设备,请先解除绑定 | AREA_HAS_DEVICES |
| 1020001003 | 不能将父级设置为自己或子孙节点 | AREA_PARENT_LOOP |
| 1020001004 | 区域编码已存在 | AREA_CODE_EXISTS |
#### 区域设备关联1-020-002-xxx
| 错误码 | 错误信息 | 说明 |
|-------|---------|------|
| 1020002000 | 设备不存在 | DEVICE_NOT_FOUND |
| 1020002001 | 该工牌已绑定至此区域 | DEVICE_ALREADY_BOUND |
| 1020002002 | 该区域已绑定{类型},一个区域只能绑定一个 | DEVICE_TYPE_ALREADY_BOUND |
| 1020002003 | 设备关联关系不存在 | DEVICE_RELATION_NOT_FOUND |
| 1020002004 | IoT 设备服务不可用,请稍后重试 | IOT_SERVICE_UNAVAILABLE |
#### 安保工单1-020-003-xxx
| 错误码 | 错误信息 | 说明 |
|-------|---------|------|
| 1020003000 | 工单不存在 | SECURITY_ORDER_NOT_FOUND |
| 1020003001 | 工单类型不匹配,期望安保工单 | SECURITY_ORDER_TYPE_MISMATCH |
| 1020003002 | 该安保人员已绑定到此区域 | SECURITY_AREA_USER_DUPLICATE |
| 1020003003 | 绑定记录不存在 | SECURITY_AREA_USER_NOT_FOUND |
| 1020003004 | 目标安保人员未绑定到工单所属区域或已停用 | SECURITY_ASSIGNEE_NOT_BOUND_TO_AREA |
| 1020003005 | 当前工单状态不允许手动派单,仅 PENDING 状态可操作 | SECURITY_ORDER_STATUS_NOT_ALLOW_DISPATCH |
| 1020003006 | 已完成或已取消的工单不允许升级优先级 | SECURITY_ORDER_PRIORITY_UPGRADE_NOT_ALLOWED |
#### 巡检模块1-020-004-xxx
| 错误码 | 错误信息 | 说明 |
|-------|---------|------|
| 1020004000 | 巡检模板不存在 | INSPECTION_TEMPLATE_NOT_FOUND |
| 1020004001 | 巡检记录不存在 | INSPECTION_RECORD_NOT_FOUND |
| 1020004002 | 该区域未启用,无法巡检 | INSPECTION_AREA_NOT_ACTIVE |
---
## 二、前端/移动端错误码
### 2.1 HTTP 状态码ResultEnum
```typescript
// http/tools/enum.ts
export enum ResultEnum {
Success0 = 0, // 成功
Success200 = 200, // 成功
Error = 400, // 错误
Unauthorized = 401, // 未授权
Forbidden = 403, // 禁止访问
NotFound = 404, // 未找到
MethodNotAllowed = 405, // 方法不允许
RequestTimeout = 408, // 请求超时
InternalServerError = 500, // 服务器错误
NotImplemented = 501, // 未实现
BadGateway = 502, // 网关错误
ServiceUnavailable = 503, // 服务不可用
GatewayTimeout = 504, // 网关超时
HttpVersionNotSupported = 505, // HTTP版本不支持
}
```
### 2.2 蓝牙扫描状态ScanStatus
```typescript
// pages-ops/inspection/composables/use-bluetooth-scan.ts
export type ScanStatus =
| 'idle' // 空闲
| 'scanning' // 扫描中
| 'success' // 定位成功
| 'failed' // 定位失败
| 'no-permission' // 缺少权限
```
### 2.3 蓝牙扫描错误信息
| 状态 | 错误信息 | 处理建议 |
|-----|---------|---------|
| no-permission | 蓝牙权限已被拒绝,请在设置中开启 | 引导用户打开设置 |
| no-permission | 需要蓝牙权限才能进行定位验证 | 申请蓝牙权限 |
| failed | 启动蓝牙扫描失败 | 检查蓝牙是否开启 |
| failed | 定位超时,未能验证位置 | 靠近信标重试 |
### 2.4 网络错误提示
```typescript
// http/tools/enum.ts
export function ShowMessage(status: number | string): string {
switch (status) {
case 400: return '请求错误(400),请检查网络或联系管理员!'
case 401: return '未授权,请重新登录(401),请检查网络或联系管理员!'
case 403: return '拒绝访问(403),请检查网络或联系管理员!'
case 404: return '请求出错(404),请检查网络或联系管理员!'
case 408: return '请求超时(408),请检查网络或联系管理员!'
case 500: return '服务器错误(500),请检查网络或联系管理员!'
case 501: return '服务未实现(501),请检查网络或联系管理员!'
case 502: return '网络错误(502),请检查网络或联系管理员!'
case 503: return '服务不可用(503),请检查网络或联系管理员!'
case 504: return '网络超时(504),请检查网络或联系管理员!'
case 505: return 'HTTP版本不受支持(505),请检查网络或联系管理员!'
default: return `连接出错(${status})!,请检查网络或联系管理员!`
}
}
```
---
## 三、错误码处理规范
### 3.1 后端错误处理
```java
// Java 后端统一返回格式
public class CommonResult<T> {
private Integer code; // 错误码
private String msg; // 错误信息
private T data; // 数据
public static <T> CommonResult<T> success(T data) {
return new CommonResult<>(0, "成功", data);
}
public static <T> CommonResult<T> error(ErrorCode errorCode) {
return new CommonResult<>(errorCode.getCode(), errorCode.getMsg(), null);
}
}
```
### 3.2 前端错误处理
```typescript
// http/http.ts 统一错误处理
success: async (res) => {
const { code, msg } = responseData
// Token 过期处理
if (res.statusCode === 401 || code === 401) {
const tokenStore = useTokenStore()
if (!isDoubleTokenMode) {
tokenStore.logout()
toLoginPage()
return reject(res)
}
// 无感刷新 Token...
}
// 业务错误处理
if (code !== ResultEnum.Success0 && code !== ResultEnum.Success200) {
uni.showToast({
title: msg || '请求错误',
icon: 'none',
})
return reject(responseData)
}
return resolve(responseData.data)
},
fail(err) {
uni.showToast({
icon: 'none',
title: '网络错误,换个网络试试',
})
reject(err)
}
```
### 3.3 错误码对照表
| 场景 | 后端错误码 | 前端处理 |
|-----|-----------|---------|
| 参数错误 | 400 | Toast 提示具体错误 |
| Token 过期 | 401 | 自动刷新或跳转登录 |
| 权限不足 | 403 | Toast 提示无权限 |
| 资源不存在 | 404 | 显示空状态或返回 |
| 服务器错误 | 500 | Toast 提示系统异常 |
| 请求频繁 | 429 | Toast 提示稍后重试 |
| 蓝牙权限拒绝 | - | 引导打开系统设置 |
| 定位超时 | - | 提示靠近信标重试 |
| 网络断开 | - | Toast 提示网络错误 |
---
## 四、错误码速查
### 4.1 按模块速查
| 模块 | 错误码范围 | 文件 |
|-----|-----------|------|
| 全局 | 0-999 | GlobalErrorCodeConstants.java |
| 区域 | 1-020-001-xxx | ErrorCodeConstants.java |
| 设备 | 1-020-002-xxx | ErrorCodeConstants.java |
| 安保 | 1-020-003-xxx | ErrorCodeConstants.java |
| 巡检 | 1-020-004-xxx | ErrorCodeConstants.java |
### 4.2 按场景速查
| 场景 | 常见错误码 | 解决方案 |
|-----|-----------|---------|
| 登录失败 | 401 | 检查账号密码,重新登录 |
| 派单失败 | 1020003005 | 确认工单状态为 PENDING |
| 信标绑定失败 | 1020002001 | 解绑后重新绑定 |
| 巡检提交失败 | 1020004002 | 确认区域已启用 |
| 蓝牙扫描失败 | no-permission | 开启蓝牙和定位权限 |
| 图片上传失败 | 408/504 | 压缩图片后重试 |