# -*- coding: utf-8 -*- """SQLAlchemy ORM 模型(auth/RBAC 域)+ Pydantic 请求/响应模型。 - ORM 模型对应 SQLite 各表(users/roles/permissions/organizations/regions/ sessions/audit_logs/agents)。 - Pydantic 模型与 PineAgents 主后端 ``src/pineagents/app/routers/auth.py`` 中的请求/响应模型一一对应,转发层透传时不会丢字段。 """ from __future__ import annotations from pydantic import BaseModel, Field from sqlalchemy import ( Boolean, Float, ForeignKey, Integer, PrimaryKeyConstraint, String, Text, ) from sqlalchemy.orm import Mapped, mapped_column from .db import Base # =========================================================================== # SQLAlchemy ORM 模型 # =========================================================================== class User(Base): __tablename__ = "users" __table_args__ = {"sqlite_autoincrement": True} id: Mapped[str] = mapped_column(String, primary_key=True) username: Mapped[str] = mapped_column(String, unique=True, nullable=False) password_hash: Mapped[str] = mapped_column(String, nullable=False) password_salt: Mapped[str] = mapped_column(String, nullable=False) nickname: Mapped[str] = mapped_column(String, default="") account: Mapped[str] = mapped_column(String, default="") company: Mapped[str] = mapped_column(String, default="") room: Mapped[str] = mapped_column(String, default="") avatar: Mapped[str] = mapped_column(String, default="") company_avatar: Mapped[str] = mapped_column(String, default="") # RBAC role: Mapped[str] = mapped_column(String, default="opc_member") # 业务角色 sub_role: Mapped[str] = mapped_column(String, nullable=True) # 政务级/运营方内部 org_id: Mapped[str | None] = mapped_column(ForeignKey("organizations.id"), nullable=True) region_id: Mapped[str | None] = mapped_column(ForeignKey("regions.id"), nullable=True) status: Mapped[str] = mapped_column(String, default="active") # active | disabled token_version: Mapped[int] = mapped_column(Integer, default=0) # revoke-all 递增 created_at: Mapped[str] = mapped_column(String, default="") updated_at: Mapped[str] = mapped_column(String, default="") class Role(Base): __tablename__ = "roles" id: Mapped[str] = mapped_column(String, primary_key=True) # 角色码 name: Mapped[str] = mapped_column(String, nullable=False) # 展示名 scope_type: Mapped[str] = mapped_column(String, default="business") # business|operator-internal|government-level description: Mapped[str] = mapped_column(String, default="") class Permission(Base): __tablename__ = "permissions" id: Mapped[str] = mapped_column(String, primary_key=True) # 权限码 menu:xx / action:xx name: Mapped[str] = mapped_column(String, nullable=False) category: Mapped[str] = mapped_column(String, default="action") # menu | action module: Mapped[str] = mapped_column(String, default="") # admin|government|... description: Mapped[str] = mapped_column(String, default="") class RolePermission(Base): __tablename__ = "role_permissions" role_id: Mapped[str] = mapped_column( ForeignKey("roles.id", ondelete="CASCADE"), primary_key=True, ) permission_id: Mapped[str] = mapped_column( ForeignKey("permissions.id", ondelete="CASCADE"), primary_key=True, ) class Organization(Base): __tablename__ = "organizations" id: Mapped[str] = mapped_column(String, primary_key=True) name: Mapped[str] = mapped_column(String, nullable=False) type: Mapped[str] = mapped_column(String, default="enterprise") # enterprise|carrier|provider region_id: Mapped[str | None] = mapped_column(ForeignKey("regions.id"), nullable=True) parent_id: Mapped[str | None] = mapped_column(ForeignKey("organizations.id"), nullable=True) status: Mapped[str] = mapped_column(String, default="active") created_at: Mapped[str] = mapped_column(String, default="") class OrganizationMember(Base): """机构成员(机构主账号 + 机构内子账号):一个账号可挂多个机构。""" __tablename__ = "organization_members" org_id: Mapped[str] = mapped_column( ForeignKey("organizations.id", ondelete="CASCADE"), primary_key=True, ) user_id: Mapped[str] = mapped_column( ForeignKey("users.id", ondelete="CASCADE"), primary_key=True, ) role: Mapped[str] = mapped_column(String, default="member") # admin/finance/publisher/service_executor/service_deliverer/clerk/... is_admin: Mapped[bool] = mapped_column(Boolean, default=False) # 机构主账号/管理员 status: Mapped[str] = mapped_column(String, default="active") joined_at: Mapped[str] = mapped_column(String, default="") class Region(Base): __tablename__ = "regions" id: Mapped[str] = mapped_column(String, primary_key=True) name: Mapped[str] = mapped_column(String, nullable=False) level: Mapped[str] = mapped_column(String, default="district") # province|city|district parent_id: Mapped[str | None] = mapped_column(ForeignKey("regions.id"), nullable=True) created_at: Mapped[str] = mapped_column(String, default="") class SessionToken(Base): __tablename__ = "sessions" id: Mapped[str] = mapped_column(String, primary_key=True) jti: Mapped[str] = mapped_column(String, unique=True, nullable=False) token: Mapped[str] = mapped_column(String, default="") # 完整 JWT(调试用) user_id: Mapped[str] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), nullable=False) username: Mapped[str] = mapped_column(String, default="") created_at: Mapped[str] = mapped_column(String, default="") expires_at: Mapped[str] = mapped_column(String, default="") revoked: Mapped[bool] = mapped_column(Boolean, default=False) class AuditLog(Base): __tablename__ = "audit_logs" id: Mapped[str] = mapped_column(String, primary_key=True) user_id: Mapped[str | None] = mapped_column(ForeignKey("users.id"), nullable=True) action: Mapped[str] = mapped_column(String, default="") # login|role.assign|data.view|... resource: Mapped[str] = mapped_column(String, default="") # user|role|enterprise|region|... resource_id: Mapped[str] = mapped_column(String, default="") detail: Mapped[str] = mapped_column(Text, default="") ip: Mapped[str] = mapped_column(String, default="") created_at: Mapped[str] = mapped_column(String, default="") class Agent(Base): __tablename__ = "agents" # id + user_id + port 复合主键:每个用户在每个端口持有独立的默认/QA 智能体(多端口彻底隔离) __table_args__ = (PrimaryKeyConstraint("id", "user_id", "port"),) id: Mapped[str] = mapped_column(String) user_id: Mapped[str] = mapped_column( ForeignKey("users.id", ondelete="CASCADE"), nullable=False, ) port: Mapped[str] = mapped_column(String, default="opc") # opc/operator/government/... 端口隔离 name: Mapped[str] = mapped_column(String, default="") description: Mapped[str] = mapped_column(Text, default="") language: Mapped[str] = mapped_column(String, default="zh") model_name: Mapped[str] = mapped_column(String, default="") deletable: Mapped[bool] = mapped_column(Boolean, default=True) use_fixed_soul: Mapped[bool] = mapped_column(Boolean, default=False) created_at: Mapped[str] = mapped_column(String, default="") updated_at: Mapped[str] = mapped_column(String, default="") # ── 运营端业务模型(平台运营/任务/服务商/内容/配置)──────────────────────── class Task(Base): """平台任务(运营端可审核/下架;后续各端口接入)。""" __tablename__ = "tasks" id: Mapped[str] = mapped_column(String, primary_key=True) title: Mapped[str] = mapped_column(String, default="") category: Mapped[str] = mapped_column(String, default="") # 设计创意/技术开发/... sub_category: Mapped[str] = mapped_column(String, default="") description: Mapped[str] = mapped_column(Text, default="") mode: Mapped[str] = mapped_column(String, default="grab") # grab/bid/designated/dispatch budget_min: Mapped[int] = mapped_column(Integer, default=0) budget_max: Mapped[int] = mapped_column(Integer, default=0) deadline: Mapped[str] = mapped_column(String, default="") status: Mapped[str] = mapped_column(String, default="draft") # draft/pending/review/published/completed/cancelled publisher_org_id: Mapped[str | None] = mapped_column(ForeignKey("organizations.id"), nullable=True) publisher_name: Mapped[str] = mapped_column(String, default="") created_at: Mapped[str] = mapped_column(String, default="") updated_at: Mapped[str] = mapped_column(String, default="") class ServiceProvider(Base): """服务商(运营端审核/评级/考核)。""" __tablename__ = "service_providers" id: Mapped[str] = mapped_column(String, primary_key=True) name: Mapped[str] = mapped_column(String, default="") category: Mapped[str] = mapped_column(String, default="") # 财税/工商/法务/知识产权/云服务... org_id: Mapped[str | None] = mapped_column(ForeignKey("organizations.id"), nullable=True) level: Mapped[str] = mapped_column(String, default="certified") # certified/premium/gold/official rating: Mapped[float] = mapped_column(Float, default=5.0) order_count: Mapped[int] = mapped_column(Integer, default=0) status: Mapped[str] = mapped_column(String, default="pending") # pending/active/blacklisted contact: Mapped[str] = mapped_column(String, default="") created_at: Mapped[str] = mapped_column(String, default="") updated_at: Mapped[str] = mapped_column(String, default="") class ContentItem(Base): """平台内容(政策/课程/资讯/公告/活动)。""" __tablename__ = "content_items" id: Mapped[str] = mapped_column(String, primary_key=True) type: Mapped[str] = mapped_column(String, default="news") # policy/course/news/notice/activity title: Mapped[str] = mapped_column(String, default="") summary: Mapped[str] = mapped_column(String, default="") body: Mapped[str] = mapped_column(Text, default="") publisher_id: Mapped[str | None] = mapped_column(ForeignKey("users.id"), nullable=True) status: Mapped[str] = mapped_column(String, default="draft") # draft/published/offline created_at: Mapped[str] = mapped_column(String, default="") updated_at: Mapped[str] = mapped_column(String, default="") class SystemConfig(Base): """平台系统配置(键值)。""" __tablename__ = "system_configs" key: Mapped[str] = mapped_column(String, primary_key=True) value: Mapped[str] = mapped_column(String, default="") description: Mapped[str] = mapped_column(String, default="") updated_at: Mapped[str] = mapped_column(String, default="") class PortalDashboard(Base): """端口工作台聚合数据(按端口存 JSON 载荷,全部由服务端提供)。""" __tablename__ = "portal_dashboards" port: Mapped[str] = mapped_column(String, primary_key=True) payload: Mapped[str] = mapped_column(Text, default="{}") updated_at: Mapped[str] = mapped_column(String, default="") class PortalPage(Base): """端口子页面数据(按 端口+页面 存 JSON 载荷,全部由服务端提供)。""" __tablename__ = "portal_pages" port: Mapped[str] = mapped_column(String, primary_key=True) page: Mapped[str] = mapped_column(String, primary_key=True) payload: Mapped[str] = mapped_column(Text, default="{}") updated_at: Mapped[str] = mapped_column(String, default="") # ── 投资人端(投融资 · 路演 · 培训)────────────────────────────────────── class InvestorPreference(Base): """投资人投资方向偏好。""" __tablename__ = "investor_preferences" user_id: Mapped[str] = mapped_column( ForeignKey("users.id", ondelete="CASCADE"), primary_key=True, ) industries: Mapped[str] = mapped_column(String, default="[]") # JSON list stage: Mapped[str] = mapped_column(String, default="") # seed/angel/series_a/... amount_min: Mapped[int] = mapped_column(Integer, default=0) amount_max: Mapped[int] = mapped_column(Integer, default=0) region_id: Mapped[str | None] = mapped_column(ForeignKey("regions.id"), nullable=True) updated_at: Mapped[str] = mapped_column(String, default="") class Roadshow(Base): """投融资路演活动(线上/线下/混合),支持按发布主体与范围审核。""" __tablename__ = "roadshows" id: Mapped[str] = mapped_column(String, primary_key=True) publisher_id: Mapped[str] = mapped_column(String, default="") publisher_role: Mapped[str] = mapped_column(String, default="investor") # investor/government/operator/carrier/enterprise title: Mapped[str] = mapped_column(String, default="") summary: Mapped[str] = mapped_column(String, default="") activity_type: Mapped[str] = mapped_column(String, default="online") # online/offline/hybrid scope_type: Mapped[str] = mapped_column(String, default="all") # all/region/org region_id: Mapped[str | None] = mapped_column(ForeignKey("regions.id"), nullable=True) start_at: Mapped[str] = mapped_column(String, default="") end_at: Mapped[str] = mapped_column(String, default="") register_deadline: Mapped[str] = mapped_column(String, default="") quota: Mapped[int] = mapped_column(Integer, default=0) venue: Mapped[str] = mapped_column(String, default="") # 线下地点/直播间 live_url: Mapped[str] = mapped_column(String, default="") status: Mapped[str] = mapped_column(String, default="draft") # draft/submitted/reviewing/published/registering/ongoing/ended/archived/rejected/cancelled review_comment: Mapped[str] = mapped_column(String, default="") need_review: Mapped[bool] = mapped_column(Boolean, default=True) created_at: Mapped[str] = mapped_column(String, default="") updated_at: Mapped[str] = mapped_column(String, default="") class RoadshowRegistration(Base): """路演活动报名(项目方/投资人)。""" __tablename__ = "roadshow_registrations" id: Mapped[str] = mapped_column(String, primary_key=True) roadshow_id: Mapped[str] = mapped_column( ForeignKey("roadshows.id", ondelete="CASCADE"), nullable=False, ) user_id: Mapped[str] = mapped_column( ForeignKey("users.id", ondelete="CASCADE"), nullable=False, ) role: Mapped[str] = mapped_column(String, default="investor") # investor/project status: Mapped[str] = mapped_column(String, default="applying") # applying/approved/attended/absent note: Mapped[str] = mapped_column(String, default="") created_at: Mapped[str] = mapped_column(String, default="") class InvestmentIntent(Base): """投资意向/约谈/尽调。""" __tablename__ = "investment_intents" id: Mapped[str] = mapped_column(String, primary_key=True) investor_id: Mapped[str] = mapped_column( ForeignKey("users.id", ondelete="CASCADE"), nullable=False, ) project_id: Mapped[str] = mapped_column(String, default="") project_name: Mapped[str] = mapped_column(String, default="") status: Mapped[str] = mapped_column(String, default="interested") # interested/contacted/meeting/term_sheet/invested/declined message: Mapped[str] = mapped_column(String, default="") created_at: Mapped[str] = mapped_column(String, default="") updated_at: Mapped[str] = mapped_column(String, default="") class Bid(Base): """任务竞标(企业端竞标模式发标)。""" __tablename__ = "bids" id: Mapped[str] = mapped_column(String, primary_key=True) task_id: Mapped[str] = mapped_column( ForeignKey("tasks.id", ondelete="CASCADE"), nullable=False, ) opc_id: Mapped[str] = mapped_column(String, default="") opc_name: Mapped[str] = mapped_column(String, default="") quote: Mapped[int] = mapped_column(Integer, default=0) plan: Mapped[str] = mapped_column(String, default="") status: Mapped[str] = mapped_column(String, default="submitted") # submitted/win/lost created_at: Mapped[str] = mapped_column(String, default="") updated_at: Mapped[str] = mapped_column(String, default="") class SubsidyApplication(Base): """补贴申报(政务三级审批:区县→市→省→发放)。""" __tablename__ = "subsidy_applications" id: Mapped[str] = mapped_column(String, primary_key=True) opc_id: Mapped[str] = mapped_column(String, default="") opc_name: Mapped[str] = mapped_column(String, default="") title: Mapped[str] = mapped_column(String, default="") amount: Mapped[int] = mapped_column(Integer, default=0) region_id: Mapped[str | None] = mapped_column(ForeignKey("regions.id"), nullable=True) status: Mapped[str] = mapped_column(String, default="applying") # applying/district_review/city_review/province_review/approved/paid/rejected comment: Mapped[str] = mapped_column(String, default="") created_at: Mapped[str] = mapped_column(String, default="") updated_at: Mapped[str] = mapped_column(String, default="") class ServiceReferral(Base): """载体-服务商引荐(三方绑定:载体引荐服务商→服务园区内 OPC)。""" __tablename__ = "service_referrals" id: Mapped[str] = mapped_column(String, primary_key=True) carrier_id: Mapped[str] = mapped_column(String, default="") provider_id: Mapped[str] = mapped_column(String, default="") provider_name: Mapped[str] = mapped_column(String, default="") opc_id: Mapped[str] = mapped_column(String, default="") opc_name: Mapped[str] = mapped_column(String, default="") status: Mapped[str] = mapped_column(String, default="referred") # referred/accepted/completed/declined created_at: Mapped[str] = mapped_column(String, default="") updated_at: Mapped[str] = mapped_column(String, default="") class TrainingEnrollment(Base): """投融资培训报名/证书。""" __tablename__ = "training_enrollments" id: Mapped[str] = mapped_column(String, primary_key=True) training_id: Mapped[str] = mapped_column(String, default="") training_name: Mapped[str] = mapped_column(String, default="") user_id: Mapped[str] = mapped_column( ForeignKey("users.id", ondelete="CASCADE"), nullable=False, ) status: Mapped[str] = mapped_column(String, default="applying") # applying/enrolled/completed/certificate created_at: Mapped[str] = mapped_column(String, default="") # ── 横切能力:结算/合同/争议/评价/通知 ──────────────────────────────────── class Escrow(Base): """任务资金托管/结算。""" __tablename__ = "escrows" id: Mapped[str] = mapped_column(String, primary_key=True) task_id: Mapped[str] = mapped_column( ForeignKey("tasks.id", ondelete="CASCADE"), nullable=False, ) task_title: Mapped[str] = mapped_column(String, default="") amount: Mapped[int] = mapped_column(Integer, default=0) commission: Mapped[int] = mapped_column(Integer, default=0) # 平台佣金 status: Mapped[str] = mapped_column(String, default="deposited") # deposited/frozen/released/refunded created_at: Mapped[str] = mapped_column(String, default="") updated_at: Mapped[str] = mapped_column(String, default="") class Contract(Base): """任务电子合同。""" __tablename__ = "contracts" id: Mapped[str] = mapped_column(String, primary_key=True) task_id: Mapped[str] = mapped_column( ForeignKey("tasks.id", ondelete="CASCADE"), nullable=False, ) task_title: Mapped[str] = mapped_column(String, default="") enterprise_id: Mapped[str] = mapped_column(String, default="") opc_id: Mapped[str] = mapped_column(String, default="") status: Mapped[str] = mapped_column(String, default="signed") # draft/signed/terminated content: Mapped[str] = mapped_column(String, default="") created_at: Mapped[str] = mapped_column(String, default="") class Dispute(Base): """任务争议。""" __tablename__ = "disputes" id: Mapped[str] = mapped_column(String, primary_key=True) task_id: Mapped[str] = mapped_column( ForeignKey("tasks.id", ondelete="CASCADE"), nullable=False, ) task_title: Mapped[str] = mapped_column(String, default="") initiator: Mapped[str] = mapped_column(String, default="") reason: Mapped[str] = mapped_column(String, default="") status: Mapped[str] = mapped_column(String, default="opened") # opened/mediating/resolved/closed resolution: Mapped[str] = mapped_column(String, default="") created_at: Mapped[str] = mapped_column(String, default="") class Rating(Base): """任务互评。""" __tablename__ = "ratings" id: Mapped[str] = mapped_column(String, primary_key=True) task_id: Mapped[str] = mapped_column( ForeignKey("tasks.id", ondelete="CASCADE"), nullable=False, ) from_id: Mapped[str] = mapped_column(String, default="") to_id: Mapped[str] = mapped_column(String, default="") score: Mapped[int] = mapped_column(Integer, default=5) # 1..5 comment: Mapped[str] = mapped_column(String, default="") created_at: Mapped[str] = mapped_column(String, default="") class Notification(Base): """站内通知。""" __tablename__ = "notifications" id: Mapped[str] = mapped_column(String, primary_key=True) user_id: Mapped[str] = mapped_column( ForeignKey("users.id", ondelete="CASCADE"), nullable=False, ) type: Mapped[str] = mapped_column(String, default="system") # task/policy/order/subsidy/system title: Mapped[str] = mapped_column(String, default="") content: Mapped[str] = mapped_column(String, default="") read: Mapped[bool] = mapped_column(Boolean, default=False) created_at: Mapped[str] = mapped_column(String, default="") # ── OPC 超级个体业务模型(工作台/我的任务/财务/消息)──────────────────── class OpcProfile(Base): """OPC 超级个体档案(信用评分等)。""" __tablename__ = "opc_profiles" user_id: Mapped[str] = mapped_column( ForeignKey("users.id", ondelete="CASCADE"), primary_key=True, ) credit_score: Mapped[int] = mapped_column(Integer, default=80) created_at: Mapped[str] = mapped_column(String, default="") updated_at: Mapped[str] = mapped_column(String, default="") class FinanceRecord(Base): """OPC 财务流水(收入/支出)。""" __tablename__ = "finance_records" id: Mapped[str] = mapped_column(String, primary_key=True) user_id: Mapped[str] = mapped_column( ForeignKey("users.id", ondelete="CASCADE"), nullable=False, ) category: Mapped[str] = mapped_column(String, default="income") # income | expense amount: Mapped[int] = mapped_column(Integer, default=0) date: Mapped[str] = mapped_column(String, default="") # YYYY-MM-DD note: Mapped[str] = mapped_column(String, default="") created_at: Mapped[str] = mapped_column(String, default="") class Message(Base): """OPC 站内消息。""" __tablename__ = "messages" id: Mapped[str] = mapped_column(String, primary_key=True) user_id: Mapped[str] = mapped_column( ForeignKey("users.id", ondelete="CASCADE"), nullable=False, ) msg_type: Mapped[str] = mapped_column(String, default="system") # task/policy/service/agent/system title: Mapped[str] = mapped_column(String, default="") content: Mapped[str] = mapped_column(String, default="") read: Mapped[bool] = mapped_column(Boolean, default=False) created_at: Mapped[str] = mapped_column(String, default="") class OpcTask(Base): """OPC 个人任务看板(「我的任务」:进行中/已完成/紧急)。""" __tablename__ = "opc_tasks" id: Mapped[str] = mapped_column(String, primary_key=True) user_id: Mapped[str] = mapped_column( ForeignKey("users.id", ondelete="CASCADE"), nullable=False, ) title: Mapped[str] = mapped_column(String, default="") status: Mapped[str] = mapped_column(String, default="in_progress") # in_progress/completed/urgent budget: Mapped[int] = mapped_column(Integer, default=0) deadline: Mapped[str] = mapped_column(String, default="") progress: Mapped[int] = mapped_column(Integer, default=0) created_at: Mapped[str] = mapped_column(String, default="") updated_at: Mapped[str] = mapped_column(String, default="") class UserIdentity(Base): """账号↔端口绑定身份:一个账号可绑定多个端口/身份。 身份即权限来源:``port`` 为业务端口(operator/government/provider/ enterprise/carrier/opc),``role``=端口角色码(默认等于 port,供 require_roles 匹配),``sub_role`` 为端口内细分角色(如 op_super_admin、 gov_province、企业/园区/服务商的 admin/operator、OPC 的 certified/ independent 等)。一个账号对应多行,登录后按所选身份签发令牌。 """ __tablename__ = "user_identities" id: Mapped[str] = mapped_column(String, primary_key=True) user_id: Mapped[str] = mapped_column( ForeignKey("users.id", ondelete="CASCADE"), nullable=False, ) port: Mapped[str] = mapped_column(String, default="opc") # operator/government/provider/enterprise/carrier/opc role: Mapped[str] = mapped_column(String, default="opc_member") # RBAC 角色码 sub_role: Mapped[str | None] = mapped_column(String, nullable=True) # 端口内细分角色 name: Mapped[str] = mapped_column(String, default="") # 身份展示名 org_id: Mapped[str | None] = mapped_column(ForeignKey("organizations.id"), nullable=True) region_id: Mapped[str | None] = mapped_column(ForeignKey("regions.id"), nullable=True) status: Mapped[str] = mapped_column(String, default="active") # active | disabled created_at: Mapped[str] = mapped_column(String, default="") updated_at: Mapped[str] = mapped_column(String, default="") # =========================================================================== # Pydantic 请求/响应模型 # =========================================================================== class LoginRequest(BaseModel): username: str password: str expires_in: int | None = Field( default=None, description="令牌有效期(秒),-1/0 表示永久,省略为 7 天", ) class RegisterRequest(BaseModel): username: str password: str expires_in: int | None = Field( default=None, description="令牌有效期(秒),-1/0 表示永久,省略为 7 天", ) class UpdateProfileRequest(BaseModel): """更新资料/凭据。``current_password`` 仅在修改用户名/密码时校验。""" current_password: str = "" new_username: str | None = None new_password: str | None = None expires_in: int | None = None # 演示用户资料字段(账户管理页可编辑) nickname: str | None = None account: str | None = None company: str | None = None room: str | None = None avatar: str | None = None company_avatar: str | None = None class RevokeTokenRequest(BaseModel): token: str | None = Field( default=None, description="要吊销的令牌;省略则吊销当前令牌", ) class AgentCreate(BaseModel): """创建智能体身份。""" name: str description: str = "" language: str = "zh" model_name: str = "" class AgentUpdate(BaseModel): """更新智能体身份字段(可空字段,仅提交需要变更的项)。""" name: str | None = None description: str | None = None language: str | None = None model_name: str | None = None use_fixed_soul: bool | None = None class AgentInfo(BaseModel): """智能体身份记录(按用户隔离)。 - ``deletable``:是否允许删除(默认智能体由服务端保护为不可删)。 - ``use_fixed_soul``:该智能体是否**必须**使用服务端托管、最高优先级的 ``fixed_soul`` 不可变段(否则忽略)。 """ id: str user_id: str port: str = "opc" name: str description: str = "" language: str = "zh" model_name: str = "" created_at: str = "" updated_at: str = "" deletable: bool = True use_fixed_soul: bool = False class AuthStatusResponse(BaseModel): enabled: bool has_users: bool class VerifyResponse(BaseModel): valid: bool username: str = "" class UserProfile(BaseModel): """用户资料(登录/me/update-profile 均返回该结构)。""" username: str = "" nickname: str = "" account: str = "" company: str = "" room: str = "" avatar: str = "" company_avatar: str = "" # RBAC(登录/me 返回;update-profile 不重签时不返回权限明细) role: str = "opc_member" sub_role: str | None = None org_id: str | None = None region_id: str | None = None permissions: list[str] = Field(default_factory=list) scope_region_ids: list[str] = Field(default_factory=list) # 当前生效的端口身份(多身份绑定) identity_id: str | None = None port: str | None = None identity_name: str = "" class LoginResponse(UserProfile): """登录/注册成功后的响应:资料 + 新令牌。""" token: str = "" identities: list[IdentityInfo] = Field(default_factory=list) class ProfileResponse(UserProfile): """update-profile 成功后的响应;修改凭据时附带新令牌。""" token: str = "" identities: list[IdentityInfo] = Field(default_factory=list) class IdentityInfo(BaseModel): """账号绑定的一端口身份摘要(登录/选择身份时返回)。""" id: str port: str = "opc" role: str = "opc_member" sub_role: str | None = None name: str = "" org_id: str | None = None region_id: str | None = None org_name: str | None = None region_name: str | None = None status: str = "active" class SelectIdentityRequest(BaseModel): """切换到指定端口身份:为新身份签发令牌。""" identity_id: str # RBAC 管理请求/响应 class SetUserRoleRequest(BaseModel): role: str sub_role: str | None = None org_id: str | None = None region_id: str | None = None class SetUserStatusRequest(BaseModel): status: str = "active" # active | disabled