from typing import List, Optional from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session from app.core.database import get_db from app.models.account import Account from app.models.tenant import Tenant from app.models.result import AccountScheduleResult from app.schemas.account import AccountCreate, AccountUpdate, AccountResponse, AccountStatusLight router = APIRouter(prefix="/accounts", tags=["accounts"]) def _next_seq_no(db: Session, tenant_id: int) -> int: max_seq = db.query(Account.seq_no).filter(Account.tenant_id == tenant_id).order_by(Account.seq_no.desc()).first() return (max_seq[0] + 1) if max_seq else 1 def _build_account_code(prefix: str, seq_no: int) -> str: return f"{prefix}{str(seq_no).zfill(4)}" def _get_lights(db: Session, account_id: int) -> Optional[AccountStatusLight]: result = ( db.query(AccountScheduleResult) .filter(AccountScheduleResult.account_id == account_id) .order_by(AccountScheduleResult.recorded_at.desc()) .first() ) if not result: return None return AccountStatusLight( sso_result=result.sso_result, mailbox_result=result.mailbox_result, nc_result=result.nc_result, quota_usage=result.quota_usage, ) @router.get("", response_model=List[AccountResponse]) def list_accounts( tenant_id: Optional[int] = Query(None), is_active: Optional[bool] = Query(None), db: Session = Depends(get_db), ): q = db.query(Account) if tenant_id is not None: q = q.filter(Account.tenant_id == tenant_id) if is_active is not None: q = q.filter(Account.is_active == is_active) accounts = q.order_by(Account.id).all() result = [] for a in accounts: resp = AccountResponse.model_validate(a) resp.tenant_name = a.tenant.name if a.tenant else None resp.lights = _get_lights(db, a.id) result.append(resp) return result @router.post("", response_model=AccountResponse, status_code=201) def create_account(payload: AccountCreate, db: Session = Depends(get_db)): tenant = db.get(Tenant, payload.tenant_id) if not tenant: raise HTTPException(status_code=404, detail="Tenant not found") seq_no = _next_seq_no(db, payload.tenant_id) account_code = _build_account_code(tenant.prefix, seq_no) email = f"{payload.sso_account}@{tenant.domain}" account = Account( **payload.model_dump(), seq_no=seq_no, account_code=account_code, email=email, ) db.add(account) db.commit() db.refresh(account) resp = AccountResponse.model_validate(account) resp.tenant_name = tenant.name resp.lights = None return resp @router.get("/{account_id}", response_model=AccountResponse) def get_account(account_id: int, db: Session = Depends(get_db)): account = db.get(Account, account_id) if not account: raise HTTPException(status_code=404, detail="Account not found") resp = AccountResponse.model_validate(account) resp.tenant_name = account.tenant.name if account.tenant else None resp.lights = _get_lights(db, account_id) return resp @router.put("/{account_id}", response_model=AccountResponse) def update_account(account_id: int, payload: AccountUpdate, db: Session = Depends(get_db)): account = db.get(Account, account_id) if not account: raise HTTPException(status_code=404, detail="Account not found") for field, value in payload.model_dump(exclude_none=True).items(): setattr(account, field, value) db.commit() db.refresh(account) resp = AccountResponse.model_validate(account) resp.tenant_name = account.tenant.name if account.tenant else None resp.lights = _get_lights(db, account_id) return resp @router.delete("/{account_id}", status_code=204) def delete_account(account_id: int, db: Session = Depends(get_db)): account = db.get(Account, account_id) if not account: raise HTTPException(status_code=404, detail="Account not found") db.delete(account) db.commit()