553 lines
13 KiB
Plaintext
553 lines
13 KiB
Plaintext
/**
|
|
* 手环位置服务 - 处理基站ID、位置信息、围栏功能
|
|
*/
|
|
|
|
import type { AkReqResponse } from '@/uni_modules/ak-req/index.uts'
|
|
|
|
// 基础坐标类型
|
|
export type LocationCoordinate = {
|
|
longitude: number
|
|
latitude: number
|
|
}
|
|
|
|
// 估算位置类型
|
|
export type EstimatedLocation = {
|
|
longitude: number
|
|
latitude: number
|
|
accuracy: number
|
|
}
|
|
|
|
// 围栏中心点类型
|
|
export type FenceCenter = {
|
|
longitude: number
|
|
latitude: number
|
|
}
|
|
|
|
// 基站信息类型
|
|
export type BaseStationInfo = {
|
|
/** 基站ID */
|
|
id: string
|
|
/** 基站名称 */
|
|
name: string
|
|
/** 基站位置描述 */
|
|
location: string
|
|
/** 经度 */
|
|
longitude: number
|
|
/** 纬度 */
|
|
latitude: number
|
|
/** 信号强度 */
|
|
signalStrength: number
|
|
/** 是否在线 */
|
|
isOnline: boolean
|
|
/** 覆盖范围(米) */
|
|
range: number
|
|
}
|
|
|
|
// 位置信息类型
|
|
export type LocationInfo = {
|
|
/** 设备ID */
|
|
deviceId: string
|
|
/** 当前连接的基站 */
|
|
baseStation: BaseStationInfo | null
|
|
/** 估算位置 */
|
|
estimatedLocation: EstimatedLocation | null
|
|
/** 最后更新时间 */
|
|
lastUpdate: string
|
|
/** 位置状态 */
|
|
status: 'online' | 'offline' | 'unknown'
|
|
}
|
|
|
|
// 围栏信息类型
|
|
export type FenceInfo = {
|
|
/** 围栏ID */
|
|
id: string
|
|
/** 围栏名称 */
|
|
name: string
|
|
/** 围栏类型 */
|
|
type: 'circle' | 'polygon'
|
|
/** 围栏中心点(圆形围栏) */
|
|
center?: FenceCenter
|
|
/** 围栏半径(米,圆形围栏) */
|
|
radius?: number
|
|
/** 围栏顶点(多边形围栏) */
|
|
points?: Array<LocationCoordinate>
|
|
/** 是否激活 */
|
|
isActive: boolean
|
|
/** 围栏事件类型 */
|
|
eventType: 'enter' | 'exit' | 'both'
|
|
/** 创建时间 */
|
|
createdAt: string
|
|
}
|
|
|
|
// 位置历史记录类型
|
|
export type LocationHistoryItem = {
|
|
/** 记录ID */
|
|
id: string
|
|
/** 设备ID */
|
|
deviceId: string
|
|
/** 基站信息 */
|
|
baseStation: BaseStationInfo
|
|
/** 记录时间 */
|
|
timestamp: string
|
|
/** 持续时间(秒) */
|
|
duration: number
|
|
/** 位置类型 */
|
|
locationType: 'basestation' | 'estimated'
|
|
}
|
|
|
|
// 围栏事件类型
|
|
export type FenceEvent = {
|
|
/** 事件ID */
|
|
id: string
|
|
/** 设备ID */
|
|
deviceId: string
|
|
/** 围栏信息 */
|
|
fence: FenceInfo
|
|
/** 事件类型 */
|
|
eventType: 'enter' | 'exit'
|
|
/** 事件时间 */
|
|
timestamp: string
|
|
/** 触发位置 */
|
|
location: LocationCoordinate
|
|
/** 是否已读 */
|
|
isRead: boolean
|
|
}
|
|
|
|
// FenceInfo 的手动 Partial 类型
|
|
export type PartialFenceInfo = {
|
|
id?: string
|
|
name?: string
|
|
type?: 'circle' | 'polygon'
|
|
center?: FenceCenter
|
|
radius?: number
|
|
points?: Array<LocationCoordinate>
|
|
isActive?: boolean
|
|
eventType?: 'enter' | 'exit' | 'both'
|
|
createdAt?: string
|
|
}
|
|
|
|
// mock generic AkReqResponse for array data
|
|
export function mockAkArrayResponse<T>(data: T[], status: number = 200): AkReqResponse<Array<T>> {
|
|
return {
|
|
status,
|
|
data,
|
|
headers: {} as UTSJSONObject,
|
|
error: null,
|
|
total: data.length,
|
|
page: 1,
|
|
limit: data.length,
|
|
hasmore: false,
|
|
origin: null,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 位置服务工厂函数
|
|
*/
|
|
export function createLocationResponse<T>(status: number, message: string, data: T[]): AkReqResponse<Array<T>> {
|
|
return mockAkArrayResponse<T>(data, status);
|
|
}
|
|
|
|
/**
|
|
* 位置服务错误响应工厂函数
|
|
*/
|
|
export function createLocationErrorResponse<T>(message: string): AkReqResponse<Array<T>> {
|
|
return createLocationResponse<T>(500, message, [])
|
|
}
|
|
|
|
/**
|
|
* 手环位置服务类
|
|
*/
|
|
export class LocationService {
|
|
/**
|
|
* 获取设备当前位置信息
|
|
*/
|
|
static async getCurrentLocation(deviceId: string): Promise<AkReqResponse<LocationInfo>> {
|
|
try {
|
|
// 模拟API调用延迟
|
|
await new Promise<void>((resolve) => {
|
|
setTimeout(() => {
|
|
resolve()
|
|
}, 1000)
|
|
})
|
|
|
|
// 模拟数据
|
|
const mockBaseStation: BaseStationInfo = {
|
|
id: 'BS001',
|
|
name: '教学楼A栋基站',
|
|
location: '教学楼A栋1层',
|
|
longitude: 116.397428,
|
|
latitude: 39.90923,
|
|
signalStrength: -45,
|
|
isOnline: true,
|
|
range: 50
|
|
}
|
|
const mockLocation: LocationInfo[] = [{
|
|
deviceId: deviceId,
|
|
baseStation: mockBaseStation,
|
|
estimatedLocation: {
|
|
longitude: 116.397428,
|
|
latitude: 39.90923,
|
|
accuracy: 15
|
|
},
|
|
lastUpdate: new Date().toISOString(),
|
|
status: 'online'
|
|
}]
|
|
|
|
return createLocationResponse(200, '获取位置成功', mockLocation)
|
|
} catch (error) {
|
|
console.error('获取设备位置失败:', error)
|
|
return createLocationErrorResponse('获取位置信息失败')
|
|
}
|
|
}
|
|
/**
|
|
* 获取可用基站列表
|
|
*/
|
|
static async getBaseStations(): Promise<AkReqResponse<BaseStationInfo[]>> {
|
|
try {
|
|
await new Promise<void>((resolve) => {
|
|
setTimeout(() => {
|
|
resolve()
|
|
}, 800)
|
|
})
|
|
|
|
const mockBaseStations: BaseStationInfo[] = [
|
|
{
|
|
id: 'BS001',
|
|
name: '教学楼A栋基站',
|
|
location: '教学楼A栋1层',
|
|
longitude: 116.397428,
|
|
latitude: 39.90923,
|
|
signalStrength: -45,
|
|
isOnline: true,
|
|
range: 50
|
|
},
|
|
{
|
|
id: 'BS002',
|
|
name: '教学楼B栋基站',
|
|
location: '教学楼B栋2层',
|
|
longitude: 116.398428,
|
|
latitude: 39.91023,
|
|
signalStrength: -52,
|
|
isOnline: true,
|
|
range: 45
|
|
},
|
|
{
|
|
id: 'BS003',
|
|
name: '操场基站',
|
|
location: '学校操场',
|
|
longitude: 116.399428,
|
|
latitude: 39.91123,
|
|
signalStrength: -38,
|
|
isOnline: true,
|
|
range: 100
|
|
},
|
|
{
|
|
id: 'BS004',
|
|
name: '宿舍楼基站',
|
|
location: '学生宿舍1号楼',
|
|
longitude: 116.396428,
|
|
latitude: 39.90823,
|
|
signalStrength: -60,
|
|
isOnline: false,
|
|
range: 40
|
|
}
|
|
]
|
|
|
|
return createLocationResponse(200, '获取基站列表成功', mockBaseStations)
|
|
} catch (error) {
|
|
console.error('获取基站列表失败:', error)
|
|
return createLocationErrorResponse('获取基站列表失败')
|
|
}
|
|
}
|
|
/**
|
|
* 获取围栏列表
|
|
*/
|
|
static async getFences(deviceId: string): Promise<AkReqResponse<FenceInfo[]>> {
|
|
try {
|
|
await new Promise<void>((resolve) => {
|
|
setTimeout(() => {
|
|
resolve()
|
|
}, 600)
|
|
})
|
|
const mockFences: FenceInfo[] = [ {
|
|
id: 'FENCE001',
|
|
name: '校园安全区域',
|
|
type: 'circle',
|
|
center: {
|
|
longitude: 116.397928,
|
|
latitude: 39.90973
|
|
},
|
|
radius: 200,
|
|
isActive: true,
|
|
eventType: 'exit',
|
|
createdAt: '2024-01-15T10:00:00Z'
|
|
},
|
|
{
|
|
id: 'FENCE002',
|
|
name: '教学区域',
|
|
type: 'polygon',
|
|
points: [
|
|
{ longitude: 116.397000, latitude: 39.909000 },
|
|
{ longitude: 116.398000, latitude: 39.909000 },
|
|
{ longitude: 116.398000, latitude: 39.910000 },
|
|
{ longitude: 116.397000, latitude: 39.910000 }
|
|
],
|
|
isActive: true,
|
|
eventType: 'both',
|
|
createdAt: '2024-01-16T14:30:00Z'
|
|
},
|
|
{
|
|
id: 'FENCE003',
|
|
name: '危险区域',
|
|
type: 'circle',
|
|
center: {
|
|
longitude: 116.395428,
|
|
latitude: 39.90723
|
|
},
|
|
radius: 30,
|
|
isActive: true,
|
|
eventType: 'enter',
|
|
createdAt: '2024-01-17T09:15:00Z'
|
|
}
|
|
]
|
|
console.log(mockFences)
|
|
return createLocationResponse<FenceInfo>(200, '获取围栏列表成功', mockFences)
|
|
} catch (error) {
|
|
console.error('获取围栏列表失败:', error)
|
|
return createLocationErrorResponse('获取围栏列表失败')
|
|
}
|
|
}
|
|
/**
|
|
* 获取位置历史记录
|
|
*/
|
|
static async getLocationHistory(deviceId: string, startDate: string, endDate: string): Promise<AkReqResponse<LocationHistoryItem[]>> {
|
|
try {
|
|
await new Promise<void>((resolve) => {
|
|
setTimeout(() => {
|
|
resolve()
|
|
}, 1200)
|
|
})
|
|
|
|
const mockHistory: LocationHistoryItem[] = [
|
|
{
|
|
id: 'HIST001',
|
|
deviceId: deviceId,
|
|
baseStation: {
|
|
id: 'BS001',
|
|
name: '教学楼A栋基站',
|
|
location: '教学楼A栋1层',
|
|
longitude: 116.397428,
|
|
latitude: 39.90923,
|
|
signalStrength: -45,
|
|
isOnline: true,
|
|
range: 50
|
|
},
|
|
timestamp: '2024-01-20T08:30:00Z',
|
|
duration: 3600,
|
|
locationType: 'basestation'
|
|
},
|
|
{
|
|
id: 'HIST002',
|
|
deviceId: deviceId,
|
|
baseStation: {
|
|
id: 'BS003',
|
|
name: '操场基站',
|
|
location: '学校操场',
|
|
longitude: 116.399428,
|
|
latitude: 39.91123,
|
|
signalStrength: -38,
|
|
isOnline: true,
|
|
range: 100
|
|
},
|
|
timestamp: '2024-01-20T10:00:00Z',
|
|
duration: 2400,
|
|
locationType: 'basestation'
|
|
},
|
|
{
|
|
id: 'HIST003',
|
|
deviceId: deviceId,
|
|
baseStation: {
|
|
id: 'BS002',
|
|
name: '教学楼B栋基站',
|
|
location: '教学楼B栋2层',
|
|
longitude: 116.398428,
|
|
latitude: 39.91023,
|
|
signalStrength: -52,
|
|
isOnline: true,
|
|
range: 45
|
|
},
|
|
timestamp: '2024-01-20T14:20:00Z',
|
|
duration: 5400,
|
|
locationType: 'basestation'
|
|
}
|
|
]
|
|
|
|
return createLocationResponse(200, '获取位置历史成功', mockHistory)
|
|
} catch (error) {
|
|
console.error('获取位置历史失败:', error)
|
|
return createLocationErrorResponse('获取位置历史失败')
|
|
}
|
|
}
|
|
/**
|
|
* 获取围栏事件记录
|
|
*/
|
|
static async getFenceEvents(deviceId: string, limit: number = 10): Promise<AkReqResponse<FenceEvent[]>> {
|
|
try {
|
|
await new Promise<void>((resolve) => {
|
|
setTimeout(() => {
|
|
resolve()
|
|
}, 900)
|
|
})
|
|
const mockEvents: FenceEvent[] = [ {
|
|
id: 'EVENT001',
|
|
deviceId: deviceId,
|
|
fence: {
|
|
id: 'FENCE001',
|
|
name: '校园安全区域',
|
|
type: 'circle',
|
|
center: {
|
|
longitude: 116.397928,
|
|
latitude: 39.90973
|
|
},
|
|
radius: 200,
|
|
isActive: true,
|
|
eventType: 'exit',
|
|
createdAt: '2024-01-15T10:00:00Z'
|
|
},
|
|
eventType: 'enter',
|
|
timestamp: '2024-01-20T07:45:00Z',
|
|
location: {
|
|
longitude: 116.397528,
|
|
latitude: 39.90943
|
|
},
|
|
isRead: false
|
|
},
|
|
{
|
|
id: 'EVENT002',
|
|
deviceId: deviceId,
|
|
fence: {
|
|
id: 'FENCE002',
|
|
name: '教学区域',
|
|
type: 'polygon',
|
|
points: [
|
|
{ longitude: 116.397000, latitude: 39.909000 },
|
|
{ longitude: 116.398000, latitude: 39.909000 },
|
|
{ longitude: 116.398000, latitude: 39.910000 },
|
|
{ longitude: 116.397000, latitude: 39.910000 }
|
|
],
|
|
isActive: true,
|
|
eventType: 'both',
|
|
createdAt: '2024-01-16T14:30:00Z'
|
|
},
|
|
eventType: 'enter',
|
|
timestamp: '2024-01-20T08:30:00Z',
|
|
location: {
|
|
longitude: 116.397500,
|
|
latitude: 39.909500
|
|
},
|
|
isRead: true
|
|
}
|
|
]
|
|
|
|
return createLocationResponse(200, '获取围栏事件成功', mockEvents)
|
|
} catch (error) {
|
|
console.error('获取围栏事件失败:', error)
|
|
return createLocationErrorResponse('获取围栏事件失败')
|
|
}
|
|
}
|
|
/**
|
|
* 创建围栏
|
|
*/
|
|
static async createFence(fence: PartialFenceInfo): Promise<AkReqResponse<FenceInfo>> {
|
|
try {
|
|
await new Promise<void>((resolve) => {
|
|
setTimeout(() => {
|
|
resolve()
|
|
}, 1500)
|
|
})
|
|
|
|
const newFence: FenceInfo[] = [{
|
|
id: `FENCE${Date.now()}`,
|
|
name: fence.name ?? '新围栏',
|
|
type: fence.type ?? 'circle',
|
|
center: fence.center,
|
|
radius: fence.radius,
|
|
points: fence.points,
|
|
isActive: fence.isActive ?? true,
|
|
eventType: fence.eventType ?? 'both',
|
|
createdAt: new Date().toISOString()
|
|
}]
|
|
|
|
return createLocationResponse(200, '创建围栏成功', newFence)
|
|
} catch (error) {
|
|
console.error('创建围栏失败:', error)
|
|
return createLocationErrorResponse('创建围栏失败')
|
|
}
|
|
}
|
|
/**
|
|
* 更新围栏
|
|
*/
|
|
static async updateFence(fenceId: string, updates: PartialFenceInfo): Promise<AkReqResponse<FenceInfo>> {
|
|
try {
|
|
await new Promise<void>((resolve) => {
|
|
setTimeout(() => {
|
|
resolve()
|
|
}, 1200)
|
|
})
|
|
|
|
// 模拟更新成功
|
|
const updatedFence: FenceInfo[] = [{
|
|
id: fenceId,
|
|
name: updates.name ?? '更新的围栏',
|
|
type: updates.type ?? 'circle',
|
|
center: updates.center,
|
|
radius: updates.radius,
|
|
points: updates.points,
|
|
isActive: updates.isActive ?? true,
|
|
eventType: updates.eventType ?? 'both',
|
|
createdAt: '2024-01-15T10:00:00Z'
|
|
}]
|
|
|
|
return createLocationResponse(200, '更新围栏成功', updatedFence)
|
|
} catch (error) {
|
|
console.error('更新围栏失败:', error)
|
|
return createLocationErrorResponse('更新围栏失败')
|
|
}
|
|
}
|
|
/**
|
|
* 删除围栏
|
|
*/
|
|
static async deleteFence(fenceId: string): Promise<AkReqResponse<Array<boolean>>> {
|
|
try {
|
|
await new Promise<void>((resolve) => {
|
|
setTimeout(() => {
|
|
resolve()
|
|
}, 800)
|
|
})
|
|
|
|
return createLocationResponse(200, '删除围栏成功', [true])
|
|
} catch (error) {
|
|
console.error('删除围栏失败:', error)
|
|
return createLocationErrorResponse('删除围栏失败')
|
|
}
|
|
}
|
|
/**
|
|
* 标记围栏事件为已读
|
|
*/
|
|
static async markEventAsRead(eventId: string): Promise<AkReqResponse<Array<boolean>>> {
|
|
try {
|
|
await new Promise<void>((resolve) => {
|
|
setTimeout(() => {
|
|
resolve()
|
|
}, 500)
|
|
})
|
|
|
|
return createLocationResponse(200, '标记事件成功', [true])
|
|
} catch (error) {
|
|
console.error('标记事件失败:', error)
|
|
return createLocationErrorResponse('标记事件失败')
|
|
}
|
|
}
|
|
}
|