Initial commit: 商品售卖网站

This commit is contained in:
2026-04-13 07:20:09 +08:00
commit c6154273f2
865 changed files with 26573 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
import sqlite3
conn = sqlite3.connect('backend/sale.db')
cursor = conn.cursor()
cursor.execute("PRAGMA table_info(users)")
print("Users table columns:")
for col in cursor.fetchall():
print(col)
+12
View File
@@ -0,0 +1,12 @@
import sqlite3
conn = sqlite3.connect('backend/sale.db')
cursor = conn.cursor()
cursor.execute("SELECT id, name FROM categories")
print("Categories in database:")
for row in cursor.fetchall():
print(f" ID: {row[0]}, Name: {row[1]}")
cursor.execute("SELECT product_id, category_id FROM product_categories")
print("\nProduct-Category relations:")
for row in cursor.fetchall():
print(f" Product ID: {row[0]}, Category ID: {row[1]}")
+7
View File
@@ -0,0 +1,7 @@
import sqlite3
conn = sqlite3.connect('backend/sale.db')
cursor = conn.cursor()
cursor.execute("SELECT id, name, price FROM products ORDER BY id DESC LIMIT 25")
print("Products in database:")
for row in cursor.fetchall():
print(f" ID: {row[0]}, Name: {row[1]}, Price: {row[2]}")
+156
View File
@@ -0,0 +1,156 @@
import requests
import json
import os
import sys
API_BASE = "http://localhost:8080/api"
ADMIN_TOKEN = None
def login():
global ADMIN_TOKEN
response = requests.post(f"{API_BASE}/auth/login", json={
"email": "admin@viaeon.com",
"password": "admin123"
})
if response.status_code == 200:
data = response.json()
ADMIN_TOKEN = data.get("token")
print(f"登录成功")
return True
else:
print(f"登录失败: {response.text}")
return False
def get_headers():
return {
"Authorization": f"Bearer {ADMIN_TOKEN}",
"Content-Type": "application/json"
}
def create_category(name):
response = requests.post(f"{API_BASE}/admin/categories",
headers=get_headers(),
json={"name": name}
)
if response.status_code == 200:
data = response.json()
return data.get("id")
else:
existing = requests.get(f"{API_BASE}/categories")
if existing.status_code == 200:
cats = existing.json()
if isinstance(cats, list):
for cat in cats:
if isinstance(cat, dict) and cat.get("name") == name:
return cat.get("id")
elif isinstance(cat, str) and cat == name:
return None
return None
def download_image(url, filepath):
try:
response = requests.get(url, timeout=30)
if response.status_code == 200:
with open(filepath, 'wb') as f:
f.write(response.content)
return True
except Exception as e:
print(f"下载图片失败: {e}")
return False
def upload_image(filepath):
try:
with open(filepath, 'rb') as f:
response = requests.post(f"{API_BASE}/upload",
headers={"Authorization": f"Bearer {ADMIN_TOKEN}"},
files={"file": f}
)
if response.status_code == 200:
data = response.json()
return data.get("url")
except Exception as e:
print(f"上传图片失败: {e}")
return None
def create_product(product, category_id, image_url):
price_cny = product.get("price_jpy", 0) * 0.048
price_cny = round(price_cny, 2)
data = {
"name": product.get("name_cn", product.get("name_jp", "未知商品")),
"description": f"{product.get('name_jp', '')}\n{product.get('description', '')}".strip(),
"price": price_cny,
"stock": 100,
"images": image_url,
"category_ids": [category_id] if category_id else [],
"is_active": True
}
response = requests.post(f"{API_BASE}/admin/products",
headers=get_headers(),
json=data
)
if response.status_code in [200, 201]:
return response.json()
else:
result = response.json()
if result.get("data") and result["data"].get("id"):
return result["data"]
print(f"创建商品失败: {response.text}")
return None
def main():
print("开始导入商品数据...")
if not login():
print("请先创建管理员账户")
return
with open('ribenyan_products.json', 'r', encoding='utf-8') as f:
products = json.load(f)
print(f"读取到 {len(products)} 个商品")
categories = {}
for product in products:
cat_name = product.get("category")
if cat_name not in categories:
cat_id = create_category(cat_name)
if cat_id:
categories[cat_name] = cat_id
print(f"创建分类: {cat_name} (ID: {cat_id})")
os.makedirs("temp_images", exist_ok=True)
success_count = 0
for i, product in enumerate(products):
print(f"\n处理商品 {i+1}/{len(products)}: {product.get('name_cn', 'N/A')}")
image_url = ""
if product.get("image_url"):
temp_path = f"temp_images/{product.get('external_id', i)}.webp"
if download_image(product["image_url"], temp_path):
uploaded_url = upload_image(temp_path)
if uploaded_url:
image_url = uploaded_url
print(f" 图片上传成功: {uploaded_url}")
cat_name = product.get("category")
cat_id = categories.get(cat_name)
result = create_product(product, cat_id, image_url)
if result:
success_count += 1
print(f" 商品创建成功")
else:
print(f" 商品创建失败")
import shutil
shutil.rmtree("temp_images", ignore_errors=True)
print(f"\n导入完成: 成功 {success_count}/{len(products)} 个商品")
if __name__ == "__main__":
main()
+72
View File
@@ -0,0 +1,72 @@
import sqlite3
conn = sqlite3.connect('backend/sale.db')
cursor = conn.cursor()
cursor.execute("SELECT id, name FROM categories")
categories = cursor.fetchall()
print(f"Found {len(categories)} categories")
brand_keywords = [
'MEVIUS', 'Camel', 'Marlboro', 'Lucky', 'Kent', 'Seven Stars', 'Winston',
'Lark', 'Pianissimo', 'Caster', 'Hope', 'Peace', 'Cabin', 'Golden Bat',
'Black Spider', 'Che', 'Ark Royal', 'Cigaronne', 'TEREA', 'IQOS', 'HEETS',
'Fiit', 'neo', 'sentia', 'virto', 'BON', 'LEAF', 'glow', 'Ploom',
'American Spirit', 'Pall Mall', 'Davidoff', 'Dunhill', 'Parliament',
'Virginia', 'Mild Seven', 'Salem', 'Kool', 'Newport', 'Carlton',
'Merit', 'Vantage', 'Now', 'Tareyton', 'Belair', 'L&M',
'Bond', 'Vogue', 'Rothmans', 'Gauloises', 'Gitanes', 'Prince',
'メビウス', 'キャメル', 'マールボロ', 'ラッキー', 'ケント', 'セブンスター', 'ウィンストン',
'ピアニッシモ', 'キャスター', 'ホープ', 'ピース', 'キャビン', 'ゴールデンバット',
'ブラック', 'チェ', 'アーク', 'シガローネ', 'テリア', 'アイコス', 'ヒーツ',
'フィット', 'ネオ', 'センシア', 'ヴェルト', 'ボン', 'リーフ', 'グロー', 'プルーム',
'アメリカンスピリット', 'パーラメント', 'バージニア', 'マイルドセブン', 'セーラム', 'クール',
'ラーク', 'ホープ', 'ピース', 'キャビン', 'キャメル', 'マールボロ'
]
migrated_count = 0
for cat_id, cat_name in categories:
is_brand = False
for keyword in brand_keywords:
if keyword.lower() in cat_name.lower():
is_brand = True
break
if is_brand:
cursor.execute("SELECT id FROM brands WHERE name = ?", (cat_name,))
existing = cursor.fetchone()
if existing:
brand_id = existing[0]
else:
cursor.execute("INSERT INTO brands (name, created_at, updated_at) VALUES (?, datetime('now'), datetime('now'))", (cat_name,))
brand_id = cursor.lastrowid
cursor.execute("UPDATE products SET brand_id = ? WHERE id IN (SELECT product_id FROM product_categories WHERE category_id = ?)", (brand_id, cat_id))
cursor.execute("DELETE FROM product_categories WHERE category_id = ?", (cat_id,))
cursor.execute("DELETE FROM categories WHERE id = ?", (cat_id,))
migrated_count += 1
print(f"Migrated brand: {cat_name}")
print(f"\nMigrated {migrated_count} brands")
new_categories = [
('卷烟', '传统卷烟产品'),
('IQOS烟弹', 'IQOS专用加热烟弹'),
('手卷烟丝', '手卷烟丝和烟纸'),
('雪茄', '雪茄产品'),
('电子烟', '电子烟及相关产品'),
('薄荷烟', '薄荷口味卷烟'),
('超细烟', '超细支卷烟'),
]
for name, desc in new_categories:
cursor.execute("SELECT id FROM categories WHERE name = ?", (name,))
if not cursor.fetchone():
cursor.execute("INSERT INTO categories (name, description, created_at, updated_at) VALUES (?, ?, datetime('now'), datetime('now'))", (name, desc))
print(f"Created category: {name}")
conn.commit()
conn.close()
print("\nMigration completed!")
+13
View File
@@ -0,0 +1,13 @@
import sqlite3
import bcrypt
conn = sqlite3.connect('backend/sale.db')
cursor = conn.cursor()
new_password = "admin123"
hashed = bcrypt.hashpw(new_password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
cursor.execute("UPDATE users SET password_hash = ? WHERE email = ?", (hashed, "admin@viaeon.com"))
conn.commit()
print("密码已重置为: admin123")
+181
View File
@@ -0,0 +1,181 @@
import requests
from bs4 import BeautifulSoup
import json
import re
import time
BASE_URL = "https://ribenyan.com"
HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,ja;q=0.7",
}
def fetch_page(url):
try:
response = requests.get(url, headers=HEADERS, timeout=30)
response.encoding = 'utf-8'
return response.text
except Exception as e:
print(f"Error fetching {url}: {e}")
return None
def get_category_links(html):
soup = BeautifulSoup(html, 'html.parser')
categories = []
accordion = soup.find('div', id='goodstypeaccordion')
if accordion:
links = accordion.find_all('a', class_='list-group-item')
for link in links:
href = link.get('href', '')
text = link.get_text(strip=True)
if href and 'm=goods&a=list' in href:
if href.startswith('./'):
href = BASE_URL + '/' + href[2:]
elif href.startswith('index.php'):
href = BASE_URL + '/' + href
elif not href.startswith('http'):
href = BASE_URL + '/' + href
categories.append({'name': text, 'url': href})
return categories
def parse_products(html):
products = []
soup = BeautifulSoup(html, 'html.parser')
rows = soup.find_all('div', class_='d-flex')
product_rows = []
for row in rows:
classes = row.get('class', [])
if 'py-2' in classes and 'border-bottom' in classes:
product_rows.append(row)
for row in product_rows:
try:
product = {}
id_div = row.find('div', class_='position-absolute')
if id_div and 'top-0' in id_div.get('class', []) and 'end-0' in id_div.get('class', []):
product['external_id'] = id_div.get_text(strip=True)
img = row.find('img', class_='img-thumbnail')
if img:
src = img.get('src', '')
if src:
if src.startswith('./'):
src = BASE_URL + '/' + src[2:]
elif not src.startswith('http'):
src = BASE_URL + src
product['image_url'] = src
p_tags = row.find_all('p')
name_found = False
for p in p_tags:
classes = p.get('class', [])
if 'mb-1' in classes and 'text-muted' not in classes and 'text-body-tertiary' not in classes and 'text-info' not in classes:
if not name_found:
product['name_cn'] = p.get_text(strip=True)
name_found = True
elif 'mb-1' in classes and 'text-muted' in classes:
product['name_jp'] = p.get_text(strip=True)
elif 'text-body-tertiary' in classes or 'text-info' in classes:
text = p.get_text(strip=True)
if text and '日元' not in text and '整大包' not in text and '限购' not in text:
product['description'] = text
if not product.get('description'):
product['description'] = ''
price_p = row.find('p', class_='mb-3')
if price_p:
price_text = price_p.get_text(strip=True)
price_match = re.search(r'(\d+)', price_text)
if price_match:
product['price_jpy'] = int(price_match.group(1))
if product.get('name_cn') or product.get('name_jp'):
products.append(product)
except Exception as e:
continue
return products
def categorize_product(product, category_name=None):
if category_name:
return category_name
name = product.get('name_cn', '') + ' ' + product.get('name_jp', '')
name_lower = name.lower()
if 'iqos' in name_lower or 'terea' in name_lower or 'iluma' in name_lower or 'sentia' in name_lower or 'virto' in name_lower or 'neo' in name_lower:
return 'IQOS烟弹'
elif 'シャグ' in name or 'shag' in name_lower or '手卷' in name or '烟丝' in name:
return '手卷烟丝'
elif 'シガー' in name or 'cigar' in name_lower or '雪茄' in name:
return '雪茄'
elif 'スーパースリム' in name or 'super slim' in name_lower or '超细' in name or '细支' in name:
return '超细烟'
elif 'メンソール' in name or 'menthol' in name_lower or '薄荷' in name or '爆珠' in name:
return '薄荷烟'
else:
return '卷烟'
def main():
print("开始采集 ribenyan.com 所有商品数据...")
html = fetch_page(BASE_URL)
if not html:
print("无法获取首页数据")
return
categories = get_category_links(html)
print(f"发现 {len(categories)} 个分类")
all_products = []
seen_ids = set()
for i, cat in enumerate(categories):
print(f"\n[{i+1}/{len(categories)}] 采集分类: {cat['name']}")
cat_html = fetch_page(cat['url'])
if not cat_html:
continue
products = parse_products(cat_html)
for p in products:
p['category'] = categorize_product(p, cat['name'])
p['price_cny'] = round(p.get('price_jpy', 0) * 0.048, 2)
pid = p.get('external_id')
if pid and pid not in seen_ids:
seen_ids.add(pid)
all_products.append(p)
print(f" 获取 {len(products)} 个商品,累计 {len(all_products)}")
time.sleep(0.5)
output_file = 'ribenyan_products.json'
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(all_products, f, ensure_ascii=False, indent=2)
print(f"\n采集完成,共获取 {len(all_products)} 个商品")
print(f"数据已保存到 {output_file}")
cat_stats = {}
for p in all_products:
cat = p.get('category', '其他')
if cat not in cat_stats:
cat_stats[cat] = 0
cat_stats[cat] += 1
print("\n分类统计:")
for cat, count in sorted(cat_stats.items(), key=lambda x: -x[1]):
print(f" {cat}: {count}")
if __name__ == "__main__":
main()