2026-04-24 10:57:52 +08:00
|
|
|
|
# coolbot_matcher - 一尘数据库号码匹配工具
|
2026-05-01 10:00:17 +08:00
|
|
|
|
# Version: 0.0.2 (2026-04-30)
|
2026-04-24 10:57:52 +08:00
|
|
|
|
# 描述:从一尘数据库(coolbot_data)查询匹配的藏品号码
|
2026-05-01 10:00:17 +08:00
|
|
|
|
# 更新:号码分类规则修正
|
2026-04-24 10:57:52 +08:00
|
|
|
|
|
2026-05-01 10:00:17 +08:00
|
|
|
|
from typing import List, Optional
|
2026-04-24 10:57:52 +08:00
|
|
|
|
from sqlalchemy import text
|
|
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
|
|
from app.core.coolbot_db import coolbot_engine
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-05-01 10:00:17 +08:00
|
|
|
|
def classify_number(number: str) -> str:
|
|
|
|
|
|
"""根据号码特征分类
|
|
|
|
|
|
|
|
|
|
|
|
规则(2026-04-30修正版):
|
|
|
|
|
|
|
|
|
|
|
|
首先区分标百、标十,单张:
|
|
|
|
|
|
- 单张:J后面9位
|
|
|
|
|
|
- 标十:J后面8位(最后一位是1)
|
|
|
|
|
|
- 标百:J后面7位(最后两位是01)
|
|
|
|
|
|
|
|
|
|
|
|
分类优先级:
|
|
|
|
|
|
| 类型 | 排除 | 可用数字 | 必须包含 |
|
|
|
|
|
|
|------|------|----------|----------|
|
|
|
|
|
|
| 通货 | - | - | 4 |
|
|
|
|
|
|
| 带7号 | 4 | - | 7 |
|
|
|
|
|
|
| 永恒号 | 47 | - | - |
|
|
|
|
|
|
| 圆圆号 | 123457 | 0689 | - |
|
|
|
|
|
|
| 倒置号 | 23457 | 01689 | 1 |
|
|
|
|
|
|
| 金马王 | 12347 | 05689 | 5 |
|
|
|
|
|
|
| 金马号 | 2347 | 015689 | 1,5 |
|
|
|
|
|
|
| 金山王 | 12457 | 03689 | 3 |
|
|
|
|
|
|
| 天马王 | 1247 | 035689 | 3,5 |
|
|
|
|
|
|
| 金山号 | 2457 | 013689 | 1,3 |
|
|
|
|
|
|
| 天马号 | 247 | 0135689 | 1,3,5 |
|
|
|
|
|
|
| 朦胧王 | 13457 | - | - |
|
|
|
|
|
|
| 朦胧号 | 3457 | - | - |
|
|
|
|
|
|
| 如意号 | 1347 | - | - |
|
|
|
|
|
|
| 钻石号 | 347 | - | - |
|
2026-04-24 10:57:52 +08:00
|
|
|
|
"""
|
2026-05-01 10:00:17 +08:00
|
|
|
|
if not number:
|
|
|
|
|
|
return "未知"
|
|
|
|
|
|
|
|
|
|
|
|
if number.startswith('J0'):
|
|
|
|
|
|
digits = number[2:]
|
|
|
|
|
|
elif number.startswith('J'):
|
|
|
|
|
|
digits = number[1:]
|
|
|
|
|
|
else:
|
|
|
|
|
|
return "未知"
|
|
|
|
|
|
|
|
|
|
|
|
if len(digits) == 9:
|
|
|
|
|
|
d = digits
|
|
|
|
|
|
elif len(digits) == 8:
|
|
|
|
|
|
d = digits[:8]
|
|
|
|
|
|
elif len(digits) == 7:
|
|
|
|
|
|
d = digits
|
|
|
|
|
|
else:
|
|
|
|
|
|
return "未知"
|
|
|
|
|
|
|
|
|
|
|
|
unique = set(d)
|
|
|
|
|
|
|
|
|
|
|
|
# 1. 通货:带4
|
|
|
|
|
|
if '4' in unique:
|
|
|
|
|
|
return "通货"
|
|
|
|
|
|
|
|
|
|
|
|
# 2. 带7号:无4,有7
|
|
|
|
|
|
if '7' in unique:
|
|
|
|
|
|
return "带7号"
|
|
|
|
|
|
|
|
|
|
|
|
# 3. 圆圆号:无123457,0689四个数字任意组合
|
|
|
|
|
|
if unique <= {'0', '6', '8', '9'}:
|
|
|
|
|
|
return "圆圆号"
|
|
|
|
|
|
|
|
|
|
|
|
# 4. 永恒号:只有0和1(无47但不符合圆圆号/倒置号)
|
|
|
|
|
|
if unique <= {'0', '1'}:
|
|
|
|
|
|
return "永恒号"
|
|
|
|
|
|
|
|
|
|
|
|
# 5. 倒置号:01689组合,必须有1,且包含6/8/9中的至少一个
|
|
|
|
|
|
if unique <= {'0', '1', '6', '8', '9'} and '1' in unique and unique & {'6', '8', '9'}:
|
|
|
|
|
|
return "倒置号"
|
|
|
|
|
|
|
|
|
|
|
|
# 6. 金马王:无12347,05689组合,必须有5(排除1)
|
|
|
|
|
|
if unique <= {'0', '5', '6', '8', '9'} and '5' in unique and '1' not in unique:
|
|
|
|
|
|
return "金马王"
|
|
|
|
|
|
|
|
|
|
|
|
# 7. 金马号:无2347,015689组合,必须有1和5(包含1)
|
|
|
|
|
|
if unique <= {'0', '1', '5', '6', '8', '9'} and '1' in unique and '5' in unique:
|
|
|
|
|
|
return "金马号"
|
2026-04-24 10:57:52 +08:00
|
|
|
|
|
2026-05-01 10:00:17 +08:00
|
|
|
|
# 8. 金山王:无12457,03689组合,必须有3(排除1和5)
|
|
|
|
|
|
if unique <= {'0', '3', '6', '8', '9'} and '3' in unique and '1' not in unique and '5' not in unique:
|
|
|
|
|
|
return "金山王"
|
|
|
|
|
|
|
|
|
|
|
|
# 9. 天马王:无1247,035689组合,必须有3和5(排除1)
|
|
|
|
|
|
if unique <= {'0', '3', '5', '6', '8', '9'} and '3' in unique and '5' in unique and '1' not in unique:
|
|
|
|
|
|
return "天马王"
|
|
|
|
|
|
|
|
|
|
|
|
# 10. 金山号:无2457,013689组合,必须有1和3(排除5)
|
|
|
|
|
|
if unique <= {'0', '1', '3', '6', '8', '9'} and '1' in unique and '3' in unique and '5' not in unique:
|
|
|
|
|
|
return "金山号"
|
|
|
|
|
|
|
|
|
|
|
|
# 11. 天马号:无247,0135689组合,必须有1、3和5
|
|
|
|
|
|
if unique <= {'0', '1', '3', '5', '6', '8', '9'} and '1' in unique and '3' in unique and '5' in unique:
|
|
|
|
|
|
return "天马号"
|
|
|
|
|
|
|
|
|
|
|
|
# 12. 朦胧王:无13457
|
|
|
|
|
|
if not unique & {'1', '3', '4', '5', '7'}:
|
|
|
|
|
|
return "朦胧王"
|
|
|
|
|
|
|
|
|
|
|
|
# 13. 朦胧号:无3457
|
|
|
|
|
|
if not unique & {'3', '4', '5', '7'}:
|
|
|
|
|
|
return "朦胧号"
|
|
|
|
|
|
|
|
|
|
|
|
# 14. 如意号:无1347
|
|
|
|
|
|
if not unique & {'1', '3', '4', '7'}:
|
|
|
|
|
|
return "如意号"
|
|
|
|
|
|
|
|
|
|
|
|
# 15. 钻石号:无347
|
|
|
|
|
|
if not unique & {'3', '4', '7'}:
|
|
|
|
|
|
return "钻石号"
|
|
|
|
|
|
|
|
|
|
|
|
# 16. 永恒号兜底:无47
|
|
|
|
|
|
if '7' not in unique:
|
|
|
|
|
|
return "永恒号"
|
|
|
|
|
|
|
|
|
|
|
|
return "其他"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def check_match(col_number: str, expect_number: str, expect_category: str) -> bool:
|
|
|
|
|
|
"""检查藏品号码是否符合期望的分类"""
|
|
|
|
|
|
if not col_number or not expect_category:
|
2026-04-24 10:57:52 +08:00
|
|
|
|
return False
|
|
|
|
|
|
|
2026-05-01 10:00:17 +08:00
|
|
|
|
col_cat = classify_number(col_number)
|
|
|
|
|
|
|
|
|
|
|
|
if col_cat == expect_category:
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
if expect_category == "其他":
|
|
|
|
|
|
return check_number_pattern(col_number, expect_number)
|
|
|
|
|
|
|
|
|
|
|
|
return False
|
2026-04-24 10:57:52 +08:00
|
|
|
|
|
|
|
|
|
|
|
2026-05-01 10:00:17 +08:00
|
|
|
|
def check_number_pattern(col_number: str, expect_number: str) -> bool:
|
|
|
|
|
|
"""检查号码特征匹配"""
|
|
|
|
|
|
if not col_number or not expect_number:
|
|
|
|
|
|
return False
|
2026-04-24 10:57:52 +08:00
|
|
|
|
|
2026-05-01 10:00:17 +08:00
|
|
|
|
if col_number.startswith('J0'):
|
|
|
|
|
|
col_digits = col_number[2:]
|
|
|
|
|
|
else:
|
|
|
|
|
|
col_digits = col_number[1:] if len(col_number) > 1 else col_number
|
2026-04-24 10:57:52 +08:00
|
|
|
|
|
2026-05-01 10:00:17 +08:00
|
|
|
|
if expect_number.startswith('J0'):
|
|
|
|
|
|
exp_digits = expect_number[2:]
|
|
|
|
|
|
else:
|
|
|
|
|
|
exp_digits = expect_number[1:] if len(expect_number) > 1 else expect_number
|
2026-04-24 10:57:52 +08:00
|
|
|
|
|
2026-05-01 10:00:17 +08:00
|
|
|
|
min_len = min(len(col_digits), len(exp_digits))
|
|
|
|
|
|
return col_digits[:min_len] == exp_digits[:min_len]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_match_type(expect_number: str) -> str:
|
|
|
|
|
|
"""判断匹配类型"""
|
|
|
|
|
|
if not expect_number:
|
|
|
|
|
|
return "unknown"
|
2026-04-24 10:57:52 +08:00
|
|
|
|
|
2026-05-01 10:00:17 +08:00
|
|
|
|
if expect_number.startswith('J0'):
|
|
|
|
|
|
digits = expect_number[2:]
|
|
|
|
|
|
elif expect_number.startswith('J'):
|
|
|
|
|
|
digits = expect_number[1:]
|
|
|
|
|
|
else:
|
|
|
|
|
|
return "unknown"
|
2026-04-24 10:57:52 +08:00
|
|
|
|
|
2026-05-01 10:00:17 +08:00
|
|
|
|
if len(digits) == 8:
|
|
|
|
|
|
return "ten"
|
|
|
|
|
|
elif len(digits) == 7:
|
|
|
|
|
|
return "hundred"
|
|
|
|
|
|
elif len(digits) == 9:
|
|
|
|
|
|
return "single"
|
|
|
|
|
|
return "unknown"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def match_self_collections_count(db: Session, user_id: str, expect_number: str, expect_category: str) -> int:
|
|
|
|
|
|
"""计算匹配藏品数量(用户自有藏品)"""
|
|
|
|
|
|
from app.models.models import Collection
|
|
|
|
|
|
|
|
|
|
|
|
if not expect_number:
|
2026-04-24 10:57:52 +08:00
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
|
|
collections = db.query(Collection).filter(
|
|
|
|
|
|
Collection.f99_91_user_id == user_id,
|
|
|
|
|
|
Collection.f01_04_status == "in_collection"
|
|
|
|
|
|
).all()
|
|
|
|
|
|
|
|
|
|
|
|
count = 0
|
|
|
|
|
|
for c in collections:
|
|
|
|
|
|
number = c.f02_10_prefix_serial or ''
|
2026-05-01 10:00:17 +08:00
|
|
|
|
if check_match(number, expect_number, expect_category):
|
|
|
|
|
|
count += 1
|
2026-04-24 10:57:52 +08:00
|
|
|
|
|
|
|
|
|
|
return count
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-05-01 10:00:17 +08:00
|
|
|
|
def match_collections_count_from_coolbot(expect_number: str, expect_category: str) -> int:
|
|
|
|
|
|
"""计算匹配藏品数量(一尘数据库)"""
|
|
|
|
|
|
if not expect_number:
|
2026-04-24 10:57:52 +08:00
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
|
|
query = text("""
|
|
|
|
|
|
SELECT id, crown_code FROM collections
|
|
|
|
|
|
WHERE crown_code IS NOT NULL
|
|
|
|
|
|
AND crown_code != ''
|
|
|
|
|
|
AND crown_code LIKE 'J0%'
|
|
|
|
|
|
""")
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
with coolbot_engine.connect() as conn:
|
|
|
|
|
|
result = conn.execute(query)
|
2026-05-01 10:00:17 +08:00
|
|
|
|
return sum(1 for row in result if check_match(row[1], expect_number, expect_category))
|
2026-04-24 10:57:52 +08:00
|
|
|
|
except Exception as e:
|
2026-05-01 10:00:17 +08:00
|
|
|
|
print(f"Error: {e}")
|
2026-04-24 10:57:52 +08:00
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-05-01 10:00:17 +08:00
|
|
|
|
def match_collections_list_from_coolbot(expect_number: str, expect_category: str, limit: int = 20) -> List[dict]:
|
|
|
|
|
|
"""获取匹配的藏品列表(一尘数据库)"""
|
|
|
|
|
|
if not expect_number:
|
2026-04-24 10:57:52 +08:00
|
|
|
|
return []
|
|
|
|
|
|
|
|
|
|
|
|
query = text("""
|
|
|
|
|
|
SELECT id, name, category, crown_code, price, post_title, post_url, author, post_crawled_at
|
|
|
|
|
|
FROM collections
|
|
|
|
|
|
WHERE crown_code IS NOT NULL
|
|
|
|
|
|
AND crown_code != ''
|
|
|
|
|
|
AND crown_code LIKE 'J0%'
|
|
|
|
|
|
""")
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
with coolbot_engine.connect() as conn:
|
|
|
|
|
|
result = conn.execute(query)
|
|
|
|
|
|
matched = []
|
|
|
|
|
|
for row in result:
|
2026-05-01 10:00:17 +08:00
|
|
|
|
if check_match(row[3], expect_number, expect_category):
|
|
|
|
|
|
matched.append({
|
|
|
|
|
|
"id": row[0],
|
|
|
|
|
|
"name": row[1],
|
|
|
|
|
|
"category": row[2],
|
|
|
|
|
|
"crown_code": row[3],
|
|
|
|
|
|
"price": float(row[4]) if row[4] else None,
|
|
|
|
|
|
"post_title": row[5],
|
|
|
|
|
|
"post_url": row[6],
|
|
|
|
|
|
"author": row[7],
|
|
|
|
|
|
"post_crawled_at": row[8].isoformat() if row[8] else None
|
|
|
|
|
|
})
|
|
|
|
|
|
if len(matched) >= limit:
|
|
|
|
|
|
break
|
2026-04-24 10:57:52 +08:00
|
|
|
|
return matched
|
|
|
|
|
|
except Exception as e:
|
2026-05-01 10:00:17 +08:00
|
|
|
|
print(f"Error: {e}")
|
2026-04-24 10:57:52 +08:00
|
|
|
|
return []
|