refactor(auth): 账号类型彻底收敛为四种 + 各端口守卫改 service(去除冗余角色)

- 迁移守卫: provider/enterprise/government/investor/developer 一律 require_roles("service")
- token/profile.role 归一为四种(operator/carrier/opc_member/service); require_roles 与之一致
- 账号类型集中 app/domain/account_types.py; 子角色停用
This commit is contained in:
Pine
2026-08-26 13:15:06 +08:00
parent 00f43976b3
commit f6f2008498
6 changed files with 41 additions and 52 deletions
+4 -13
View File
@@ -108,16 +108,7 @@ def _is_phone(account: str) -> bool:
return bool(_PHONE_RE.match(account.strip()))
# 账户管理页可编辑的资料字段(username 作为登录名走 update_credentials,单独处理)
_PROFILE_FIELDS = (
"nickname",
"account",
"room",
"avatar",
"company_avatar",
"email",
"gender",
"birthday",
)
_PROFILE_FIELDS = ("nickname", "account", "room", "avatar", "company_avatar", "email", "gender", "birthday")
def _identity_summaries(identities: list[dict]) -> list[IdentityInfo]:
@@ -157,7 +148,7 @@ async def _issue_token(
scope_level = await db.regions.level(region_id)
token_user = dict(user)
token_user.update(account_type=atype, role=role, sub_role=None, org_id=org_id, region_id=region_id)
token_user.update(account_type=atype, role=atype, sub_role=None, org_id=org_id, region_id=region_id)
await db.users.mark_login(user["id"]) # 记录最近登录时间(每次签发令牌=一次活动)
return await db.tokens.create(
token_user,
@@ -199,9 +190,9 @@ async def _profile_for(
profile["affiliation"] = user.get("affiliation", "independent")
profile["park_id"] = user.get("park_id", "")
profile["park_name"] = user.get("park_name", "")
profile["account_type"] = atype # 四种账号类型(operator/carrier/opc/service
profile["account_type"] = atype # 四种账号类型(operator/carrier/opc/"service"
profile["account_type_label"] = account_type_label(atype)
profile["role"] = role # 端口角色(兼容守卫)
profile["role"] = atype # 账号类型(四种)
profile["sub_role"] = None # 子角色已停用
profile["org_id"] = org_id
profile["region_id"] = region_id
+5 -5
View File
@@ -19,27 +19,27 @@ async def _page(port: str, page: str):
@router.get("/dashboard", summary="开发者工作台")
async def dev_dashboard(db: Database = Depends(get_db), _u: dict = Depends(require_roles("developer"))):
async def dev_dashboard(db: Database = Depends(get_db), _u: dict = Depends(require_roles("service"))):
return await db.portal_pages.get("developer", "dashboard") or {"stats": []}
@router.get("/apps", summary="我的应用")
async def dev_apps(db: Database = Depends(get_db), _u: dict = Depends(require_roles("developer"))):
async def dev_apps(db: Database = Depends(get_db), _u: dict = Depends(require_roles("service"))):
return await db.portal_pages.get("developer", "apps") or {"items": []}
@router.get("/plugins", summary="我的插件")
async def dev_plugins(db: Database = Depends(get_db), _u: dict = Depends(require_roles("developer"))):
async def dev_plugins(db: Database = Depends(get_db), _u: dict = Depends(require_roles("service"))):
return await db.portal_pages.get("developer", "plugins") or {"items": []}
@router.get("/skills", summary="技能市场")
async def dev_skills(db: Database = Depends(get_db), _u: dict = Depends(require_roles("developer"))):
async def dev_skills(db: Database = Depends(get_db), _u: dict = Depends(require_roles("service"))):
return await db.portal_pages.get("developer", "skills") or {"items": []}
@router.get("/api-keys", summary="API 凭证")
async def dev_api_keys(db: Database = Depends(get_db), _u: dict = Depends(require_roles("developer"))):
async def dev_api_keys(db: Database = Depends(get_db), _u: dict = Depends(require_roles("service"))):
return {"items": [
{"id": "key_001", "name": "生产环境", "prefix": "pa_live_****abcd", "created_at": "2026-07-01"},
{"id": "key_002", "name": "测试环境", "prefix": "pa_test_****wxyz", "created_at": "2026-07-05"},
+8 -8
View File
@@ -13,7 +13,7 @@ from ...infrastructure.repositories import Database
router = APIRouter(tags=["ecosystem"])
_ANY = ("opc_member", "carrier", "enterprise", "provider", "government", "operator", "investor")
_ANY = ("opc_member", "carrier", "service", "operator")
# ── 通知中心 ──────────────────────────────────────────────────────────────
@@ -51,7 +51,7 @@ async def rate_task(
req: RatingRequest,
request: Request,
db: Database = Depends(get_db),
actor: dict = Depends(require_roles("enterprise", "opc_member")),
actor: dict = Depends(require_roles("service", "opc_member")),
):
if not req.to_id:
raise HTTPException(status_code=400, detail="缺少被评对象")
@@ -66,7 +66,7 @@ async def rate_task(
async def get_contract(
task_id: str,
db: Database = Depends(get_db),
_u: dict = Depends(require_roles("enterprise", "opc_member")),
_u: dict = Depends(require_roles("service", "opc_member")),
):
contract = await db.contracts.get_for_task(task_id)
if contract is None:
@@ -80,7 +80,7 @@ async def sign_contract(
req: SignRequest,
request: Request,
db: Database = Depends(get_db),
actor: dict = Depends(require_roles("enterprise", "opc_member")),
actor: dict = Depends(require_roles("service", "opc_member")),
):
from ...services.settlement_service import SettlementService
@@ -96,7 +96,7 @@ async def release_escrow(
task_id: str,
request: Request,
db: Database = Depends(get_db),
actor: dict = Depends(require_roles("enterprise")),
actor: dict = Depends(require_roles("service")),
):
from ...services.settlement_service import SettlementService
@@ -122,7 +122,7 @@ async def create_dispute(
req: DisputeRequest,
request: Request,
db: Database = Depends(get_db),
actor: dict = Depends(require_roles("enterprise", "opc_member")),
actor: dict = Depends(require_roles("service", "opc_member")),
):
from ...services.settlement_service import SettlementService
@@ -151,7 +151,7 @@ async def resolve_dispute(
@router.get("/investor/matches", summary="投资撮合(按偏好推荐)")
async def investor_matches(
db: Database = Depends(get_db),
user: dict = Depends(require_roles("investor")),
user: dict = Depends(require_roles("service")),
):
from ...services.settlement_service import SettlementService
@@ -164,7 +164,7 @@ async def join_roadshow(
roadshow_id: str,
request: Request,
db: Database = Depends(get_db),
actor: dict = Depends(require_roles("investor", "government", "operator", "carrier", "enterprise")),
actor: dict = Depends(require_roles("service", "operator", "carrier")),
):
rs = await db.roadshows.get(roadshow_id)
if rs is None:
+12 -12
View File
@@ -19,7 +19,7 @@ from ...infrastructure.repositories import Database
router = APIRouter(prefix="/investor", tags=["investor"])
# 可发布投融资活动的端口角色(审核规则见 _need_review
_ROADSHOW_ROLES = ("investor", "government", "operator", "carrier", "enterprise")
_ROADSHOW_ROLES = ("service", "operator", "carrier")
def _need_review(user: dict, req: RoadshowCreate, db: Database) -> bool:
@@ -27,7 +27,7 @@ def _need_review(user: dict, req: RoadshowCreate, db: Database) -> bool:
role = user.get("role")
if role == "operator":
return False
if role == "government":
if role == "service":
scope = user.get("scope_region_ids", [])
if req.scope_type == "region" and req.region_id and req.region_id in scope:
return False # 自身权限范围内,免审
@@ -43,7 +43,7 @@ def _pub_status(need_review: bool) -> str:
@router.get("/dashboard", summary="投资人工作台")
async def investor_dashboard(
db: Database = Depends(get_db),
user: dict = Depends(require_roles("investor")),
user: dict = Depends(require_roles("service")),
):
pref = await db.investor_prefs.get(user["id"]) or {}
portfolio = await db.portal_pages.get("investor", "portfolio") or {}
@@ -59,7 +59,7 @@ async def investor_dashboard(
@router.get("/preferences", summary="投资方向")
async def get_preferences(
db: Database = Depends(get_db),
user: dict = Depends(require_roles("investor")),
user: dict = Depends(require_roles("service")),
):
return await db.investor_prefs.get(user["id"]) or {}
@@ -69,7 +69,7 @@ async def set_preferences(
req: PreferenceRequest,
request: Request,
db: Database = Depends(get_db),
actor: dict = Depends(require_roles("investor")),
actor: dict = Depends(require_roles("service")),
):
pref = await db.investor_prefs.upsert(actor["id"], req.model_dump(exclude_none=True))
await write_audit(db, action="preference.update", resource="investor", resource_id=actor["id"],
@@ -80,7 +80,7 @@ async def set_preferences(
@router.get("/projects", summary="项目库")
async def list_projects(
db: Database = Depends(get_db),
_u: dict = Depends(require_roles("investor")),
_u: dict = Depends(require_roles("service")),
):
return await db.portal_pages.get("investor", "projects") or {"items": []}
@@ -88,7 +88,7 @@ async def list_projects(
@router.get("/trainings", summary="投融资培训")
async def list_trainings(
db: Database = Depends(get_db),
_u: dict = Depends(require_roles("investor")),
_u: dict = Depends(require_roles("service")),
):
return await db.portal_pages.get("investor", "trainings") or {"items": []}
@@ -98,7 +98,7 @@ async def enroll_training(
training_id: str,
request: Request,
db: Database = Depends(get_db),
actor: dict = Depends(require_roles("investor")),
actor: dict = Depends(require_roles("service")),
):
data = await db.portal_pages.get("investor", "trainings") or {"items": []}
item = next((it for it in data.get("items", []) if it.get("title") == training_id), None)
@@ -112,7 +112,7 @@ async def enroll_training(
@router.get("/my-trainings", summary="我的培训")
async def my_trainings(
db: Database = Depends(get_db),
user: dict = Depends(require_roles("investor")),
user: dict = Depends(require_roles("service")),
):
return {"items": await db.training_enrolls.list_for(user["id"])}
@@ -120,7 +120,7 @@ async def my_trainings(
@router.get("/portfolio", summary="投资组合")
async def portfolio(
db: Database = Depends(get_db),
_u: dict = Depends(require_roles("investor")),
_u: dict = Depends(require_roles("service")),
):
return await db.portal_pages.get("investor", "portfolio") or {"items": []}
@@ -131,7 +131,7 @@ async def create_intent(
req: IntentRequest,
request: Request,
db: Database = Depends(get_db),
actor: dict = Depends(require_roles("investor")),
actor: dict = Depends(require_roles("service")),
):
intent = await db.intents.create(
actor["id"], req.project_id or project_id, req.project_name, req.message,
@@ -144,7 +144,7 @@ async def create_intent(
@router.get("/intents", summary="我的投资意向")
async def list_intents(
db: Database = Depends(get_db),
user: dict = Depends(require_roles("investor")),
user: dict = Depends(require_roles("service")),
):
return {"items": await db.intents.list_for(user["id"])}
+6 -8
View File
@@ -16,9 +16,7 @@ router = APIRouter(tags=["org"])
@router.get("/org/me", summary="当前组织信息")
async def org_me(
db: Database = Depends(get_db),
user: dict = Depends(require_roles(
"opc_member", "carrier", "enterprise", "provider", "government", "operator",
)),
user: dict = Depends(require_roles("opc_member", "carrier", "service", "operator")),
):
org = await db.orgs.get(user["org_id"]) if user.get("org_id") else None
region = await db.regions.get(user["region_id"]) if user.get("region_id") else None
@@ -34,7 +32,7 @@ async def org_me(
@router.get("/enterprise/profile", summary="企业资料")
async def enterprise_profile(
db: Database = Depends(get_db),
user: dict = Depends(require_roles("enterprise")),
user: dict = Depends(require_roles("service")),
):
org = await db.orgs.get(user["org_id"]) if user.get("org_id") else None
if org is None:
@@ -55,7 +53,7 @@ async def carrier_enterprises(
@router.get("/provider/orders", summary="服务商订单(骨架)")
async def provider_orders(
user: dict = Depends(require_roles("provider")),
user: dict = Depends(require_roles("service")),
):
"""占位:服务商仅返回本 org 的订单(业务逻辑后续阶段补齐)。"""
return {"org_id": user.get("org_id"), "orders": []}
@@ -67,7 +65,7 @@ async def provider_orders(
async def org_members(
org_id: str,
db: Database = Depends(get_db),
user: dict = Depends(require_roles("enterprise", "carrier", "provider", "investor", "operator")),
user: dict = Depends(require_roles("service", "carrier", "operator")),
):
# 机构管理员本人 或 平台运营 可查看
if user.get("role") not in ("operator",) and not await db.org_members.is_admin(org_id, user["id"]):
@@ -80,7 +78,7 @@ async def org_add_member(
org_id: str,
req: OrgMemberAdd,
db: Database = Depends(get_db),
user: dict = Depends(require_roles("enterprise", "carrier", "provider", "investor", "operator")),
user: dict = Depends(require_roles("service", "carrier", "operator")),
):
if user.get("role") not in ("operator",) and not await db.org_members.is_admin(org_id, user["id"]):
raise HTTPException(status_code=403, detail="Forbidden: not org admin")
@@ -98,7 +96,7 @@ async def org_add_member(
@router.get("/me/orgs", summary="我加入的机构")
async def my_orgs(
db: Database = Depends(get_db),
user: dict = Depends(require_roles("enterprise", "carrier", "provider", "investor")),
user: dict = Depends(require_roles("service", "carrier")),
):
"""返回当前账号挂靠的所有机构及成员角色(支持一账号多机构)。"""
from ...services.org_service import OrgService
+6 -6
View File
@@ -79,7 +79,7 @@ router.get("/government/policies", summary="政务·政策管理")(_page("govern
@router.get("/enterprise/tasks", summary="企业·我的任务列表")
async def enterprise_tasks(
db: Database = Depends(get_db),
_u: dict = Depends(require_roles("enterprise")),
_u: dict = Depends(require_roles("service")),
):
return {"items": await db.tasks.list(status="published")}
@@ -89,7 +89,7 @@ async def enterprise_create_task(
req: EnterpriseTaskCreate,
request: Request,
db: Database = Depends(get_db),
actor: dict = Depends(require_roles("enterprise")),
actor: dict = Depends(require_roles("service")),
):
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"],
@@ -102,7 +102,7 @@ async def enterprise_submit_task(
task_id: str,
request: Request,
db: Database = Depends(get_db),
actor: dict = Depends(require_roles("enterprise")),
actor: dict = Depends(require_roles("service")),
):
task = await db.tasks.set_status(task_id, "published")
if task is None:
@@ -116,7 +116,7 @@ async def enterprise_submit_task(
async def enterprise_list_bids(
task_id: str,
db: Database = Depends(get_db),
_u: dict = Depends(require_roles("enterprise")),
_u: dict = Depends(require_roles("service")),
):
return {"items": await db.bids.list_for_task(task_id)}
@@ -126,7 +126,7 @@ async def enterprise_win_bid(
task_id: str, bid_id: str,
request: Request,
db: Database = Depends(get_db),
actor: dict = Depends(require_roles("enterprise")),
actor: dict = Depends(require_roles("service")),
):
bid = await db.bids.set_status(bid_id, "win")
if bid is None:
@@ -143,7 +143,7 @@ async def enterprise_review_task(
req: TaskReviewRequest,
request: Request,
db: Database = Depends(get_db),
actor: dict = Depends(require_roles("enterprise")),
actor: dict = Depends(require_roles("service")),
):
task = await db.tasks.get(task_id)
if task is None: