From 6de39e55ae06c5fd087b45b189a3529d89ac138c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B2=E8=BE=B0=E7=94=9F=E4=BA=A7?= Date: Wed, 8 Apr 2026 07:44:58 +0800 Subject: [PATCH] =?UTF-8?q?v1.2.52=20-=20=E5=AE=89=E5=85=A8=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=EF=BC=9A=E7=BB=9F=E4=B8=80=E7=89=88=E6=9C=AC=E5=8F=B7?= =?UTF-8?q?=E3=80=81CORS=E9=85=8D=E7=BD=AE=E3=80=81SECRET=5FKEY=E3=80=81St?= =?UTF-8?q?ats=E6=80=A7=E8=83=BD=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- VERSION | 2 +- backend/app/core/auth.py | 4 +- backend/app/main.py | 5 +- backend/app/routers/collections.py | 159 +++++++++++++++++++---------- config/VERSION | 2 +- frontend/package.json | 2 +- 6 files changed, 113 insertions(+), 61 deletions(-) diff --git a/VERSION b/VERSION index 50e400b..209d0be 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.2.41 +VERSION=1.2.52 diff --git a/backend/app/core/auth.py b/backend/app/core/auth.py index 602723a..2fe7a71 100644 --- a/backend/app/core/auth.py +++ b/backend/app/core/auth.py @@ -11,7 +11,9 @@ from app.core.database import SessionLocal from app.models.models import User # 配置 -SECRET_KEY = os.getenv("SECRET_KEY", "your-secret-key-change-in-production") +SECRET_KEY = os.getenv("SECRET_KEY") +if not SECRET_KEY: + raise ValueError("SECRET_KEY environment variable is not set. Please configure it in production!") ALGORITHM = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES = int(os.getenv("ACCESS_TOKEN_EXPIRE_MINUTES", "10080")) # 7天 diff --git a/backend/app/main.py b/backend/app/main.py index 7624c39..7ea27d2 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -51,10 +51,11 @@ app = FastAPI( # 设置全局错误处理器 setup_error_handlers(app) -# CORS 配置 +# CORS 配置 - 生产环境限制域名 +ALLOWED_ORIGINS = os.getenv("ALLOWED_ORIGINS", "http://47.103.29.111,http://120.55.81.21,https://socoolbot.com").split(",") app.add_middleware( CORSMiddleware, - allow_origins=["*"], # 生产环境应该限制域名 + allow_origins=ALLOWED_ORIGINS, # 生产环境限制域名 allow_credentials=True, allow_methods=["*"], allow_headers=["*"], diff --git a/backend/app/routers/collections.py b/backend/app/routers/collections.py index bf48d73..9c94aeb 100644 --- a/backend/app/routers/collections.py +++ b/backend/app/routers/collections.py @@ -4,7 +4,8 @@ import uuid import re from typing import Optional, List from fastapi import APIRouter, Depends, HTTPException, status, Query, UploadFile, File -from sqlalchemy import func, text +from sqlalchemy import func, text, case, nullsfirst +from sqlalchemy.dialects import postgresql as pg from sqlalchemy.orm import Session from app.core.database import get_db from app.core.auth import get_current_user @@ -275,68 +276,117 @@ def get_stats( current_user: User = Depends(get_current_user), db: Session = Depends(get_db) ): - """获取藏品统计""" - # 获取所有藏品 - if current_user.role == "admin": - all_collections = db.query(Collection).all() - else: - all_collections = db.query(Collection).filter( - Collection.f99_91_user_id == current_user.f99_90_id - ).all() + """获取藏品统计 - 使用数据库聚合查询优化性能""" + from sqlalchemy import case - # 总数 - total_count = len(all_collections) + # 基础查询条件 + if current_user.role == "admin": + base_filter = True + user_filter = True + else: + base_filter = Collection.f99_91_user_id == current_user.f99_90_id + user_filter = Collection.f99_91_user_id == current_user.f99_90_id + + # 总数 - 使用 COUNT + total_count = db.query(func.count(Collection.f99_90_id)).filter(base_filter).scalar() or 0 # 按分类统计 - from collections import Counter - by_category = Counter(c.f01_03_category for c in all_collections).items() + by_category = db.query( + Collection.f01_03_category, + func.count(Collection.f99_90_id) + ).filter(base_filter).group_by(Collection.f01_03_category).all() # 按状态统计 - by_status = Counter(c.f01_04_status for c in all_collections).items() + by_status = db.query( + Collection.f01_04_status, + func.count(Collection.f99_90_id) + ).filter(base_filter).group_by(Collection.f01_04_status).all() # 按是否评级统计 - by_graded = Counter(c.f03_20_is_graded for c in all_collections).items() + by_graded = db.query( + Collection.f03_20_is_graded, + func.count(Collection.f99_90_id) + ).filter(base_filter).group_by(Collection.f03_20_is_graded).all() - # 新增:8 个分布统计 - by_packaging = Counter(c.f02_12_packaging for c in all_collections if c.f02_12_packaging).items() - by_rarity = Counter(c.f02_13_rarity for c in all_collections if c.f02_13_rarity).items() - by_version = Counter(c.f02_11_version for c in all_collections if c.f02_11_version).items() - by_grading_company = Counter(c.f03_21_grading_company for c in all_collections if c.f03_20_is_graded and c.f03_21_grading_company).items() - by_grading_score = Counter(c.f03_22_grading_score for c in all_collections if c.f03_20_is_graded and c.f03_22_grading_score).items() - by_special_mark = Counter(c.f04_30_special_mark for c in all_collections if c.f04_30_special_mark).items() - by_number_category = Counter(c.f02_14_number_category for c in all_collections if c.f02_14_number_category).items() + # 按包装统计 + by_packaging = db.query( + Collection.f02_12_packaging, + func.count(Collection.f99_90_id) + ).filter(base_filter, Collection.f02_12_packaging.isnot(None)).group_by(Collection.f02_12_packaging).all() - # 总成本: SUM(cost_price + repair_fee + grading_fee) for ALL collections - total_cost = sum( - (c.f05_40_cost_price or 0) + (c.f05_43_repair_fee or 0) + (c.f05_44_grading_fee or 0) - for c in all_collections - ) + # 按稀有度统计 + by_rarity = db.query( + Collection.f02_13_rarity, + func.count(Collection.f99_90_id) + ).filter(base_filter, Collection.f02_13_rarity.isnot(None)).group_by(Collection.f02_13_rarity).all() - # 预期利润: SUM(target_price - cost_price) for collections with target_price > 0 - expected_profit = sum( - (c.f05_41_target_price or 0) - (c.f05_40_cost_price or 0) - for c in all_collections - if c.f05_41_target_price and c.f05_41_target_price > 0 - ) + # 按版本统计 + by_version = db.query( + Collection.f02_11_version, + func.count(Collection.f99_90_id) + ).filter(base_filter, Collection.f02_11_version.isnot(None)).group_by(Collection.f02_11_version).all() - # 已售商品:状态为 sold 且出售价 > 0 - sold_collections = [ - c for c in all_collections - if c.f01_04_status == 'sold' and c.f05_42_goal_price and c.f05_42_goal_price > 0 - ] + # 按评级公司统计 + by_grading_company = db.query( + Collection.f03_21_grading_company, + func.count(Collection.f99_90_id) + ).filter(base_filter, Collection.f03_20_is_graded == True, Collection.f03_21_grading_company.isnot(None)).group_by(Collection.f03_21_grading_company).all() - # 总收入:SUM(出售价) for 已售商品(售价>0) - total_revenue = sum( - c.f05_42_goal_price or 0 - for c in sold_collections - ) + # 按评级分数统计 + by_grading_score = db.query( + Collection.f03_22_grading_score, + func.count(Collection.f99_90_id) + ).filter(base_filter, Collection.f03_20_is_graded == True, Collection.f03_22_grading_score.isnot(None)).group_by(Collection.f03_22_grading_score).all() - # 总利润(已实现利润):SUM(出售价 - 成本价 - 修复费 - 评级费) for 已售商品 - # 单藏品总成本 = 成本价 + 修复费 + 评级费 - total_profit = sum( - (c.f05_42_goal_price or 0) - (c.f05_40_cost_price or 0) - (c.f05_43_repair_fee or 0) - (c.f05_44_grading_fee or 0) - for c in sold_collections - ) + # 按特殊标记统计 + by_special_mark = db.query( + Collection.f04_30_special_mark, + func.count(Collection.f99_90_id) + ).filter(base_filter, Collection.f04_30_special_mark.isnot(None)).group_by(Collection.f04_30_special_mark).all() + + # 按号码分类统计 + by_number_category = db.query( + Collection.f02_14_number_category, + func.count(Collection.f99_90_id) + ).filter(base_filter, Collection.f02_14_number_category.isnot(None)).group_by(Collection.f02_14_number_category).all() + + # 财务统计 - 使用 SUM + cost_result = db.query( + func.sum( + (Collection.f05_40_cost_price or 0) + + (Collection.f05_43_repair_fee or 0) + + (Collection.f05_44_grading_fee or 0) + ) + ).filter(base_filter).scalar() or 0 + + target_result = db.query( + func.sum(Collection.f05_41_target_price) + ).filter(base_filter, Collection.f05_41_target_price > 0).scalar() or 0 + + expected_profit = target_result - cost_result + + # 已售商品统计 + sold_filter = base_filter & (Collection.f01_04_status == 'sold') & (Collection.f05_42_goal_price > 0) + + total_revenue = db.query(func.sum(Collection.f05_42_goal_price)).filter(sold_filter).scalar() or 0 + + total_profit = db.query( + func.sum( + (Collection.f05_42_goal_price or 0) - + (Collection.f05_40_cost_price or 0) - + (Collection.f05_43_repair_fee or 0) - + (Collection.f05_44_grading_fee or 0) + ) + ).filter(sold_filter).scalar() or 0 + + # 盈亏统计 + profit_count = db.query(func.count(Collection.f99_90_id)).filter( + sold_filter, Collection.f05_42_goal_price > Collection.f05_40_cost_price + ).scalar() or 0 + + loss_count = db.query(func.count(Collection.f99_90_id)).filter( + sold_filter, Collection.f05_42_goal_price <= Collection.f05_40_cost_price + ).scalar() or 0 return { "totalCount": total_count, @@ -350,13 +400,12 @@ def get_stats( "byGradingScore": [{"score": s, "count": n} for s, n in by_grading_score], "bySpecialMark": [{"mark": m, "count": n} for m, n in by_special_mark], "byNumberCategory": [{"numberCategory": n, "count": c} for n, c in by_number_category], - # 盈亏统计(只统计已售且有价格的藏品) "byProfitLoss": [ - {"type": "profit", "label": "盈利", "count": sum(1 for c in sold_collections if c.f05_42_goal_price and c.f05_40_cost_price and c.f05_42_goal_price > c.f05_40_cost_price)}, - {"type": "loss", "label": "亏损", "count": sum(1 for c in sold_collections if c.f05_42_goal_price and c.f05_40_cost_price and c.f05_42_goal_price <= c.f05_40_cost_price)} + {"type": "profit", "label": "盈利", "count": profit_count}, + {"type": "loss", "label": "亏损", "count": loss_count} ], - "totalCost": total_cost, - "totalTarget": sum(c.f05_41_target_price or 0 for c in all_collections), + "totalCost": cost_result, + "totalTarget": target_result, "expectedProfit": expected_profit, "totalRevenue": total_revenue, "totalProfit": total_profit diff --git a/config/VERSION b/config/VERSION index a4f8462..209d0be 100644 --- a/config/VERSION +++ b/config/VERSION @@ -1 +1 @@ -v1.2.41 +VERSION=1.2.52 diff --git a/frontend/package.json b/frontend/package.json index ad0ec71..729e6c4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "jiachenlong-frontend", - "version": "1.2.12", + "version": "1.2.51", "private": true, "description": "甲辰藏品管理系统 - 移动端前端", "scripts": {