From 3cf8079b330be01030f628d0052cc6d589f2f95a 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 14:15:53 +0800 Subject: [PATCH] =?UTF-8?q?v1.2.62=20-=20=E6=80=A7=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96=EF=BC=9AStats=E6=95=B0=E6=8D=AE=E5=BA=93=E8=81=9A?= =?UTF-8?q?=E5=90=88+=E7=BC=96=E5=8F=B7=E5=B9=B6=E5=8F=91=E9=94=81+images?= =?UTF-8?q?=E6=98=A0=E5=B0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/routers/collections.py | 171 ++++++++++++++++++----------- 1 file changed, 108 insertions(+), 63 deletions(-) diff --git a/backend/app/routers/collections.py b/backend/app/routers/collections.py index bf48d73..05f50cc 100644 --- a/backend/app/routers/collections.py +++ b/backend/app/routers/collections.py @@ -56,6 +56,7 @@ def to_camel_case(data: dict) -> dict: 'f05_43_repair_fee': 'repairFee', 'f05_44_grading_fee': 'gradingFee', 'f06_50_purpose': 'purpose', + 'images': 'images', } return {mapping.get(k, k): v for k, v in data.items()} @@ -63,25 +64,30 @@ def to_camel_case(data: dict) -> dict: # 编码生成函数 def generate_code(version: str, user_id: str, db: Session) -> str: - """自动生成藏品编号 - 按用户独立编码""" + """自动生成藏品编号 - 按用户独立编码,使用行锁防止并发冲突""" import re + from sqlalchemy import text - # 查询当前用户的非空编码(不与其他用户混算) - user_codes = db.query(Collection.f01_02_code).filter( - Collection.f01_02_code.isnot(None), - Collection.f99_91_user_id == user_id - ).all() + # 使用 FOR UPDATE 行锁防止并发冲突 + result = db.execute( + text(""" + SELECT f01_02_code FROM collections + WHERE f99_91_user_id = :user_id + AND f01_02_code IS NOT NULL + AND f01_02_code ~ '^\\d{4,5}$' + ORDER BY f01_02_code::int DESC + LIMIT 1 + FOR UPDATE + """), + {"user_id": user_id} + ).fetchone() max_num = 0 - for (code,) in user_codes: - # 处理纯数字编码(支持4位和5位) - if re.match(r'^\d{4,5}$', code): - try: - num = int(code) - if num > max_num: - max_num = num - except (ValueError, TypeError): - pass + if result and result[0]: + try: + max_num = int(result[0]) + except (ValueError, TypeError): + pass # 当前用户最大号 +1 next_num = max_num + 1 @@ -275,68 +281,107 @@ 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 func, case + + # 基础查询条件 + base_filter = True if current_user.role == "admin" else Collection.f99_91_user_id == current_user.f99_90_id # 总数 - total_count = len(all_collections) + 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() + + # 总成本 + 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 = (Collection.f01_04_status == 'sold') & (Collection.f05_42_goal_price > 0) + if current_user.role != "admin": + sold_filter = sold_filter & (Collection.f99_91_user_id == current_user.f99_90_id) + + 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 return { "totalCount": total_count,