Files
2025-10-20 12:32:18 +08:00

39 KiB

In [ ]:
import threading
import requests
from queue import Queue
from time import time,sleep
import os

# 配置信息
ip = "http://localhost:8080"
resource_paths = [
    "/static/index.2da1efab.css",
    "/static/js/chunk-vendors.js",
    "/static/js/index.js",
    "/static/js/pages-login-index.js"
]

# 创建下载结果队列
download_results = Queue()

def download_resource(path, thread_name):
    """下载单个资源的线程函数"""
    try:
        url = ip + path
        start_time = time()
        response = requests.get(url)
        
        # 模拟浏览器行为:检查状态码和内容类型
        if response.status_code != 200:
            result = f"线程 {thread_name}: {path} 下载失败, 状态码 {response.status_code}"
        else:
            # 简略显示内容(实际应用中应保存文件)
            content_preview = response.text[:50].replace('\n', ' ') + "..." if len(response.text) > 50 else response.text
            
            # 模拟浏览器解析CSS/JS的时间延迟
            process_time = 0.1 if any(ext in path for ext in ['.css', '.js']) else 0.0
            
            result = (
                f"线程 {thread_name}: 成功下载 {path}\n"
                f"类型: {'CSS' if '.css' in path else 'JS' if '.js' in path else '其他'}\n"
                f"大小: {len(response.text)/1024:.1f} KB\n"
                f"耗时: {time()-start_time:.3f}\n"
                f"内容预览: {content_preview}"
            )
        
        # 添加处理延迟,模拟浏览器执行
        sleep(process_time)
        
    except Exception as e:
        result = f"线程 {thread_name}: {path} 下载异常 - {str(e)}"
    
    download_results.put(result)

def simulate_browser_download(max_workers=6):
    """模拟浏览器并发下载行为"""
    print(f"模拟浏览器行为 - 并发下载线程数: {max_workers}")
    print("=" * 60)
    
    threads = []
    for i, path in enumerate(resource_paths):
        # 使用线程名标识资源类型
        thread_name = f"资源#{i+1}"
        
        # 创建并启动线程
        t = threading.Thread(
            target=download_resource,
            args=(path, thread_name),
            name=thread_name
        )
        t.start()
        threads.append(t)
        
        # 控制最大并发数,模拟浏览器的连接限制
        if len(threads) >= max_workers:
            for t in threads:
                t.join()
            threads = []
    
    # 等待剩余的线程完成
    for t in threads:
        t.join()
    
    # 打印结果
        while not download_results.empty():
            print(download_results.get())
            print("-" * 60)

    print("完成")
if __name__ == "__main__":
    print("浏览器静态资源请求模拟器")
    print("=" * 60)
    simulate_browser_download()
    
In [ ]:
from faker import Faker
import random
import json
from datetime import datetime

# 初始化Faker生成器(简体中文)
fake = Faker('zh_CN')

# 定义固定值
TEAM_ID = 106
PLAN_ID = 131

# 常见民族列表
ETHNIC_GROUPS = ["", "", "", "", "", "维吾尔", "土家", "", "蒙古", "", "布依", 
                "", "", "朝鲜", "", "哈尼", "哈萨克", "", "", "", "傈僳", "仡佬",
                "东乡", "高山", "拉祜", "", "", "纳西", "", "", "仫佬", "锡伯", "柯尔克孜",
                "达斡尔", "景颇", "毛南", "撒拉", "布朗", "塔吉克", "阿昌", "普米", "鄂温克", "",
                "", "基诺", "德昂", "保安", "俄罗斯", "裕固", "乌兹别克", "门巴", "鄂伦春", "独龙",
                "塔塔尔", "赫哲", "珞巴"]

# 疾病列表(健康状态)
HEALTH_CONDITIONS = [
    "", "近视", "弱视", "鼻炎", "哮喘", "过敏体质", "蛀牙", 
    "肥胖", "发育迟缓", "ADHD", "缺铁性贫血", "维生素缺乏",
    "易感冒体质", "偏瘦", "过敏(花粉)", "过敏(尘螨)", "过敏(牛奶)", 
    "消化不良", "多动症", "抽动症", "癫痫", "小儿麻痹", "乙肝病毒携带"
]

