# -*- coding: utf-8 -*- """横切能力端点:通知/信用/合同/结算/争议/评价/投资撮合/路演参与。 """ from __future__ import annotations from fastapi import APIRouter, Depends, HTTPException, Request from pydantic import BaseModel from ..dependencies import get_db from ..schemas.ecosystem import RatingRequest, SignRequest, DisputeRequest from ...rbac import require_permission, require_roles, write_audit from ...infrastructure.repositories import Database router = APIRouter(tags=["ecosystem"]) _ANY = ("opc_member", "carrier", "service", "operator") # ── 通知中心 ────────────────────────────────────────────────────────────── @router.get("/notifications", summary="我的通知") async def list_notifications( db: Database = Depends(get_db), user: dict = Depends(require_roles(*_ANY)), ): return {"items": await db.notifications.list_for(user["id"]), "unread": await db.notifications.unread(user["id"])} @router.post("/notifications/read-all", summary="全部已读") async def read_all_notifications( db: Database = Depends(get_db), user: dict = Depends(require_roles(*_ANY)), ): return {"marked": await db.notifications.mark_read(user["id"])} # ── 信用/评价 ────────────────────────────────────────────────────────────── @router.get("/me/credit", summary="我的信用") async def my_credit( db: Database = Depends(get_db), user: dict = Depends(require_roles(*_ANY)), ): profile = await db.opc_profiles.get(user["id"]) return {"credit_score": (profile or {}).get("credit_score", 80), "avg_rating": await db.ratings.avg_for(user["id"]), "rating_count": len(await db.ratings.list_for(user["id"])) if hasattr(db.ratings, "list_for") else 0} @router.post("/tasks/{task_id}/rate", summary="任务互评") async def rate_task( task_id: str, req: RatingRequest, request: Request, db: Database = Depends(get_db), actor: dict = Depends(require_roles("service", "opc_member")), ): if not req.to_id: raise HTTPException(status_code=400, detail="缺少被评对象") rating = await db.ratings.create(task_id, actor["id"], req.to_id, req.score, req.comment) await write_audit(db, action="task.rate", resource="task", resource_id=task_id, detail=f"{req.score}星", user=actor, request=request) return rating # ── 合同 ────────────────────────────────────────────────────────────────── @router.get("/tasks/{task_id}/contract", summary="查看合同") async def get_contract( task_id: str, db: Database = Depends(get_db), _u: dict = Depends(require_roles("service", "opc_member")), ): contract = await db.contracts.get_for_task(task_id) if contract is None: raise HTTPException(status_code=404, detail="Contract not found") return contract @router.post("/tasks/{task_id}/sign", summary="签订电子合同") async def sign_contract( task_id: str, req: SignRequest, request: Request, db: Database = Depends(get_db), actor: dict = Depends(require_roles("service", "opc_member")), ): from ...services.settlement_service import SettlementService contract = await SettlementService(db).sign_contract(task_id, actor, req.opc_id) await write_audit(db, action="contract.sign", resource="contract", resource_id=contract["id"], user=actor, request=request) return contract # ── 结算托管 ────────────────────────────────────────────────────────────── @router.post("/enterprise/tasks/{task_id}/release", summary="验收通过并结算") async def release_escrow( task_id: str, request: Request, db: Database = Depends(get_db), actor: dict = Depends(require_roles("service")), ): from ...services.settlement_service import SettlementService released = await SettlementService(db).release_escrow(task_id) await write_audit(db, action="escrow.release", resource="escrow", resource_id=released["id"], detail=f"amount={released['amount']}", user=actor, request=request) return released @router.get("/operator/settlements", summary="运营结算台账") async def operator_settlements( status: str | None = None, db: Database = Depends(get_db), _u: dict = Depends(require_permission("action:settlement.manage")), ): return {"items": await db.escrows.list(status=status)} # ── 争议 ────────────────────────────────────────────────────────────────── @router.post("/tasks/{task_id}/dispute", summary="发起争议") async def create_dispute( task_id: str, req: DisputeRequest, request: Request, db: Database = Depends(get_db), actor: dict = Depends(require_roles("service", "opc_member")), ): from ...services.settlement_service import SettlementService dispute = await SettlementService(db).create_dispute(task_id, actor, req.reason) await write_audit(db, action="dispute.open", resource="dispute", resource_id=dispute["id"], user=actor, request=request) return dispute @router.post("/operator/disputes/{dispute_id}/resolve", summary="争议调解/解决") async def resolve_dispute( dispute_id: str, request: Request, db: Database = Depends(get_db), actor: dict = Depends(require_roles("operator")), ): from ...services.settlement_service import SettlementService dispute = await SettlementService(db).resolve_dispute(dispute_id) await write_audit(db, action="dispute.resolve", resource="dispute", resource_id=dispute_id, user=actor, request=request) return dispute # ── 投资撮合(按偏好排序项目)─────────────────────────────────────────────── @router.get("/investor/matches", summary="投资撮合(按偏好推荐)") async def investor_matches( db: Database = Depends(get_db), user: dict = Depends(require_roles("service")), ): from ...services.settlement_service import SettlementService return await SettlementService(db).investor_matches(user["id"]) # ── 路演参与(进入直播/出席)──────────────────────────────────────────────── @router.post("/roadshows/{roadshow_id}/join", summary="进入路演直播") async def join_roadshow( roadshow_id: str, request: Request, db: Database = Depends(get_db), actor: dict = Depends(require_roles("service", "operator", "carrier")), ): rs = await db.roadshows.get(roadshow_id) if rs is None: raise HTTPException(status_code=404, detail="Roadshow not found") # 标记出席(若已报名则更新为 attended;否则记录出席) await db.roadshow_regs.create(roadshow_id, actor["id"], role="attendee") await write_audit(db, action="roadshow.join", resource="roadshow", resource_id=roadshow_id, user=actor, request=request) return {"joined": True, "live_url": rs.get("live_url") or "http://live.example/roadshow"}