feat(桌面与监控): 完善工作台导航和登录活动定位
Client Quality Gates / Shared client and Web (push) Has been cancelled
Client Quality Gates / macOS Desktop (push) Has been cancelled

- 优化桌面标签、上下文标题、前进后退、导航栏隐藏和原生菜单体验

- 补充登录会话 IP 采集、地理位置回退、管理端展示及数据库迁移

- 更新桌面发布检查、运维文档和前后端测试覆盖
This commit is contained in:
Cheng Zhou
2026-07-13 16:03:20 +08:00
parent b26ebdda02
commit ab59476d10
50 changed files with 2086 additions and 191 deletions
+66 -2
View File
@@ -1,6 +1,7 @@
from datetime import datetime, timedelta, timezone
import uuid
from datetime import datetime, timedelta, timezone
from pathlib import Path
from types import SimpleNamespace
import pytest
@@ -8,7 +9,13 @@ from app.core.config import settings
from app.crud import user as user_crud
from app.models.user import User, UserStatus
from app.models.user_login_session import UserLoginSession
from app.services.user_login_sessions import get_login_summaries, session_id_from_payload
from app.schemas.user import UserLoginActivityRead
from app.services.user_login_sessions import (
create_login_session,
get_login_summaries,
login_activity_payload,
session_id_from_payload,
)
def test_legacy_session_id_is_stable_without_storing_a_token():
@@ -28,6 +35,63 @@ def test_session_heartbeat_returns_server_observed_client_ip():
assert '"client_ip": resolve_client_ip(request)' in auth_source
@pytest.mark.asyncio
async def test_login_session_stores_server_observed_login_ip(db_session):
user = User(
id=uuid.uuid4(),
email="login-ip@example.com",
password_hash="hash",
full_name="Login IP",
clinical_department="IT",
status=UserStatus.ACTIVE,
)
db_session.add(user)
await db_session.commit()
request = SimpleNamespace(
headers={
"x-ctms-client-type": "desktop",
"x-ctms-client-platform": "macos",
"x-ctms-client-version": "0.1.0",
},
client=SimpleNamespace(host="203.0.113.18"),
)
session = await create_login_session(
db_session,
session_id=uuid.uuid4(),
user_id=user.id,
request=request,
)
assert session.login_ip == "203.0.113.18"
assert session.client_type == "desktop"
def test_login_activity_payload_uses_server_authoritative_status_and_local_ip_location(monkeypatch):
now = datetime.now(timezone.utc)
monkeypatch.setattr(settings, "USER_SESSION_ONLINE_SECONDS", 300)
session = UserLoginSession(
id=uuid.uuid4(),
user_id=uuid.uuid4(),
client_type="web",
login_ip="127.0.0.1",
login_at=now - timedelta(minutes=10),
last_seen_at=now - timedelta(seconds=30),
)
online = login_activity_payload(session, reference_time=now)
assert online["activity_status"] == "ONLINE"
assert online["login_ip"] == "127.0.0.1"
assert online["ip_location"] == "本机"
assert UserLoginActivityRead.model_validate(online).activity_status == "ONLINE"
session.last_seen_at = now - timedelta(minutes=6)
assert login_activity_payload(session, reference_time=now)["activity_status"] == "OFFLINE"
session.ended_at = now - timedelta(minutes=1)
assert login_activity_payload(session, reference_time=now)["activity_status"] == "ENDED"
@pytest.mark.asyncio
async def test_login_summary_marks_recent_unended_sessions_online(db_session, monkeypatch):
now = datetime.now(timezone.utc)