snack-mall/create_products.py

382 lines
19 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
创建500条商品数据的脚本
"""
import urllib.request
import urllib.error
import json
import random
import time
from datetime import datetime
BASE_URL = "http://localhost:8080"
AUTH_TOKEN = "Bearer 96dd2c26-8072-4a17-a449-7d302634f63a"
HEADERS = {
"Content-Type": "application/json",
"Authorization": AUTH_TOKEN
}
# 已验证的真实网络图片URL食物/零食相关)
REAL_IMAGE_URLS = [
"https://images.unsplash.com/photo-1590080875515-8a3a8dc5735e?w=400&h=400&fit=crop",
"https://images.unsplash.com/photo-1621939514649-280e2ee25f60?w=400&h=400&fit=crop",
"https://images.unsplash.com/photo-1578985545062-69928b1d9587?w=400&h=400&fit=crop",
"https://images.unsplash.com/photo-1558961363-fa8fdf82db35?w=400&h=400&fit=crop",
"https://images.unsplash.com/photo-1582058091505-f87a2e55a40f?w=400&h=400&fit=crop",
"https://images.unsplash.com/photo-1581793745862-99fde7fa73d2?w=400&h=400&fit=crop",
"https://images.unsplash.com/photo-1622483767028-3f66f32aef97?w=400&h=400&fit=crop",
"https://images.unsplash.com/photo-1566478989037-eec170784d0b?w=400&h=400&fit=crop",
"https://images.unsplash.com/photo-1549007994-cb92caebd54b?w=400&h=400&fit=crop",
"https://images.unsplash.com/photo-1481391319762-47dff72954d9?w=400&h=400&fit=crop",
"https://images.unsplash.com/photo-1528821128474-27f963b062bf?w=400&h=400&fit=crop",
"https://images.unsplash.com/photo-1606313564200-e75d5e30476c?w=400&h=400&fit=crop",
"https://images.unsplash.com/photo-1622484212850-eb596d769edc?w=400&h=400&fit=crop",
"https://images.unsplash.com/photo-1599599810769-bcde5a160d32?w=400&h=400&fit=crop",
"https://images.unsplash.com/photo-1587132137056-bfbf0166836e?w=400&h=400&fit=crop",
"https://images.unsplash.com/photo-1617374128851-c84e37dc9f37?w=400&h=400&fit=crop",
"https://images.unsplash.com/photo-1559598467-f8b76c8155d0?w=400&h=400&fit=crop",
"https://images.unsplash.com/photo-1509365465985-25d11c17e812?w=400&h=400&fit=crop",
"https://images.unsplash.com/photo-1603569283847-aa295f0d016a?w=400&h=400&fit=crop",
"https://images.unsplash.com/photo-1582281298055-e25b84a30b0b?w=400&h=400&fit=crop",
"https://images.unsplash.com/photo-1599490659213-e2b9527bd087?w=400&h=400&fit=crop",
"https://images.unsplash.com/photo-1559563362-c667ba5f5480?w=400&h=400&fit=crop",
"https://images.unsplash.com/photo-1551024601-bec78aea704b?w=400&h=400&fit=crop",
"https://images.unsplash.com/photo-1486893732792-e4f533048797?w=400&h=400&fit=crop",
"https://images.unsplash.com/photo-1511381939415-e44015466834?w=400&h=400&fit=crop",
"https://images.unsplash.com/photo-1606312619070-d48b4c652a52?w=400&h=400&fit=crop",
"https://images.unsplash.com/photo-1601058268499-e526168d9f37?w=400&h=400&fit=crop",
"https://images.unsplash.com/photo-1585225257732-537f48f9116e?w=400&h=400&fit=crop",
"https://images.unsplash.com/photo-1606313564200-e75d5e30476c?w=400&h=400&fit=crop",
"https://images.unsplash.com/photo-1590080875515-8a3a8dc5735e?w=400&h=400&fit=crop",
]
# 按分类组织的商品数据
PRODUCT_DATA = {
1: { # 坚果炒货
"brands": ["三只松鼠", "良品铺子", "百草味", "洽洽", "来伊份", "沃隆", "楼兰蜜语", "华味亨", "松鼠集团", "坚果工坊"],
"items": [
("原味夏威夷果", ["500g", "250g", "1000g"], [39.9, 59.9, 89.9]),
("奶油味腰果", ["200g", "500g", "1000g"], [29.9, 49.9, 79.9]),
("盐焗开心果", ["300g", "500g", "150g"], [49.9, 69.9, 29.9]),
("蜂蜜核桃仁", ["250g", "500g", "100g"], [25.9, 39.9, 15.9]),
("原味瓜子", ["500g", "1000g", "200g"], [12.9, 19.9, 8.9]),
("炭烧腰果", ["200g", "500g", "100g"], [32.9, 55.9, 19.9]),
("碧根果", ["250g", "500g", "1000g"], [35.9, 59.9, 99.9]),
("巴旦木", ["300g", "500g", "150g"], [29.9, 45.9, 18.9]),
("松子", ["200g", "500g", "100g"], [59.9, 119.9, 35.9]),
("榛子", ["250g", "500g", "150g"], [39.9, 69.9, 25.9]),
("南瓜子", ["500g", "1000g", "200g"], [15.9, 25.9, 9.9]),
("杏仁", ["300g", "500g", "150g"], [32.9, 49.9, 19.9]),
("混合坚果", ["500g", "750g", "300g"], [69.9, 89.9, 45.9]),
("每日坚果", ["30包", "15包", "7包"], [99.9, 55.9, 29.9]),
("琥珀核桃", ["250g", "500g", "150g"], [22.9, 35.9, 15.9]),
("蒜香花生", ["500g", "1000g", "200g"], [9.9, 16.9, 5.9]),
("怪味胡豆", ["300g", "500g", "150g"], [12.9, 18.9, 8.9]),
("兰花豆", ["300g", "500g", "150g"], [11.9, 17.9, 7.9]),
("香酥蚕豆", ["300g", "500g", "150g"], [10.9, 16.9, 6.9]),
("青豆", ["500g", "1000g", "200g"], [14.9, 24.9, 8.9]),
]
},
2: { # 肉干果脯
"brands": ["无穷", "老四川", "周黑鸭", "绝味", "三只松鼠", "良品铺子", "百草味", "来伊份", "劲仔", "友臣"],
"items": [
("蜜汁猪肉脯", ["250g", "500g", "100g"], [29.9, 49.9, 15.9]),
("麻辣牛肉干", ["200g", "500g", "100g"], [39.9, 79.9, 22.9]),
("五香鸭脖", ["300g", "500g", "150g"], [25.9, 39.9, 15.9]),
("香辣鸡翅", ["200g", "400g", "100g"], [29.9, 49.9, 18.9]),
("鱿鱼丝", ["200g", "500g", "100g"], [22.9, 45.9, 14.9]),
("烤鱼片", ["200g", "400g", "100g"], [25.9, 45.9, 15.9]),
("手撕牛肉", ["200g", "500g", "100g"], [45.9, 99.9, 25.9]),
("卤蛋", ["10枚", "20枚", "5枚"], [15.9, 25.9, 9.9]),
("鸡爪", ["300g", "500g", "150g"], [22.9, 35.9, 13.9]),
("鸭掌", ["200g", "400g", "100g"], [25.9, 45.9, 15.9]),
("肉枣", ["300g", "500g", "150g"], [19.9, 29.9, 12.9]),
("香辣小鱼干", ["200g", "500g", "100g"], [15.9, 32.9, 9.9]),
("鱿鱼仔", ["200g", "400g", "100g"], [19.9, 35.9, 12.9]),
("墨鱼仔", ["200g", "400g", "100g"], [22.9, 39.9, 13.9]),
("卤牛肉", ["200g", "500g", "100g"], [49.9, 109.9, 28.9]),
("酱板鸭", ["半只", "整只", "1/4只"], [39.9, 69.9, 22.9]),
("腊肉", ["500g", "1000g", "250g"], [59.9, 109.9, 32.9]),
("腊肠", ["500g", "1000g", "250g"], [49.9, 89.9, 28.9]),
("肉松", ["200g", "500g", "100g"], [25.9, 49.9, 15.9]),
("鱼豆腐", ["300g", "500g", "150g"], [15.9, 23.9, 9.9]),
]
},
3: { # 糖果巧克力
"brands": ["德芙", "费列罗", "阿尔卑斯", "徐福记", "好时", "Lindt", "雀巢", "玛氏", "金帝", "明治"],
"items": [
("牛奶巧克力", ["100g", "200g", "500g"], [19.9, 35.9, 79.9]),
("黑巧克力85%", ["100g", "200g", "500g"], [29.9, 49.9, 109.9]),
("草莓味软糖", ["200g", "500g", "100g"], [12.9, 25.9, 7.9]),
("水果硬糖", ["300g", "500g", "150g"], [9.9, 14.9, 5.9]),
("棒棒糖", ["20支", "50支", "10支"], [15.9, 32.9, 8.9]),
("棉花糖", ["200g", "500g", "100g"], [11.9, 22.9, 6.9]),
("牛轧糖", ["300g", "500g", "150g"], [19.9, 29.9, 11.9]),
("太妃糖", ["200g", "500g", "100g"], [15.9, 32.9, 8.9]),
("夹心巧克力", ["150g", "300g", "500g"], [25.9, 45.9, 69.9]),
("酒心巧克力", ["150g", "300g", "500g"], [29.9, 52.9, 79.9]),
("薄荷糖", ["200g", "500g", "100g"], [9.9, 19.9, 5.9]),
("跳跳糖", ["30包", "50包", "10包"], [12.9, 18.9, 5.9]),
("橡皮糖", ["200g", "500g", "100g"], [11.9, 22.9, 6.9]),
("QQ糖", ["200g", "500g", "100g"], [10.9, 19.9, 5.9]),
("酥糖", ["300g", "500g", "150g"], [15.9, 24.9, 8.9]),
("花生糖", ["300g", "500g", "150g"], [14.9, 22.9, 8.9]),
("芝麻糖", ["300g", "500g", "150g"], [13.9, 21.9, 7.9]),
("果冻", ["500g", "1000g", "250g"], [15.9, 27.9, 8.9]),
("巧克力豆", ["200g", "500g", "100g"], [18.9, 39.9, 9.9]),
("松露巧克力", ["200g", "500g", "100g"], [35.9, 79.9, 19.9]),
]
},
4: { # 饼干糕点
"brands": ["奥利奥", "好利来", "良品铺子", "三只松鼠", "达利园", "盼盼", "康师傅", "华美", "嘉士利", "好吃点"],
"items": [
("夹心饼干", ["388g", "500g", "200g"], [19.9, 24.9, 11.9]),
("曲奇饼干", ["300g", "500g", "150g"], [22.9, 35.9, 12.9]),
("威化饼干", ["200g", "400g", "100g"], [15.9, 28.9, 8.9]),
("苏打饼干", ["500g", "1000g", "250g"], [12.9, 22.9, 7.9]),
("消化饼干", ["400g", "800g", "200g"], [15.9, 27.9, 8.9]),
("蛋黄酥", ["6枚", "12枚", "4枚"], [25.9, 45.9, 18.9]),
("凤梨酥", ["300g", "500g", "150g"], [29.9, 45.9, 16.9]),
("绿豆糕", ["300g", "500g", "150g"], [19.9, 29.9, 11.9]),
("老婆饼", ["300g", "500g", "150g"], [15.9, 24.9, 8.9]),
("麻花", ["300g", "500g", "150g"], [12.9, 19.9, 6.9]),
("沙琪玛", ["500g", "1000g", "250g"], [14.9, 25.9, 8.9]),
("蛋黄派", ["300g", "500g", "150g"], [15.9, 24.9, 8.9]),
("瑞士卷", ["300g", "500g", "150g"], [18.9, 29.9, 10.9]),
("蛋糕", ["6个装", "12个装", "3个装"], [29.9, 52.9, 16.9]),
("面包", ["500g", "1000g", "250g"], [15.9, 27.9, 8.9]),
("吐司", ["300g", "500g", "150g"], [12.9, 19.9, 6.9]),
("蛋卷", ["300g", "500g", "150g"], [19.9, 29.9, 10.9]),
("煎饼", ["300g", "500g", "150g"], [11.9, 18.9, 6.9]),
("桃酥", ["300g", "500g", "150g"], [13.9, 21.9, 7.9]),
("云片糕", ["300g", "500g", "150g"], [14.9, 22.9, 7.9]),
]
},
5: { # 蜜饯果干
"brands": ["溜溜梅", "楼兰蜜语", "良品铺子", "百草味", "来伊份", "三只松鼠", "华味亨", "好想你", "西域美农", "果园老农"],
"items": [
("芒果干", ["100g", "200g", "500g"], [12.9, 22.9, 45.9]),
("葡萄干", ["200g", "500g", "1000g"], [15.9, 29.9, 52.9]),
("草莓干", ["100g", "200g", "500g"], [18.9, 32.9, 69.9]),
("话梅", ["200g", "500g", "100g"], [14.9, 29.9, 8.9]),
("山楂片", ["300g", "500g", "150g"], [9.9, 14.9, 5.9]),
("山楂条", ["300g", "500g", "150g"], [10.9, 16.9, 5.9]),
("果脯", ["300g", "500g", "150g"], [15.9, 24.9, 8.9]),
("杏干", ["200g", "500g", "100g"], [18.9, 39.9, 10.9]),
("桃干", ["200g", "500g", "100g"], [16.9, 35.9, 9.9]),
("苹果干", ["200g", "500g", "100g"], [14.9, 29.9, 8.9]),
("香蕉片", ["200g", "500g", "100g"], [12.9, 25.9, 7.9]),
("菠萝干", ["200g", "500g", "100g"], [15.9, 32.9, 8.9]),
("猕猴桃干", ["200g", "500g", "100g"], [16.9, 34.9, 9.9]),
("柠檬干", ["100g", "200g", "50g"], [12.9, 22.9, 6.9]),
("陈皮", ["200g", "500g", "100g"], [15.9, 32.9, 8.9]),
("杨梅干", ["200g", "500g", "100g"], [17.9, 35.9, 9.9]),
("酸枣", ["200g", "500g", "100g"], [13.9, 27.9, 7.9]),
("无花果干", ["200g", "500g", "100g"], [19.9, 39.9, 10.9]),
("椰枣", ["200g", "500g", "100g"], [22.9, 45.9, 12.9]),
("蔓越莓干", ["200g", "500g", "100g"], [18.9, 37.9, 9.9]),
]
},
6: { # 饮料冲饮
"brands": ["可口可乐", "百事可乐", "农夫山泉", "汇源", "三得利", "统一", "康师傅", "香飘飘", "雀巢", "星巴克"],
"items": [
("可乐", ["330ml*6", "500ml*6", "1.5L*2"], [22.9, 28.9, 15.9]),
("雪碧", ["330ml*6", "500ml*6", "1.5L*2"], [22.9, 28.9, 15.9]),
("橙汁", ["1L", "500ml*6", "250ml*12"], [19.9, 35.9, 42.9]),
("苹果汁", ["1L", "500ml*6", "250ml*12"], [18.9, 33.9, 39.9]),
("葡萄汁", ["1L", "500ml*6", "250ml*12"], [22.9, 39.9, 45.9]),
("蜜桃乌龙茶", ["500ml*6", "1L", "300ml*6"], [25.9, 18.9, 22.9]),
("绿茶", ["500ml*6", "1L", "300ml*6"], [19.9, 15.9, 18.9]),
("红茶", ["500ml*6", "1L", "300ml*6"], [19.9, 15.9, 18.9]),
("奶茶", ["500ml*6", "1L", "300ml*6"], [25.9, 19.9, 22.9]),
("咖啡", ["250ml*6", "500ml*6", "1L"], [29.9, 45.9, 22.9]),
("豆浆", ["250ml*12", "500ml*6", "1L"], [25.9, 22.9, 12.9]),
("牛奶", ["250ml*12", "500ml*6", "1L*2"], [39.9, 35.9, 22.9]),
("酸奶", ["200g*12", "100g*16", "500g*3"], [35.9, 29.9, 25.9]),
("气泡水", ["500ml*6", "1L", "330ml*6"], [29.9, 15.9, 22.9]),
("椰子水", ["500ml*6", "1L", "330ml*6"], [35.9, 19.9, 25.9]),
("功能饮料", ["250ml*6", "500ml*6", "1L"], [29.9, 39.9, 18.9]),
("苏打水", ["500ml*6", "1L", "330ml*6"], [22.9, 12.9, 16.9]),
("蜂蜜水", ["500ml*6", "1L", "350ml*6"], [25.9, 15.9, 19.9]),
("酸梅汤", ["500ml*6", "1L", "300ml*6"], [22.9, 14.9, 17.9]),
("凉茶", ["500ml*6", "1L", "310ml*6"], [24.9, 15.9, 19.9]),
]
}
}
def http_request(url, method="GET", data=None):
"""发送HTTP请求"""
req = urllib.request.Request(url, method=method)
for k, v in HEADERS.items():
req.add_header(k, v)
if data:
req.data = json.dumps(data, ensure_ascii=False).encode("utf-8")
try:
with urllib.request.urlopen(req, timeout=15) as resp:
return json.loads(resp.read().decode("utf-8"))
except urllib.error.HTTPError as e:
return {"code": e.code, "message": str(e.read().decode("utf-8", errors="ignore"))}
except Exception as e:
return {"code": -1, "message": str(e)}
def get_categories():
"""获取分类列表"""
url = f"{BASE_URL}/api/category/tree"
data = http_request(url)
if data.get("code") == 200:
return data.get("data", [])
print(f"获取分类失败: {data}")
return []
def create_product(product_data):
"""创建商品"""
url = f"{BASE_URL}/api/admin/product"
return http_request(url, method="POST", data=product_data)
def generate_sku_list(item_name, specs, prices, image_url):
"""生成SKU列表"""
sku_list = []
for i, (spec, price) in enumerate(zip(specs, prices)):
sku_name = f"{item_name.split()[0]}/{spec}"
stock = random.randint(100, 2000)
sku_list.append({
"skuName": sku_name,
"price": round(price, 2),
"stock": stock,
"image": image_url,
"sort": i + 1,
"weight": random.randint(100, 1000)
})
return sku_list
def generate_products(categories, total=500):
"""生成商品数据"""
products = []
category_ids = [c["id"] for c in categories]
for i in range(total):
cat_id = random.choice(category_ids)
cat_data = PRODUCT_DATA.get(cat_id, PRODUCT_DATA[1])
brand = random.choice(cat_data["brands"])
item = random.choice(cat_data["items"])
item_name, specs, prices = item
# 构造商品名称
prefix = random.choice(["", "精选", "进口", "网红", "手工", "传统", "", ""])
suffix = random.choice(["", " 礼盒装", " 家庭装", " 便携装", "", ""])
name = f"{prefix}{brand} {item_name}{suffix}".strip()
# 随机选择图片
main_image = random.choice(REAL_IMAGE_URLS)
sub_images = json.dumps([random.choice(REAL_IMAGE_URLS) for _ in range(3)])
# 生成原价(比售价略高)
origin_price = round(max(prices) * random.uniform(1.1, 1.5), 2)
# 生成详情HTML
detail = f"""
<div style="padding: 20px;">
<h2>{name}</h2>
<p>品牌:{brand}</p>
<p>规格:{', '.join(specs)}</p>
<p>产地:{random.choice(['中国', '美国', '日本', '韩国', '泰国', '越南', '马来西亚'])}</p>
<p>保质期:{random.choice(['6个月', '9个月', '12个月', '18个月'])}</p>
<p>储存方式:{random.choice(['常温保存', '阴凉干燥处', '冷藏保存'])}</p>
<img src="{main_image}" style="width:100%;max-width:600px;" />
<p>商品描述:精选优质原料,传统工艺制作,口感酥脆,回味无穷。{random.choice(['适合办公休闲', '聚会必备零食', '追剧好伴侣', '下午茶首选', '送礼佳品'])}。</p>
</div>
"""
sku_list = generate_sku_list(item_name, specs, prices, main_image)
product = {
"name": name,
"categoryId": cat_id,
"brand": brand,
"mainImage": main_image,
"subImages": sub_images,
"detail": detail,
"originPrice": origin_price,
"isHot": random.choice([0, 0, 0, 1]), # 25%概率热门
"isNew": random.choice([0, 0, 0, 1]), # 25%概率新品
"status": 1,
"skuList": sku_list
}
products.append(product)
return products
def main():
print("=" * 60)
print("开始创建500条商品数据")
print(f"时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print("=" * 60)
# 1. 获取分类
print("\n[1/3] 获取商品分类...")
categories = get_categories()
if not categories:
print("获取分类失败,退出")
return
print(f"获取到 {len(categories)} 个分类: {[c['name'] for c in categories]}")
# 2. 生成商品数据
print("\n[2/3] 生成500条商品数据...")
products = generate_products(categories, 500)
print(f"已生成 {len(products)} 条商品数据")
# 3. 创建商品
print("\n[3/3] 开始调用接口创建商品...")
success = 0
failed = 0
errors = []
for idx, product in enumerate(products, 1):
try:
result = create_product(product)
if result.get("code") == 200:
success += 1
print(f"[{idx}/500] ✓ 创建成功: {product['name'][:30]}... (ID: {result.get('data')})")
else:
failed += 1
errors.append({"index": idx, "name": product["name"], "error": result})
print(f"[{idx}/500] ✗ 创建失败: {product['name'][:30]}... - {result.get('message')}")
except Exception as e:
failed += 1
errors.append({"index": idx, "name": product["name"], "error": str(e)})
print(f"[{idx}/500] ✗ 异常: {product['name'][:30]}... - {e}")
# 适当延迟,避免请求过快
if idx % 10 == 0:
time.sleep(0.5)
print("\n" + "=" * 60)
print("执行结果统计")
print("=" * 60)
print(f"总计: 500")
print(f"成功: {success}")
print(f"失败: {failed}")
print(f"成功率: {success / 500 * 100:.2f}%")
if errors:
print(f"\n前10条错误详情:")
for err in errors[:10]:
print(f" [{err['index']}] {err['name'][:40]}: {err['error']}")
print("\n" + "=" * 60)
print(f"完成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print("=" * 60)
if __name__ == "__main__":
main()