first commit

This commit is contained in:
2025-10-16 21:32:16 +08:00
commit c446df73b5
229 changed files with 499497 additions and 0 deletions

View File

@@ -0,0 +1,916 @@
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();