b9b5e2e2ff
- Task 加 task_code/tags/display_priority/claimed_by/claimed_at/doing_at;新增 TaskClaim 流水表。
- TaskService 加 claimed/doing/completed 状态机(claim/start_doing/complete),grab 并入 claim。
- TaskRepository 加 list_published/get_by_code/update/claim/set_doing + TaskClaimRepository;挂 Database。
- 端口:
· opc /tasks/grab-by-code、/tasks/{id}/doing、/tasks/{id}/complete
· operator POST /tasks(auto task_code) + PATCH /tasks/:id
· park /park/api/tasks(大屏展示,含 scan_payload 二维码载荷)
· training /api/tasks/claim-by-code、/api/tasks/my(小程序 C 端账号→OPC 身份 find-or-create 领单)
- 迁移 0008(tasks 加列 + task_claims)已应用+stamp;seed 补 task_code/tags/grab demo。
- tests/test_task_claim.py(自包含内存库, 状态机+流水+list_published), 直接 async 校验通过。
Co-Authored-By: Claude <noreply@anthropic.com>
833 lines
40 KiB
Python
833 lines
40 KiB
Python
# -*- 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)
|
||
# 统一登录:微信 openid(登录即建 opc_member 身份);phone 与 username(手机号)对齐
|
||
wx_openid: Mapped[str] = mapped_column(String, default="", index=True) # 公众号/网页开放平台
|
||
wx_unionid: Mapped[str] = mapped_column(String, default="", index=True) # 微信 unionid(跨应用统一)
|
||
wx_mini_openid: Mapped[str] = mapped_column(String, default="", index=True) # 小程序 openid(与网页不同)
|
||
phone: Mapped[str] = mapped_column(String, default="")
|
||
email: Mapped[str] = mapped_column(String, default="")
|
||
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="")
|
||
gender: Mapped[str] = mapped_column(String, default="") # male|female|unknown
|
||
birthday: Mapped[str] = mapped_column(String, default="") # YYYY-MM-DD
|
||
# 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 递增
|
||
# ── 来源与登录跟踪(全局用户统一,记录准确来源) ──
|
||
source: Mapped[str] = mapped_column(String, default="") # mini_program|web|desktop|park|admin|seed|wx|phone|unknown
|
||
auth_type: Mapped[str] = mapped_column(String, default="") # password|phone|wx_openid|admin
|
||
register_ip: Mapped[str] = mapped_column(String, default="")
|
||
last_login_ip: Mapped[str] = mapped_column(String, default="")
|
||
last_login_at: Mapped[str] = mapped_column(String, default="")
|
||
# ── 算力(引擎用户镜像,全局一致) ──
|
||
compute_provisioned: Mapped[bool] = mapped_column(Boolean, default=False) # 是否已在算力引擎建号
|
||
compute_username: Mapped[str] = mapped_column(String, default="") # 引擎用户名(= username)
|
||
compute_quota: Mapped[int] = mapped_column(Integer, default=0) # 缓存引擎可用额度
|
||
compute_used_quota: Mapped[int] = mapped_column(Integer, default=0) # 缓存引擎已用额度
|
||
# ── 状态与身份分类 ──
|
||
certification_status: Mapped[str] = mapped_column(String, default="uncertified") # uncertified|pending|certified|rejected
|
||
certification_time: Mapped[str] = mapped_column(String, default="")
|
||
affiliation: Mapped[str] = mapped_column(String, default="independent") # independent:独立OPC | park:所属园区
|
||
park_id: Mapped[str] = mapped_column(String, default="") # 所属园区(id)
|
||
park_name: Mapped[str] = mapped_column(String, default="") # 所属园区(名)
|
||
account_type: Mapped[str] = mapped_column(String, default="opc_default") # opc_default|park_staff|platform_staff|provider|enterprise
|
||
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):
|
||
"""平台任务(运营端可审核/下架;后续各端口接入)。
|
||
|
||
状态机(扩展支持扫码接单):draft/pending/review/published/claimed/doing/completed/cancelled。
|
||
扫码接单流转:published → claimed(claimed_by/claimed_at) → doing(doing_at) → completed。
|
||
历史接单流水见 TaskClaim;claim 记录 claimed_by/claimed_at/doing_at。
|
||
"""
|
||
|
||
__tablename__ = "tasks"
|
||
|
||
id: Mapped[str] = mapped_column(String, primary_key=True)
|
||
task_code: Mapped[str] = mapped_column(String, default="", index=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="")
|
||
tags: Mapped[str] = mapped_column(String, 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="")
|
||
display_priority: Mapped[int] = mapped_column(Integer, default=0) # 大屏展示排序(越大越前)
|
||
status: Mapped[str] = mapped_column(String, default="draft") # draft/pending/review/published/claimed/doing/completed/cancelled
|
||
claimed_by: Mapped[str | None] = mapped_column(ForeignKey("users.id"), nullable=True)
|
||
claimed_at: Mapped[str] = mapped_column(String, default="")
|
||
doing_at: Mapped[str] = mapped_column(String, default="")
|
||
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 TaskClaim(Base):
|
||
"""任务接单流水(扫码/抢单/投标中标),rich 记录任务状态流转历史。"""
|
||
|
||
__tablename__ = "task_claims"
|
||
|
||
id: Mapped[str] = mapped_column(String, primary_key=True)
|
||
task_id: Mapped[str] = mapped_column(
|
||
ForeignKey("tasks.id", ondelete="CASCADE"), nullable=False, index=True,
|
||
)
|
||
claimer_user_id: Mapped[str] = mapped_column(
|
||
ForeignKey("users.id", ondelete="SET NULL"), nullable=True,
|
||
)
|
||
claimer_name: Mapped[str] = mapped_column(String, default="")
|
||
claim_source: Mapped[str] = mapped_column(String, default="scan") # scan/grab/bid/dispatch
|
||
claimed_at: Mapped[str] = mapped_column(String, default="")
|
||
status: Mapped[str] = mapped_column(String, default="claimed") # claimed/doing/completed/withdrawn
|
||
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="")
|
||
|
||
|
||
# ===========================================================================
|
||
# 培训业务子应用(原 app/training 独立 opc.db → 并入唯一总库)
|
||
# ===========================================================================
|
||
|
||
class TrainingAccount(Base):
|
||
"""培训站账号(原 accounts,含手机/微信登录)。"""
|
||
__tablename__ = "accounts"
|
||
id: Mapped[str | None] = mapped_column(String, primary_key=True)
|
||
username: Mapped[str | None] = mapped_column(String, unique=True, nullable=False)
|
||
wxid: Mapped[str | None] = mapped_column(String, default="")
|
||
phone: Mapped[str | None] = mapped_column(String, default="")
|
||
contact: Mapped[str | None] = mapped_column(String, default="")
|
||
name: Mapped[str | None] = mapped_column(String, default="")
|
||
avatar: Mapped[str | None] = mapped_column(String, default="")
|
||
password: Mapped[str | None] = mapped_column(String, default="")
|
||
phone_bound: Mapped[int | None] = mapped_column(Integer, default=0)
|
||
identities: Mapped[str | None] = mapped_column(Text, default="[]")
|
||
status_label: Mapped[str | None] = mapped_column(String, default="")
|
||
topics: Mapped[str | None] = mapped_column(Text, default="")
|
||
source: Mapped[str | None] = mapped_column(String, default="")
|
||
created_at: Mapped[str | None] = mapped_column(String, default="")
|
||
|
||
|
||
class Event(Base):
|
||
__tablename__ = "events"
|
||
id: Mapped[str | None] = mapped_column(String, primary_key=True)
|
||
type: Mapped[str | None] = mapped_column(String, default="")
|
||
mode: Mapped[str | None] = mapped_column(String, default="offline")
|
||
title: Mapped[str | None] = mapped_column(String, default="")
|
||
subtitle: Mapped[str | None] = mapped_column(String, default="")
|
||
desc: Mapped[str | None] = mapped_column(Text, default="")
|
||
location: Mapped[str | None] = mapped_column(String, default="")
|
||
host: Mapped[str | None] = mapped_column(String, default="")
|
||
image: Mapped[str | None] = mapped_column(String, default="")
|
||
link: Mapped[str | None] = mapped_column(String, default="")
|
||
start_at: Mapped[str | None] = mapped_column(String, default="")
|
||
end_at: Mapped[str | None] = mapped_column(String, default="")
|
||
checkin_at: Mapped[str | None] = mapped_column(String, default="")
|
||
duration_min: Mapped[int | None] = mapped_column(Integer, default=90)
|
||
capacity: Mapped[int | None] = mapped_column(Integer, default=0)
|
||
show_capacity: Mapped[int | None] = mapped_column(Integer, default=0)
|
||
audit_mode: Mapped[str | None] = mapped_column(String, default="auto")
|
||
status: Mapped[str | None] = mapped_column(String, default="open")
|
||
|
||
|
||
class Booking(Base):
|
||
__tablename__ = "bookings"
|
||
id: Mapped[str | None] = mapped_column(String, primary_key=True)
|
||
created_at: Mapped[str | None] = mapped_column(String, default="")
|
||
status: Mapped[str | None] = mapped_column(String, default="pending")
|
||
audit_status: Mapped[str | None] = mapped_column(String, default="pending")
|
||
username: Mapped[str | None] = mapped_column(String, default="")
|
||
name: Mapped[str | None] = mapped_column(String, default="")
|
||
contact: Mapped[str | None] = mapped_column(String, default="")
|
||
status_label: Mapped[str | None] = mapped_column(String, default="")
|
||
want: Mapped[str | None] = mapped_column(Text, default="")
|
||
event_id: Mapped[str | None] = mapped_column(String, default="")
|
||
event_title: Mapped[str | None] = mapped_column(String, default="")
|
||
event_start: Mapped[str | None] = mapped_column(String, default="")
|
||
topics: Mapped[str | None] = mapped_column(Text, default="")
|
||
question: Mapped[str | None] = mapped_column(Text, default="")
|
||
source: Mapped[str | None] = mapped_column(String, default="")
|
||
checkin_at: Mapped[str | None] = mapped_column(String, default="")
|
||
|
||
|
||
class Test(Base):
|
||
__tablename__ = "tests"
|
||
id: Mapped[str | None] = mapped_column(String, primary_key=True)
|
||
created_at: Mapped[str | None] = mapped_column(String, default="")
|
||
username: Mapped[str | None] = mapped_column(String, default="")
|
||
type_code: Mapped[str | None] = mapped_column(String, default="")
|
||
persona: Mapped[str | None] = mapped_column(String, default="")
|
||
adapt_index: Mapped[int | None] = mapped_column(Integer, default=0)
|
||
adapt_level: Mapped[str | None] = mapped_column(String, default="")
|
||
tracks: Mapped[str | None] = mapped_column(Text, default="")
|
||
version: Mapped[str | None] = mapped_column(String, default="")
|
||
|
||
|
||
class Course(Base):
|
||
__tablename__ = "courses"
|
||
id: Mapped[str | None] = mapped_column(String, primary_key=True)
|
||
category: Mapped[str | None] = mapped_column(String, default="")
|
||
level: Mapped[str | None] = mapped_column(String, default="")
|
||
title: Mapped[str | None] = mapped_column(String, default="")
|
||
subtitle: Mapped[str | None] = mapped_column(String, default="")
|
||
desc: Mapped[str | None] = mapped_column(Text, default="")
|
||
price: Mapped[float | None] = mapped_column(Float, default=0.0)
|
||
status: Mapped[str | None] = mapped_column(String, default="draft")
|
||
start_at: Mapped[str | None] = mapped_column(String, default="")
|
||
end_at: Mapped[str | None] = mapped_column(String, default="")
|
||
venue: Mapped[str | None] = mapped_column(String, default="")
|
||
quota: Mapped[int | None] = mapped_column(Integer, default=0)
|
||
image: Mapped[str | None] = mapped_column(String, default="")
|
||
host: Mapped[str | None] = mapped_column(String, default="")
|
||
created_at: Mapped[str | None] = mapped_column(String, default="")
|
||
|
||
|
||
class PolicyLog(Base):
|
||
__tablename__ = "policy_logs"
|
||
id: Mapped[str | None] = mapped_column(String, primary_key=True)
|
||
created_at: Mapped[str | None] = mapped_column(String, default="")
|
||
username: Mapped[str | None] = mapped_column(String, default="")
|
||
answers: Mapped[str | None] = mapped_column(Text, default="")
|
||
policies_count: Mapped[int | None] = mapped_column(Integer, default=0)
|
||
subsidies_count: Mapped[int | None] = mapped_column(Integer, default=0)
|
||
loans_count: Mapped[int | None] = mapped_column(Integer, default=0)
|
||
summary: Mapped[str | None] = mapped_column(Text, default="")
|
||
|
||
|
||
class PlanLog(Base):
|
||
__tablename__ = "plan_logs"
|
||
id: Mapped[str | None] = mapped_column(String, primary_key=True)
|
||
created_at: Mapped[str | None] = mapped_column(String, default="")
|
||
username: Mapped[str | None] = mapped_column(String, default="")
|
||
region: Mapped[str | None] = mapped_column(String, default="")
|
||
status: Mapped[str | None] = mapped_column(String, default="")
|
||
need_park: Mapped[int | None] = mapped_column(Integer, default=0)
|
||
has_staff: Mapped[int | None] = mapped_column(Integer, default=0)
|
||
steps_count: Mapped[int | None] = mapped_column(Integer, default=0)
|
||
|
||
|
||
class SurveyLog(Base):
|
||
__tablename__ = "survey_logs"
|
||
id: Mapped[str | None] = mapped_column(String, primary_key=True)
|
||
created_at: Mapped[str | None] = mapped_column(String, default="")
|
||
username: Mapped[str | None] = mapped_column(String, default="")
|
||
source: Mapped[str | None] = mapped_column(String, default="")
|
||
answers: Mapped[str | None] = mapped_column(Text, default="")
|
||
|
||
|
||
# ===========================================================================
|
||
# 园区子应用(原 app/park 独立 park.db → 并入唯一总库)
|
||
# ===========================================================================
|
||
|
||
class ParkTenant(Base):
|
||
__tablename__ = "park_tenants"
|
||
id: Mapped[str] = mapped_column(String, primary_key=True)
|
||
name: Mapped[str] = mapped_column(String, default="")
|
||
intro_json: Mapped[str] = mapped_column(Text, default="[]")
|
||
username: Mapped[str] = mapped_column(String, default="")
|
||
salt: Mapped[str] = mapped_column(String, default="")
|
||
password_hash: Mapped[str] = mapped_column(String, default="")
|
||
admin_username: Mapped[str] = mapped_column(String, default="")
|
||
status: Mapped[str] = mapped_column(String, default="active")
|
||
data_json: Mapped[str] = mapped_column(Text, default="{}")
|
||
agent_json: Mapped[str] = mapped_column(Text, default="{}")
|
||
created_at: Mapped[str] = mapped_column(String, default="")
|
||
|
||
|
||
class ParkCompany(Base):
|
||
__tablename__ = "park_companies"
|
||
id: Mapped[str] = mapped_column(String, primary_key=True)
|
||
tenant_id: Mapped[str] = mapped_column(ForeignKey("park_tenants.id", ondelete="CASCADE"), nullable=False, index=True)
|
||
name: Mapped[str] = mapped_column(String, default="")
|
||
zone: Mapped[str] = mapped_column(String, default="")
|
||
room: Mapped[str] = mapped_column(String, default="")
|
||
industry: Mapped[str] = mapped_column(String, default="")
|
||
bio: Mapped[str] = mapped_column(Text, default="")
|
||
founder: Mapped[str] = mapped_column(String, default="")
|
||
status: Mapped[str] = mapped_column(String, default="applying")
|
||
employees: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
||
created_at: Mapped[str] = mapped_column(String, default="")
|
||
|
||
|
||
class ParkKbDoc(Base):
|
||
__tablename__ = "park_kb_docs"
|
||
id: Mapped[str] = mapped_column(String, primary_key=True)
|
||
tenant_id: Mapped[str] = mapped_column(ForeignKey("park_tenants.id", ondelete="CASCADE"), nullable=False, index=True)
|
||
grp: Mapped[str] = mapped_column(String, default="general")
|
||
title: Mapped[str] = mapped_column(String, default="")
|
||
content_md: Mapped[str] = mapped_column(Text, default="")
|
||
created_at: Mapped[str] = mapped_column(String, default="")
|
||
|
||
|
||
class ParkSetting(Base):
|
||
"""园区全局配置 kv(如视频识别开关/地址),admin 经 /park/admin/* 读写,运行时不建表/不灌种子。"""
|
||
__tablename__ = "park_settings"
|
||
key: Mapped[str] = mapped_column(String, primary_key=True)
|
||
value: Mapped[str] = mapped_column(Text, default="")
|
||
updated_at: Mapped[str] = mapped_column(String, default="")
|
||
|
||
|
||
class ParkScreen(Base):
|
||
__tablename__ = "park_screens"
|
||
id: Mapped[str] = mapped_column(String, primary_key=True)
|
||
tenant_id: Mapped[str | None] = mapped_column(ForeignKey("park_tenants.id", ondelete="CASCADE"), nullable=True, index=True)
|
||
device_id: Mapped[str] = mapped_column(String, default="", index=True)
|
||
name: Mapped[str] = mapped_column(String, default="")
|
||
role: Mapped[str] = mapped_column(String, default="main")
|
||
location: Mapped[str] = mapped_column(String, default="")
|
||
status: Mapped[str] = mapped_column(String, default="unbound") # unbound | bound
|
||
code: Mapped[str] = mapped_column(String, default="") # 待绑定连接码
|
||
code_expires: Mapped[str] = mapped_column(String, default="") # 码过期时间(ISO)
|
||
bound_at: Mapped[str] = mapped_column(String, default="")
|
||
created_at: Mapped[str] = mapped_column(String, default="")
|