from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select from datetime import datetime from pydantic import BaseModel from app.core.database import get_db from app.models.models import User, Debt from app.schemas.schemas import LoanCreate, LoanResponse from app.core.deps import get_current_user router = APIRouter(prefix="/loans", tags=["借款管理"]) METHOD_MAP = {"e": "equal_installment", "p": "interest_first", "i": "lump_sum"} METHOD_REV = {"equal_installment": "e", "interest_first": "p", "lump_sum": "i"} class PaidUpdate(BaseModel): paid: list[int] def _debt_to_loan(d: Debt, owner_name: str = "") -> LoanResponse: return LoanResponse( id=d.id, name=d.creditor_name, date=d.borrow_date.strftime("%Y-%m-%d") if d.borrow_date else "", amount=d.amount / 100, rate=d.interest_rate, periods=d.period_count, method=METHOD_REV.get(d.repayment_method, "e"), purpose=d.purpose or "", schedule=d.schedule_data or [], paid=d.paid_periods or [], user_id=d.user_id, owner_name=owner_name, ) async def _get_visible_user_ids(user: User, db: AsyncSession) -> list[int]: if user.role == "admin": result = await db.execute(select(User.id)) return [r[0] for r in result.all()] if user.group_id: result = await db.execute(select(User.id).where(User.group_id == user.group_id)) ids = [r[0] for r in result.all()] if user.id not in ids: ids.append(user.id) return ids return [user.id] @router.get("", response_model=list[LoanResponse]) async def list_loans( user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): visible_ids = await _get_visible_user_ids(user, db) result = await db.execute(select(Debt).where(Debt.user_id.in_(visible_ids)).order_by(Debt.created_at.desc())) debts = result.scalars().all() users_result = await db.execute(select(User.id, User.nickname)) users_map = {uid: (nick or "") for uid, nick in users_result.all()} return [_debt_to_loan(d, users_map.get(d.user_id, "")) for d in debts] @router.post("", response_model=LoanResponse, status_code=201) async def create_loan( req: LoanCreate, user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): debt = Debt( user_id=user.id, creditor_name=req.name, amount=int(req.amount * 100), borrow_date=datetime.strptime(req.date, "%Y-%m-%d"), interest_rate=req.rate, rate_type="annual", repayment_method=METHOD_MAP.get(req.method, "equal_installment"), period_count=req.periods, period_unit="month", purpose=req.purpose, schedule_data=req.schedule, paid_periods=req.paid, ) db.add(debt) await db.commit() await db.refresh(debt) return _debt_to_loan(debt, user.nickname or "") @router.put("/{loan_id}", response_model=LoanResponse) async def update_loan( loan_id: int, req: LoanCreate, user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): result = await db.execute(select(Debt).where(Debt.id == loan_id)) debt = result.scalar_one_or_none() if not debt: raise HTTPException(status_code=404, detail="借款不存在") if debt.user_id != user.id and user.role != "admin": raise HTTPException(status_code=403, detail="无权修改") debt.creditor_name = req.name debt.amount = int(req.amount * 100) debt.borrow_date = datetime.strptime(req.date, "%Y-%m-%d") debt.interest_rate = req.rate debt.repayment_method = METHOD_MAP.get(req.method, "equal_installment") debt.period_count = req.periods debt.purpose = req.purpose debt.schedule_data = req.schedule debt.paid_periods = req.paid await db.commit() await db.refresh(debt) return _debt_to_loan(debt, user.nickname or "") @router.delete("/{loan_id}") async def delete_loan( loan_id: int, user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): result = await db.execute(select(Debt).where(Debt.id == loan_id)) debt = result.scalar_one_or_none() if not debt: raise HTTPException(status_code=404, detail="借款不存在") if debt.user_id != user.id and user.role != "admin": raise HTTPException(status_code=403, detail="无权删除") await db.delete(debt) await db.commit() return {"message": "已删除"} @router.patch("/{loan_id}/toggle-paid") async def toggle_paid( loan_id: int, req: PaidUpdate, user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): result = await db.execute(select(Debt).where(Debt.id == loan_id)) debt = result.scalar_one_or_none() if not debt: raise HTTPException(status_code=404, detail="借款不存在") if debt.user_id != user.id: raise HTTPException(status_code=403, detail="只能标记自己的还款") debt.paid_periods = sorted(req.paid) await db.commit() return {"paid": debt.paid_periods}