# 艺术特长
ART_TALENTS = ["", "钢琴", "小提琴", "绘画", "书法", "舞蹈", "声乐", "戏剧表演", 
             "摄影", "编程", "动画制作", "模型制作", "手工艺术"]

# 体育特长
SPORTS_TALENTS = ["", "游泳", "篮球", "足球", "乒乓球", "羽毛球", "田径", 
                "武术", "跆拳道", "空手道", "轮滑", "滑板", "自行车", "健美操"]

# 科技特长
TECH_TALENTS = ["", "机器人编程", "电子制作", "3D打印", "人工智能", "网页设计", 
              "科学实验", "天文观测", "植物培养", "动物观察", "航模制作"]

# 职业列表
OCCUPATIONS = ["医生", "教师", "工程师", "程序员", "设计师", "建筑师", "销售经理",
              "会计", "律师", "公务员", "记者", "警察", "消防员", "厨师", "商人",
              "自由职业", "个体经营者", "银行职员", "人力资源", "市场营销"]

# 公司类型
WORKPLACES = [
    "国有企业", "民营企业", "外资企业", "合资企业", "政府机关", 
    "事业单位", "学校", "医院", "设计事务所", "律师事务所",
    "科技公司", "互联网公司", "金融机构", "建筑公司", "零售企业"
]

# 小学名称
PRIMARY_SCHOOLS = [
    "第一实验小学", "第二实验小学", "育才小学", "阳光小学", "希望小学", "和平小学", 
    "实验小学分校", "新世纪小学", "东方小学", "先锋小学", "星光小学", "明德小学"
]

def generate_id_num(birthday):
    """生成符合规则的身份证号"""
    # 生成前6位(地区码,使用真实的地区码)
    region_codes = ["110101", "110105", "110106", "110107", "310112", "330102", "440106", "440304"]
    
    # 出生日期码(8位)
    birth_code = birthday.replace("-", "")
    
    # 顺序码(3位)
    sequence_code = str(random.randint(101, 998))
    
    # 生成校验码(1位)
    base = region_codes[random.randint(0, len(region_codes)-1)] + birth_code + sequence_code
    weight = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
    checksum_map = {0: '1', 1: '0', 2: 'X', 3: '9', 4: '8', 5: '7', 6: '6', 7: '5', 8: '4', 9: '3', 10: '2'}
    
    total = 0
    for i in range(17):
        total += int(base[i]) * weight[i]
    checksum = checksum_map[total % 11]
    
    return base + checksum

def generate_student_data():
    """生成单个学生数据"""
    # 基础信息
    sex = random.choice(["", ""])
    
    # 生日 (2010-2015年出生)
    birthday = fake.date_between(start_date="-15y", end_date="-8y").strftime("%Y-%m-%d")
    
    # 生成符合规则的身份证号
    id_num = generate_id_num(birthday)
    
    # 计算年龄(仅用于展示)
    birth_year = int(birthday[:4])
    current_year = datetime.now().year
    age = current_year - birth_year
    
    # 生成学生信息
    data = {
        "teamId": TEAM_ID,
        "stuName": fake.name(),  # 学生姓名
        "idNum": id_num,  # 身份证号
        "formerName": random.choices(["", fake.last_name() + fake.first_name()], weights=[0.8, 0.2])[0],  # 曾用名
        "sex": sex,  # 性别
        "nation": random.choice(ETHNIC_GROUPS),  # 民族
        "birthday": birthday,  # 生日
        "finishSchool": random.choice(PRIMARY_SCHOOLS),  # 毕业学校
        "file": None,  # 文件
        "hkadr": fake.province() + "/" + fake.city() + "/" + fake.district(),  # 户口所在地
        "homeAddress": fake.address(),  # 家庭住址
        "health": random.choice(HEALTH_CONDITIONS),  # 健康状况
        "artisticSpecialty": random.choice(ART_TALENTS),  # 艺术特长
        "artisticAchievements": "",  # 艺术成就
        "technologicalSpecialty": random.choice(TECH_TALENTS),  # 科技特长
        "technologicalAchievements": "",  # 科技成就
        "sportsSpecialty": random.choice(SPORTS_TALENTS),  # 体育特长
        "sportsAchievements": "",  # 体育成就
        "planId": PLAN_ID,  # 计划ID
        "imageUrl": "",  # 图片URL
        "schoolCode": "G" + id_num[:-1],  # 学籍号
        "age": age,  # 年龄(仅用于展示)
        "email": fake.email() if random.random() > 0.7 else "",  # 邮箱
        "height": str(random.randint(130, 170)),  # 身高
        "weight": str(random.randint(25, 60)),  # 体重
        "measurements": f"{random.randint(60, 90)},{random.randint(60, 90)},{random.randint(60, 90)}"  # 三围
    }
    
    # 生成监护人信息
    guardian_prefix = fake.last_name()
    
    # 监护人1 - 通常为父母之一
    relationship_choices = ["父亲", "母亲", "爷爷", "奶奶", "外公", "外婆"]
    relationship = random.choice(relationship_choices[:2])  # 主要选择父母
    
    data.update({
        "guardian1Name": guardian_prefix + ("先生" if relationship == "父亲" else "女士"),  # 监护人1姓名
        "guardian1Relationship": relationship,  # 关系
        "guardian1IdCardNumber": generate_id_num(fake.date_between(start_date="-55y", end_date="-25y").strftime("%Y-%m-%d")),  # 身份证号
        "guardian1Contact": fake.phone_number(),  # 联系方式
        "guardian1Occupation": random.choice(OCCUPATIONS),  # 职业
        "guardian1Workplace": random.choice(WORKPLACES)  # 工作单位
    })
    
    # 监护人2 - 可能是另一位家长或其他人
    if relationship == "父亲":
        guardian2_relationship = "母亲"
    else:
        guardian2_relationship = random.choices(
            ["父亲", "爷爷", "奶奶", "外公", "外婆", "其他亲属"], 
            weights=[0.7, 0.1, 0.1, 0.05, 0.05, 0.1]
        )[0]
    
    data.update({
        "guardian2Name": guardian_prefix + (
            "先生" if guardian2_relationship in ["父亲", "爷爷", "外公"] else "女士"
        ),  # 监护人2姓名
        "guardian2Relationship": guardian2_relationship,  # 关系
        "guardian2IdCardNumber": generate_id_num(fake.date_between(start_date="-55y", end_date="-25y").strftime("%Y-%m-%d")),  # 身份证号
        "guardian2Contact": fake.phone_number() if guardian2_relationship != "" else "",  # 联系方式
        "guardian2Occupation": random.choice(OCCUPATIONS) if guardian2_relationship != "" else "",  # 职业
        "guardian2Workplace": random.choice(WORKPLACES) if guardian2_relationship != "" else ""  # 工作单位
    })
    
    return data

def generate_students():
    student = generate_student_data()
    return student
In [ ]:
from faker import Faker
import random
import json
from datetime import datetime
import threading
import requests
from time import time, sleep
import uuid
from concurrent.futures import ThreadPoolExecutor, as_completed
# 初始化Faker生成器(简体中文)
fake = Faker('zh_CN')

# 定义固定值
TEAM_ID = 106
PLAN_ID = 131

# 常见民族列表
ETHNIC_GROUPS = ["", "", "", "", "", "维吾尔", "土家", "", "蒙古", "", "布依", 
                "", "", "朝鲜", "", "哈尼", "哈萨克", "", "", "", "傈僳", "仡佬",
                "东乡", "高山", "拉祜", "", "", "纳西", "", "", "仫佬", "锡伯", "柯尔克孜",
                "达斡尔", "景颇", "毛南", "撒拉", "布朗", "塔吉克", "阿昌", "普米", "鄂温克", "",
                "", "基诺", "德昂", "保安", "俄罗斯", "裕固", "乌兹别克", "门巴", "鄂伦春", "独龙",
                "塔塔尔", "赫哲", "珞巴"]

# 疾病列表(健康状态)
HEALTH_CONDITIONS = [
    "", "近视", "弱视", "鼻炎", "哮喘", "过敏体质", "蛀牙", 
    "肥胖", "发育迟缓", "ADHD", "缺铁性贫血", "维生素缺乏",
    "易感冒体质", "偏瘦", "过敏(花粉)", "过敏(尘螨)", "过敏(牛奶)", 
    "消化不良", "多动症", "抽动症", "癫痫", "小儿麻痹", "乙肝病毒携带"
]

# 艺术特长
ART_TALENTS = ["", "钢琴", "小提琴", "绘画", "书法", "舞蹈", "声乐", "戏剧表演", 
             "摄影", "编程", "动画制作", "模型制作", "手工艺术"]

# 体育特长
SPORTS_TALENTS = ["", "游泳", "篮球", "足球", "乒乓球", "羽毛球", "田径", 
                "武术", "跆拳道", "空手道", "轮滑", "滑板", "自行车", "健美操"]

# 科技特长
TECH_TALENTS = ["", "机器人编程", "电子制作", "3D打印", "人工智能", "网页设计", 
              "科学实验", "天文观测", "植物培养", "动物观察", "航模制作"]

# 职业列表
OCCUPATIONS = ["医生", "教师", "工程师", "程序员", "设计师", "建筑师", "销售经理",
              "会计", "律师", "公务员", "记者", "警察", "消防员", "厨师", "商人",
              "自由职业", "个体经营者", "银行职员", "人力资源", "市场营销"]

# 公司类型
WORKPLACES = [
    "国有企业", "民营企业", "外资企业", "合资企业", "政府机关", 
    "事业单位", "学校", "医院", "设计事务所", "律师事务所",
    "科技公司", "互联网公司", "金融机构", "建筑公司", "零售企业"
]

# 小学名称
PRIMARY_SCHOOLS = [
    "第一实验小学", "第二实验小学", "育才小学", "阳光小学", "希望小学", "和平小学", 
    "实验小学分校", "新世纪小学", "东方小学", "先锋小学", "星光小学", "明德小学"
]

def generate_id_num(birthday):
    """生成符合规则的身份证号"""
    # 生成前6位(地区码,使用真实的地区码)
    region_codes = ["110101", "110105", "110106", "110107", "310112", "330102", "440106", "440304"]
    
    # 出生日期码(8位)
    birth_code = birthday.replace("-", "")
    
    # 顺序码(3位)
    sequence_code = str(random.randint(101, 998))
    
    # 生成校验码(1位)
    base = region_codes[random.randint(0, len(region_codes)-1)] + birth_code + sequence_code
    weight = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
    checksum_map = {0: '1', 1: '0', 2: 'X', 3: '9', 4: '8', 5: '7', 6: '6', 7: '5', 8: '4', 9: '3', 10: '2'}
    
    total = 0
    for i in range(17):
        total += int(base[i]) * weight[i]
    checksum = checksum_map[total % 11]
    
    return base + checksum

