Files
ele-HeiXiu/src/services/modules/auth.ts
YoungestSongMo 4237dcbaf6 feat: 数据获取架构重构 + Mac指纹采集 + Token刷新容错 + 账单页面 (0.0.19)
## 架构重构
  - useAsyncData 集成 AbortController:cleanup 时真正中断 HTTP 请求,
    不再仅丢弃响应(解决 StrictMode 双重请求问题)
  - API 模块统一 signal?: AbortSignal 参数,透传 axios config
  - use-api.ts 成为数据 Hook 集中管理中心,新增 useAccountInfo、
    useBillingBalance、useBillingLedger
  - 页面组件改为纯渲染:const {data,loading,error,refetch} = useXxx()

  ## 新功能
  - Mac 硬件指纹采集(electron/preload/device.ts):
    ioreg(UUID) + sysctl(CPU) + system_profiler(HDD三级回退)
  - 账单页面 BillingInfo(余额卡片 + 账单表格 + 分页)
  - PageDispatcher 注册 /billing 页面(FloatingPanel 960×680)
  - 共享工具函数 centToYuan / formatDate(src/utils/display.ts)

  ## Bug 修复
  - Token 刷新容错:网络错误时保留 refresh_token,等网络恢复后重试;
    仅 RefreshError(token 过期)时清除(auth-token.ts)
  - AppProvider 自动登录时 nullable 字段默认值(email→''、admin_permissions→[])
  - AccountInfo 重构消除 ~40 行手写状态管理代码

  ## 代码质量
  - AccountInfo 消除 centToYuan/formatDate 重复定义
  - BillingInfo 标题层级精简(移除冗余页面/Card标题)
  - antd Table align 字面量类型修正('left' as const)
  - billing.ts signal 参数位置修复(params→config)
  - modelFirstFetchDone 模块级变量移除(StrictMode 不安全)
2026-06-11 20:08:44 +08:00

