Files
DP/tempdocs/设备与在线时长接口文档.md

107 lines
3.1 KiB
Markdown
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.
# 设备与在线时长接口
## 概述
- 基础地址:`http://localhost:8000`
- 版本前缀:`/api/v1`
- 说明:同一账号仅允许一个设备同时在线(依据 `device_id`);后端会记录登录登出并可统计在线时长。
## 登录(含单设备限制)
- 方法与路径:`POST /api/v1/auth/login`
- 请求体:
```json
{
"username": "alice",
"password": "secret123",
"device_id": "devA"
}
```
- 成功响应:
```json
{
"access_token": "<JWT>",
"token_type": "bearer",
"username": "alice"
}
```
- 失败响应:
- 401`用户名或密码错误`
- 403`该账号已在其他设备在线`
- 说明:
- 必须传入稳定的 `device_id`(前端生成并持久化),用于限制并发登录与统计时长
- 登录成功会记录会话的 `login_at`,并将设备会话标记为活跃
## 登出(记录在线时长)
- 方法与路径:`POST /api/v1/auth/logout`
- 请求体:
```json
{
"username": "alice",
"device_id": "devA"
}
```
- 成功响应:
```json
{ "detail": "已退出登录" }
```
- 行为:
- 将对应设备的会话置为非活跃,并记录 `logout_at`
- 若存在 `login_at`,会计算本次会话的在线时长 `duration_seconds`(单位:秒)
## 在线时长统计
- 方法与路径:`GET /api/v1/auth/online-time/{username}`
- 成功响应:
```json
{
"username": "alice",
"total_seconds": 1234,
"active_seconds": 56
}
```
- 字段说明:
- `total_seconds`:历史所有已登出会话的累计在线时长(秒)
- `active_seconds`:当前活跃会话的实时在线时长(秒),若无活跃会话则为 0实时时长基于最近心跳时间 `last_seen_at``login_at` 的差值
- 统计逻辑:
- 登录时写入 `login_at`UTC
- 登出时写入 `logout_at` 并计算 `duration_seconds`
- 查询时将已登出会话的 `duration_seconds` 累加为 `total_seconds`,并以当前时间与活跃会话的 `login_at` 计算 `active_seconds`
## 心跳接口(保持在线时长统计)
- 方法与路径:`POST /api/v1/auth/heartbeat`
- 请求体:
```json
{
"username": "alice",
"device_id": "devA"
}
```
- 成功响应:
```json
{ "detail": "心跳已更新" }
```
- 说明:
- 前端应在用户在线期间定期调用心跳(例如每 3060 秒),以更新会话的 `last_seen_at`
- 若未调用登出而直接关闭应用,在线时长将以最近一次心跳为准,不会无限累计
## 错误信息(统一中文)
- 401`用户名或密码错误`
- 403`该账号已在其他设备在线`
- 404`用户不存在`
## 前端调用示例Axios
```ts
import axios from 'axios';
const API = 'http://localhost:8000/api/v1/auth';
export async function login(username: string, password: string, deviceId: string) {
return axios.post(`${API}/login`, { username, password, device_id: deviceId });
}
export async function logout(username: string, deviceId: string) {
return axios.post(`${API}/logout`, { username, device_id: deviceId });
}
export async function getOnlineTime(username: string) {
return axios.get(`${API}/online-time/${encodeURIComponent(username)}`);
}
```