def generate_student_data():
    """生成单个学生数据"""
    # 基础信息
    sex = random.choice(["", ""])
    
    # 生日 (2010-2015年出生)
    birthday = fake.date_between(start_date="-15y", end_date="-8y").strftime("%Y-%m-%d")
    
    # 生成符合规则的身份证号
    id_num = generate_id_num(birthday)
    
    # 计算年龄(仅用于展示)
    birth_year = int(birthday[:4])
    current_year = datetime.now().year
    age = current_year - birth_year
    
    # 生成学生信息
    data = {
        "teamId": TEAM_ID,
        "stuName": fake.name(),  # 学生姓名
        "idNum": id_num,  # 身份证号
        "formerName": random.choices(["", fake.last_name() + fake.first_name()], weights=[0.8, 0.2])[0],  # 曾用名
        "sex": sex,  # 性别
        "nation": random.choice(ETHNIC_GROUPS),  # 民族
        "birthday": birthday,  # 生日
        "finishSchool": random.choice(PRIMARY_SCHOOLS),  # 毕业学校
        "file": None,  # 文件
        "hkadr": fake.province() + "/" + fake.city() + "/" + fake.district(),  # 户口所在地
        "homeAddress": fake.address(),  # 家庭住址
        "health": random.choice(HEALTH_CONDITIONS),  # 健康状况
        "artisticSpecialty": random.choice(ART_TALENTS),  # 艺术特长
        "artisticAchievements": "",  # 艺术成就
        "technologicalSpecialty": random.choice(TECH_TALENTS),  # 科技特长
        "technologicalAchievements": "",  # 科技成就
        "sportsSpecialty": random.choice(SPORTS_TALENTS),  # 体育特长
        "sportsAchievements": "",  # 体育成就
        "planId": PLAN_ID,  # 计划ID
        "imageUrl": "",  # 图片URL
        "schoolCode": "G" + id_num[:-1],  # 学籍号
        "age": age,  # 年龄(仅用于展示)
        "email": fake.email() if random.random() > 0.7 else "",  # 邮箱
        "height": str(random.randint(130, 170)),  # 身高
        "weight": str(random.randint(25, 60)),  # 体重
        "measurements": f"{random.randint(60, 90)},{random.randint(60, 90)},{random.randint(60, 90)}"  # 三围
    }
    
    # 生成监护人信息
    guardian_prefix = fake.last_name()
    
    # 监护人1 - 通常为父母之一
    relationship_choices = ["父亲", "母亲", "爷爷", "奶奶", "外公", "外婆"]
    relationship = random.choice(relationship_choices[:2])  # 主要选择父母
    
    data.update({
        "guardian1Name": guardian_prefix + ("先生" if relationship == "父亲" else "女士"),  # 监护人1姓名
        "guardian1Relationship": relationship,  # 关系
        "guardian1IdCardNumber": generate_id_num(fake.date_between(start_date="-55y", end_date="-25y").strftime("%Y-%m-%d")),  # 身份证号
        "guardian1Contact": fake.phone_number(),  # 联系方式
        "guardian1Occupation": random.choice(OCCUPATIONS),  # 职业
        "guardian1Workplace": random.choice(WORKPLACES)  # 工作单位
    })
    
    # 监护人2 - 可能是另一位家长或其他人
    if relationship == "父亲":
        guardian2_relationship = "母亲"
    else:
        guardian2_relationship = random.choices(
            ["父亲", "爷爷", "奶奶", "外公", "外婆", "其他亲属"], 
            weights=[0.7, 0.1, 0.1, 0.05, 0.05, 0.1]
        )[0]
    
    data.update({
        "guardian2Name": guardian_prefix + (
            "先生" if guardian2_relationship in ["父亲", "爷爷", "外公"] else "女士"
        ),  # 监护人2姓名
        "guardian2Relationship": guardian2_relationship,  # 关系
        "guardian2IdCardNumber": generate_id_num(fake.date_between(start_date="-55y", end_date="-25y").strftime("%Y-%m-%d")),  # 身份证号
        "guardian2Contact": fake.phone_number() if guardian2_relationship != "" else "",  # 联系方式
        "guardian2Occupation": random.choice(OCCUPATIONS) if guardian2_relationship != "" else "",  # 职业
        "guardian2Workplace": random.choice(WORKPLACES) if guardian2_relationship != "" else ""  # 工作单位
    })
    
    return data

def generate_students():
    student = generate_student_data()
    return student


# 配置信息
BASE_URL = "http://localhost:8080"
STATIC_PATHS = [
    "/static/index.2da1efab.css",
    "/static/js/chunk-vendors.js",
    "/static/js/index.js",
    "/static/js/pages-login-index.js"
]

# 用户数据生成
USER_AGENTS = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15",
    "Mozilla/5.0 (iPhone; CPU iPhone OS 15_5 like Mac OS X) AppleWebKit/605.1.15",
    "Mozilla/5.0 (Linux; Android 12; SM-G991B) AppleWebKit/537.36",
    "Mozilla/5.0 (iPad; CPU OS 15_5 like Mac OS X) AppleWebKit/605.1.15"
]

def generate_user_data(user_id):
    """生成模拟用户数据"""
    return {
        "id": user_id,
        "session_id": str(uuid.uuid4()),
        "user_agent": random.choice(USER_AGENTS),
        "request_count": 0,
        "start_time": time()
    }

def simulate_user_session(user_id):
    """模拟一个用户的完整会话"""
    user_data = generate_user_data(user_id)
    print(f"👤 用户 {user_id} 开始会话, Session ID: {user_data['session_id']}")
    start = time()
    # 使用线程池请求所有静态资源
    with ThreadPoolExecutor(max_workers=len(STATIC_PATHS)) as executor:
        futures = {}
        
        # 提交所有静态资源请求任务
        for path in STATIC_PATHS:
            url = BASE_URL + path
            future = executor.submit(
                fetch_resource, 
                url, 
                user_data
            )
            futures[future] = path
            
        # 等待所有静态资源加载完成
        for future in as_completed(futures):
            path = futures[future]
            try:
                result = future.result()
                print(f"✅ 用户 {user_id} 资源 {path} 加载完成: {result}")
            except Exception as e:
                print(f"❌ 用户 {user_id} 资源加载失败: {str(e)}")
    end = time()
    # 所有静态资源加载完成后访问百度提交数据
    print(f"📊 用户 {user_id} 加载所有静态资源耗时: {end - start:.3f}")
    print(f"🚀 用户 {user_id} 开始看表单")
    submit_to_baidu(user_data)
    
    return user_data