219 lines
6.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {post, get} from '../request';
// ============================================================
// AuthUrlObj — 认证模块路由常量
// 集中管理,后续接口升级 v2 等只需改此处
// ============================================================
const AuthUrlObj = {
login: '/api/v1/auth/login',
register: '/api/v1/auth/register',
sendSms: '/api/v1/auth/send-sms',
refreshToken: '/api/v1/auth/refresh',
userInfo: '/api/v1/auth/me',
changePassword: '/api/v1/auth/change-password',
resetPassword: '/api/v1/auth/reset-password',
logout: '/api/v1/auth/logout',
} as const;
// ============================================================
// 认证相关 DTO 传输模型
// ============================================================
export const UserRoleTypeEnum = {
User: "user",
Admin: "admin",
SuperAdmin: "superadmin",
} as const;
export type UserRoleTypeValue = typeof UserRoleTypeEnum[keyof typeof UserRoleTypeEnum];
export const UserRoleTypeLabelMap: Record<UserRoleTypeValue, string> = {
[UserRoleTypeEnum.User]: "用户账户",
[UserRoleTypeEnum.Admin]: "管理账户",
[UserRoleTypeEnum.SuperAdmin]: "超管账户"
};
export const UserAccountTypeEnum = {
Individual: 'individual',
Company: 'company',
Employee: 'employee',
} as const;
export type UserAccountTypeValue = typeof UserAccountTypeEnum[keyof typeof UserAccountTypeEnum];
export const UserAccountTypeLabelMap: Record<UserAccountTypeValue, string> = {
[UserAccountTypeEnum.Individual]: "个人用户",
[UserAccountTypeEnum.Company]: "企业用户",
[UserAccountTypeEnum.Employee]: "员工账号"
};
/** 短信验证码请求体 */
export interface SendSMSRequestBody {
phone: string;
}
/** 注册请求体 */
export interface RegisterRequestBody {
// 用户名3~64 位,仅允许字母、数字、-、_
username: string;
// 密码6~128 位
password: string;
// 手机号5~32 位
phone: string;
// 短信验证码4~8 位
sms_code: string;
// 设备标识(网卡 MAC8~128 位
device_id: string;
// 设备标签/备注(可选)
device_label: string;
// 个人昵称(可选)
display_name: string;
// 邀请码(可选),填写后触发邀请佣金逻辑
referral_code: string;
}
export interface RegisterResponseBody {
access_token: string;
refresh_token: string;
token_type: string;
expires_in: 0;
refresh_expires_in: 0;
user_id: string;
username: string;
role: UserRoleTypeValue;
email: string;
balance_cent: 0;
account_type: UserAccountTypeValue;
display_name: string;
company_id: string;
real_name: string;
phone: string;
license_code: string;
admin_permissions: string []
}
/** 登录请求体 */
export interface LoginRequestBody {
username: string;
password: string;
device_id: string;
user_ip: string;
}
export interface LoginResponseBody {
access_token: string;
refresh_token: string;
token_type: string;
expires_in: number;
refresh_expires_in: number;
user_id: string;
username: string;
role: UserRoleTypeValue;
email: string;
balance_cent: number;
account_type: UserAccountTypeValue;
display_name: string;
company_id: string;
real_name: string;
phone: string;
license_code: string;
admin_permissions: string[];
}
export interface RefreshTokenRequestBody {
refresh_token: string;
}
export interface RefreshTokenResponseBody {
access_token: string;
refresh_token: string;
token_type: string;
expires_in: number;
refresh_expires_in: number;
}
export interface UserInfoBody {
id: string;
username: string;
email: string | null;
balance_cent: number;
gift_balance_cent: number;
role: UserRoleTypeValue;
status: string;
account_type: UserAccountTypeValue;
display_name: string | null;
real_name: string | null;
phone: string;
company_id: string | null;
discount_rate: number | null;
subscription_plan: string;
subscription_expires_at: Date;
seat_limit: number | null;
last_login_at: Date;
created_at: Date;
model_package_id: number;
vip_level_id: number;
software_expires_at: Date;
referral_code: string | null;
admin_permissions: string[] | null;
}
export interface ChangePasswordRequestBody {
current_password: string;
new_password: string;
}
export interface UsePhoneResetPasswordRequestBody {
phone: string;
sms_code: string;
new_password: string;
}
// ============================================================
// AuthAPI — 认证相关 API 静态方法类
// 调用方式AuthAPI.login() / AuthAPI.register() 等
// ============================================================
export class AuthAPI {
/** 登录 */
static async login(data: LoginRequestBody): Promise<LoginResponseBody> {
return await post<LoginResponseBody>(AuthUrlObj.login, data);
}
/** 注册 */
static async register(data: RegisterRequestBody): Promise<RegisterResponseBody> {
return await post<RegisterResponseBody>(AuthUrlObj.register, data);
}
/** 发送短信验证码 */
static async sendSms(data: SendSMSRequestBody): Promise<void> {
return await post<void>(AuthUrlObj.sendSms, data);
}
/** 刷新 Token */
static async refreshToken(data: RefreshTokenRequestBody): Promise<RefreshTokenResponseBody> {
return await post<RefreshTokenResponseBody>(AuthUrlObj.refreshToken, data);
}
/** 获取当前用户信息 */
static async getUserInfo(signal?: AbortSignal): Promise<UserInfoBody> {
return await get<UserInfoBody>(AuthUrlObj.userInfo, undefined, { signal });
}
/** 修改密码(已登录) */
static async changePassword(data: ChangePasswordRequestBody): Promise<void> {
return await post<void>(AuthUrlObj.changePassword, data);
}
/** 手机验证码重置密码 */
static async resetPassword(data: UsePhoneResetPasswordRequestBody): Promise<void> {
return await post<void>(AuthUrlObj.resetPassword, data);
}
/** 退出登录 */
static async logout(): Promise<void> {
return await post<void>(AuthUrlObj.logout);
}
}