feat: 修复 H5 底部导航覆盖 + 更新项目进度文档
## H5 底部导航修复 (Bug #10) - 精简 App.vue,移除重复 tabbar,仅保留全局样式 - uni-page 设置 height: calc(100% - 50px) + overflow-y: auto - 内容区域精确停在底部导航上方,独立滚动不再叠加 - 恢复 custom-tab-bar 组件 ## 项目进度文档 - PROGRESS.md 更新至 10 个 Bug 修复 - 新增 H5 底部导航修复记录 - 新增历史变更条目
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
from typing import Dict, Any, List
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, func, and_
|
||||
from app.models.user import User
|
||||
from app.models.team import Team, TeamMember
|
||||
from app.models.analytics import UsageLog
|
||||
from app.models.customer import Customer
|
||||
from app.models.quotation import Quotation
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AdminService:
|
||||
def __init__(self, db: AsyncSession):
|
||||
self.db = db
|
||||
|
||||
async def get_dashboard(self) -> Dict[str, Any]:
|
||||
now = datetime.utcnow()
|
||||
today_start = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
|
||||
user_count = await self.db.execute(select(func.count(User.id)))
|
||||
team_count = await self.db.execute(select(func.count(Team.id)))
|
||||
customer_count = await self.db.execute(select(func.count(Customer.id)))
|
||||
quotation_count = await self.db.execute(select(func.count(Quotation.id)))
|
||||
|
||||
today_logs = await self.db.execute(
|
||||
select(func.count(UsageLog.id)).where(UsageLog.created_at >= today_start)
|
||||
)
|
||||
total_logs = await self.db.execute(select(func.count(UsageLog.id)))
|
||||
|
||||
recent_users_result = await self.db.execute(
|
||||
select(User).order_by(User.created_at.desc()).limit(5)
|
||||
)
|
||||
recent_users = recent_users_result.scalars().all()
|
||||
|
||||
return {
|
||||
"users": {
|
||||
"total": user_count.scalar() or 0,
|
||||
},
|
||||
"teams": {
|
||||
"total": team_count.scalar() or 0,
|
||||
},
|
||||
"customers": {
|
||||
"total": customer_count.scalar() or 0,
|
||||
},
|
||||
"quotations": {
|
||||
"total": quotation_count.scalar() or 0,
|
||||
},
|
||||
"usage": {
|
||||
"today": today_logs.scalar() or 0,
|
||||
"total": total_logs.scalar() or 0,
|
||||
},
|
||||
"recent_users": [
|
||||
{
|
||||
"id": str(u.id),
|
||||
"username": u.username,
|
||||
"tier": u.tier,
|
||||
"is_active": u.is_active,
|
||||
"created_at": u.created_at.isoformat() if u.created_at else None,
|
||||
}
|
||||
for u in recent_users
|
||||
],
|
||||
}
|
||||
|
||||
async def list_users(self, page: int = 1, size: int = 20) -> Dict[str, Any]:
|
||||
query = select(User).order_by(User.created_at.desc()).offset((page - 1) * size).limit(size)
|
||||
count_query = select(func.count(User.id))
|
||||
|
||||
total = await self.db.execute(count_query)
|
||||
result = await self.db.execute(query)
|
||||
users = result.scalars().all()
|
||||
|
||||
return {
|
||||
"items": [
|
||||
{
|
||||
"id": str(u.id),
|
||||
"username": u.username,
|
||||
"phone": u.phone,
|
||||
"tier": u.tier,
|
||||
"is_active": u.is_active,
|
||||
"created_at": u.created_at.isoformat() if u.created_at else None,
|
||||
}
|
||||
for u in users
|
||||
],
|
||||
"total": total.scalar(),
|
||||
"page": page,
|
||||
"size": size,
|
||||
}
|
||||
|
||||
async def update_user_tier(self, user_id: str, tier: str) -> bool:
|
||||
result = await self.db.execute(select(User).where(User.id == user_id))
|
||||
user = result.scalar_one_or_none()
|
||||
if not user:
|
||||
return False
|
||||
user.tier = tier
|
||||
await self.db.flush()
|
||||
return True
|
||||
|
||||
async def toggle_user_active(self, user_id: str) -> bool:
|
||||
result = await self.db.execute(select(User).where(User.id == user_id))
|
||||
user = result.scalar_one_or_none()
|
||||
if not user:
|
||||
return False
|
||||
user.is_active = not user.is_active
|
||||
await self.db.flush()
|
||||
return True
|
||||
|
||||
async def get_system_health(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"status": "healthy",
|
||||
"version": "1.0.0",
|
||||
"timestamp": datetime.utcnow().isoformat(),
|
||||
}
|
||||
|
||||
async def log_usage(self, user_id: str, action: str, detail: Dict = None, ip: str = None, ua: str = None):
|
||||
try:
|
||||
log = UsageLog(
|
||||
user_id=user_id,
|
||||
action=action,
|
||||
detail=detail or {},
|
||||
ip_address=ip,
|
||||
user_agent=ua,
|
||||
)
|
||||
self.db.add(log)
|
||||
await self.db.flush()
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to log usage: {e}")
|
||||
Reference in New Issue
Block a user