d851b54755
1. 运行迁移脚本:同步 2 条园区管理员 + 1 条企业管理员到 organization_members 2. 新增 get_user_organizations / get_user_primary_org 辅助函数 3. 改造各路由优先从 organization_members 查询组织: - rbac_org.py: /org/me 返回所有绑定组织 - rbac_portals.py: referrals 和 carrier_park 优先用 organization_members - rbac_hall.py: 发布岗位优先用 organization_members 获取企业 4. users.org_id 保留作回退,不删除字段,风险可控
173 lines
7.3 KiB
Python
173 lines
7.3 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""各业务端口工作台与子页面端点(仅保留园区 carrier 端)。
|
|
|
|
列表类页面数据全部由服务端(portal_pages 表)提供。
|
|
OPC 端工作台在 ``rbac_opc.py`` 中(按用户聚合)。
|
|
运营端工作台在 ``rbac_operator.py`` 中。
|
|
|
|
已移除:enterprise / provider / government 端路由。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Request
|
|
from pydantic import BaseModel
|
|
|
|
from ..dependencies import get_db, get_user_primary_org
|
|
from ..schemas.portals import ReferralCreate
|
|
from ...rbac import require_roles, write_audit
|
|
from ...infrastructure.repositories import Database
|
|
|
|
router = APIRouter(tags=["portals"])
|
|
|
|
|
|
def _dashboard(port: str):
|
|
role = "carrier"
|
|
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 = "carrier"
|
|
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("/carrier/dashboard", summary="载体/园区端工作台")(_dashboard("carrier"))
|
|
|
|
# ── 子页面(列表/数据均由服务端提供)──────────────────────────────────────
|
|
router.get("/carrier/opc", summary="载体·OPC 管理")(_page("carrier", "opc"))
|
|
router.get("/carrier/services", summary="载体·服务管理")(_page("carrier", "services"))
|
|
|
|
|
|
# ── 载体·服务商引荐(三方绑定:载体引荐服务商→服务园区内 OPC)─────────────
|
|
@router.get("/carrier/referrals", summary="载体·引荐记录")
|
|
async def carrier_referrals(
|
|
db: Database = Depends(get_db),
|
|
user: dict = Depends(require_roles("carrier")),
|
|
):
|
|
carrier_org = await get_user_primary_org(db, user, org_type="carrier")
|
|
carrier_id = carrier_org["org_id"] if carrier_org else user.get("org_id")
|
|
return {"items": await db.referrals.list_for(carrier_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")),
|
|
):
|
|
carrier_org = await get_user_primary_org(db, actor, org_type="carrier")
|
|
carrier_id = carrier_org["org_id"] if carrier_org else actor.get("org_id") or actor["id"]
|
|
referral = await db.referrals.create({
|
|
**req.model_dump(exclude_none=True), "carrier_id": carrier_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
|
|
|
|
|
|
# ── 载体·任务管理(平台级任务展示 + 本载体下架/上架;不可编辑任务内容)────────
|
|
def _carrier_park_id(user: dict) -> str:
|
|
"""载体用户 → 其管理的园区 id(取第一个有效园区成员关系)。"""
|
|
return user.get("park_id") or user.get("tenant_id") or ""
|
|
|
|
|
|
async def _resolve_carrier_park(db: Database, user: dict) -> str:
|
|
"""解析载体用户所属园区:优先 organization_members 中的 carrier 组织,回退旧表。"""
|
|
carrier_org = await get_user_primary_org(db, user, org_type="carrier")
|
|
if carrier_org:
|
|
return carrier_org["org_id"]
|
|
park_id = _carrier_park_id(user)
|
|
if park_id:
|
|
return park_id
|
|
members = await db.park_members.list_by_user(user["id"])
|
|
return members["park_id"] if members else ""
|
|
|
|
|
|
def _is_platform_task(t: dict) -> bool:
|
|
"""平台级任务:非本园区专属、公开可下发的任务(园区专属 park_id 任务不算)。"""
|
|
if t.get("park_id"):
|
|
return False
|
|
return t.get("visibility", "public") in ("public", "c_visible") or t.get("park_public")
|
|
|
|
|
|
@router.get("/carrier/tasks", summary="载体端任务管理(平台级任务 + 本载体下架状态)")
|
|
async def carrier_tasks(
|
|
db: Database = Depends(get_db),
|
|
user: dict = Depends(require_roles("carrier")),
|
|
):
|
|
park_id = await _resolve_carrier_park(db, user)
|
|
tasks = [t for t in await db.tasks.list_published()
|
|
if t.get("status") in ("published", "claimed", "doing") and _is_platform_task(t)]
|
|
hidden = await db.task_park_visibility.hidden_ids([t["id"] for t in tasks], park_id)
|
|
items = []
|
|
for t in tasks:
|
|
items.append({
|
|
"id": t["id"], "task_code": t.get("task_code", ""), "title": t.get("title", ""),
|
|
"category": t.get("category", ""), "sub_category": t.get("sub_category", ""),
|
|
"mode": t.get("mode", ""), "status": t.get("status", ""),
|
|
"budget_min": t.get("budget_min", 0), "budget_max": t.get("budget_max", 0),
|
|
"deadline": t.get("deadline", ""), "delivery_days": t.get("delivery_days", 0),
|
|
"headcount": t.get("headcount", 0), "publisher_name": t.get("publisher_name", ""),
|
|
"publisher_role": t.get("publisher_role", ""),
|
|
"display_priority": t.get("display_priority", 0),
|
|
"hidden": t["id"] in hidden,
|
|
})
|
|
items.sort(key=lambda x: -x.get("display_priority", 0))
|
|
return {"items": items, "park_id": park_id}
|
|
|
|
|
|
async def _carrier_task_visibility(
|
|
task_id: str, visible: bool, request: Request,
|
|
db: Database, actor: dict,
|
|
) -> dict:
|
|
park_id = await _resolve_carrier_park(db, actor)
|
|
if not park_id:
|
|
raise HTTPException(status_code=400, detail="载体未绑定园区,无法管理任务展示")
|
|
task = await db.tasks.get(task_id)
|
|
if task is None:
|
|
raise HTTPException(status_code=404, detail="任务不存在")
|
|
if not _is_platform_task(task):
|
|
raise HTTPException(status_code=403, detail="仅平台级任务可在载体端管理展示")
|
|
rec = await db.task_park_visibility.set(task_id, park_id, visible, actor["id"])
|
|
await write_audit(db, action="carrier.task.visibility", resource="task", resource_id=task_id,
|
|
detail=f"visible={visible} park={park_id}", user=actor, request=request)
|
|
return {"ok": True, "task_id": task_id, "park_id": park_id, "hidden": not visible}
|
|
|
|
|
|
@router.post("/carrier/tasks/{task_id}/hide", summary="载体端任务下架(本载体隐藏,不影响其他端)")
|
|
async def carrier_task_hide(
|
|
task_id: str,
|
|
request: Request,
|
|
db: Database = Depends(get_db),
|
|
actor: dict = Depends(require_roles("carrier")),
|
|
):
|
|
return await _carrier_task_visibility(task_id, False, request, db, actor)
|
|
|
|
|
|
@router.post("/carrier/tasks/{task_id}/unhide", summary="载体端任务上架(恢复本载体展示)")
|
|
async def carrier_task_unhide(
|
|
task_id: str,
|
|
request: Request,
|
|
db: Database = Depends(get_db),
|
|
actor: dict = Depends(require_roles("carrier")),
|
|
):
|
|
return await _carrier_task_visibility(task_id, True, request, db, actor)
|