def fetch_resource(url, user_data):
    """模拟用户获取单个资源"""
    # 更新用户请求计数
    user_data["request_count"] += 1
    
    # 生成请求头
    headers = {
        "User-Agent": user_data["user_agent"],
        "X-Session-Id": user_data["session_id"],
        "X-Request-Count": str(user_data["request_count"]),
        "Accept-Encoding": "gzip, deflate"
    }
    
    # 添加随机延迟(0.1-0.5秒)模拟网络波动
    sleep(random.uniform(0.1, 0.5))
    
    try:
        # 执行资源请求
        start_time = time()
        response = requests.get(url, headers=headers)
        elapsed_time = time() - start_time
        
        if response.status_code != 200:
            return f"错误状态码: {response.status_code} | 耗时: {elapsed_time:.3f}"
        
        # 提取文件名
        file_name = url.split('/')[-1]
        
        return f"文件: {file_name} | 大小: {len(response.text)/1024:.1f}KB | 耗时: {elapsed_time:.3f}"
    
    except Exception as e:
        return f"请求异常: {str(e)}"

def submit_to_baidu(user_data):
    """提交数据到百度"""
    # 构造提交数据
    payload = generate_students()
    sleep(random.uniform(5, 20))
    print(f"🎉 用户 {user_data['id']} 提交数据 ")
    try:
        # 提交到百度
        start_time = time()
        response = requests.post(
            "http://band.hxzhxy.cn/register/student/add",  # 模拟的百度提交API
            json=payload,
            headers={
                "User-Agent": user_data["user_agent"],
                "X-Session-Id": user_data["session_id"],
                "Content-Type": "application/json"
            }
        )
        elapsed_time = time() - start_time
        
        # 记录用户总用时
        total_time = time() - user_data["start_time"]
        
        if response.status_code == 200:
            print(f"🎉 用户 {user_data['id']} 数据提交成功! | 提交耗时: {elapsed_time:.3f}秒 | 总用时: {total_time:.3f}")
            return True
        else:
            print(f"⚠️ 用户 {user_data['id']} 提交失败: 状态码 {response.status_code} | 总用时: {total_time:.3f}")
            return False
    
    except Exception as e:
        print(f"‼️ 用户 {user_data['id']} 提交异常: {str(e)}")
        return False

def simulate_multiple_users(num_users=10):
    """模拟多个用户并发访问"""
    print(f"🎬 开始模拟 {num_users} 个用户并发访问...")
    print("=" * 70)
    
    with ThreadPoolExecutor(max_workers=num_users) as executor:
        # 提交所有用户会话任务
        futures = [executor.submit(simulate_user_session, i+1) for i in range(5)]
        
        # 等待所有用户完成并收集结果
        results = []
        for future in as_completed(futures):
            try:
                user_data = future.result()
                results.append(user_data)
            except Exception as e:
                print(f"用户会话异常: {str(e)}")
    
    # 生成性能报告
    print("\n" + "=" * 70)
    print("🏁 所有用户已完成操作! 性能摘要:")
    print("-" * 70)
    
    total_time = 0
    total_requests = 0
    completed_submissions = 0
    
    for user in results:
        session_time = time() - user["start_time"]
        total_time += session_time
        total_requests += user["request_count"]
        
        print(f"用户 {user['id']:2d} | 用时: {session_time:.3f}秒 | 请求数: {user['request_count']}")
    
    print("\n汇总统计:")
    print(f"- 平均用时: {total_time/len(results):.3f}秒/用户")
    print(f"- 总请求数: {total_requests}")
    print(f"- 用户提交率: {completed_submissions}/{len(results)}")
    print("=" * 70)

if __name__ == "__main__":
    simulate_multiple_users(num_users=10)
