Files
WXProgram/miniprogram/services/apiService.ts

917 lines
25 KiB
TypeScript
Raw Normal View History

2025-10-16 21:32:16 +08:00
import { WarehouseInfo, UserInfo, DeliveryPerson, Order, EmployeeInfo } from '../types';
/**
* API服务基类
* API交互的核心功能
*/
// API基础URL配置
const IS_LOCAL_DEV = false; // true: 本地开发环境, false: 生产环境
const API_BASE_URL = IS_LOCAL_DEV ? 'http://localhost:8080' : 'https://www.doubleyin.cn';
console.log(`当前API地址: ${API_BASE_URL} (${IS_LOCAL_DEV ? '本地开发环境' : '生产环境'})`);
/**
*
* 使
*/
export const isMockMode = false;
/**
* API服务类
* API交互的方法
*/
class ApiService {
/**
*
*/
private locationUpdateCallbacks: Set<(location: any) => void> | null;
/**
* WebSocket连接
*/
private locationWebSocket: any | null;
/**
*
*/
constructor() {
this.locationUpdateCallbacks = null;
this.locationWebSocket = null;
}
/**
*
* @param endpoint API端点路径
* @param options
* @returns Promise<T>
*/
private async request<T>(endpoint: string, options: {
method?: string;
data?: any;
headers?: Record<string, string>
} = {}): Promise<T> {
const requestUrl = `${API_BASE_URL}${endpoint}`;
// 获取全局token
const app = getApp<any>();
const token = app.globalData.token;
// 构建请求头自动添加Authorization头如果存在token
const headers: Record<string, string> = {
'Content-Type': 'application/json',
...options.headers,
};
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
const requestOptions = {
method: options.method || 'GET',
data: options.data,
header: headers,
};
// 打印请求信息
console.log('\n========== API Request ==========');
console.log(`Method: ${requestOptions.method}`);
console.log(`URL: ${requestUrl}`);
console.log(`Headers:`, requestOptions.header);
console.log(`Data:`, requestOptions.data);
console.log('================================');
return new Promise((resolve, reject) => {
wx.request({
url: requestUrl,
method: requestOptions.method as any,
data: requestOptions.data,
header: requestOptions.header,
success: (res: any) => {
// 打印响应信息
console.log('\n========== API Response ==========');
console.log(`URL: ${requestUrl}`);
console.log(`Status: ${res.statusCode}`);
console.log(`Response Data:`, res.data);
console.log('==================================');
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve(res.data);
} else {
console.error(`API Error: HTTP ${res.statusCode}: ${res.errMsg}`);
reject(new Error(`HTTP ${res.statusCode}: ${res.errMsg}`));
}
},
fail: (err: any) => {
console.error('\n========== API Request Failed ==========');
console.error(`URL: ${requestUrl}`);
console.error(`Error: ${err.errMsg}`);
console.error('=======================================');
reject(new Error(`Request failed: ${err.errMsg}`));
}
});
});
}
// ==================== 用户模块 ====================
// ====== 认证相关 ======
/**
*
* @param code
* @returns
*/
async ServerLogin(code: string) {
console.log('API wxLogin调用参数code:', code);
return await this.request<{ user: UserInfo; token: string; openid: string; session_key: string }>('/user/wxlogin', {
method: 'POST',
data: { code: code },
});
}
/**
*
* @param userId ID
* @returns
*/
async userSignIn(userId: number): Promise<{ success: boolean; employeeInfo: EmployeeInfo; message?: string }> {
console.log('API userSignIn调用参数userId:', userId);
// 服务器返回的是用户对象,需要转换为前端期望的格式
const response = await this.request<any>('/user/signin', {
method: 'POST',
data: { userId },
});
// 转换响应格式
return {
success: true,
employeeInfo: {
id: response.id,
name: response.name,
phone: response.phone,
role: response.role || 'DELIVERY_PERSON'
},
message: '签到成功'
};
}
/**
*
* @param userInfo
* @returns
*/
async userRegister(userInfo: { name: string; phone: string }): Promise<{ success: boolean; employeeInfo: EmployeeInfo; message?: string }> {
console.log('API userRegister调用参数userInfo:', userInfo);
// 服务器返回的是用户对象,需要转换为前端期望的格式
const response = await this.request<any>('/user/register', {
method: 'POST',
data: userInfo,
});
// 转换响应格式
return {
success: true,
employeeInfo: {
id: response.id,
name: response.name || userInfo.name, // 如果服务器返回null使用用户输入的值
phone: response.phone || userInfo.phone, // 如果服务器返回null使用用户输入的值
role: response.role || 'DELIVERY_PERSON'
},
message: '注册成功'
};
}
/**
*
*/
async logout(): Promise<void> {
return await this.request('/user/logout', { method: 'POST' });
}
/**
*
* @returns
*/
async getUserStatus(): Promise<{ status: 'signed_in' | 'signed_out' | 'registered' | 'unregistered'; lastSignInTime?: string; lastSignOutTime?: string }> {
return await this.request('/user/status', { method: 'GET' });
}
// ====== 信息相关 ======
/**
*
* @returns
*/
async getUserInfo() {
return await this.request<UserInfo>('/user/info');
}
/**
*
* @param userInfo
* @returns
*/
async updateUserInfo(userInfo: Partial<UserInfo>): Promise<UserInfo> {
return await this.request<UserInfo>('/user/update', {
method: 'PUT',
data: userInfo,
});
}
/**
*
* @returns
*/
async getUserPermissions(): Promise<string[]> {
return await this.request<string[]>('/user/permissions');
}
/**
* 线
* @returns 线
*/
async checkUserOnline(): Promise<boolean> {
return await this.request<boolean>('/user/online');
}
/**
*
* @returns
*/
async getSessionInfo(): Promise<{
userId: number;
sessionId: string;
expiresAt: number;
permissions: string[];
}> {
return await this.request<{
userId: number;
sessionId: string;
expiresAt: number;
permissions: string[];
}>('/user/session');
}
/**
*
* @param oldToken
* @returns
*/
async refreshToken(oldToken: string): Promise<{ token: string; expiresAt: number }> {
return await this.request<{ token: string; expiresAt: number }>('/user/refresh-token', {
method: 'POST',
data: { token: oldToken },
});
}
/**
*
* @param permission
* @returns
*/
async verifyPermission(permission: string): Promise<boolean> {
return await this.request<boolean>(`/user/verify-permission?permission=${permission}`);
}
// ====== 管理相关 ======
/**
*
* @param query
* @param page
* @param pageSize
* @returns
*/
async searchUsers(query: string, page: number = 1, pageSize: number = 20): Promise<{
users: UserInfo[];
total: number;
page: number;
pageSize: number;
}> {
return await this.request<{ users: UserInfo[]; total: number; page: number; pageSize: number }>(
`/user/search?query=${query}&page=${page}&pageSize=${pageSize}`
);
}
/**
*
* @param userIds ID列表
* @param status
*/
async batchUpdateUserStatus(userIds: number[], status: 'active' | 'inactive' | 'suspended'): Promise<void> {
return await this.request('/user/batch-update-status', {
method: 'POST',
data: { userIds, status },
});
}
// ==================== 仓库相关接口 ====================
/**
*
* @returns
*/
async getWarehouses(): Promise<WarehouseInfo[]> {
return await this.request<WarehouseInfo[]>('/warehouses');
}
/**
*
* @param id ID
* @returns
*/
async getWarehouseById(id: number): Promise<WarehouseInfo> {
return await this.request<WarehouseInfo>(`/warehouses/${id}`);
}
/**
*
* @param warehouseId ID
* @returns
*/
async getWarehouseOrders(warehouseId: number): Promise<Order[]> {
return await this.request<Order[]>(`/warehouses/${warehouseId}/orders`);
}
// ==================== 货运人员接口 ====================
/**
*
* @returns
*/
async getDeliveryPersons(): Promise<DeliveryPerson[]> {
return await this.request<DeliveryPerson[]>('/delivery-persons');
}
/**
*
* @returns
*/
async getAllDeliveryPersonLocations(): Promise<Array<{
deliveryPersonId: number;
name: string;
latitude: number;
longitude: number;
status: string;
}>> {
return await this.request<Array<{
deliveryPersonId: number;
name: string;
latitude: number;
longitude: number;
status: string;
}>>('/location-sync/delivery-persons/locations');
}
/**
*
* @param id ID
* @returns
*/
async getDeliveryPersonById(id: number): Promise<DeliveryPerson> {
return await this.request<DeliveryPerson>(`/delivery-persons/${id}`);
}
/**
*
* @param id ID
* @param location
*/
async updateDeliveryPersonLocation(id: number, location: { longitude: number; latitude: number }): Promise<void> {
return await this.request(`/delivery-persons/${id}/location`, {
method: 'PUT',
data: location,
});
}
// 地图相关接口支持
/**
* mapService参数
* @param locations
*/
async batchUpdateDeliveryPersonLocations(locations: Array<{
userId: number;
latitude: number;
longitude: number;
timestamp: number;
}>): Promise<void> {
// 转换参数格式以匹配原有API
const formattedLocations = locations.map(loc => ({
deliveryPersonId: loc.userId,
longitude: loc.longitude,
latitude: loc.latitude,
timestamp: loc.timestamp
}));
await this.request('/delivery-persons/locations/batch', {
method: 'POST',
data: { locations: formattedLocations }
});
}
/**
*
* @returns
*/
async getIdleDeliveryPersons(): Promise<DeliveryPerson[]> {
return await this.request<DeliveryPerson[]>('/delivery-persons/idle');
}
/**
*
* @returns
*/
async getBusyDeliveryPersons(): Promise<DeliveryPerson[]> {
return await this.request<DeliveryPerson[]>('/delivery-persons/busy');
}
/**
*
* @param deliveryPersonId ID
* @param limit
* @returns
*/
async getLocationHistory(deliveryPersonId: number, limit: number = 50): Promise<Array<{
timestamp: number;
longitude: number;
latitude: number;
speed?: number;
accuracy?: number;
}>> {
return await this.request<Array<{
timestamp: number;
longitude: number;
latitude: number;
speed?: number;
accuracy?: number;
}>>(`/delivery-persons/${deliveryPersonId}/location-history?limit=${limit}`);
}
/**
* ID列表
* @param deliveryPersonIds ID列表
* @returns
*/
async subscribeToRealTimeLocations(deliveryPersonIds: number[]): Promise<{
subscriptionId: string;
expiresAt: number;
}> {
if (isMockMode) {
// 模拟数据 - 返回模拟的订阅信息
console.log('[MOCK] 订阅实时位置更新货运人员ID列表:', deliveryPersonIds);
return {
subscriptionId: `mock-subscription-${Date.now()}`,
expiresAt: Date.now() + 3600000 // 1小时后过期
};
}
// 真实环境中调用API
try {
return await this.request<{
subscriptionId: string;
expiresAt: number;
}>('/locations/subscribe', {
method: 'POST',
data: { deliveryPersonIds }
});
} catch (error) {
console.error('订阅实时位置更新失败:', error);
throw error;
}
}
/**
*
* @param subscriptionId ID
*/
async unsubscribeFromRealTimeLocations(subscriptionId: string): Promise<void> {
if (isMockMode) {
// 模拟数据 - 记录取消订阅操作
console.log('[MOCK] 取消订阅实时位置更新订阅ID:', subscriptionId);
return;
}
// 真实环境中调用API
try {
await this.request<void>('/locations/unsubscribe', {
method: 'POST',
data: { subscriptionId }
});
} catch (error) {
console.error('取消订阅实时位置更新失败:', error);
throw error;
}
}
/**
*
* @param callback
* @returns
*/
onLocationUpdate(callback: (location: any) => void): () => void {
// 添加回调到集合
if (!this.locationUpdateCallbacks) {
this.locationUpdateCallbacks = new Set();
}
this.locationUpdateCallbacks.add(callback);
// 返回取消订阅函数
return () => {
if (this.locationUpdateCallbacks) {
this.locationUpdateCallbacks.delete(callback);
// 如果没有回调了可以关闭WebSocket连接
if (this.locationUpdateCallbacks.size === 0 && this.locationWebSocket) {
this.locationWebSocket.close();
this.locationWebSocket = null;
}
}
};
}
// ===== 私有属性和方法 =====
/**
* WebSocket连接
*/
public async initLocationWebSocket(): Promise<void> {
if (this.locationWebSocket) {
console.log('WebSocket连接已存在');
return;
}
try {
if (typeof wx !== 'undefined' && typeof wx.connectSocket === 'function') {
console.log('初始化位置更新WebSocket连接小程序环境');
// 连接到服务端的WebSocket地址使用配置的服务器地址
const serverUrl = API_BASE_URL.replace('http', 'ws');
const wsUrl = `${serverUrl}/ws/location`;
this.locationWebSocket = wx.connectSocket({
url: wsUrl,
header: {
'content-type': 'application/json'
}
});
// WebSocket连接打开事件
wx.onSocketOpen(() => {
console.log('WebSocket连接已打开');
});
// WebSocket消息接收事件
wx.onSocketMessage((res) => {
try {
const message = JSON.parse(typeof res.data === 'string' ? res.data : String(res.data));
this.handleWebSocketMessage(message);
} catch (error) {
console.error('解析WebSocket消息失败:', error);
}
});
// WebSocket连接关闭事件
wx.onSocketClose(() => {
console.log('WebSocket连接已关闭');
this.locationWebSocket = null;
});
// WebSocket错误事件
wx.onSocketError((error) => {
console.error('WebSocket连接错误:', error);
this.locationWebSocket = null;
});
} else {
console.warn('当前环境不支持WebSocket连接');
}
} catch (error) {
console.error('初始化位置更新WebSocket连接失败:', error);
this.locationWebSocket = null;
}
}
/**
* WebSocket连接
*/
public async closeLocationWebSocket(): Promise<void> {
if (this.locationWebSocket && typeof wx !== 'undefined' && typeof wx.closeSocket === 'function') {
wx.closeSocket();
this.locationWebSocket = null;
console.log('WebSocket连接已关闭');
}
}
/**
* WebSocket消息
* @param message
*/
private handleWebSocketMessage(message: any): void {
if (!message || typeof message.type !== 'string') {
console.warn('收到无效的WebSocket消息:', message);
return;
}
switch (message.type) {
case 'updateLocation':
// 处理位置更新消息
this.onLocationUpdate(message);
break;
default:
console.warn('收到未知类型的WebSocket消息:', message);
break;
}
}
/**
* WebSocket消息
* @param message
*/
private sendWebSocketMessage(message: any): void {
if (!this.locationWebSocket) {
console.warn('WebSocket连接未建立无法发送消息');
return;
}
try {
const messageStr = JSON.stringify(message);
if (typeof wx !== 'undefined' && typeof wx.sendSocketMessage === 'function') {
wx.sendSocketMessage({
data: messageStr
});
}
} catch (error) {
console.error('发送WebSocket消息失败:', error);
}
}
/**
*
* @param deliveryPersonId ID
*/
async subscribeToLocationUpdates(deliveryPersonId: number): Promise<void> {
if (isMockMode) {
console.log('[MOCK] 订阅位置更新');
return;
}
// 确保WebSocket连接已建立
if (!this.locationWebSocket) {
this.initLocationWebSocket();
}
// 发送订阅消息
const subscribeMessage = {
type: 'subscribe',
deliveryPersonId: deliveryPersonId,
timestamp: Date.now()
};
this.sendWebSocketMessage(subscribeMessage);
}
/**
*
* @param deliveryPersonId ID
*/
async unsubscribeFromLocationUpdates(deliveryPersonId: number): Promise<void> {
if (isMockMode) {
console.log('[MOCK] 取消订阅位置更新');
return;
}
// 发送取消订阅消息
const unsubscribeMessage = {
type: 'unsubscribe',
deliveryPersonId: deliveryPersonId,
timestamp: Date.now()
};
this.sendWebSocketMessage(unsubscribeMessage);
}
/**
*
* @param deliveryPersonId ID
* @param latitude
* @param longitude
*/
async sendLocationUpdate(deliveryPersonId: number, latitude: number, longitude: number): Promise<void> {
if (isMockMode) {
console.log('[MOCK] 发送位置更新');
return;
}
// 确保WebSocket连接已建立
if (!this.locationWebSocket) {
this.initLocationWebSocket();
}
// 发送位置更新消息
const locationMessage = {
type: 'updateLocation',
deliveryPersonId: deliveryPersonId,
latitude: latitude,
longitude: longitude,
timestamp: Date.now()
};
this.sendWebSocketMessage(locationMessage);
}
// ==================== 订单接口 ====================
/**
*
* @returns
*/
async getPendingOrders(): Promise<Order[]> {
return await this.request<Order[]>('/orders/pending');
}
/**
*
* @param orderId ID
* @param deliveryPersonId ID
*/
async assignOrder(orderId: number, deliveryPersonId: number): Promise<{ success: boolean; message?: string }> {
return await this.request<{ success: boolean; message?: string }>(`/orders/${orderId}/assign`, {
method: 'POST',
data: { deliveryPersonId },
});
}
/**
*
* @param deliveryPersonId ID
* @returns
*/
async getDeliveryPersonOrders(deliveryPersonId: number): Promise<Order[]> {
return await this.request<Order[]>(`/delivery-persons/${deliveryPersonId}/orders`);
}
/**
* ID获取订单
* @param id ID
* @returns
*/
async getOrderById(id: number): Promise<Order> {
return await this.request<Order>(`/orders/${id}`);
}
/**
*
* @param orderId ID
* @param status
*/
async updateOrderStatus(orderId: number, status: Order['status']): Promise<{ success: boolean; message?: string }> {
return await this.request<{ success: boolean; message?: string }>(`/orders/${orderId}/status`, {
method: 'PUT',
data: { status },
});
}
/**
*
* @param orderData
* @returns
*/
async createOrder(orderData: Omit<Order, 'id' | 'createTime'>): Promise<Order> {
return await this.request<Order>('/orders', {
method: 'POST',
data: orderData,
});
}
/**
*
* @param orderId ID
*/
async deleteOrder(orderId: number): Promise<void> {
return await this.request(`/orders/${orderId}`, {
method: 'DELETE',
});
}
// ==================== 统计和日志接口 ====================
/**
*
* @returns
*/
async getSystemStats(): Promise<{
totalWarehouses: number;
totalDeliveryPersons: number;
pendingOrders: number;
activeOrders: number;
}> {
return await this.request<{
totalWarehouses: number;
totalDeliveryPersons: number;
pendingOrders: number;
activeOrders: number;
}>('/stats/system');
}
/**
*
* @param timeRange
* @returns
*/
async getOrderStats(timeRange: 'today' | 'week' | 'month'): Promise<{
totalOrders: number;
completedOrders: number;
inProgressOrders: number;
averageDeliveryTime: number;
}> {
return await this.request<{
totalOrders: number;
completedOrders: number;
inProgressOrders: number;
averageDeliveryTime: number;
}>(`/stats/orders?timeRange=${timeRange}`);
}
/**
*
* @returns
*/
async getUserStats(): Promise<{
totalUsers: number;
activeUsers: number;
onlineUsers: number;
newUsersToday: number;
}> {
return await this.request<{
totalUsers: number;
activeUsers: number;
onlineUsers: number;
newUsersToday: number;
}>('/user/stats');
}
/**
*
* @param userId ID
* @param limit
* @returns
*/
async getUserActivityLogs(userId: number, limit: number = 20): Promise<Array<{
id: number;
action: string;
timestamp: number;
details?: any;
}>> {
return await this.request<Array<{ id: number; action: string; timestamp: number; details?: any }>>(
`/user/${userId}/activity-logs?limit=${limit}`
);
}
/**
*
* @param error
*/
async uploadErrorLog(error: any): Promise<void> {
await this.request('/logs/error', {
method: 'POST',
data: {
error: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined,
timestamp: Date.now(),
userAgent: 'miniprogram/' + (typeof wx !== 'undefined' ? wx.getSystemInfoSync().platform : 'unknown')
}
});
}
// ==================== 系统接口 ====================
/**
*
* @returns
*/
async healthCheck(): Promise<{
status: 'ok' | 'degraded' | 'down';
message: string;
timestamp: number;
}> {
return await this.request<{
status: 'ok' | 'degraded' | 'down';
message: string;
timestamp: number;
}>('/health');
}
}
/**
* API服务单例实例
* 使
*/
export default new ApiService();