980a2db6d9
- 基础设施层 async:SQLAlchemy 异步引擎/会话、33 Repository async 化、models/security/seed 迁入 infrastructure、新增 cache.py(redis.asyncio) 与 oss.py(aioboto3) - 接口层:routers 迁 api/routers 并全 async,dependencies 迁 api/dependencies(get_db/get_current_user async) - 依赖:sqlalchemy[asyncio]/aiosqlite/asyncmy/redis/aioboto3;config 异步 URL + Redis/OSS 配置 - 删除废弃:旧同步 db/dependencies/repositories/storage - 验证:平台 19 路由 + 培训 48 路由全注册;/health /auth/login /auth/me /admin/tasks /notifications 等接口 async 可用
203 lines
7.4 KiB
Python
203 lines
7.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""各业务端口工作台与子页面端点(企业/载体/服务商/政务)。
|
|
|
|
列表类页面数据全部由服务端(portal_pages 表)提供;企业发布任务写入 tasks 表。
|
|
OPC 端工作台在 ``rbac_opc.py`` 中(按用户聚合)。
|
|
"""
|
|
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
|
|
|
|
router = APIRouter(tags=["portals"])
|
|
|
|
# 端口 -> 允许的角色
|
|
_PORT_ROLE = {
|
|
"enterprise": "enterprise",
|
|
"carrier": "carrier",
|
|
"provider": "provider",
|
|
"government": "government",
|
|
}
|
|
|
|
|
|
def _dashboard(port: str):
|
|
role = _PORT_ROLE.get(port)
|
|
if role is None:
|
|
raise HTTPException(status_code=404, detail="Unknown port")
|
|
|
|
async def handler(
|
|
db: Database = Depends(get_db),
|
|
_u: dict = Depends(require_roles(role)),
|
|
):
|
|
payload = await db.portal_dashboards.get(port)
|
|
if payload is None:
|
|
raise HTTPException(status_code=404, detail="Dashboard not found")
|
|
return payload
|
|
|
|
return handler
|
|
|
|
|
|
def _page(port: str, page: str):
|
|
role = _PORT_ROLE.get(port)
|
|
if role is None:
|
|
raise HTTPException(status_code=404, detail="Unknown port")
|
|
|
|
async def handler(
|
|
db: Database = Depends(get_db),
|
|
_u: dict = Depends(require_roles(role)),
|
|
):
|
|
payload = await db.portal_pages.get(port, page)
|
|
if payload is None:
|
|
raise HTTPException(status_code=404, detail="Page data not found")
|
|
payload.setdefault("port", port)
|
|
payload.setdefault("page", page)
|
|
return payload
|
|
|
|
return handler
|
|
|
|
|
|
router.get("/enterprise/dashboard", summary="企业端工作台")(_dashboard("enterprise"))
|
|
router.get("/carrier/dashboard", summary="载体/园区端工作台")(_dashboard("carrier"))
|
|
router.get("/provider/dashboard", summary="服务商端工作台")(_dashboard("provider"))
|
|
router.get("/government/dashboard", summary="政务端工作台")(_dashboard("government"))
|
|
|
|
# ── 子页面(列表/数据均由服务端提供)──────────────────────────────────────
|
|
router.get("/enterprise/talents", summary="企业·人才搜索")(_page("enterprise", "talents"))
|
|
router.get("/carrier/opc", summary="载体·OPC 管理")(_page("carrier", "opc"))
|
|
router.get("/carrier/services", summary="载体·服务管理")(_page("carrier", "services"))
|
|
router.get("/provider/orders", summary="服务商·订单管理")(_page("provider", "orders"))
|
|
router.get("/provider/services", summary="服务商·服务管理")(_page("provider", "services"))
|
|
router.get("/government/policies", summary="政务·政策管理")(_page("government", "policies"))
|
|
|
|
|
|
# ── 企业·任务(发布任务/我的任务)─────────────────────────────────────────
|
|
class EnterpriseTaskCreate(BaseModel):
|
|
title: str
|
|
category: str = ""
|
|
sub_category: str = ""
|
|
description: str = ""
|
|
mode: str = "grab"
|
|
budget_min: int = 0
|
|
budget_max: int = 0
|
|
deadline: str = ""
|
|
publisher_name: str = ""
|
|
|
|
|
|
@router.get("/enterprise/tasks", summary="企业·我的任务列表")
|
|
async def enterprise_tasks(
|
|
db: Database = Depends(get_db),
|
|
_u: dict = Depends(require_roles("enterprise")),
|
|
):
|
|
return {"items": await db.tasks.list(status="published")}
|
|
|
|
|
|
@router.post("/enterprise/tasks", summary="企业·发布任务")
|
|
async def enterprise_create_task(
|
|
req: EnterpriseTaskCreate,
|
|
request: Request,
|
|
db: Database = Depends(get_db),
|
|
actor: dict = Depends(require_roles("enterprise")),
|
|
):
|
|
task = await db.tasks.create({**req.model_dump(exclude_none=True), "status": "pending"})
|
|
await write_audit(db, action="task.publish", resource="task", resource_id=task["id"],
|
|
detail=task["title"], user=actor, request=request)
|
|
return task
|
|
|
|
|
|
@router.post("/enterprise/tasks/{task_id}/submit", summary="企业·提交审核并发布")
|
|
async def enterprise_submit_task(
|
|
task_id: str,
|
|
request: Request,
|
|
db: Database = Depends(get_db),
|
|
actor: dict = Depends(require_roles("enterprise")),
|
|
):
|
|
task = await db.tasks.set_status(task_id, "published")
|
|
if task is None:
|
|
raise HTTPException(status_code=404, detail="Task not found")
|
|
await write_audit(db, action="task.submit", resource="task", resource_id=task_id,
|
|
user=actor, request=request)
|
|
return task
|
|
|
|
|
|
@router.get("/enterprise/tasks/{task_id}/bids", summary="企业·查看竞标")
|
|
async def enterprise_list_bids(
|
|
task_id: str,
|
|
db: Database = Depends(get_db),
|
|
_u: dict = Depends(require_roles("enterprise")),
|
|
):
|
|
return {"items": await db.bids.list_for_task(task_id)}
|
|
|
|
|
|
@router.post("/enterprise/tasks/{task_id}/bids/{bid_id}/win", summary="企业·评标中标")
|
|
async def enterprise_win_bid(
|
|
task_id: str, bid_id: str,
|
|
request: Request,
|
|
db: Database = Depends(get_db),
|
|
actor: dict = Depends(require_roles("enterprise")),
|
|
):
|
|
bid = await db.bids.set_status(bid_id, "win")
|
|
if bid is None:
|
|
raise HTTPException(status_code=404, detail="Bid not found")
|
|
await db.tasks.set_status(task_id, "in_progress")
|
|
await write_audit(db, action="bid.win", resource="bid", resource_id=bid_id,
|
|
detail=f"task={task_id} winner={bid['opc_name']}", user=actor, request=request)
|
|
return bid
|
|
|
|
|
|
class TaskReviewRequest(BaseModel):
|
|
action: str # accept | reject
|
|
comment: str = ""
|
|
|
|
|
|
@router.post("/enterprise/tasks/{task_id}/review", summary="企业·验收任务")
|
|
async def enterprise_review_task(
|
|
task_id: str,
|
|
req: TaskReviewRequest,
|
|
request: Request,
|
|
db: Database = Depends(get_db),
|
|
actor: dict = Depends(require_roles("enterprise")),
|
|
):
|
|
task = await db.tasks.get(task_id)
|
|
if task is None:
|
|
raise HTTPException(status_code=404, detail="Task not found")
|
|
new_status = "completed" if req.action == "accept" else "in_progress"
|
|
updated = await db.tasks.set_status(task_id, new_status)
|
|
await write_audit(db, action="task.review", resource="task", resource_id=task_id,
|
|
detail=req.action, user=actor, request=request)
|
|
return updated
|
|
|
|
|
|
# ── 载体·服务商引荐(三方绑定:载体引荐服务商→服务园区内 OPC)─────────────
|
|
class ReferralCreate(BaseModel):
|
|
provider_id: str = ""
|
|
provider_name: str = ""
|
|
opc_id: str = ""
|
|
opc_name: str = ""
|
|
|
|
|
|
@router.get("/carrier/referrals", summary="载体·引荐记录")
|
|
async def carrier_referrals(
|
|
db: Database = Depends(get_db),
|
|
user: dict = Depends(require_roles("carrier")),
|
|
):
|
|
return {"items": await db.referrals.list_for(user.get("org_id"))}
|
|
|
|
|
|
@router.post("/carrier/referrals", summary="载体·引荐服务商给园区内 OPC")
|
|
async def carrier_create_referral(
|
|
req: ReferralCreate,
|
|
request: Request,
|
|
db: Database = Depends(get_db),
|
|
actor: dict = Depends(require_roles("carrier")),
|
|
):
|
|
referral = await db.referrals.create({
|
|
**req.model_dump(exclude_none=True), "carrier_id": actor.get("org_id") or actor["id"],
|
|
})
|
|
await write_audit(db, action="referral.create", resource="referral", resource_id=referral["id"],
|
|
detail=f"{referral['provider_name']}->{referral['opc_name']}", user=actor, request=request)
|
|
return referral
|