mirror of
https://gitee.com/san-bing/JChargePointProtocol
synced 2026-05-05 10:29:56 +08:00
* !44 comment * !39 添加下行日志打印 * !36 扩展计价领域模型 * !35 webui 初步成型 * !34 webui 初步成型
This commit is contained in:
176
jcpp-web-ui/src/services/api.ts
Normal file
176
jcpp-web-ui/src/services/api.ts
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* 开源代码,仅供学习和交流研究使用,商用请联系三丙
|
||||
* 微信:mohan_88888
|
||||
* 抖音:程序员三丙
|
||||
* 付费课程知识星球:https://t.zsxq.com/aKtXo
|
||||
*/
|
||||
import axios, {AxiosError, AxiosResponse} from 'axios';
|
||||
import {message} from 'antd';
|
||||
|
||||
// 创建axios实例
|
||||
const api = axios.create({
|
||||
baseURL: process.env.REACT_APP_API_BASE_URL || 'http://localhost:8080',
|
||||
timeout: 10000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json;charset=UTF-8',
|
||||
'Accept': 'application/json;charset=UTF-8'
|
||||
}
|
||||
});
|
||||
|
||||
// 请求拦截器
|
||||
api.interceptors.request.use(
|
||||
(config) => {
|
||||
const token = localStorage.getItem('token');
|
||||
console.log('Request interceptor - Token:', token ? 'exists' : 'missing'); // 调试日志
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
console.log('Authorization header set:', config.headers.Authorization); // 调试日志
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// 根据HTTP状态码返回友好的错误提示
|
||||
const getErrorMessage = (error: AxiosError): string => {
|
||||
if (!error.response) {
|
||||
return '网络连接失败,请检查网络连接';
|
||||
}
|
||||
|
||||
const status = error.response.status;
|
||||
const data = error.response.data as any;
|
||||
|
||||
// 优先使用ApiResponse格式的错误信息
|
||||
const apiErrorMessage = data?.message;
|
||||
const errorCode = data?.errorCode;
|
||||
|
||||
// 根据错误码提供特殊处理
|
||||
if (errorCode) {
|
||||
switch (errorCode) {
|
||||
// 通用错误码
|
||||
case 'BUSINESS_ERROR':
|
||||
return apiErrorMessage || '业务处理失败';
|
||||
case 'SYSTEM_ERROR':
|
||||
return apiErrorMessage || '系统错误';
|
||||
|
||||
// 参数校验相关
|
||||
case 'VALIDATION_ERROR':
|
||||
return apiErrorMessage || '数据验证失败';
|
||||
case 'BINDING_ERROR':
|
||||
return apiErrorMessage || '数据绑定失败';
|
||||
case 'ILLEGAL_ARGUMENT':
|
||||
return apiErrorMessage || '参数错误';
|
||||
case 'ILLEGAL_STATE':
|
||||
return apiErrorMessage || '状态错误';
|
||||
|
||||
// 认证授权相关
|
||||
case 'UNAUTHORIZED':
|
||||
return apiErrorMessage || '用户未认证';
|
||||
case 'AUTH_FAILED':
|
||||
return apiErrorMessage || '用户名或密码错误';
|
||||
case 'JWT_AUTH_FAILED':
|
||||
return apiErrorMessage || 'Token认证失败';
|
||||
case 'FORBIDDEN':
|
||||
return apiErrorMessage || '权限不足';
|
||||
|
||||
// 资源相关
|
||||
case 'NOT_FOUND':
|
||||
return apiErrorMessage || '资源不存在';
|
||||
case 'CONFLICT':
|
||||
return apiErrorMessage || '资源冲突';
|
||||
|
||||
// 业务特定错误码
|
||||
case 'PILE_CODE_EXISTS':
|
||||
return apiErrorMessage || '充电桩编码已存在';
|
||||
case 'STATION_NAME_EXISTS':
|
||||
return apiErrorMessage || '充电站名称已存在';
|
||||
case 'GUN_CODE_EXISTS':
|
||||
return apiErrorMessage || '充电枪编号已存在';
|
||||
case 'PILE_NOT_FOUND':
|
||||
return apiErrorMessage || '充电桩不存在';
|
||||
case 'STATION_NOT_FOUND':
|
||||
return apiErrorMessage || '充电站不存在';
|
||||
case 'GUN_NOT_FOUND':
|
||||
return apiErrorMessage || '充电枪不存在';
|
||||
|
||||
default:
|
||||
// 对于未知错误码,继续使用消息内容
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 根据HTTP状态码提供后备错误信息
|
||||
switch (status) {
|
||||
case 400:
|
||||
return apiErrorMessage || '请求参数错误,请检查输入信息';
|
||||
case 401:
|
||||
return apiErrorMessage || '未授权,请重新登录';
|
||||
case 403:
|
||||
return apiErrorMessage || '没有权限执行此操作';
|
||||
case 404:
|
||||
return apiErrorMessage || '请求的资源不存在';
|
||||
case 409:
|
||||
return apiErrorMessage || '数据冲突,请刷新后重试';
|
||||
case 422:
|
||||
return apiErrorMessage || '数据验证失败,请检查输入信息';
|
||||
case 500:
|
||||
return apiErrorMessage || '服务器内部错误,请稍后重试';
|
||||
case 502:
|
||||
return '服务器网关错误,请稍后重试';
|
||||
case 503:
|
||||
return '服务不可用,请稍后重试';
|
||||
case 504:
|
||||
return '服务器响应超时,请稍后重试';
|
||||
default:
|
||||
return apiErrorMessage || `操作失败(状态码: ${status})`;
|
||||
}
|
||||
};
|
||||
|
||||
// 响应拦截器
|
||||
api.interceptors.response.use(
|
||||
(response: AxiosResponse) => {
|
||||
return response;
|
||||
},
|
||||
(error: AxiosError) => {
|
||||
if (error.response) {
|
||||
const { status } = error.response;
|
||||
const config = error.config;
|
||||
const data = error.response.data as any;
|
||||
const apiErrorMessage = data?.message;
|
||||
// const errorCode = data?.errorCode; // 暂时不在拦截器中使用errorCode
|
||||
|
||||
// 如果是登录接口的401错误,不进行全局处理,让组件自己处理
|
||||
if (status === 401 && config?.url?.includes('/api/auth/login')) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case 401:
|
||||
message.error(apiErrorMessage || '未授权,请重新登录');
|
||||
localStorage.removeItem('token');
|
||||
window.location.href = '/login';
|
||||
break;
|
||||
case 403:
|
||||
message.error(apiErrorMessage || '没有权限访问');
|
||||
break;
|
||||
case 404:
|
||||
message.error(apiErrorMessage || '请求的资源不存在');
|
||||
break;
|
||||
default:
|
||||
// 对于其他错误(包括500),不在拦截器中显示消息,让组件自己处理
|
||||
break;
|
||||
}
|
||||
} else if (error.request) {
|
||||
message.error('网络错误,请检查网络连接');
|
||||
} else {
|
||||
message.error('请求配置错误');
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
export { getErrorMessage, api };
|
||||
export default api;
|
||||
69
jcpp-web-ui/src/services/dashboardService.ts
Normal file
69
jcpp-web-ui/src/services/dashboardService.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 开源代码,仅供学习和交流研究使用,商用请联系三丙
|
||||
* 微信:mohan_88888
|
||||
* 抖音:程序员三丙
|
||||
* 付费课程知识星球:https://t.zsxq.com/aKtXo
|
||||
*/
|
||||
import {api} from './api';
|
||||
|
||||
/**
|
||||
* 总览统计
|
||||
*/
|
||||
export interface Overview {
|
||||
totalStations: number; // 总充电站数
|
||||
totalPiles: number; // 总充电桩数
|
||||
totalGuns: number; // 总充电枪数
|
||||
}
|
||||
|
||||
/**
|
||||
* 充电桩在线状态分布
|
||||
*/
|
||||
export interface PileStatusDistribution {
|
||||
onlinePiles: number; // 在线充电桩数
|
||||
offlinePiles: number; // 离线充电桩数
|
||||
totalPiles: number; // 总充电桩数
|
||||
onlinePercentage: number; // 在线百分比
|
||||
offlinePercentage: number; // 离线百分比
|
||||
}
|
||||
|
||||
/**
|
||||
* 充电枪运行状态分布
|
||||
*/
|
||||
export interface GunStatusDistribution {
|
||||
idleGuns: number; // 空闲 (IDLE)
|
||||
insertedGuns: number; // 已插枪未充电 (INSERTED)
|
||||
chargingGuns: number; // 充电中 (CHARGING)
|
||||
chargeCompleteGuns: number; // 充电完成 (CHARGE_COMPLETE)
|
||||
dischargeReadyGuns: number; // 放电准备 (DISCHARGE_READY)
|
||||
dischargingGuns: number; // 放电中 (DISCHARGING)
|
||||
dischargeCompleteGuns: number; // 放电完成 (DISCHARGE_COMPLETE)
|
||||
reservedGuns: number; // 预约 (RESERVED)
|
||||
faultGuns: number; // 故障 (FAULT)
|
||||
totalGuns: number; // 总充电枪数
|
||||
idlePercentage: number; // 空闲百分比
|
||||
insertedPercentage: number; // 已插枪百分比
|
||||
chargingPercentage: number; // 充电中百分比
|
||||
chargeCompletePercentage: number; // 充电完成百分比
|
||||
dischargeReadyPercentage: number; // 放电准备百分比
|
||||
dischargingPercentage: number; // 放电中百分比
|
||||
dischargeCompletePercentage: number; // 放电完成百分比
|
||||
reservedPercentage: number; // 预约百分比
|
||||
faultPercentage: number; // 故障百分比
|
||||
}
|
||||
|
||||
/**
|
||||
* 仪表盘统计数据
|
||||
*/
|
||||
export interface DashboardStats {
|
||||
overview: Overview;
|
||||
pileStatusDistribution: PileStatusDistribution;
|
||||
gunStatusDistribution: GunStatusDistribution;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取仪表盘统计数据
|
||||
*/
|
||||
export const getDashboardStats = async (): Promise<DashboardStats> => {
|
||||
const response = await api.get('/api/dashboard/stats');
|
||||
return response.data.data;
|
||||
};
|
||||
33
jcpp-web-ui/src/services/gunService.ts
Normal file
33
jcpp-web-ui/src/services/gunService.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 开源代码,仅供学习和交流研究使用,商用请联系三丙
|
||||
* 微信:mohan_88888
|
||||
* 抖音:程序员三丙
|
||||
* 付费课程知识星球:https://t.zsxq.com/aKtXo
|
||||
*/
|
||||
import {api} from './api';
|
||||
import type {Gun, GunCreateRequest, PageResponse, QueryParams} from '../types';
|
||||
|
||||
export const getGuns = async (params: QueryParams): Promise<PageResponse<Gun>> => {
|
||||
const response = await api.get('/api/guns', { params });
|
||||
return response.data.data;
|
||||
};
|
||||
|
||||
export const createGun = async (data: GunCreateRequest): Promise<Gun> => {
|
||||
const response = await api.post('/api/guns', data);
|
||||
return response.data.data;
|
||||
};
|
||||
|
||||
export const updateGun = async (id: string, data: Partial<Gun>): Promise<Gun> => {
|
||||
const response = await api.put(`/api/guns/${id}`, data);
|
||||
return response.data.data;
|
||||
};
|
||||
|
||||
export const deleteGun = async (id: string): Promise<void> => {
|
||||
await api.delete(`/api/guns/${id}`);
|
||||
};
|
||||
|
||||
export const getGun = async (id: string): Promise<Gun> => {
|
||||
const response = await api.get(`/api/guns/${id}`);
|
||||
return response.data.data;
|
||||
};
|
||||
|
||||
71
jcpp-web-ui/src/services/pileService.ts
Normal file
71
jcpp-web-ui/src/services/pileService.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 开源代码,仅供学习和交流研究使用,商用请联系三丙
|
||||
* 微信:mohan_88888
|
||||
* 抖音:程序员三丙
|
||||
* 付费课程知识星球:https://t.zsxq.com/aKtXo
|
||||
*/
|
||||
import api from './api';
|
||||
import {
|
||||
ApiResponse,
|
||||
PageResponse,
|
||||
Pile,
|
||||
PileCreateRequest,
|
||||
PileOption,
|
||||
PileQueryRequest,
|
||||
PileUpdateRequest
|
||||
} from '../types';
|
||||
|
||||
// 增强的API响应类型,包含HTTP状态码
|
||||
export interface EnhancedApiResponse<T> extends ApiResponse<T> {
|
||||
httpStatus: number;
|
||||
}
|
||||
|
||||
// 充电桩相关API
|
||||
export const pileService = {
|
||||
// 分页查询充电桩
|
||||
async getPiles(params: PileQueryRequest): Promise<ApiResponse<PageResponse<Pile>>> {
|
||||
console.log('🔍 前端发送的查询参数:', params); // 添加调试日志
|
||||
const response = await api.get('/api/piles', { params });
|
||||
console.log('📡 后端返回的响应:', response.data); // 添加调试日志
|
||||
return response.data;
|
||||
},
|
||||
|
||||
// 根据ID获取充电桩详情
|
||||
async getPile(id: string): Promise<ApiResponse<Pile>> {
|
||||
const response = await api.get(`/api/piles/${id}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
// 创建充电桩
|
||||
async createPile(data: PileCreateRequest): Promise<EnhancedApiResponse<Pile>> {
|
||||
const response = await api.post('/api/piles', data);
|
||||
return {
|
||||
...response.data,
|
||||
httpStatus: response.status
|
||||
};
|
||||
},
|
||||
|
||||
// 更新充电桩
|
||||
async updatePile(id: string, data: PileUpdateRequest): Promise<EnhancedApiResponse<Pile>> {
|
||||
const response = await api.put(`/api/piles/${id}`, data);
|
||||
return {
|
||||
...response.data,
|
||||
httpStatus: response.status
|
||||
};
|
||||
},
|
||||
|
||||
// 删除充电桩
|
||||
async deletePile(id: string): Promise<EnhancedApiResponse<void>> {
|
||||
const response = await api.delete(`/api/piles/${id}`);
|
||||
return {
|
||||
...response.data,
|
||||
httpStatus: response.status
|
||||
};
|
||||
},
|
||||
|
||||
// 获取充电桩选项列表
|
||||
async getPileOptions(): Promise<ApiResponse<PileOption[]>> {
|
||||
const response = await api.get('/api/piles/options');
|
||||
return response.data;
|
||||
}
|
||||
};
|
||||
22
jcpp-web-ui/src/services/protocolService.ts
Normal file
22
jcpp-web-ui/src/services/protocolService.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 开源代码,仅供学习和交流研究使用,商用请联系三丙
|
||||
* 微信:mohan_88888
|
||||
* 抖音:程序员三丙
|
||||
* 付费课程知识星球:https://t.zsxq.com/aKtXo
|
||||
*/
|
||||
import {api} from './api';
|
||||
|
||||
// 协议选项接口
|
||||
export interface ProtocolOption {
|
||||
value: string; // 协议标识符
|
||||
label: string; // 显示名称
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有支持的协议列表
|
||||
* @returns 协议选项列表
|
||||
*/
|
||||
export const getSupportedProtocols = async (): Promise<ProtocolOption[]> => {
|
||||
const response = await api.get('/api/protocols/supported');
|
||||
return response.data.data || [];
|
||||
};
|
||||
42
jcpp-web-ui/src/services/stationService.ts
Normal file
42
jcpp-web-ui/src/services/stationService.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 开源代码,仅供学习和交流研究使用,商用请联系三丙
|
||||
* 微信:mohan_88888
|
||||
* 抖音:程序员三丙
|
||||
* 付费课程知识星球:https://t.zsxq.com/aKtXo
|
||||
*/
|
||||
import {api} from './api';
|
||||
import type {PageResponse, QueryParams, Station, StationOption, StationSearchRequest} from '../types';
|
||||
|
||||
export const getStations = async (params: QueryParams): Promise<PageResponse<Station>> => {
|
||||
const response = await api.get('/api/stations', { params });
|
||||
return response.data.data;
|
||||
};
|
||||
|
||||
export const createStation = async (data: Partial<Station>): Promise<Station> => {
|
||||
const response = await api.post('/api/stations', data);
|
||||
return response.data.data;
|
||||
};
|
||||
|
||||
export const updateStation = async (id: string, data: Partial<Station>): Promise<Station> => {
|
||||
const response = await api.put(`/api/stations/${id}`, data);
|
||||
return response.data.data;
|
||||
};
|
||||
|
||||
export const deleteStation = async (id: string): Promise<void> => {
|
||||
await api.delete(`/api/stations/${id}`);
|
||||
};
|
||||
|
||||
export const getStation = async (id: string): Promise<Station> => {
|
||||
const response = await api.get(`/api/stations/${id}`);
|
||||
return response.data.data;
|
||||
};
|
||||
|
||||
export const searchStationOptions = async (params: StationSearchRequest): Promise<StationOption[]> => {
|
||||
const response = await api.get('/api/stations/search', { params });
|
||||
return response.data.data;
|
||||
};
|
||||
|
||||
export const getStationOptions = async (): Promise<StationOption[]> => {
|
||||
const response = await api.get('/api/stations/options');
|
||||
return response.data.data;
|
||||
};
|
||||
Reference in New Issue
Block a user