Files
2026-09-21 19:03:31 +08:00

62 lines
2.4 KiB
Python

"""认证业务:登录、建号、改密。"""
from __future__ import annotations
from datetime import datetime
from sqlalchemy.orm import Session
from app.core.config import settings
from app.core.exceptions import AuthError, BusinessError, ConflictError
from app.core.security import create_access_token, hash_password, verify_password
from app.dao.account_dao import AccountDao
from app.model import Account, Role
from app.schema.auth_schema import AccountCreate, PasswordChange
class AuthService:
@classmethod
def login(cls, db: Session, username: str, password: str) -> tuple[Account, str, int]:
account = AccountDao.get_by_username(db, username)
if account is None or not verify_password(password, account.hashed_password):
# 不区分"用户不存在"和"密码错误",避免账号枚举
raise AuthError("用户名或密码不正确")
if not account.is_active:
raise AuthError("该账号已被停用,请联系管理员")
account.last_login_at = datetime.now()
db.flush()
expires_seconds = settings.JWT_EXPIRE_MINUTES * 60
token = create_access_token(
{"sub": str(account.id), "username": account.username, "role": account.role}
)
return account, token, expires_seconds
@classmethod
def create_account(cls, db: Session, payload: AccountCreate) -> Account:
if AccountDao.get_by_username(db, payload.username, with_deleted=True):
raise ConflictError(f"登录名「{payload.username}」已被占用")
if payload.role not in {r.value for r in Role}:
raise BusinessError(f"角色只能是 {'/'.join(r.value for r in Role)}")
account = Account(
username=payload.username,
hashed_password=hash_password(payload.password),
real_name=payload.real_name,
role=payload.role,
advisor_id=payload.advisor_id,
)
db.add(account)
db.flush()
return account
@classmethod
def change_password(cls, db: Session, account: Account, payload: PasswordChange) -> None:
if not verify_password(payload.old_password, account.hashed_password):
raise BusinessError("原密码不正确")
if payload.old_password == payload.new_password:
raise BusinessError("新密码不能与原密码相同")
account.hashed_password = hash_password(payload.new_password)
db.flush()