fix: 金额/算力/信用额度列 int→bigint,修复充值 Out of range 500(compute_quota 等 43 列)+ alembic 0077

This commit is contained in:
Pine
2026-09-10 14:48:09 +08:00
parent 46b06b28f1
commit 052fb589ca
2 changed files with 123 additions and 34 deletions
@@ -0,0 +1,88 @@
"""0077 金额/算力/信用额度列 int → bigint(杜绝 Out of range 溢出)
覆盖全部金额、算力额度、信用积分列(含 raw SQL 表,无 ORM 模型者一并处理)。
Revision ID: 0077
Revises: 0076
Create Date: 2026-09-10
"""
from alembic import op
import sqlalchemy as sa
revision = "0077"
down_revision = "0076"
branch_labels = None
depends_on = None
# (表, 列):opc 库中所有金额/算力/信用积分列
COLUMNS = [
("users", "compute_quota"),
("users", "compute_used_quota"),
("users", "compute_personal_balance"),
("park_companies", "compute_quota"),
("park_companies", "compute_quota_used"),
("park_companies", "compute_balance"),
("park_companies", "compute_balance_used"),
("company_members", "compute_balance"),
("company_members", "compute_balance_used"),
("compute_recharges", "amount"),
("compute_recharges", "compute_amount"),
("compute_balance_allocations", "amount"),
("compute_usage_records", "standard_price"),
("compute_usage_records", "actual_amount"),
("compute_recharge_orders", "amount_fen"),
("compute_recharge_orders", "quota_micro"),
("compute_recharge_orders", "bonus_quota_micro"),
("market_items", "price_fen"),
("market_purchase_orders", "amount_fen"),
("market_purchases", "amount_fen"),
("event_orders", "amount_fen"),
("events", "price_fen"),
("opc_services", "price"),
("service_orders", "price"),
("service_orders", "amount_cents"),
("service_orders", "platform_fee_cents"),
("service_orders", "refund_amount_cents"),
("service_orders", "seller_income_cents"),
("settlement_logs", "amount"),
("certification_types", "fee"),
("certification_types", "credit_points"),
("certifications", "fee_amount"),
("certifications", "credit_points"),
("training_courses", "price"),
("training_course_enrollments", "price_paid"),
("subsidy_applications", "amount"),
("escrows", "amount"),
("invoices", "amount"),
("finance_records", "amount"),
("investor_preferences", "amount_min"),
("investor_preferences", "amount_max"),
("badges", "credit_bonus"),
("credit_ledger", "balance"),
]
def upgrade() -> None:
conn = op.get_bind()
for table, col in COLUMNS:
# 幂等:仅当列存在且为 int 时才改
row = conn.execute(sa.text(
"SELECT COLUMN_TYPE, IS_NULLABLE, IFNULL(COLUMN_DEFAULT,'') FROM information_schema.COLUMNS "
"WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=:t AND COLUMN_NAME=:c"
), {"t": table, "c": col}).fetchone()
if not row:
continue
col_type, nullable, default = row
if col_type.lower().startswith("bigint"):
continue
nn = "" if nullable == "YES" else " NOT NULL"
dflt = ""
if default == "0":
dflt = " DEFAULT 0"
conn.execute(sa.text(
f"ALTER TABLE `{table}` MODIFY COLUMN `{col}` BIGINT{nn}{dflt}"
))
def downgrade() -> None:
# 金额列不降级回 int(历史溢出风险,且不可逆恢复成本高)
pass
+35 -34
View File
@@ -10,6 +10,7 @@ from __future__ import annotations
from pydantic import BaseModel, Field
from sqlalchemy import (
BigInteger,
Boolean,
Float,
ForeignKey,
@@ -74,10 +75,10 @@ class User(Base):
# ── 算力(引擎用户镜像,全局一致) ──
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) # 缓存引擎已用额度
compute_quota: Mapped[int] = mapped_column(BigInteger, default=0) # 缓存引擎可用额度
compute_used_quota: Mapped[int] = mapped_column(BigInteger, default=0) # 缓存引擎已用额度
# ── 个人算力余额(分;个人充值/企业转入,与 company_members.compute_balance 企业分配分离) ──
compute_personal_balance: Mapped[int] = mapped_column(Integer, default=0) # 个人充值/转入余额(分)
compute_personal_balance: Mapped[int] = mapped_column(BigInteger, default=0) # 个人充值/转入余额(分)
# ── 状态与身份分类 ──
certification_status: Mapped[str] = mapped_column(String, default="uncertified") # uncertified|pending|certified|rejected
certification_time: Mapped[str] = mapped_column(String, default="")
@@ -542,8 +543,8 @@ class InvestorPreference(Base):
)
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)
amount_min: Mapped[int] = mapped_column(BigInteger, default=0)
amount_max: Mapped[int] = mapped_column(BigInteger, default=0)
region_id: Mapped[str | None] = mapped_column(ForeignKey("regions.id"), nullable=True)
updated_at: Mapped[str] = mapped_column(String, default="")
@@ -645,7 +646,7 @@ class SubsidyApplication(Base):
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)
amount: Mapped[int] = mapped_column(BigInteger, 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="")
@@ -714,7 +715,7 @@ class Escrow(Base):
ForeignKey("tasks.id", ondelete="CASCADE"), nullable=False,
)
task_title: Mapped[str] = mapped_column(String, default="")
amount: Mapped[int] = mapped_column(Integer, default=0)
amount: Mapped[int] = mapped_column(BigInteger, default=0)
commission: Mapped[int] = mapped_column(Integer, default=0) # 平台佣金
status: Mapped[str] = mapped_column(String, default="deposited") # deposited/frozen/released/refunded
# ── 微信服务商分账(资金托管)字段 ──
@@ -854,7 +855,7 @@ class Invoice(Base):
issuer_id: Mapped[str] = mapped_column(String, default="") # 开票方(接单人)
issuer_name: Mapped[str] = mapped_column(String, default="")
payer_name: Mapped[str] = mapped_column(String, default="") # 抬头(发包方)
amount: Mapped[int] = mapped_column(Integer, default=0)
amount: Mapped[int] = mapped_column(BigInteger, default=0)
tax_rate: Mapped[float] = mapped_column(Float, default=0.01)
invoice_type: Mapped[str] = mapped_column(String, default="personal") # personal/vat
status: Mapped[str] = mapped_column(String, default="applied") # applied/issued/rejected
@@ -927,7 +928,7 @@ class FinanceRecord(Base):
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)
amount: Mapped[int] = mapped_column(BigInteger, 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="")
@@ -1189,13 +1190,13 @@ class ParkCompany(Base):
emp_migrant: Mapped[int | None] = mapped_column(Integer, nullable=True) # 返乡农民工
# 企业算力(只增不减):折扣% off 模型价;配额为发放给成员的算力额度
compute_discount: Mapped[int] = mapped_column(Integer, default=0) # 折扣%0-100,单调非降)
compute_quota: Mapped[int] = mapped_column(Integer, default=0) # 企业算力配额(单调非降/增量)
compute_quota_used: Mapped[int] = mapped_column(Integer, default=0) # 已分配/已用配额(统计)
compute_quota: Mapped[int] = mapped_column(BigInteger, default=0) # 企业算力配额(单调非降/增量)
compute_quota_used: Mapped[int] = mapped_column(BigInteger, default=0) # 已分配/已用配额(统计)
engine_group: Mapped[str] = mapped_column(String, default="") # 引擎用户组名(折扣倍率载体)
owner_user_id: Mapped[str] = mapped_column(String, default="") # 负责人/管理员账号id(必填)
# ── 企业算力余额(企业管理员充值后分配给成员,0056) ──
compute_balance: Mapped[int] = mapped_column(Integer, default=0) # 企业算力余额(分)
compute_balance_used: Mapped[int] = mapped_column(Integer, default=0) # 已分配给成员的余额(分)
compute_balance: Mapped[int] = mapped_column(BigInteger, default=0) # 企业算力余额(分)
compute_balance_used: Mapped[int] = mapped_column(BigInteger, default=0) # 已分配给成员的余额(分)
created_at: Mapped[str] = mapped_column(String, default="")
@@ -1344,8 +1345,8 @@ class CompanyMember(Base):
role: Mapped[str] = mapped_column(String, default="staff") # 企业内角色(admin/finance/hr/staff0056
status: Mapped[str] = mapped_column(String, default="active") # active/left
# ── 成员算力余额(从企业分配,0056) ──
compute_balance: Mapped[int] = mapped_column(Integer, default=0) # 成员个人余额(分)
compute_balance_used: Mapped[int] = mapped_column(Integer, default=0) # 已使用余额(分)
compute_balance: Mapped[int] = mapped_column(BigInteger, default=0) # 成员个人余额(分)
compute_balance_used: Mapped[int] = mapped_column(BigInteger, default=0) # 已使用余额(分)
# ── 退出企业(0056 ──
left_at: Mapped[str] = mapped_column(String, default="") # 退出时间
left_reason: Mapped[str] = mapped_column(String, default="") # 退出原因
@@ -1393,8 +1394,8 @@ class ComputeRecharge(Base):
id: Mapped[str] = mapped_column(String, primary_key=True)
company_id: Mapped[str] = mapped_column(String, default="", index=True) # 企业充值时填写
user_id: Mapped[str] = mapped_column(String, default="", index=True) # 个人充值时填写
amount: Mapped[int] = mapped_column(Integer, default=0) # 充值金额(分)
compute_amount: Mapped[int] = mapped_column(Integer, default=0) # 到账算力(分)
amount: Mapped[int] = mapped_column(BigInteger, default=0) # 充值金额(分)
compute_amount: Mapped[int] = mapped_column(BigInteger, default=0) # 到账算力(分)
payment_method: Mapped[str] = mapped_column(String, default="")
payment_status: Mapped[str] = mapped_column(String, default="pending") # pending/paid/failed
transaction_id: Mapped[str] = mapped_column(String, default="")
@@ -1410,7 +1411,7 @@ class ComputeBalanceAllocation(Base):
company_id: Mapped[str] = mapped_column(String, index=True)
from_user_id: Mapped[str] = mapped_column(String, default="") # 分配人(企业管理员)
to_user_id: Mapped[str] = mapped_column(String, index=True) # 接收成员
amount: Mapped[int] = mapped_column(Integer, default=0) # 分配金额(分)
amount: Mapped[int] = mapped_column(BigInteger, default=0) # 分配金额(分)
type: Mapped[str] = mapped_column(String, default="allocate") # allocate/reclaim(分配/回收)
reason: Mapped[str] = mapped_column(Text, default="")
created_at: Mapped[str] = mapped_column(String, default="")
@@ -1424,10 +1425,10 @@ class ComputeUsageRecord(Base):
company_id: Mapped[str] = mapped_column(String, default="", index=True) # 从企业余额扣费时填写
model: Mapped[str] = mapped_column(String, default="")
token_count: Mapped[int] = mapped_column(Integer, default=0)
standard_price: Mapped[int] = mapped_column(Integer, default=0) # 标准价(分)
standard_price: Mapped[int] = mapped_column(BigInteger, default=0) # 标准价(分)
discount: Mapped[int] = mapped_column(Integer, default=0) # 实际折扣(0-100
discount_source: Mapped[str] = mapped_column(String, default="standard") # standard/tenant/company/tenant_user
actual_amount: Mapped[int] = mapped_column(Integer, default=0) # 实际扣费(分)
actual_amount: Mapped[int] = mapped_column(BigInteger, default=0) # 实际扣费(分)
balance_source: Mapped[str] = mapped_column(String, default="personal") # company/personal
created_at: Mapped[str] = mapped_column(String, default="")
@@ -1484,7 +1485,7 @@ class OpcService(Base):
description: Mapped[str] = mapped_column(Text, default="")
cover: Mapped[str] = mapped_column(String, default="")
gallery_json: Mapped[str] = mapped_column(Text, default="[]") # 作品集 URL 数组
price: Mapped[int] = mapped_column(Integer, default=0) # 定价(元,0=面议)
price: Mapped[int] = mapped_column(BigInteger, default=0) # 定价(元,0=面议)
delivery_days: Mapped[int] = mapped_column(Integer, default=0) # 交付时限(天)
tags: Mapped[str] = mapped_column(String, default="")
status: Mapped[str] = mapped_column(String, default="pending") # pending/published/offline/hidden
@@ -1618,7 +1619,7 @@ class MarketItem(Base):
bundle_json: Mapped[str] = mapped_column(Text, default="{}") # 安装包元数据(skill 内嵌完整 SKILL.mdMySQL 已 ALTER 为 MEDIUMTEXT
bundle_url: Mapped[str] = mapped_column(String, default="") # OSS 对象路径(plugin/app zip
bundle_sha256: Mapped[str] = mapped_column(String, default="")
price_fen: Mapped[int] = mapped_column(Integer, default=0) # 定价(分);0=免费(skill 恒 0)
price_fen: Mapped[int] = mapped_column(BigInteger, default=0) # 定价(分);0=免费(skill 恒 0)
stats_json: Mapped[str] = mapped_column(Text, default="{}") # {"downloads":0,"views":0,"likes":0}
is_featured: Mapped[int] = mapped_column(Integer, default=0)
status: Mapped[str] = mapped_column(String, default="draft", index=True) # draft|pending|published|rejected|offline
@@ -1663,7 +1664,7 @@ class MarketPurchaseOrder(Base):
item_name: Mapped[str] = mapped_column(String, default="")
user_id: Mapped[str] = mapped_column(String, default="", index=True)
username: Mapped[str] = mapped_column(String, default="")
amount_fen: Mapped[int] = mapped_column(Integer, default=0) # 实付金额(分)
amount_fen: Mapped[int] = mapped_column(BigInteger, default=0) # 实付金额(分)
client_type: Mapped[str] = mapped_column(String, default="mp") # mp(小程序码) | jsapi | native
appid: Mapped[str] = mapped_column(String, default="")
status: Mapped[str] = mapped_column(String, default="pending", index=True)
@@ -1689,7 +1690,7 @@ class MarketPurchase(Base):
user_id: Mapped[str] = mapped_column(String, default="", index=True)
item_id: Mapped[str] = mapped_column(String, default="", index=True)
item_type: Mapped[str] = mapped_column(String, default="plugin")
amount_fen: Mapped[int] = mapped_column(Integer, default=0)
amount_fen: Mapped[int] = mapped_column(BigInteger, default=0)
order_no: Mapped[str] = mapped_column(String, default="")
created_at: Mapped[str] = mapped_column(String, default="")
@@ -1709,7 +1710,7 @@ class ServiceOrder(Base):
buyer_name: Mapped[str] = mapped_column(String, default="")
contact: Mapped[str] = mapped_column(String, default="")
schedule_at: Mapped[str] = mapped_column(String, default="") # 预约时间
price: Mapped[int] = mapped_column(Integer, default=0) # 成交价(元,0=按服务定价)
price: Mapped[int] = mapped_column(BigInteger, default=0) # 成交价(元,0=按服务定价)
note: Mapped[str] = mapped_column(Text, default="")
status: Mapped[str] = mapped_column(String, default="pending")
accept_at: Mapped[str] = mapped_column(String, default="")
@@ -1918,9 +1919,9 @@ class CertificationType(Base):
category: Mapped[str] = mapped_column(String, default="personal") # personal/enterprise/opc_talent/skill
name: Mapped[str] = mapped_column(String, default="")
description: Mapped[str] = mapped_column(Text, default="")
fee: Mapped[int] = mapped_column(Integer, default=0) # 分,0=免费
fee: Mapped[int] = mapped_column(BigInteger, default=0) # 分,0=免费
validity_days: Mapped[int] = mapped_column(Integer, default=0) # 0=永久
credit_points: Mapped[int] = mapped_column(Integer, default=0)
credit_points: Mapped[int] = mapped_column(BigInteger, default=0)
levels_json: Mapped[str] = mapped_column(Text, default="[]") # 等级配置JSON
materials_schema: Mapped[str] = mapped_column(Text, default="{}") # 表单JSON Schema
review_mode: Mapped[str] = mapped_column(String, default="manual") # auto/manual/hybrid
@@ -1948,8 +1949,8 @@ class Certification(Base):
# not_started/pending/approved/rejected/active/expiring/expired/revoked
materials_json: Mapped[str] = mapped_column(Text, default="{}") # 提交材料(脱敏)
score: Mapped[int] = mapped_column(Integer, default=0) # 认证分数(技能测试用)
credit_points: Mapped[int] = mapped_column(Integer, default=0) # 获得的信用加分
fee_amount: Mapped[int] = mapped_column(Integer, default=0) # 费用(分)
credit_points: Mapped[int] = mapped_column(BigInteger, default=0) # 获得的信用加分
fee_amount: Mapped[int] = mapped_column(BigInteger, default=0) # 费用(分)
payment_id: Mapped[str] = mapped_column(String, default="")
submitted_at: Mapped[str] = mapped_column(String, default="")
approved_at: Mapped[str] = mapped_column(String, default="")
@@ -2002,7 +2003,7 @@ class TrainingCourse(Base):
# ui_design/graphic_design/frontend_dev/backend_dev/copywriting/translation/data_analysis/project_management
level: Mapped[str] = mapped_column(String, default="beginner")
# beginner/intermediate/advanced/expert
price: Mapped[int] = mapped_column(Integer, default=0) # 分,0=免费
price: Mapped[int] = mapped_column(BigInteger, default=0) # 分,0=免费
duration_minutes: Mapped[int] = mapped_column(Integer, default=0)
lesson_count: Mapped[int] = mapped_column(Integer, default=0)
chapter_count: Mapped[int] = mapped_column(Integer, default=0)
@@ -2069,7 +2070,7 @@ class TrainingCourseEnrollment(Base):
total_watch_seconds: Mapped[int] = mapped_column(Integer, default=0)
enrolled_at: Mapped[str] = mapped_column(String, default="")
completed_at: Mapped[str] = mapped_column(String, default="")
price_paid: Mapped[int] = mapped_column(Integer, default=0)
price_paid: Mapped[int] = mapped_column(BigInteger, default=0)
created_at: Mapped[str] = mapped_column(String, default="")
@@ -2249,7 +2250,7 @@ class CreditLedger(Base):
event_code: Mapped[str] = mapped_column(String, default="", index=True)
dimension: Mapped[str] = mapped_column(String, default="system")
delta: Mapped[int] = mapped_column(Integer, default=0)
balance: Mapped[int] = mapped_column(Integer, default=0)
balance: Mapped[int] = mapped_column(BigInteger, default=0)
reason: Mapped[str] = mapped_column(String, default="")
ref_type: Mapped[str] = mapped_column(String, default="")
ref_id: Mapped[str] = mapped_column(String, default="")
@@ -2288,7 +2289,7 @@ class Badge(Base):
description: Mapped[str] = mapped_column(String, default="")
condition: Mapped[str] = mapped_column(String, default="")
grant_rule_json: Mapped[str] = mapped_column(Text, default="{}")
credit_bonus: Mapped[int] = mapped_column(Integer, default=0)
credit_bonus: Mapped[int] = mapped_column(BigInteger, default=0)
lifetime: Mapped[int] = mapped_column(Integer, default=1)
revocable: Mapped[int] = mapped_column(Integer, default=1)
expiry_days: Mapped[int] = mapped_column(Integer, default=0)
@@ -2492,4 +2493,4 @@ class TeamMember(Base):
role: Mapped[str] = mapped_column(String, default="member") # owner/admin/member
status: Mapped[str] = mapped_column(String, default="active") # active/left
created_at: Mapped[str] = mapped_column(String, default="")
left_at: Mapped[str] = mapped_column(String, default="")
left_at: Mapped[str] = mapped_column(String, default="")