🎬 开始模拟 10 个用户并发访问...
======================================================================
👤 用户 1 开始会话, Session ID: 679e3877-56ac-4791-a1e5-880468dc1d87
👤 用户 2 开始会话, Session ID: 96acf953-61d0-4499-900e-482f75112453
👤 用户 3 开始会话, Session ID: 588c0b3d-05ec-438e-a327-bc51fbc3927a
👤 用户 4 开始会话, Session ID: 52eb7ae8-d1b7-48ae-a0f2-8b1bd384d5b8
👤 用户 5 开始会话, Session ID: 42561435-6a9c-4eac-8282-ce4dd7d11547
👤 用户 6 开始会话, Session ID: ee1daae1-584b-409c-affe-d14ad170d309
👤 用户 7 开始会话, Session ID: 43901a21-0c91-45a5-9c98-b6fddd2dcb50
👤 用户 8 开始会话, Session ID: e8ad6873-751f-4f81-8409-cf1c34ee50ee
👤 用户 9 开始会话, Session ID: ae1d258d-545e-4c45-8b81-d9a8ba3d94e8
👤 用户 10 开始会话, Session ID: fed2a8aa-a697-4e4b-b433-11a9edbe1640
✅ 用户 5 资源 /static/index.2da1efab.css 加载完成: 文件: index.2da1efab.css | 大小: 93.7KB | 耗时: 2.032秒
✅ 用户 2 资源 /static/js/pages-login-index.js 加载完成: 文件: pages-login-index.js | 大小: 79.8KB | 耗时: 2.040秒
✅ 用户 4 资源 /static/index.2da1efab.css 加载完成: 文件: index.2da1efab.css | 大小: 93.7KB | 耗时: 2.040秒
✅ 用户 2 资源 /static/js/chunk-vendors.js 加载完成: 文件: chunk-vendors.js | 大小: 2655.9KB | 耗时: 2.057秒
✅ 用户 9 资源 /static/index.2da1efab.css 加载完成: 文件: index.2da1efab.css | 大小: 93.7KB | 耗时: 2.038秒
✅ 用户 4 资源 /static/js/index.js 加载完成: 文件: index.js | 大小: 100.2KB | 耗时: 2.044秒
✅ 用户 6 资源 /static/index.2da1efab.css 加载完成: 文件: index.2da1efab.css | 大小: 93.7KB | 耗时: 2.034秒
✅ 用户 1 资源 /static/js/index.js 加载完成: 文件: index.js | 大小: 100.2KB | 耗时: 2.036秒
✅ 用户 9 资源 /static/js/pages-login-index.js 加载完成: 文件: pages-login-index.js | 大小: 79.8KB | 耗时: 2.029秒
✅ 用户 10 资源 /static/js/index.js 加载完成: 文件: index.js | 大小: 100.2KB | 耗时: 2.037秒
✅ 用户 1 资源 /static/js/pages-login-index.js 加载完成: 文件: pages-login-index.js | 大小: 79.8KB | 耗时: 2.033秒
✅ 用户 6 资源 /static/js/pages-login-index.js 加载完成: 文件: pages-login-index.js | 大小: 79.8KB | 耗时: 2.040秒
✅ 用户 4 资源 /static/js/pages-login-index.js 加载完成: 文件: pages-login-index.js | 大小: 79.8KB | 耗时: 2.040秒
✅ 用户 10 资源 /static/index.2da1efab.css 加载完成: 文件: index.2da1efab.css | 大小: 93.7KB | 耗时: 2.040秒
✅ 用户 8 资源 /static/js/chunk-vendors.js 加载完成: 文件: chunk-vendors.js | 大小: 2655.9KB | 耗时: 2.051秒
✅ 用户 8 资源 /static/index.2da1efab.css 加载完成: 文件: index.2da1efab.css | 大小: 93.7KB | 耗时: 2.032秒
✅ 用户 2 资源 /static/index.2da1efab.css 加载完成: 文件: index.2da1efab.css | 大小: 93.7KB | 耗时: 2.038秒
✅ 用户 10 资源 /static/js/chunk-vendors.js 加载完成: 文件: chunk-vendors.js | 大小: 2655.9KB | 耗时: 2.077秒
✅ 用户 7 资源 /static/js/index.js 加载完成: 文件: index.js | 大小: 100.2KB | 耗时: 2.053秒
✅ 用户 10 资源 /static/js/pages-login-index.js 加载完成: 文件: pages-login-index.js | 大小: 79.8KB | 耗时: 2.047秒
📊 用户 10 加载所有静态资源耗时: 2.305秒
🚀 用户 10 开始看表单
✅ 用户 5 资源 /static/js/chunk-vendors.js 加载完成: 文件: chunk-vendors.js | 大小: 2655.9KB | 耗时: 2.066秒
✅ 用户 6 资源 /static/js/index.js 加载完成: 文件: index.js | 大小: 100.2KB | 耗时: 2.046秒
✅ 用户 4 资源 /static/js/chunk-vendors.js 加载完成: 文件: chunk-vendors.js | 大小: 2655.9KB | 耗时: 2.069秒
📊 用户 4 加载所有静态资源耗时: 2.348秒
🚀 用户 4 开始看表单
✅ 用户 7 资源 /static/index.2da1efab.css 加载完成: 文件: index.2da1efab.css | 大小: 93.7KB | 耗时: 2.055秒
✅ 用户 7 资源 /static/js/pages-login-index.js 加载完成: 文件: pages-login-index.js | 大小: 79.8KB | 耗时: 2.055秒
✅ 用户 6 资源 /static/js/chunk-vendors.js 加载完成: 文件: chunk-vendors.js | 大小: 2655.9KB | 耗时: 2.069秒
📊 用户 6 加载所有静态资源耗时: 2.391秒
🚀 用户 6 开始看表单
✅ 用户 3 资源 /static/js/index.js 加载完成: 文件: index.js | 大小: 100.2KB | 耗时: 2.043秒
✅ 用户 9 资源 /static/js/index.js 加载完成: 文件: index.js | 大小: 100.2KB | 耗时: 2.057秒
✅ 用户 1 资源 /static/index.2da1efab.css 加载完成: 文件: index.2da1efab.css | 大小: 93.7KB | 耗时: 2.055秒
✅ 用户 8 资源 /static/js/pages-login-index.js 加载完成: 文件: pages-login-index.js | 大小: 79.8KB | 耗时: 2.057秒
✅ 用户 3 资源 /static/index.2da1efab.css 加载完成: 文件: index.2da1efab.css | 大小: 93.7KB | 耗时: 2.063秒
✅ 用户 7 资源 /static/js/chunk-vendors.js 加载完成: 文件: chunk-vendors.js | 大小: 2655.9KB | 耗时: 2.085秒
📊 用户 7 加载所有静态资源耗时: 2.434秒
🚀 用户 7 开始看表单
✅ 用户 1 资源 /static/js/chunk-vendors.js 加载完成: 文件: chunk-vendors.js | 大小: 2655.9KB | 耗时: 2.073秒
📊 用户 1 加载所有静态资源耗时: 2.450秒
🚀 用户 1 开始看表单
✅ 用户 3 资源 /static/js/pages-login-index.js 加载完成: 文件: pages-login-index.js | 大小: 79.8KB | 耗时: 2.037秒
✅ 用户 3 资源 /static/js/chunk-vendors.js 加载完成: 文件: chunk-vendors.js | 大小: 2655.9KB | 耗时: 2.059秒
📊 用户 3 加载所有静态资源耗时: 2.487秒
🚀 用户 3 开始看表单
✅ 用户 2 资源 /static/js/index.js 加载完成: 文件: index.js | 大小: 100.2KB | 耗时: 2.049秒
📊 用户 2 加载所有静态资源耗时: 2.507秒
🚀 用户 2 开始看表单
✅ 用户 9 资源 /static/js/chunk-vendors.js 加载完成: 文件: chunk-vendors.js | 大小: 2655.9KB | 耗时: 2.051秒
📊 用户 9 加载所有静态资源耗时: 2.519秒
🚀 用户 9 开始看表单
✅ 用户 8 资源 /static/js/index.js 加载完成: 文件: index.js | 大小: 100.2KB | 耗时: 2.040秒
📊 用户 8 加载所有静态资源耗时: 2.527秒
🚀 用户 8 开始看表单
✅ 用户 5 资源 /static/js/index.js 加载完成: 文件: index.js | 大小: 100.2KB | 耗时: 2.043秒
✅ 用户 5 资源 /static/js/pages-login-index.js 加载完成: 文件: pages-login-index.js | 大小: 79.8KB | 耗时: 2.035秒
📊 用户 5 加载所有静态资源耗时: 2.542秒
🚀 用户 5 开始看表单