264 lines
8.3 KiB
Python
264 lines
8.3 KiB
Python
from datetime import datetime
|
|
|
|
from sqlalchemy import or_, select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.models.admin import Admin
|
|
from app.models.match import Match
|
|
from app.models.system_config import SystemConfig
|
|
from app.models.user import User
|
|
from app.schemas.admin import AdminAuditRequest, AdminBanRequest, AdminCreateRequest
|
|
from app.utils.security import create_access_token, hash_password, verify_password
|
|
|
|
|
|
async def admin_login(session: AsyncSession, username: str, password: str) -> dict:
|
|
result = await session.execute(select(Admin).where(Admin.username == username, Admin.is_active == 1))
|
|
admin = result.scalar_one_or_none()
|
|
if admin is None or not verify_password(password, admin.password_hash):
|
|
raise ValueError("账号或密码错误")
|
|
|
|
admin.last_login = datetime.now()
|
|
session.add(admin)
|
|
await session.commit()
|
|
|
|
token = create_access_token({"admin_id": admin.id, "role": admin.role})
|
|
return {
|
|
"access_token": token,
|
|
"token_type": "bearer",
|
|
"admin_id": admin.id,
|
|
"username": admin.username,
|
|
"role": admin.role,
|
|
}
|
|
|
|
|
|
async def list_admin_accounts(session: AsyncSession) -> list[Admin]:
|
|
result = await session.execute(select(Admin).order_by(Admin.created_at.asc()))
|
|
return result.scalars().all()
|
|
|
|
|
|
async def create_admin_account(session: AsyncSession, payload: AdminCreateRequest) -> Admin:
|
|
exists = await session.execute(select(Admin).where(Admin.username == payload.username))
|
|
if exists.scalar_one_or_none() is not None:
|
|
raise ValueError("管理员用户名已存在")
|
|
|
|
admin = Admin(
|
|
username=payload.username,
|
|
password_hash=hash_password(payload.password),
|
|
role=payload.role,
|
|
is_active=1,
|
|
)
|
|
session.add(admin)
|
|
await session.commit()
|
|
await session.refresh(admin)
|
|
return admin
|
|
|
|
|
|
async def update_admin_status(session: AsyncSession, admin_id: int, is_active: int) -> Admin | None:
|
|
admin = await session.get(Admin, admin_id)
|
|
if admin is None:
|
|
return None
|
|
admin.is_active = is_active
|
|
session.add(admin)
|
|
await session.commit()
|
|
await session.refresh(admin)
|
|
return admin
|
|
|
|
|
|
async def reset_admin_password(session: AsyncSession, admin_id: int, password: str) -> Admin | None:
|
|
admin = await session.get(Admin, admin_id)
|
|
if admin is None:
|
|
return None
|
|
admin.password_hash = hash_password(password)
|
|
session.add(admin)
|
|
await session.commit()
|
|
await session.refresh(admin)
|
|
return admin
|
|
|
|
|
|
async def list_users(
|
|
session: AsyncSession,
|
|
*,
|
|
keyword: str | None = None,
|
|
audit_status: int | None = None,
|
|
gender: int | None = None,
|
|
only_pending: bool = False,
|
|
) -> list[User]:
|
|
stmt = select(User)
|
|
if only_pending:
|
|
stmt = stmt.where(User.audit_status == 1)
|
|
if audit_status is not None:
|
|
stmt = stmt.where(User.audit_status == audit_status)
|
|
if gender is not None:
|
|
stmt = stmt.where(User.gender == gender)
|
|
if keyword:
|
|
like_keyword = f"%{keyword}%"
|
|
stmt = stmt.where(or_(User.nickname.like(like_keyword), User.real_name.like(like_keyword)))
|
|
|
|
stmt = stmt.order_by(User.updated_at.asc())
|
|
result = await session.execute(stmt)
|
|
return result.scalars().all()
|
|
|
|
|
|
async def audit_user(
|
|
session: AsyncSession,
|
|
user_id: int,
|
|
admin_id: int,
|
|
payload: AdminAuditRequest,
|
|
) -> User | None:
|
|
user = await session.get(User, user_id)
|
|
if user is None:
|
|
return None
|
|
if payload.audit_status not in {2, 3}:
|
|
raise ValueError("审核状态仅支持通过或驳回")
|
|
if payload.audit_status == 3 and not payload.audit_remark:
|
|
raise ValueError("驳回时必须填写原因")
|
|
|
|
user.audit_status = payload.audit_status
|
|
user.audit_remark = payload.audit_remark
|
|
user.audit_time = datetime.now()
|
|
user.auditor_id = admin_id
|
|
session.add(user)
|
|
await session.commit()
|
|
await session.refresh(user)
|
|
return user
|
|
|
|
|
|
async def get_user_detail(session: AsyncSession, user_id: int) -> User | None:
|
|
return await session.get(User, user_id)
|
|
|
|
|
|
async def ban_user(session: AsyncSession, user_id: int, payload: AdminBanRequest) -> User | None:
|
|
user = await session.get(User, user_id)
|
|
if user is None:
|
|
return None
|
|
user.is_active = payload.is_active
|
|
user.ban_reason = payload.ban_reason if payload.is_active == 0 else None
|
|
session.add(user)
|
|
await session.commit()
|
|
await session.refresh(user)
|
|
return user
|
|
|
|
|
|
async def list_admin_matches(
|
|
session: AsyncSession,
|
|
*,
|
|
user_id: int | None = None,
|
|
start_date: datetime | None = None,
|
|
end_date: datetime | None = None,
|
|
) -> list[dict]:
|
|
stmt = select(Match)
|
|
if user_id is not None:
|
|
stmt = stmt.where(or_(Match.user_a_id == user_id, Match.user_b_id == user_id))
|
|
if start_date is not None:
|
|
stmt = stmt.where(Match.matched_at >= start_date)
|
|
if end_date is not None:
|
|
stmt = stmt.where(Match.matched_at < end_date)
|
|
|
|
result = await session.execute(stmt.order_by(Match.matched_at.desc()))
|
|
matches = result.scalars().all()
|
|
data = []
|
|
for item in matches:
|
|
user_a = await session.get(User, item.user_a_id)
|
|
user_b = await session.get(User, item.user_b_id)
|
|
data.append(
|
|
{
|
|
"match_id": item.id,
|
|
"matched_at": item.matched_at.isoformat() if item.matched_at else None,
|
|
"match_score": float(item.match_score) if item.match_score is not None else None,
|
|
"match_type": item.match_type,
|
|
"user_a": {
|
|
"id": user_a.id if user_a else item.user_a_id,
|
|
"nickname": user_a.nickname if user_a else None,
|
|
},
|
|
"user_b": {
|
|
"id": user_b.id if user_b else item.user_b_id,
|
|
"nickname": user_b.nickname if user_b else None,
|
|
},
|
|
}
|
|
)
|
|
return data
|
|
|
|
|
|
DEFAULT_SYSTEM_CONFIGS = {
|
|
"ai_weights": {
|
|
"age": 0.20,
|
|
"hobbies": 0.18,
|
|
"values": 0.18,
|
|
"education": 0.14,
|
|
"activity": 0.12,
|
|
"location": 0.10,
|
|
"height": 0.08,
|
|
},
|
|
"daily_recommend_limit": 10,
|
|
"matches_per_activity_limit": 3,
|
|
"api_base_url": "https://ghxiangqin.com/api/v1",
|
|
"customer_service_wechat": "service_wechat_001",
|
|
}
|
|
|
|
|
|
async def get_system_configs(session: AsyncSession) -> dict:
|
|
result = await session.execute(select(SystemConfig))
|
|
items = result.scalars().all()
|
|
if not items:
|
|
for key, value in DEFAULT_SYSTEM_CONFIGS.items():
|
|
session.add(
|
|
SystemConfig(config_key=key, config_value=json_dumps(value), description=f"default {key}")
|
|
)
|
|
await session.commit()
|
|
result = await session.execute(select(SystemConfig))
|
|
items = result.scalars().all()
|
|
|
|
configs = {}
|
|
for item in items:
|
|
configs[item.config_key] = json_loads(item.config_value)
|
|
return configs
|
|
|
|
|
|
async def update_system_configs(session: AsyncSession, configs: dict) -> dict:
|
|
result = await session.execute(select(SystemConfig))
|
|
items = {item.config_key: item for item in result.scalars().all()}
|
|
|
|
for key, value in configs.items():
|
|
if key in items:
|
|
items[key].config_value = json_dumps(value)
|
|
session.add(items[key])
|
|
else:
|
|
session.add(SystemConfig(config_key=key, config_value=json_dumps(value), description=f"custom {key}"))
|
|
|
|
await session.commit()
|
|
return await get_system_configs(session)
|
|
|
|
|
|
async def get_system_config_value(session: AsyncSession, key: str, default=None):
|
|
result = await session.execute(select(SystemConfig).where(SystemConfig.config_key == key))
|
|
item = result.scalar_one_or_none()
|
|
if item is None:
|
|
return default
|
|
try:
|
|
return json_loads(item.config_value)
|
|
except Exception:
|
|
return default
|
|
|
|
|
|
async def get_system_config_value(session: AsyncSession, key: str, default=None):
|
|
result = await session.execute(select(SystemConfig).where(SystemConfig.config_key == key))
|
|
item = result.scalar_one_or_none()
|
|
if item is None:
|
|
return default
|
|
try:
|
|
return json_loads(item.config_value)
|
|
except Exception:
|
|
return default
|
|
|
|
|
|
def json_dumps(value) -> str:
|
|
import json
|
|
|
|
return json.dumps(value, ensure_ascii=False)
|
|
|
|
|
|
def json_loads(value: str):
|
|
import json
|
|
|
|
return json.loads(value)
|