# -*- coding: utf-8 -*- """OPC 超级个体端端点(工作台聚合数据)。 所有数据均由服务端(SQLite 演示库)提供,前端不落地任何演示数据。 当前角色要求 opc_member;后续各页面端点在此扩展。 """ from __future__ import annotations from fastapi import APIRouter, Depends, HTTPException, Request from pydantic import BaseModel from ..dependencies import get_db from ...rbac import require_roles, write_audit from ...infrastructure.repositories import Database, new_id, utcnow_iso from ...infrastructure.models import FinanceRecord router = APIRouter(prefix="/opc", tags=["opc"]) @router.get("/dashboard", summary="OPC 工作台总览") async def opc_dashboard( db: Database = Depends(get_db), user: dict = Depends(require_roles("opc_member")), ): """返回当前 OPC 用户的工作台聚合数据(统计/进行中任务/智能体建议/月度收入/最新消息)。""" uid = user["id"] profile = await db.opc_profiles.get(uid) task_counts = await db.opc_tasks.counts(uid) opc_tasks = await db.opc_tasks.list_by_user(uid) # 进行中任务:进行中/紧急优先,取前 3 条 active_tasks = [ t for t in opc_tasks if t["status"] in ("in_progress", "urgent") ][:3] # 智能体建议(由服务端依据真实数据生成) policy_count = len( [c for c in await db.content.list(ctype="policy", status="published")] ) agent_tips = [ { "title": "政策匹配提醒", "desc": f"{policy_count} 项新政策与您高度匹配", "type": "policy", }, { "title": "报税截止提醒", "desc": "本月报税截止 7月31日", "type": "tax", }, { "title": "技能提升推荐", "desc": "推荐参加 UI 设计研修班", "type": "skill", }, ] return { "stats": { "inProgressTasks": task_counts["in_progress"], "completedTasks": task_counts["completed"], "totalEarnings": await db.finance.total_income(uid), "creditScore": (profile or {}).get("credit_score", 80), }, "activeTasks": active_tasks, "agentTips": agent_tips, "monthlyIncome": await db.finance.monthly_income(uid), "recentMessages": await db.messages.recent(uid, limit=4), } # ── OPC 子页面(全部由服务端提供标准数据)───────────────────────────────── @router.get("/agent", summary="OPC 智能体助手") async def opc_agent( db: Database = Depends(get_db), user: dict = Depends(require_roles("opc_member")), ): agents = await db.agents.get_by_user(user["id"], port=user.get("port") or "opc") default = next((a for a in agents if a.get("id", "").startswith("pine_agents_official_001")), None) return { "agent": { "name": (default or {}).get("name", "小云"), "desc": (default or {}).get("description", "您的智能体助手,可处理创业、政策、财税等事务"), "port": user.get("port") or "opc", }, "quick_actions": [ {"title": "查询政策", "desc": "为您匹配最新创业政策"}, {"title": "发布能力", "desc": "快速发布您的服务能力"}, {"title": "财务助手", "desc": "查看收入与支出"}, ], "chat_path": "/chat", } @router.get("/tasks", summary="任务广场(已发布平台任务)") async def opc_task_square( db: Database = Depends(get_db), _u: dict = Depends(require_roles("opc_member")), ): return {"items": await db.tasks.list(status="published")} @router.get("/my-tasks", summary="我的任务看板") async def opc_my_tasks( db: Database = Depends(get_db), user: dict = Depends(require_roles("opc_member")), ): return {"items": await db.opc_tasks.list_by_user(user["id"])} @router.get("/services", summary="服务市场(在营服务商)") async def opc_services( db: Database = Depends(get_db), _u: dict = Depends(require_roles("opc_member")), ): return {"items": await db.providers.list(status="active")} @router.get("/policy", summary="政策列表(已发布政策)") async def opc_policy( db: Database = Depends(get_db), _u: dict = Depends(require_roles("opc_member")), ): return {"items": await db.content.list(ctype="policy", status="published")} @router.get("/finance", summary="财务流水") async def opc_finance( db: Database = Depends(get_db), user: dict = Depends(require_roles("opc_member")), ): uid = user["id"] return { "records": await db.finance.list_by_user(uid), "total_income": await db.finance.total_income(uid), } @router.get("/messages", summary="站内消息") async def opc_messages( db: Database = Depends(get_db), user: dict = Depends(require_roles("opc_member")), ): return {"items": await db.messages.list_by_user(user["id"])} @router.get("/profile", summary="个人资料") async def opc_profile( db: Database = Depends(get_db), user: dict = Depends(require_roles("opc_member")), ): profile = await db.users.to_profile(user) profile["opc_profile"] = await db.opc_profiles.get(user["id"]) return profile class BidRequest(BaseModel): quote: int = 0 plan: str = "" @router.post("/tasks/{task_id}/grab", summary="OPC 抢单") async def opc_grab( task_id: str, request: Request, db: Database = Depends(get_db), actor: dict = Depends(require_roles("opc_member")), ): from ...services.task_service import TaskService updated = await TaskService(db).grab(task_id, actor) await write_audit(db, action="task.grab", resource="task", resource_id=task_id, detail=actor.get("username"), user=actor, request=request) return updated @router.post("/tasks/{task_id}/bid", summary="OPC 投标") async def opc_bid( task_id: str, req: BidRequest, request: Request, db: Database = Depends(get_db), actor: dict = Depends(require_roles("opc_member")), ): from ...services.task_service import TaskService bid = await TaskService(db).bid(task_id, actor, req.quote, req.plan) await write_audit(db, action="task.bid", resource="bid", resource_id=bid["id"], user=actor, request=request) return bid @router.post("/tasks/{task_id}/deliver", summary="OPC 交付") async def opc_deliver( task_id: str, request: Request, db: Database = Depends(get_db), actor: dict = Depends(require_roles("opc_member")), ): from ...services.task_service import TaskService updated = await TaskService(db).deliver(task_id, actor) await write_audit(db, action="task.deliver", resource="task", resource_id=task_id, user=actor, request=request) return updated class ProfileUpdate(BaseModel): nickname: str | None = None account: str | None = None company: str | None = None room: str | None = None skills: list[str] = [] credit_score: int | None = None @router.put("/profile", summary="OPC 基础资料编辑") async def opc_update_profile( req: ProfileUpdate, request: Request, db: Database = Depends(get_db), actor: dict = Depends(require_roles("opc_member")), ): profile_updates = {k: v for k, v in req.model_dump(exclude_none=True).items() if k in ("nickname", "account", "company", "room")} if profile_updates: await db.users.update_profile(actor["id"], profile_updates) if req.credit_score is not None: await db.opc_profiles.upsert(actor["id"], req.credit_score) await write_audit(db, action="profile.update", resource="user", resource_id=actor["id"], user=actor, request=request) fresh_user = await db.users.get_by_id(actor["id"]) return await opc_profile(db, fresh_user) class FinanceRecordCreate(BaseModel): category: str = "income" # income | expense amount: int date: str = "" note: str = "" @router.post("/finance/record", summary="OPC 记一笔收支") async def opc_add_finance( req: FinanceRecordCreate, request: Request, db: Database = Depends(get_db), actor: dict = Depends(require_roles("opc_member")), ): from ...services.finance_service import FinanceService record = await FinanceService(db).add_record( user_id=actor["id"], category=req.category, amount=req.amount, date=req.date or utcnow_iso()[:10], note=req.note, ) await write_audit(db, action="finance.add", resource="finance", resource_id=record["id"], user=actor, request=request) return record @router.get("/tax", summary="OPC 税务申报") async def opc_tax( db: Database = Depends(get_db), _u: dict = Depends(require_roles("opc_member")), ): return await db.portal_pages.get("opc", "tax") or {"items": []} @router.get("/affairs", summary="OPC 工商/政务办事") async def opc_affairs( db: Database = Depends(get_db), _u: dict = Depends(require_roles("opc_member")), ): return await db.portal_pages.get("opc", "affairs") or {"items": []}