feat(recruiter): 添加招聘者账号管理模块及相关支持

- 新增招聘者账号数据库表结构及SQL建表脚本
- 实现招聘者实体类及账号状态枚举
- 添加SQLAlchemy数据库模型及管理器支持招聘者数据存储
- 实现招聘者数据访问层(Mapper)进行增删改查操作
- 开发招聘者服务层,支持账号添加、启用、停用、删除、列表及爬虫注册
- 新增命令行工具add_recruiter.py,便于管理招聘者账号
- 修改主应用初始化流程,集成招聘者服务并通过数据库加载活跃账号爬虫
- 主程序示例中新增招聘者账号展示与调用爬取任务示范
- 更新项目依赖,增加SQLAlchemy、PyMySQL及Cryptography库支持
- 修改.gitignore,新增.qoder目录例外规则
This commit is contained in:
2026-03-24 14:36:01 +08:00
parent 6a5495005e
commit 04596d298b
12 changed files with 955 additions and 19 deletions

3
.gitignore vendored
View File

@@ -6,6 +6,9 @@ __pycache__/
# C extensions # C extensions
*.so *.so
#qoder
**/.qoder/**
# Distribution / packaging # Distribution / packaging
.Python .Python
build/ build/

106
add_recruiter.py Normal file
View File

@@ -0,0 +1,106 @@
"""添加招聘者账号工具
Usage:
uv run python add_recruiter.py --name "张三" --token "your_wt_token"
uv run python add_recruiter.py --name "李四" --token "your_wt_token" --source boss
uv run python add_recruiter.py --list
"""
import sys
import argparse
from pathlib import Path
# 添加源码路径
src_path = Path(__file__).parent / "src" / "main" / "python"
if str(src_path) not in sys.path:
sys.path.insert(0, str(src_path))
from cn.yinlihupo.ylhp_hr_2_0.domain.candidate import CandidateSource
from cn.yinlihupo.ylhp_hr_2_0.domain.recruiter import Recruiter, RecruiterStatus
from cn.yinlihupo.ylhp_hr_2_0.mapper.recruiter_mapper import RecruiterMapper
from cn.yinlihupo.ylhp_hr_2_0.service.recruiter_service import RecruiterService
from cn.yinlihupo.ylhp_hr_2_0.config.settings import get_settings
def main():
parser = argparse.ArgumentParser(description="招聘者账号管理工具")
parser.add_argument("--name", "-n", help="招聘者名称/标识")
parser.add_argument("--token", "-t", help="WT Token")
parser.add_argument("--source", "-s", default="boss", help="平台来源 (默认: boss)")
parser.add_argument("--list", "-l", action="store_true", help="列出所有账号")
parser.add_argument("--deactivate", "-d", help="停用指定ID的账号")
parser.add_argument("--activate", "-a", help="启用指定ID的账号")
parser.add_argument("--delete", help="删除指定ID的账号")
args = parser.parse_args()
# 初始化服务
settings = get_settings()
print(f"使用数据库: {settings.db_url}")
mapper = RecruiterMapper(db_url=settings.db_url)
service = RecruiterService(mapper=mapper)
# 列出所有账号
if args.list:
recruiters = service.list_recruiters()
print("\n招聘者账号列表:")
print("-" * 80)
print(f"{'ID':<36} {'名称':<15} {'平台':<10} {'状态':<10} {'最后使用':<20}")
print("-" * 80)
for r in recruiters:
last_used = r.last_used_at.strftime("%Y-%m-%d %H:%M") if r.last_used_at else "从未"
print(f"{r.id:<36} {r.name:<15} {r.source.value:<10} {r.status.value:<10} {last_used:<20}")
print("-" * 80)
print(f"{len(recruiters)} 个账号")
return
# 停用账号
if args.deactivate:
if service.deactivate_recruiter(args.deactivate):
print(f"账号 {args.deactivate} 已停用")
else:
print(f"停用失败,账号不存在")
return
# 启用账号
if args.activate:
if service.activate_recruiter(args.activate):
print(f"账号 {args.activate} 已启用")
else:
print(f"启用失败,账号不存在")
return
# 删除账号
if args.delete:
if service.delete_recruiter(args.delete):
print(f"账号 {args.delete} 已删除")
else:
print(f"删除失败,账号不存在")
return
# 添加新账号
if args.name and args.token:
try:
source = CandidateSource(args.source.lower())
except ValueError:
print(f"错误: 不支持的平台 '{args.source}',支持的平台: boss")
return
recruiter = service.add_recruiter(
name=args.name,
source=source,
wt_token=args.token
)
print(f"\n招聘者账号添加成功!")
print(f" ID: {recruiter.id}")
print(f" 名称: {recruiter.name}")
print(f" 平台: {recruiter.source.value}")
print(f" 状态: {recruiter.status.value}")
return
# 如果没有参数,显示帮助
parser.print_help()
if __name__ == "__main__":
main()

22
main.py
View File

@@ -46,6 +46,16 @@ async def demo():
# 初始化应用 # 初始化应用
app = get_app() app = get_app()
# 显示招聘者账号
print("\n已配置的招聘者账号:")
recruiters = app.recruiter_service.list_recruiters()
if recruiters:
for r in recruiters:
status = "" if r.is_active() else ""
print(f" {status} {r.name} ({r.source.value}) - {r.status.value}")
else:
print(" - 暂无账号,请使用 add_recruiter.py 添加")
print("\n已注册爬虫:") print("\n已注册爬虫:")
for source in app.crawler_factory.get_registered_sources(): for source in app.crawler_factory.get_registered_sources():
print(f" - {source.value}") print(f" - {source.value}")
@@ -67,10 +77,18 @@ async def demo():
print("系统初始化完成") print("系统初始化完成")
print("=" * 50) print("=" * 50)
# 示例:爬取并入库(需要配置 CRAWLER_BOSS_WT_TOKEN # 示例:爬取并入库
# 1. 先获取职位列表
# jobs = app.get_jobs(CandidateSource.BOSS)
# if jobs:
# print(f"\n找到 {len(jobs)} 个职位")
# first_job = jobs[0]
# print(f"使用职位: {first_job.title}")
#
# # 2. 爬取该职位下的候选人
# await app.crawl_and_ingest( # await app.crawl_and_ingest(
# source=CandidateSource.BOSS, # source=CandidateSource.BOSS,
# job_id="your_job_id" # job_id=first_job.source_id
# ) # )

View File

@@ -2,7 +2,23 @@
-- 支持: MySQL 8.0+ / SQLite 3 / PostgreSQL -- 支持: MySQL 8.0+ / SQLite 3 / PostgreSQL
-- ============================================ -- ============================================
-- 1. 候选人主表 -- 1. 招聘者账号表 (存储各平台账号信息)
-- ============================================
CREATE TABLE IF NOT EXISTS recruiters (
id VARCHAR(64) PRIMARY KEY,
name VARCHAR(128) NOT NULL, -- 招聘者名称/标识
source VARCHAR(32) NOT NULL, -- 平台: BOSS, LIEPIN, etc.
wt_token VARCHAR(512) NOT NULL, -- WT Token (加密存储)
status VARCHAR(32) DEFAULT 'ACTIVE', -- ACTIVE, INACTIVE, EXPIRED
last_used_at TIMESTAMP, -- 最后使用时间
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_source_token (source, wt_token(255)),
INDEX idx_status (status)
);
-- ============================================
-- 2. 候选人主表
-- ============================================ -- ============================================
CREATE TABLE IF NOT EXISTS candidates ( CREATE TABLE IF NOT EXISTS candidates (
id VARCHAR(64) PRIMARY KEY, id VARCHAR(64) PRIMARY KEY,
@@ -34,7 +50,7 @@ CREATE TABLE IF NOT EXISTS candidates (
); );
-- ============================================ -- ============================================
-- 2. 简历内容表 -- 3. 简历内容表
-- ============================================ -- ============================================
CREATE TABLE IF NOT EXISTS resumes ( CREATE TABLE IF NOT EXISTS resumes (
id VARCHAR(64) PRIMARY KEY, id VARCHAR(64) PRIMARY KEY,
@@ -51,7 +67,7 @@ CREATE TABLE IF NOT EXISTS resumes (
); );
-- ============================================ -- ============================================
-- 3. 职位信息表 -- 4. 职位信息表
-- ============================================ -- ============================================
CREATE TABLE IF NOT EXISTS jobs ( CREATE TABLE IF NOT EXISTS jobs (
id VARCHAR(64) PRIMARY KEY, id VARCHAR(64) PRIMARY KEY,
@@ -72,7 +88,7 @@ CREATE TABLE IF NOT EXISTS jobs (
); );
-- ============================================ -- ============================================
-- 4. 评价方案表 -- 5. 评价方案表
-- ============================================ -- ============================================
CREATE TABLE IF NOT EXISTS evaluation_schemas ( CREATE TABLE IF NOT EXISTS evaluation_schemas (
id VARCHAR(64) PRIMARY KEY, id VARCHAR(64) PRIMARY KEY,
@@ -88,7 +104,7 @@ CREATE TABLE IF NOT EXISTS evaluation_schemas (
); );
-- ============================================ -- ============================================
-- 5. 评价记录表 -- 6. 评价记录表
-- ============================================ -- ============================================
CREATE TABLE IF NOT EXISTS evaluations ( CREATE TABLE IF NOT EXISTS evaluations (
id VARCHAR(64) PRIMARY KEY, id VARCHAR(64) PRIMARY KEY,
@@ -114,7 +130,7 @@ CREATE TABLE IF NOT EXISTS evaluations (
); );
-- ============================================ -- ============================================
-- 6. 通知记录表 -- 7. 通知记录表
-- ============================================ -- ============================================
CREATE TABLE IF NOT EXISTS notifications ( CREATE TABLE IF NOT EXISTS notifications (
id VARCHAR(64) PRIMARY KEY, id VARCHAR(64) PRIMARY KEY,
@@ -134,7 +150,7 @@ CREATE TABLE IF NOT EXISTS notifications (
); );
-- ============================================ -- ============================================
-- 7. 插入默认评价方案 -- 8. 插入默认评价方案
-- ============================================ -- ============================================
INSERT INTO evaluation_schemas (id, name, description, dimensions, weights, is_default) VALUES INSERT INTO evaluation_schemas (id, name, description, dimensions, weights, is_default) VALUES
('general', '通用评价方案', '适用于各类岗位的通用评价方案', ('general', '通用评价方案', '适用于各类岗位的通用评价方案',

View File

@@ -9,6 +9,9 @@ dependencies = [
"pydantic>=2.0", "pydantic>=2.0",
"pydantic-settings>=2.0", "pydantic-settings>=2.0",
"aiohttp>=3.8", "aiohttp>=3.8",
"sqlalchemy>=2.0",
"pymysql>=1.1",
"cryptography>=41.0",
] ]
[project.optional-dependencies] [project.optional-dependencies]

View File

@@ -0,0 +1,209 @@
"""Database configuration using SQLAlchemy"""
from sqlalchemy import create_engine, Column, String, DateTime, Text, DECIMAL, Integer, ForeignKey, JSON
from sqlalchemy.orm import declarative_base, sessionmaker, Session
from sqlalchemy.sql import func
from typing import Optional
Base = declarative_base()
class RecruiterModel(Base):
"""招聘者账号表"""
__tablename__ = 'recruiters'
id = Column(String(64), primary_key=True)
name = Column(String(128), nullable=False)
source = Column(String(32), nullable=False)
wt_token = Column(String(512), nullable=False)
status = Column(String(32), default='active')
last_used_at = Column(DateTime)
created_at = Column(DateTime, server_default=func.now())
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
class CandidateModel(Base):
"""候选人主表"""
__tablename__ = 'candidates'
id = Column(String(64), primary_key=True)
source = Column(String(32), nullable=False)
source_id = Column(String(128), nullable=False)
name = Column(String(64), nullable=False)
phone = Column(String(32))
email = Column(String(128))
wechat = Column(String(64))
gender = Column(Integer, default=0)
age = Column(Integer)
location = Column(String(128))
current_company = Column(String(256))
current_position = Column(String(128))
work_years = Column(DECIMAL(4, 1))
education = Column(String(64))
school = Column(String(256))
salary_min = Column(Integer)
salary_max = Column(Integer)
status = Column(String(32), default='NEW')
created_at = Column(DateTime, server_default=func.now())
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
class ResumeModel(Base):
"""简历内容表"""
__tablename__ = 'resumes'
id = Column(String(64), primary_key=True)
candidate_id = Column(String(64), ForeignKey('candidates.id'), nullable=False)
raw_content = Column(Text)
parsed_content = Column(JSON)
attachment_url = Column(String(512))
attachment_type = Column(String(32))
version = Column(Integer, default=1)
created_at = Column(DateTime, server_default=func.now())
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
class JobModel(Base):
"""职位信息表"""
__tablename__ = 'jobs'
id = Column(String(64), primary_key=True)
source = Column(String(32), nullable=False)
source_id = Column(String(128), nullable=False)
title = Column(String(256), nullable=False)
department = Column(String(128))
location = Column(String(128))
salary_min = Column(Integer)
salary_max = Column(Integer)
requirements = Column(Text)
description = Column(Text)
status = Column(String(32), default='ACTIVE')
created_at = Column(DateTime, server_default=func.now())
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
class EvaluationSchemaModel(Base):
"""评价方案表"""
__tablename__ = 'evaluation_schemas'
id = Column(String(64), primary_key=True)
name = Column(String(128), nullable=False)
description = Column(Text)
dimensions = Column(JSON, nullable=False)
weights = Column(JSON, nullable=False)
prompt_template = Column(Text)
is_default = Column(Integer, default=0)
created_at = Column(DateTime, server_default=func.now())
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
class EvaluationModel(Base):
"""评价记录表"""
__tablename__ = 'evaluations'
id = Column(String(64), primary_key=True)
candidate_id = Column(String(64), ForeignKey('candidates.id'), nullable=False)
schema_id = Column(String(64), ForeignKey('evaluation_schemas.id'), nullable=False)
job_id = Column(String(64), ForeignKey('jobs.id'))
overall_score = Column(DECIMAL(4, 1))
dimension_scores = Column(JSON)
tags = Column(JSON)
summary = Column(Text)
strengths = Column(JSON)
weaknesses = Column(JSON)
recommendation = Column(String(32))
raw_response = Column(Text)
created_at = Column(DateTime, server_default=func.now())
class NotificationModel(Base):
"""通知记录表"""
__tablename__ = 'notifications'
id = Column(String(64), primary_key=True)
candidate_id = Column(String(64), ForeignKey('candidates.id'), nullable=False)
evaluation_id = Column(String(64), ForeignKey('evaluations.id'))
channel = Column(String(32), nullable=False)
content = Column(Text)
status = Column(String(32), default='PENDING')
error_message = Column(Text)
sent_at = Column(DateTime)
created_at = Column(DateTime, server_default=func.now())
class DatabaseManager:
"""数据库管理器"""
def __init__(self, db_url: str):
self.db_url = db_url
self.engine = create_engine(db_url, echo=False, pool_pre_ping=True)
self.SessionLocal = sessionmaker(bind=self.engine)
def create_tables(self):
"""创建所有表"""
Base.metadata.create_all(self.engine)
print("Database tables created")
def get_session(self) -> Session:
"""获取数据库会话"""
return self.SessionLocal()
def init_default_data(self):
"""初始化默认数据"""
session = self.get_session()
try:
# 检查是否已有默认评价方案
from sqlalchemy import select
result = session.execute(select(EvaluationSchemaModel).where(EvaluationSchemaModel.id == 'general'))
if result.scalar() is None:
# 插入通用评价方案
general = EvaluationSchemaModel(
id='general',
name='通用评价方案',
description='适用于各类岗位的通用评价方案',
dimensions=[
{"id": "professional", "name": "专业能力", "description": "岗位相关专业技能水平"},
{"id": "experience", "name": "工作经验", "description": "相关工作经验丰富度"},
{"id": "education", "name": "教育背景", "description": "学历和专业匹配度"},
{"id": "potential", "name": "发展潜力", "description": "未来成长空间"},
{"id": "culture_fit", "name": "文化匹配", "description": "与企业文化的匹配度"}
],
weights={"professional": 0.30, "experience": 0.25, "education": 0.15, "potential": 0.15, "culture_fit": 0.15},
is_default=1
)
session.add(general)
# 插入Java后端评价方案
java = EvaluationSchemaModel(
id='java_backend',
name='Java后端工程师评价方案',
description='针对Java后端开发岗位的综合评价方案',
dimensions=[
{"id": "tech_capability", "name": "技术能力", "description": "Java技术栈掌握程度", "criteria": ["Java基础扎实程度", "Spring生态熟悉度", "数据库设计与优化", "分布式系统经验"]},
{"id": "project_exp", "name": "项目经验", "description": "项目经历的丰富度和质量", "criteria": ["项目复杂度", "承担角色重要性", "技术挑战解决能力"]},
{"id": "learning_ability", "name": "学习能力", "description": "学习新技术和适应新环境的能力", "criteria": ["技术广度", "新技术掌握速度", "自我驱动学习"]},
{"id": "communication", "name": "沟通协作", "description": "团队协作和沟通能力", "criteria": ["跨团队协作经验", "技术文档能力", "问题表达能力"]},
{"id": "stability", "name": "稳定性", "description": "职业稳定性和忠诚度", "criteria": ["平均在职时长", "跳槽频率", "职业发展规划清晰度"]}
],
weights={"tech_capability": 0.35, "project_exp": 0.25, "learning_ability": 0.15, "communication": 0.15, "stability": 0.10}
)
session.add(java)
session.commit()
print("Default evaluation schemas inserted")
finally:
session.close()
# 全局数据库管理器实例
_db_manager: Optional[DatabaseManager] = None
def get_db_manager(db_url: Optional[str] = None) -> DatabaseManager:
"""获取数据库管理器实例(单例)"""
global _db_manager
if _db_manager is None:
if db_url is None:
from .settings import get_settings
db_url = get_settings().db_url
_db_manager = DatabaseManager(db_url)
return _db_manager

View File

@@ -4,6 +4,7 @@ from .candidate import Candidate, CandidateSource, CandidateStatus
from .resume import Resume, ResumeParsed from .resume import Resume, ResumeParsed
from .job import Job, JobStatus from .job import Job, JobStatus
from .evaluation import Evaluation, EvaluationSchema, Dimension, DimensionScore from .evaluation import Evaluation, EvaluationSchema, Dimension, DimensionScore
from .recruiter import Recruiter, RecruiterStatus
from .enums import Gender, Education, Recommendation from .enums import Gender, Education, Recommendation
__all__ = [ __all__ = [
@@ -18,6 +19,8 @@ __all__ = [
"EvaluationSchema", "EvaluationSchema",
"Dimension", "Dimension",
"DimensionScore", "DimensionScore",
"Recruiter",
"RecruiterStatus",
"Gender", "Gender",
"Education", "Education",
"Recommendation", "Recommendation",

View File

@@ -0,0 +1,41 @@
"""Recruiter entity definitions"""
from dataclasses import dataclass
from datetime import datetime
from typing import Optional
from enum import Enum
from .candidate import CandidateSource
class RecruiterStatus(Enum):
"""招聘者账号状态"""
ACTIVE = "active" # 活跃
INACTIVE = "inactive" # 停用
EXPIRED = "expired" # Token过期
@dataclass
class Recruiter:
"""招聘者账号实体"""
id: Optional[str] = None
name: str = "" # 招聘者名称/标识
source: CandidateSource = CandidateSource.BOSS
wt_token: str = "" # WT Token
status: RecruiterStatus = RecruiterStatus.ACTIVE
last_used_at: Optional[datetime] = None
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
def __post_init__(self):
if self.created_at is None:
self.created_at = datetime.now()
if self.updated_at is None:
self.updated_at = datetime.now()
def is_active(self) -> bool:
"""检查账号是否活跃"""
return self.status == RecruiterStatus.ACTIVE
def mark_used(self):
"""标记为已使用"""
self.last_used_at = datetime.now()

View File

@@ -67,6 +67,7 @@ class HRAgentApplication:
self.ingestion_service: Optional[UnifiedIngestionService] = None self.ingestion_service: Optional[UnifiedIngestionService] = None
self.analyzer: Optional[ResumeAnalyzer] = None self.analyzer: Optional[ResumeAnalyzer] = None
self.notification_service: Optional[NotificationService] = None self.notification_service: Optional[NotificationService] = None
self.recruiter_service: Optional[RecruiterService] = None
self._initialized = False self._initialized = False
@@ -75,7 +76,10 @@ class HRAgentApplication:
if self._initialized: if self._initialized:
return return
# 1. 初始化爬虫 # 0. 初始化招聘者服务
self._init_recruiter_service()
# 1. 初始化爬虫(从数据库加载)
self._init_crawlers() self._init_crawlers()
# 2. 初始化入库服务 # 2. 初始化入库服务
@@ -90,13 +94,31 @@ class HRAgentApplication:
self._initialized = True self._initialized = True
print(f"HR Agent {self.settings.app_version} initialized successfully") print(f"HR Agent {self.settings.app_version} initialized successfully")
def _init_recruiter_service(self):
"""初始化招聘者服务"""
from cn.yinlihupo.ylhp_hr_2_0.service.recruiter_service import RecruiterService
from cn.yinlihupo.ylhp_hr_2_0.mapper.recruiter_mapper import RecruiterMapper
mapper = RecruiterMapper(db_url=self.settings.db_url)
self.recruiter_service = RecruiterService(mapper=mapper)
def _init_crawlers(self): def _init_crawlers(self):
"""初始化爬虫""" """初始化爬虫 - 从数据库加载招聘者账号"""
# Boss 爬虫 # 从数据库加载 Boss 爬虫
if self.recruiter_service:
count = self.recruiter_service.register_crawlers_to_factory(
self.crawler_factory,
CandidateSource.BOSS
)
if count > 0:
print(f"Registered {count} Boss crawlers from database")
# 兼容:从环境变量加载(如果数据库中没有)
if not self.crawler_factory.has_crawler(CandidateSource.BOSS):
if self.settings.crawler_boss_wt_token: if self.settings.crawler_boss_wt_token:
boss_crawler = BossCrawler(wt_token=self.settings.crawler_boss_wt_token) boss_crawler = BossCrawler(wt_token=self.settings.crawler_boss_wt_token)
self.crawler_factory.register(CandidateSource.BOSS, boss_crawler) self.crawler_factory.register(CandidateSource.BOSS, boss_crawler)
print("Boss crawler registered") print("Boss crawler registered from environment")
def _init_ingestion_service(self): def _init_ingestion_service(self):
"""初始化入库服务""" """初始化入库服务"""

View File

@@ -0,0 +1,176 @@
"""Recruiter data mapper using SQLAlchemy"""
from typing import List, Optional
from datetime import datetime
import uuid
from sqlalchemy import select, update, delete
from sqlalchemy.orm import Session
from ..domain.recruiter import Recruiter, RecruiterStatus
from ..domain.candidate import CandidateSource
from ..config.database import get_db_manager, RecruiterModel
class RecruiterMapper:
"""招聘者账号数据访问 - SQLAlchemy实现"""
def __init__(self, db_url: Optional[str] = None):
self.db_manager = get_db_manager(db_url)
# 确保表存在
self.db_manager.create_tables()
def _get_session(self) -> Session:
"""获取数据库会话"""
return self.db_manager.get_session()
def _model_to_entity(self, model: RecruiterModel) -> Recruiter:
"""将模型转换为实体"""
# 处理 source 的大小写(数据库可能是 BOSS但枚举是 boss
source_value = model.source.lower() if model.source else "boss"
# 处理 status 的大小写
status_value = model.status.lower() if model.status else "active"
return Recruiter(
id=model.id,
name=model.name,
source=CandidateSource(source_value),
wt_token=model.wt_token,
status=RecruiterStatus(status_value),
last_used_at=model.last_used_at,
created_at=model.created_at,
updated_at=model.updated_at
)
def _entity_to_model(self, entity: Recruiter) -> RecruiterModel:
"""将实体转换为模型"""
return RecruiterModel(
id=entity.id or str(uuid.uuid4()),
name=entity.name,
source=entity.source.value,
wt_token=entity.wt_token,
status=entity.status.value,
last_used_at=entity.last_used_at
)
def save(self, recruiter: Recruiter) -> Recruiter:
"""保存招聘者账号"""
session = self._get_session()
try:
if recruiter.id:
# 更新
stmt = (
update(RecruiterModel)
.where(RecruiterModel.id == recruiter.id)
.values(
name=recruiter.name,
source=recruiter.source.value,
wt_token=recruiter.wt_token,
status=recruiter.status.value,
last_used_at=recruiter.last_used_at
)
)
session.execute(stmt)
else:
# 插入
recruiter.id = str(uuid.uuid4())
model = self._entity_to_model(recruiter)
session.add(model)
session.commit()
return recruiter
finally:
session.close()
def find_by_id(self, recruiter_id: str) -> Optional[Recruiter]:
"""根据ID查询"""
session = self._get_session()
try:
result = session.execute(
select(RecruiterModel).where(RecruiterModel.id == recruiter_id)
)
model = result.scalar_one_or_none()
return self._model_to_entity(model) if model else None
finally:
session.close()
def find_by_source(self, source: CandidateSource) -> List[Recruiter]:
"""根据平台查询"""
session = self._get_session()
try:
result = session.execute(
select(RecruiterModel)
.where(RecruiterModel.source == source.value)
.order_by(RecruiterModel.created_at.desc())
)
models = result.scalars().all()
return [self._model_to_entity(m) for m in models]
finally:
session.close()
def find_active_by_source(self, source: CandidateSource) -> List[Recruiter]:
"""查询指定平台的活跃账号"""
session = self._get_session()
try:
result = session.execute(
select(RecruiterModel)
.where(RecruiterModel.source == source.value)
.where(RecruiterModel.status == 'active')
.order_by(RecruiterModel.last_used_at)
)
models = result.scalars().all()
return [self._model_to_entity(m) for m in models]
finally:
session.close()
def find_all(self) -> List[Recruiter]:
"""查询所有账号"""
session = self._get_session()
try:
result = session.execute(
select(RecruiterModel).order_by(RecruiterModel.created_at.desc())
)
models = result.scalars().all()
return [self._model_to_entity(m) for m in models]
finally:
session.close()
def update_status(self, recruiter_id: str, status: RecruiterStatus) -> bool:
"""更新账号状态"""
session = self._get_session()
try:
stmt = (
update(RecruiterModel)
.where(RecruiterModel.id == recruiter_id)
.values(status=status.value)
)
result = session.execute(stmt)
session.commit()
return result.rowcount > 0
finally:
session.close()
def update_last_used(self, recruiter_id: str) -> bool:
"""更新最后使用时间"""
session = self._get_session()
try:
stmt = (
update(RecruiterModel)
.where(RecruiterModel.id == recruiter_id)
.values(last_used_at=datetime.now())
)
result = session.execute(stmt)
session.commit()
return result.rowcount > 0
finally:
session.close()
def delete(self, recruiter_id: str) -> bool:
"""删除账号"""
session = self._get_session()
try:
stmt = delete(RecruiterModel).where(RecruiterModel.id == recruiter_id)
result = session.execute(stmt)
session.commit()
return result.rowcount > 0
finally:
session.close()

View File

@@ -0,0 +1,120 @@
"""Recruiter service - Manage recruiter accounts"""
from typing import List, Optional
from ..domain.recruiter import Recruiter, RecruiterStatus
from ..domain.candidate import CandidateSource
from ..mapper.recruiter_mapper import RecruiterMapper
from ..service.crawler import BossCrawler, CrawlerFactory
class RecruiterService:
"""
招聘者账号服务
管理招聘者账号的增删改查,以及基于账号的爬虫初始化
"""
def __init__(self, mapper: Optional[RecruiterMapper] = None):
self.mapper = mapper or RecruiterMapper()
def add_recruiter(self, name: str, source: CandidateSource, wt_token: str) -> Recruiter:
"""
添加招聘者账号
Args:
name: 账号名称/标识
source: 平台来源
wt_token: WT Token
Returns:
创建的招聘者账号
"""
recruiter = Recruiter(
name=name,
source=source,
wt_token=wt_token,
status=RecruiterStatus.ACTIVE
)
return self.mapper.save(recruiter)
def get_recruiter(self, recruiter_id: str) -> Optional[Recruiter]:
"""获取指定账号"""
return self.mapper.find_by_id(recruiter_id)
def list_recruiters(self, source: Optional[CandidateSource] = None) -> List[Recruiter]:
"""
列出招聘者账号
Args:
source: 按平台筛选,不传则返回所有
Returns:
账号列表
"""
if source:
return self.mapper.find_by_source(source)
return self.mapper.find_all()
def list_active_recruiters(self, source: CandidateSource) -> List[Recruiter]:
"""获取指定平台的活跃账号"""
return self.mapper.find_active_by_source(source)
def deactivate_recruiter(self, recruiter_id: str) -> bool:
"""停用账号"""
return self.mapper.update_status(recruiter_id, RecruiterStatus.INACTIVE)
def activate_recruiter(self, recruiter_id: str) -> bool:
"""启用账号"""
return self.mapper.update_status(recruiter_id, RecruiterStatus.ACTIVE)
def mark_recruiter_used(self, recruiter_id: str) -> bool:
"""标记账号已使用"""
return self.mapper.update_last_used(recruiter_id)
def delete_recruiter(self, recruiter_id: str) -> bool:
"""删除账号"""
return self.mapper.delete(recruiter_id)
def create_crawler_for_recruiter(self, recruiter: Recruiter) -> Optional[BossCrawler]:
"""
为招聘者创建爬虫实例
Args:
recruiter: 招聘者账号
Returns:
爬虫实例,如果平台不支持返回 None
"""
if not recruiter.is_active():
print(f"Recruiter {recruiter.name} is not active")
return None
if recruiter.source == CandidateSource.BOSS:
return BossCrawler(wt_token=recruiter.wt_token)
# TODO: 支持其他平台
print(f"Source {recruiter.source.value} not supported yet")
return None
def register_crawlers_to_factory(self, factory: CrawlerFactory, source: CandidateSource) -> int:
"""
将指定平台的所有活跃账号注册到爬虫工厂
Args:
factory: 爬虫工厂
source: 平台来源
Returns:
注册的账号数量
"""
recruiters = self.list_active_recruiters(source)
count = 0
for recruiter in recruiters:
crawler = self.create_crawler_for_recruiter(recruiter)
if crawler:
factory.register(source, crawler)
count += 1
print(f"Registered crawler for recruiter: {recruiter.name}")
return count

219
uv.lock generated
View File

@@ -200,6 +200,63 @@ wheels = [
{ url = "http://mirrors.aliyun.com/pypi/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa" }, { url = "http://mirrors.aliyun.com/pypi/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa" },
] ]
[[package]]
name = "cffi"
version = "2.0.0"
source = { registry = "http://mirrors.aliyun.com/pypi/simple" }
dependencies = [
{ name = "pycparser", marker = "implementation_name != 'PyPy'" },
]
sdist = { url = "http://mirrors.aliyun.com/pypi/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529" }
wheels = [
{ url = "http://mirrors.aliyun.com/pypi/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d" },
{ url = "http://mirrors.aliyun.com/pypi/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c" },
{ url = "http://mirrors.aliyun.com/pypi/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe" },
{ url = "http://mirrors.aliyun.com/pypi/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062" },
{ url = "http://mirrors.aliyun.com/pypi/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e" },
{ url = "http://mirrors.aliyun.com/pypi/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037" },
{ url = "http://mirrors.aliyun.com/pypi/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba" },
{ url = "http://mirrors.aliyun.com/pypi/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94" },
{ url = "http://mirrors.aliyun.com/pypi/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187" },
{ url = "http://mirrors.aliyun.com/pypi/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18" },
{ url = "http://mirrors.aliyun.com/pypi/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5" },
{ url = "http://mirrors.aliyun.com/pypi/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6" },
{ url = "http://mirrors.aliyun.com/pypi/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb" },
{ url = "http://mirrors.aliyun.com/pypi/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca" },
{ url = "http://mirrors.aliyun.com/pypi/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b" },
{ url = "http://mirrors.aliyun.com/pypi/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b" },
{ url = "http://mirrors.aliyun.com/pypi/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2" },
{ url = "http://mirrors.aliyun.com/pypi/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3" },
{ url = "http://mirrors.aliyun.com/pypi/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26" },
{ url = "http://mirrors.aliyun.com/pypi/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c" },
{ url = "http://mirrors.aliyun.com/pypi/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b" },
{ url = "http://mirrors.aliyun.com/pypi/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27" },
{ url = "http://mirrors.aliyun.com/pypi/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75" },
{ url = "http://mirrors.aliyun.com/pypi/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91" },
{ url = "http://mirrors.aliyun.com/pypi/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5" },
{ url = "http://mirrors.aliyun.com/pypi/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13" },
{ url = "http://mirrors.aliyun.com/pypi/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b" },
{ url = "http://mirrors.aliyun.com/pypi/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c" },
{ url = "http://mirrors.aliyun.com/pypi/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef" },
{ url = "http://mirrors.aliyun.com/pypi/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775" },
{ url = "http://mirrors.aliyun.com/pypi/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205" },
{ url = "http://mirrors.aliyun.com/pypi/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1" },
{ url = "http://mirrors.aliyun.com/pypi/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f" },
{ url = "http://mirrors.aliyun.com/pypi/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25" },
{ url = "http://mirrors.aliyun.com/pypi/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad" },
{ url = "http://mirrors.aliyun.com/pypi/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9" },
{ url = "http://mirrors.aliyun.com/pypi/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d" },
{ url = "http://mirrors.aliyun.com/pypi/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c" },
{ url = "http://mirrors.aliyun.com/pypi/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8" },
{ url = "http://mirrors.aliyun.com/pypi/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc" },
{ url = "http://mirrors.aliyun.com/pypi/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592" },
{ url = "http://mirrors.aliyun.com/pypi/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512" },
{ url = "http://mirrors.aliyun.com/pypi/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4" },
{ url = "http://mirrors.aliyun.com/pypi/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e" },
{ url = "http://mirrors.aliyun.com/pypi/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6" },
{ url = "http://mirrors.aliyun.com/pypi/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9" },
]
[[package]] [[package]]
name = "click" name = "click"
version = "8.3.1" version = "8.3.1"
@@ -221,6 +278,59 @@ wheels = [
{ url = "http://mirrors.aliyun.com/pypi/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" }, { url = "http://mirrors.aliyun.com/pypi/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" },
] ]
[[package]]
name = "cryptography"
version = "46.0.5"
source = { registry = "http://mirrors.aliyun.com/pypi/simple" }
dependencies = [
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
]
sdist = { url = "http://mirrors.aliyun.com/pypi/packages/60/04/ee2a9e8542e4fa2773b81771ff8349ff19cdd56b7258a0cc442639052edb/cryptography-46.0.5.tar.gz", hash = "sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d" }
wheels = [
{ url = "http://mirrors.aliyun.com/pypi/packages/f7/81/b0bb27f2ba931a65409c6b8a8b358a7f03c0e46eceacddff55f7c84b1f3b/cryptography-46.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad" },
{ url = "http://mirrors.aliyun.com/pypi/packages/ff/9e/6b4397a3e3d15123de3b1806ef342522393d50736c13b20ec4c9ea6693a6/cryptography-46.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b" },
{ url = "http://mirrors.aliyun.com/pypi/packages/63/e7/471ab61099a3920b0c77852ea3f0ea611c9702f651600397ac567848b897/cryptography-46.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b" },
{ url = "http://mirrors.aliyun.com/pypi/packages/37/53/a18500f270342d66bf7e4d9f091114e31e5ee9e7375a5aba2e85a91e0044/cryptography-46.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263" },
{ url = "http://mirrors.aliyun.com/pypi/packages/22/29/c2e812ebc38c57b40e7c583895e73c8c5adb4d1e4a0cc4c5a4fdab2b1acc/cryptography-46.0.5-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d" },
{ url = "http://mirrors.aliyun.com/pypi/packages/6b/e7/237155ae19a9023de7e30ec64e5d99a9431a567407ac21170a046d22a5a3/cryptography-46.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed" },
{ url = "http://mirrors.aliyun.com/pypi/packages/2d/87/fc628a7ad85b81206738abbd213b07702bcbdada1dd43f72236ef3cffbb5/cryptography-46.0.5-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2" },
{ url = "http://mirrors.aliyun.com/pypi/packages/84/29/65b55622bde135aedf4565dc509d99b560ee4095e56989e815f8fd2aa910/cryptography-46.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2" },
{ url = "http://mirrors.aliyun.com/pypi/packages/bc/36/45e76c68d7311432741faf1fbf7fac8a196a0a735ca21f504c75d37e2558/cryptography-46.0.5-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0" },
{ url = "http://mirrors.aliyun.com/pypi/packages/6d/1a/c1ba8fead184d6e3d5afcf03d569acac5ad063f3ac9fb7258af158f7e378/cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731" },
{ url = "http://mirrors.aliyun.com/pypi/packages/f9/e5/3fb22e37f66827ced3b902cf895e6a6bc1d095b5b26be26bd13c441fdf19/cryptography-46.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82" },
{ url = "http://mirrors.aliyun.com/pypi/packages/1a/df/9d58bb32b1121a8a2f27383fabae4d63080c7ca60b9b5c88be742be04ee7/cryptography-46.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1" },
{ url = "http://mirrors.aliyun.com/pypi/packages/ea/ed/325d2a490c5e94038cdb0117da9397ece1f11201f425c4e9c57fe5b9f08b/cryptography-46.0.5-cp311-abi3-win32.whl", hash = "sha256:60ee7e19e95104d4c03871d7d7dfb3d22ef8a9b9c6778c94e1c8fcc8365afd48" },
{ url = "http://mirrors.aliyun.com/pypi/packages/e9/5a/ac0f49e48063ab4255d9e3b79f5def51697fce1a95ea1370f03dc9db76f6/cryptography-46.0.5-cp311-abi3-win_amd64.whl", hash = "sha256:38946c54b16c885c72c4f59846be9743d699eee2b69b6988e0a00a01f46a61a4" },
{ url = "http://mirrors.aliyun.com/pypi/packages/00/13/3d278bfa7a15a96b9dc22db5a12ad1e48a9eb3d40e1827ef66a5df75d0d0/cryptography-46.0.5-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:94a76daa32eb78d61339aff7952ea819b1734b46f73646a07decb40e5b3448e2" },
{ url = "http://mirrors.aliyun.com/pypi/packages/67/c8/581a6702e14f0898a0848105cbefd20c058099e2c2d22ef4e476dfec75d7/cryptography-46.0.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5be7bf2fb40769e05739dd0046e7b26f9d4670badc7b032d6ce4db64dddc0678" },
{ url = "http://mirrors.aliyun.com/pypi/packages/dd/4a/ba1a65ce8fc65435e5a849558379896c957870dd64fecea97b1ad5f46a37/cryptography-46.0.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe346b143ff9685e40192a4960938545c699054ba11d4f9029f94751e3f71d87" },
{ url = "http://mirrors.aliyun.com/pypi/packages/f8/67/8ffdbf7b65ed1ac224d1c2df3943553766914a8ca718747ee3871da6107e/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c69fd885df7d089548a42d5ec05be26050ebcd2283d89b3d30676eb32ff87dee" },
{ url = "http://mirrors.aliyun.com/pypi/packages/f8/e5/f52377ee93bc2f2bba55a41a886fd208c15276ffbd2569f2ddc89d50e2c5/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:8293f3dea7fc929ef7240796ba231413afa7b68ce38fd21da2995549f5961981" },
{ url = "http://mirrors.aliyun.com/pypi/packages/3b/02/cfe39181b02419bbbbcf3abdd16c1c5c8541f03ca8bda240debc467d5a12/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:1abfdb89b41c3be0365328a410baa9df3ff8a9110fb75e7b52e66803ddabc9a9" },
{ url = "http://mirrors.aliyun.com/pypi/packages/c0/96/2fcaeb4873e536cf71421a388a6c11b5bc846e986b2b069c79363dc1648e/cryptography-46.0.5-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d66e421495fdb797610a08f43b05269e0a5ea7f5e652a89bfd5a7d3c1dee3648" },
{ url = "http://mirrors.aliyun.com/pypi/packages/d8/d2/b27631f401ddd644e94c5cf33c9a4069f72011821cf3dc7309546b0642a0/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4e817a8920bfbcff8940ecfd60f23d01836408242b30f1a708d93198393a80b4" },
{ url = "http://mirrors.aliyun.com/pypi/packages/f4/a7/60d32b0370dae0b4ebe55ffa10e8599a2a59935b5ece1b9f06edb73abdeb/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:68f68d13f2e1cb95163fa3b4db4bf9a159a418f5f6e7242564fc75fcae667fd0" },
{ url = "http://mirrors.aliyun.com/pypi/packages/d2/b9/cf73ddf8ef1164330eb0b199a589103c363afa0cf794218c24d524a58eab/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a3d1fae9863299076f05cb8a778c467578262fae09f9dc0ee9b12eb4268ce663" },
{ url = "http://mirrors.aliyun.com/pypi/packages/5f/eb/eee00b28c84c726fe8fa0158c65afe312d9c3b78d9d01daf700f1f6e37ff/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4143987a42a2397f2fc3b4d7e3a7d313fbe684f67ff443999e803dd75a76826" },
{ url = "http://mirrors.aliyun.com/pypi/packages/65/f4/6bc1a9ed5aef7145045114b75b77c2a8261b4d38717bd8dea111a63c3442/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7d731d4b107030987fd61a7f8ab512b25b53cef8f233a97379ede116f30eb67d" },
{ url = "http://mirrors.aliyun.com/pypi/packages/86/ef/5d00ef966ddd71ac2e6951d278884a84a40ffbd88948ef0e294b214ae9e4/cryptography-46.0.5-cp314-cp314t-win32.whl", hash = "sha256:c3bcce8521d785d510b2aad26ae2c966092b7daa8f45dd8f44734a104dc0bc1a" },
{ url = "http://mirrors.aliyun.com/pypi/packages/b7/57/f3f4160123da6d098db78350fdfd9705057aad21de7388eacb2401dceab9/cryptography-46.0.5-cp314-cp314t-win_amd64.whl", hash = "sha256:4d8ae8659ab18c65ced284993c2265910f6c9e650189d4e3f68445ef82a810e4" },
{ url = "http://mirrors.aliyun.com/pypi/packages/e2/fa/a66aa722105ad6a458bebd64086ca2b72cdd361fed31763d20390f6f1389/cryptography-46.0.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31" },
{ url = "http://mirrors.aliyun.com/pypi/packages/0f/04/c85bdeab78c8bc77b701bf0d9bdcf514c044e18a46dcff330df5448631b0/cryptography-46.0.5-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18" },
{ url = "http://mirrors.aliyun.com/pypi/packages/5c/32/9b87132a2f91ee7f5223b091dc963055503e9b442c98fc0b8a5ca765fab0/cryptography-46.0.5-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235" },
{ url = "http://mirrors.aliyun.com/pypi/packages/a1/a6/a7cb7010bec4b7c5692ca6f024150371b295ee1c108bdc1c400e4c44562b/cryptography-46.0.5-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a" },
{ url = "http://mirrors.aliyun.com/pypi/packages/8e/7c/c4f45e0eeff9b91e3f12dbd0e165fcf2a38847288fcfd889deea99fb7b6d/cryptography-46.0.5-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76" },
{ url = "http://mirrors.aliyun.com/pypi/packages/37/19/e1b8f964a834eddb44fa1b9a9976f4e414cbb7aa62809b6760c8803d22d1/cryptography-46.0.5-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614" },
{ url = "http://mirrors.aliyun.com/pypi/packages/db/ed/db15d3956f65264ca204625597c410d420e26530c4e2943e05a0d2f24d51/cryptography-46.0.5-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229" },
{ url = "http://mirrors.aliyun.com/pypi/packages/41/e2/df40a31d82df0a70a0daf69791f91dbb70e47644c58581d654879b382d11/cryptography-46.0.5-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1" },
{ url = "http://mirrors.aliyun.com/pypi/packages/33/45/726809d1176959f4a896b86907b98ff4391a8aa29c0aaaf9450a8a10630e/cryptography-46.0.5-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d" },
{ url = "http://mirrors.aliyun.com/pypi/packages/99/0f/a3076874e9c88ecb2ecc31382f6e7c21b428ede6f55aafa1aa272613e3cd/cryptography-46.0.5-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c" },
{ url = "http://mirrors.aliyun.com/pypi/packages/02/ef/ffeb542d3683d24194a38f66ca17c0a4b8bf10631feef44a7ef64e631b1a/cryptography-46.0.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4" },
{ url = "http://mirrors.aliyun.com/pypi/packages/96/93/682d2b43c1d5f1406ed048f377c0fc9fc8f7b0447a478d5c65ab3d3a66eb/cryptography-46.0.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9" },
{ url = "http://mirrors.aliyun.com/pypi/packages/45/2d/9c5f2926cb5300a8eefc3f4f0b3f3df39db7f7ce40c8365444c49363cbda/cryptography-46.0.5-cp38-abi3-win32.whl", hash = "sha256:02f547fce831f5096c9a567fd41bc12ca8f11df260959ecc7c3202555cc47a72" },
{ url = "http://mirrors.aliyun.com/pypi/packages/48/ef/0c2f4a8e31018a986949d34a01115dd057bf536905dca38897bacd21fac3/cryptography-46.0.5-cp38-abi3-win_amd64.whl", hash = "sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595" },
]
[[package]] [[package]]
name = "distro" name = "distro"
version = "1.9.0" version = "1.9.0"
@@ -328,6 +438,45 @@ wheels = [
{ url = "http://mirrors.aliyun.com/pypi/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d" }, { url = "http://mirrors.aliyun.com/pypi/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d" },
] ]
[[package]]
name = "greenlet"
version = "3.3.2"
source = { registry = "http://mirrors.aliyun.com/pypi/simple" }
sdist = { url = "http://mirrors.aliyun.com/pypi/packages/a3/51/1664f6b78fc6ebbd98019a1fd730e83fa78f2db7058f72b1463d3612b8db/greenlet-3.3.2.tar.gz", hash = "sha256:2eaf067fc6d886931c7962e8c6bede15d2f01965560f3359b27c80bde2d151f2" }
wheels = [
{ url = "http://mirrors.aliyun.com/pypi/packages/ea/ab/1608e5a7578e62113506740b88066bf09888322a311cff602105e619bd87/greenlet-3.3.2-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:ac8d61d4343b799d1e526db579833d72f23759c71e07181c2d2944e429eb09cd" },
{ url = "http://mirrors.aliyun.com/pypi/packages/a5/23/0eae412a4ade4e6623ff7626e38998cb9b11e9ff1ebacaa021e4e108ec15/greenlet-3.3.2-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ceec72030dae6ac0c8ed7591b96b70410a8be370b6a477b1dbc072856ad02bd" },
{ url = "http://mirrors.aliyun.com/pypi/packages/f8/16/5b1678a9c07098ecb9ab2dd159fafaf12e963293e61ee8d10ecb55273e5e/greenlet-3.3.2-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a2a5be83a45ce6188c045bcc44b0ee037d6a518978de9a5d97438548b953a1ac" },
{ url = "http://mirrors.aliyun.com/pypi/packages/50/1f/5155f55bd71cabd03765a4aac9ac446be129895271f73872c36ebd4b04b6/greenlet-3.3.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43e99d1749147ac21dde49b99c9abffcbc1e2d55c67501465ef0930d6e78e070" },
{ url = "http://mirrors.aliyun.com/pypi/packages/fc/dd/845f249c3fcd69e32df80cdab059b4be8b766ef5830a3d0aa9d6cad55beb/greenlet-3.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4c956a19350e2c37f2c48b336a3afb4bff120b36076d9d7fb68cb44e05d95b79" },
{ url = "http://mirrors.aliyun.com/pypi/packages/2a/50/2649fe21fcc2b56659a452868e695634722a6655ba245d9f77f5656010bf/greenlet-3.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6c6f8ba97d17a1e7d664151284cb3315fc5f8353e75221ed4324f84eb162b395" },
{ url = "http://mirrors.aliyun.com/pypi/packages/9b/40/cc802e067d02af8b60b6771cea7d57e21ef5e6659912814babb42b864713/greenlet-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:34308836d8370bddadb41f5a7ce96879b72e2fdfb4e87729330c6ab52376409f" },
{ url = "http://mirrors.aliyun.com/pypi/packages/58/2e/fe7f36ff1982d6b10a60d5e0740c759259a7d6d2e1dc41da6d96de32fff6/greenlet-3.3.2-cp312-cp312-win_arm64.whl", hash = "sha256:d3a62fa76a32b462a97198e4c9e99afb9ab375115e74e9a83ce180e7a496f643" },
{ url = "http://mirrors.aliyun.com/pypi/packages/ac/48/f8b875fa7dea7dd9b33245e37f065af59df6a25af2f9561efa8d822fde51/greenlet-3.3.2-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:aa6ac98bdfd716a749b84d4034486863fd81c3abde9aa3cf8eff9127981a4ae4" },
{ url = "http://mirrors.aliyun.com/pypi/packages/49/8d/9771d03e7a8b1ee456511961e1b97a6d77ae1dea4a34a5b98eee706689d3/greenlet-3.3.2-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ab0c7e7901a00bc0a7284907273dc165b32e0d109a6713babd04471327ff7986" },
{ url = "http://mirrors.aliyun.com/pypi/packages/59/0e/4223c2bbb63cd5c97f28ffb2a8aee71bdfb30b323c35d409450f51b91e3e/greenlet-3.3.2-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d248d8c23c67d2291ffd47af766e2a3aa9fa1c6703155c099feb11f526c63a92" },
{ url = "http://mirrors.aliyun.com/pypi/packages/7a/34/259b28ea7a2a0c904b11cd36c79b8cef8019b26ee5dbe24e73b469dea347/greenlet-3.3.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6997d360a4e6a4e936c0f9625b1c20416b8a0ea18a8e19cabbefc712e7397ab" },
{ url = "http://mirrors.aliyun.com/pypi/packages/0a/03/996c2d1689d486a6e199cb0f1cf9e4aa940c500e01bdf201299d7d61fa69/greenlet-3.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:64970c33a50551c7c50491671265d8954046cb6e8e2999aacdd60e439b70418a" },
{ url = "http://mirrors.aliyun.com/pypi/packages/d9/c4/2570fc07f34a39f2caf0bf9f24b0a1a0a47bc2e8e465b2c2424821389dfc/greenlet-3.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1a9172f5bf6bd88e6ba5a84e0a68afeac9dc7b6b412b245dd64f52d83c81e55b" },
{ url = "http://mirrors.aliyun.com/pypi/packages/91/39/5ef5aa23bc545aa0d31e1b9b55822b32c8da93ba657295840b6b34124009/greenlet-3.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:a7945dd0eab63ded0a48e4dcade82939783c172290a7903ebde9e184333ca124" },
{ url = "http://mirrors.aliyun.com/pypi/packages/62/6b/a89f8456dcb06becff288f563618e9f20deed8dd29beea14f9a168aef64b/greenlet-3.3.2-cp313-cp313-win_arm64.whl", hash = "sha256:394ead29063ee3515b4e775216cb756b2e3b4a7e55ae8fd884f17fa579e6b327" },
{ url = "http://mirrors.aliyun.com/pypi/packages/3f/ae/8bffcbd373b57a5992cd077cbe8858fff39110480a9d50697091faea6f39/greenlet-3.3.2-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:8d1658d7291f9859beed69a776c10822a0a799bc4bfe1bd4272bb60e62507dab" },
{ url = "http://mirrors.aliyun.com/pypi/packages/d1/c0/45f93f348fa49abf32ac8439938726c480bd96b2a3c6f4d949ec0124b69f/greenlet-3.3.2-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:18cb1b7337bca281915b3c5d5ae19f4e76d35e1df80f4ad3c1a7be91fadf1082" },
{ url = "http://mirrors.aliyun.com/pypi/packages/b3/de/dd7589b3f2b8372069ab3e4763ea5329940fc7ad9dcd3e272a37516d7c9b/greenlet-3.3.2-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2e47408e8ce1c6f1ceea0dffcdf6ebb85cc09e55c7af407c99f1112016e45e9" },
{ url = "http://mirrors.aliyun.com/pypi/packages/d2/d8/09bfa816572a4d83bccd6750df1926f79158b1c36c5f73786e26dbe4ee38/greenlet-3.3.2-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63d10328839d1973e5ba35e98cccbca71b232b14051fd957b6f8b6e8e80d0506" },
{ url = "http://mirrors.aliyun.com/pypi/packages/48/cf/56832f0c8255d27f6c35d41b5ec91168d74ec721d85f01a12131eec6b93c/greenlet-3.3.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8e4ab3cfb02993c8cc248ea73d7dae6cec0253e9afa311c9b37e603ca9fad2ce" },
{ url = "http://mirrors.aliyun.com/pypi/packages/0a/23/b90b60a4aabb4cec0796e55f25ffbfb579a907c3898cd2905c8918acaa16/greenlet-3.3.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94ad81f0fd3c0c0681a018a976e5c2bd2ca2d9d94895f23e7bb1af4e8af4e2d5" },
{ url = "http://mirrors.aliyun.com/pypi/packages/f3/ca/2101ca3d9223a1dc125140dbc063644dca76df6ff356531eb27bc267b446/greenlet-3.3.2-cp314-cp314-win_amd64.whl", hash = "sha256:8c4dd0f3997cf2512f7601563cc90dfb8957c0cff1e3a1b23991d4ea1776c492" },
{ url = "http://mirrors.aliyun.com/pypi/packages/f6/4a/ecf894e962a59dea60f04877eea0fd5724618da89f1867b28ee8b91e811f/greenlet-3.3.2-cp314-cp314-win_arm64.whl", hash = "sha256:cd6f9e2bbd46321ba3bbb4c8a15794d32960e3b0ae2cc4d49a1a53d314805d71" },
{ url = "http://mirrors.aliyun.com/pypi/packages/98/6d/8f2ef704e614bcf58ed43cfb8d87afa1c285e98194ab2cfad351bf04f81e/greenlet-3.3.2-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:e26e72bec7ab387ac80caa7496e0f908ff954f31065b0ffc1f8ecb1338b11b54" },
{ url = "http://mirrors.aliyun.com/pypi/packages/5e/0d/93894161d307c6ea237a43988f27eba0947b360b99ac5239ad3fe09f0b47/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b466dff7a4ffda6ca975979bab80bdadde979e29fc947ac3be4451428d8b0e4" },
{ url = "http://mirrors.aliyun.com/pypi/packages/f5/2c/d2d506ebd8abcb57386ec4f7ba20f4030cbe56eae541bc6fd6ef399c0b41/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b8bddc5b73c9720bea487b3bffdb1840fe4e3656fba3bd40aa1489e9f37877ff" },
{ url = "http://mirrors.aliyun.com/pypi/packages/8e/30/3a09155fbf728673a1dea713572d2d31159f824a37c22da82127056c44e4/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b26b0f4428b871a751968285a1ac9648944cea09807177ac639b030bddebcea4" },
{ url = "http://mirrors.aliyun.com/pypi/packages/f3/fd/d05a4b7acd0154ed758797f0a43b4c0962a843bedfe980115e842c5b2d08/greenlet-3.3.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1fb39a11ee2e4d94be9a76671482be9398560955c9e568550de0224e41104727" },
{ url = "http://mirrors.aliyun.com/pypi/packages/6f/e1/50ee92a5db521de8f35075b5eff060dd43d39ebd46c2181a2042f7070385/greenlet-3.3.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:20154044d9085151bc309e7689d6f7ba10027f8f5a8c0676ad398b951913d89e" },
{ url = "http://mirrors.aliyun.com/pypi/packages/29/4b/45d90626aef8e65336bed690106d1382f7a43665e2249017e9527df8823b/greenlet-3.3.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c04c5e06ec3e022cbfe2cd4a846e1d4e50087444f875ff6d2c2ad8445495cf1a" },
]
[[package]] [[package]]
name = "h11" name = "h11"
version = "0.16.0" version = "0.16.0"
@@ -698,6 +847,15 @@ wheels = [
{ url = "http://mirrors.aliyun.com/pypi/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237" }, { url = "http://mirrors.aliyun.com/pypi/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237" },
] ]
[[package]]
name = "pycparser"
version = "3.0"
source = { registry = "http://mirrors.aliyun.com/pypi/simple" }
sdist = { url = "http://mirrors.aliyun.com/pypi/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29" }
wheels = [
{ url = "http://mirrors.aliyun.com/pypi/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992" },
]
[[package]] [[package]]
name = "pydantic" name = "pydantic"
version = "2.12.5" version = "2.12.5"
@@ -807,6 +965,15 @@ wheels = [
{ url = "http://mirrors.aliyun.com/pypi/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b" }, { url = "http://mirrors.aliyun.com/pypi/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b" },
] ]
[[package]]
name = "pymysql"
version = "1.1.2"
source = { registry = "http://mirrors.aliyun.com/pypi/simple" }
sdist = { url = "http://mirrors.aliyun.com/pypi/packages/f5/ae/1fe3fcd9f959efa0ebe200b8de88b5a5ce3e767e38c7ac32fb179f16a388/pymysql-1.1.2.tar.gz", hash = "sha256:4961d3e165614ae65014e361811a724e2044ad3ea3739de9903ae7c21f539f03" }
wheels = [
{ url = "http://mirrors.aliyun.com/pypi/packages/7c/4c/ad33b92b9864cbde84f259d5df035a6447f91891f5be77788e2a3892bce3/pymysql-1.1.2-py3-none-any.whl", hash = "sha256:e6b1d89711dd51f8f74b1631fe08f039e7d76cf67a42a323d3178f0f25762ed9" },
]
[[package]] [[package]]
name = "pytest" name = "pytest"
version = "9.0.2" version = "9.0.2"
@@ -908,6 +1075,52 @@ wheels = [
{ url = "http://mirrors.aliyun.com/pypi/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2" }, { url = "http://mirrors.aliyun.com/pypi/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2" },
] ]
[[package]]
name = "sqlalchemy"
version = "2.0.48"
source = { registry = "http://mirrors.aliyun.com/pypi/simple" }
dependencies = [
{ name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" },
{ name = "typing-extensions" },
]
sdist = { url = "http://mirrors.aliyun.com/pypi/packages/1f/73/b4a9737255583b5fa858e0bb8e116eb94b88c910164ed2ed719147bde3de/sqlalchemy-2.0.48.tar.gz", hash = "sha256:5ca74f37f3369b45e1f6b7b06afb182af1fd5dde009e4ffd831830d98cbe5fe7" }
wheels = [
{ url = "http://mirrors.aliyun.com/pypi/packages/ef/91/a42ae716f8925e9659df2da21ba941f158686856107a61cc97a95e7647a3/sqlalchemy-2.0.48-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:348174f228b99f33ca1f773e85510e08927620caa59ffe7803b37170df30332b" },
{ url = "http://mirrors.aliyun.com/pypi/packages/b9/52/f75f516a1f3888f027c1cfb5d22d4376f4b46236f2e8669dcb0cddc60275/sqlalchemy-2.0.48-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53667b5f668991e279d21f94ccfa6e45b4e3f4500e7591ae59a8012d0f010dcb" },
{ url = "http://mirrors.aliyun.com/pypi/packages/37/9a/0c28b6371e0cdcb14f8f1930778cb3123acfcbd2c95bb9cf6b4a2ba0cce3/sqlalchemy-2.0.48-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34634e196f620c7a61d18d5cf7dc841ca6daa7961aed75d532b7e58b309ac894" },
{ url = "http://mirrors.aliyun.com/pypi/packages/1c/46/0aee8f3ff20b1dcbceb46ca2d87fcc3d48b407925a383ff668218509d132/sqlalchemy-2.0.48-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:546572a1793cc35857a2ffa1fe0e58571af1779bcc1ffa7c9fb0839885ed69a9" },
{ url = "http://mirrors.aliyun.com/pypi/packages/ce/8c/a957bc91293b49181350bfd55e6dfc6e30b7f7d83dc6792d72043274a390/sqlalchemy-2.0.48-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:07edba08061bc277bfdc772dd2a1a43978f5a45994dd3ede26391b405c15221e" },
{ url = "http://mirrors.aliyun.com/pypi/packages/4b/44/1d257d9f9556661e7bdc83667cc414ba210acfc110c82938cb3611eea58f/sqlalchemy-2.0.48-cp312-cp312-win32.whl", hash = "sha256:908a3fa6908716f803b86896a09a2c4dde5f5ce2bb07aacc71ffebb57986ce99" },
{ url = "http://mirrors.aliyun.com/pypi/packages/f2/af/c3c7e1f3a2b383155a16454df62ae8c62a30dd238e42e68c24cebebbfae6/sqlalchemy-2.0.48-cp312-cp312-win_amd64.whl", hash = "sha256:68549c403f79a8e25984376480959975212a670405e3913830614432b5daa07a" },
{ url = "http://mirrors.aliyun.com/pypi/packages/d1/c6/569dc8bf3cd375abc5907e82235923e986799f301cd79a903f784b996fca/sqlalchemy-2.0.48-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e3070c03701037aa418b55d36532ecb8f8446ed0135acb71c678dbdf12f5b6e4" },
{ url = "http://mirrors.aliyun.com/pypi/packages/6d/ff/f4e04a4bd5a24304f38cb0d4aa2ad4c0fb34999f8b884c656535e1b2b74c/sqlalchemy-2.0.48-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2645b7d8a738763b664a12a1542c89c940daa55196e8d73e55b169cc5c99f65f" },
{ url = "http://mirrors.aliyun.com/pypi/packages/fe/88/cb59509e4668d8001818d7355d9995be90c321313078c912420603a7cb95/sqlalchemy-2.0.48-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b19151e76620a412c2ac1c6f977ab1b9fa7ad43140178345136456d5265b32ed" },
{ url = "http://mirrors.aliyun.com/pypi/packages/87/dc/1609a4442aefd750ea2f32629559394ec92e89ac1d621a7f462b70f736ff/sqlalchemy-2.0.48-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5b193a7e29fd9fa56e502920dca47dffe60f97c863494946bd698c6058a55658" },
{ url = "http://mirrors.aliyun.com/pypi/packages/37/c3/6ae2ab5ea2fa989fbac4e674de01224b7a9d744becaf59bb967d62e99bed/sqlalchemy-2.0.48-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:36ac4ddc3d33e852da9cb00ffb08cea62ca05c39711dc67062ca2bb1fae35fd8" },
{ url = "http://mirrors.aliyun.com/pypi/packages/6f/82/ea4665d1bb98c50c19666e672f21b81356bd6077c4574e3d2bbb84541f53/sqlalchemy-2.0.48-cp313-cp313-win32.whl", hash = "sha256:389b984139278f97757ea9b08993e7b9d1142912e046ab7d82b3fbaeb0209131" },
{ url = "http://mirrors.aliyun.com/pypi/packages/b7/2b/b9040bec58c58225f073f5b0c1870defe1940835549dafec680cbd58c3c3/sqlalchemy-2.0.48-cp313-cp313-win_amd64.whl", hash = "sha256:d612c976cbc2d17edfcc4c006874b764e85e990c29ce9bd411f926bbfb02b9a2" },
{ url = "http://mirrors.aliyun.com/pypi/packages/f4/f4/7b17bd50244b78a49d22cc63c969d71dc4de54567dc152a9b46f6fae40ce/sqlalchemy-2.0.48-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69f5bc24904d3bc3640961cddd2523e361257ef68585d6e364166dfbe8c78fae" },
{ url = "http://mirrors.aliyun.com/pypi/packages/20/0d/213668e9aca61d370f7d2a6449ea4ec699747fac67d4bda1bb3d129025be/sqlalchemy-2.0.48-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd08b90d211c086181caed76931ecfa2bdfc83eea3cfccdb0f82abc6c4b876cb" },
{ url = "http://mirrors.aliyun.com/pypi/packages/85/d7/a84edf412979e7d59c69b89a5871f90a49228360594680e667cb2c46a828/sqlalchemy-2.0.48-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:1ccd42229aaac2df431562117ac7e667d702e8e44afdb6cf0e50fa3f18160f0b" },
{ url = "http://mirrors.aliyun.com/pypi/packages/86/55/42404ce5770f6be26a2b0607e7866c31b9a4176c819e9a7a5e0a055770be/sqlalchemy-2.0.48-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f0dcbc588cd5b725162c076eb9119342f6579c7f7f55057bb7e3c6ff27e13121" },
{ url = "http://mirrors.aliyun.com/pypi/packages/ae/ae/29b87775fadc43e627cf582fe3bda4d02e300f6b8f2747c764950d13784c/sqlalchemy-2.0.48-cp313-cp313t-win32.whl", hash = "sha256:9764014ef5e58aab76220c5664abb5d47d5bc858d9debf821e55cfdd0f128485" },
{ url = "http://mirrors.aliyun.com/pypi/packages/91/44/f39d063c90f2443e5b46ec4819abd3d8de653893aae92df42a5c4f5843de/sqlalchemy-2.0.48-cp313-cp313t-win_amd64.whl", hash = "sha256:e2f35b4cccd9ed286ad62e0a3c3ac21e06c02abc60e20aa51a3e305a30f5fa79" },
{ url = "http://mirrors.aliyun.com/pypi/packages/f7/b3/f437eaa1cf028bb3c927172c7272366393e73ccd104dcf5b6963f4ab5318/sqlalchemy-2.0.48-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e2d0d88686e3d35a76f3e15a34e8c12d73fc94c1dea1cd55782e695cc14086dd" },
{ url = "http://mirrors.aliyun.com/pypi/packages/6c/1c/b3abdf0f402aa3f60f0df6ea53d92a162b458fca2321d8f1f00278506402/sqlalchemy-2.0.48-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49b7bddc1eebf011ea5ab722fdbe67a401caa34a350d278cc7733c0e88fecb1f" },
{ url = "http://mirrors.aliyun.com/pypi/packages/f2/5e/327428a034407651a048f5e624361adf3f9fbac9d0fa98e981e9c6ff2f5e/sqlalchemy-2.0.48-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:426c5ca86415d9b8945c7073597e10de9644802e2ff502b8e1f11a7a2642856b" },
{ url = "http://mirrors.aliyun.com/pypi/packages/2a/ca/ece73c81a918add0965b76b868b7b5359e068380b90ef1656ee995940c02/sqlalchemy-2.0.48-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:288937433bd44e3990e7da2402fabc44a3c6c25d3704da066b85b89a85474ae0" },
{ url = "http://mirrors.aliyun.com/pypi/packages/88/11/fbaf1ae91fa4ee43f4fe79661cead6358644824419c26adb004941bdce7c/sqlalchemy-2.0.48-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8183dc57ae7d9edc1346e007e840a9f3d6aa7b7f165203a99e16f447150140d2" },
{ url = "http://mirrors.aliyun.com/pypi/packages/fa/a8/5fb0deb13930b4f2f698c5541ae076c18981173e27dd00376dbaea7a9c82/sqlalchemy-2.0.48-cp314-cp314-win32.whl", hash = "sha256:1182437cb2d97988cfea04cf6cdc0b0bb9c74f4d56ec3d08b81e23d621a28cc6" },
{ url = "http://mirrors.aliyun.com/pypi/packages/95/7e/e83615cb63f80047f18e61e31e8e32257d39458426c23006deeaf48f463b/sqlalchemy-2.0.48-cp314-cp314-win_amd64.whl", hash = "sha256:144921da96c08feb9e2b052c5c5c1d0d151a292c6135623c6b2c041f2a45f9e0" },
{ url = "http://mirrors.aliyun.com/pypi/packages/83/e3/69d8711b3f2c5135e9cde5f063bc1605860f0b2c53086d40c04017eb1f77/sqlalchemy-2.0.48-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5aee45fd2c6c0f2b9cdddf48c48535e7471e42d6fb81adfde801da0bd5b93241" },
{ url = "http://mirrors.aliyun.com/pypi/packages/f8/4f/a7cce98facca73c149ea4578981594aaa5fd841e956834931de503359336/sqlalchemy-2.0.48-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7cddca31edf8b0653090cbb54562ca027c421c58ddde2c0685f49ff56a1690e0" },
{ url = "http://mirrors.aliyun.com/pypi/packages/cd/7d/5936c7a03a0b0cb0fa0cc425998821c6029756b0855a8f7ee70fba1de955/sqlalchemy-2.0.48-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7a936f1bb23d370b7c8cc079d5fce4c7d18da87a33c6744e51a93b0f9e97e9b3" },
{ url = "http://mirrors.aliyun.com/pypi/packages/f4/33/cea7dfc31b52904efe3dcdc169eb4514078887dff1f5ae28a7f4c5d54b3c/sqlalchemy-2.0.48-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e004aa9248e8cb0a5f9b96d003ca7c1c0a5da8decd1066e7b53f59eb8ce7c62b" },
{ url = "http://mirrors.aliyun.com/pypi/packages/c8/95/32107c4d13be077a9cae61e9ae49966a35dc4bf442a8852dd871db31f62e/sqlalchemy-2.0.48-cp314-cp314t-win32.whl", hash = "sha256:b8438ec5594980d405251451c5b7ea9aa58dda38eb7ac35fb7e4c696712ee24f" },
{ url = "http://mirrors.aliyun.com/pypi/packages/d2/d7/1e073da7a4bc645eb83c76067284a0374e643bc4be57f14cc6414656f92c/sqlalchemy-2.0.48-cp314-cp314t-win_amd64.whl", hash = "sha256:d854b3970067297f3a7fbd7a4683587134aa9b3877ee15aa29eea478dc68f933" },
{ url = "http://mirrors.aliyun.com/pypi/packages/46/2c/9664130905f03db57961b8980b05cab624afd114bf2be2576628a9f22da4/sqlalchemy-2.0.48-py3-none-any.whl", hash = "sha256:a66fe406437dd65cacd96a72689a3aaaecaebbcd62d81c5ac1c0fdbeac835096" },
]
[[package]] [[package]]
name = "tqdm" name = "tqdm"
version = "4.67.3" version = "4.67.3"
@@ -1064,8 +1277,11 @@ version = "0.1.0"
source = { virtual = "." } source = { virtual = "." }
dependencies = [ dependencies = [
{ name = "aiohttp" }, { name = "aiohttp" },
{ name = "cryptography" },
{ name = "pydantic" }, { name = "pydantic" },
{ name = "pydantic-settings" }, { name = "pydantic-settings" },
{ name = "pymysql" },
{ name = "sqlalchemy" },
{ name = "ylhp-boss-hr" }, { name = "ylhp-boss-hr" },
] ]
@@ -1086,12 +1302,15 @@ requires-dist = [
{ name = "aiohttp", specifier = ">=3.8" }, { name = "aiohttp", specifier = ">=3.8" },
{ name = "anthropic", marker = "extra == 'llm'", specifier = ">=0.20" }, { name = "anthropic", marker = "extra == 'llm'", specifier = ">=0.20" },
{ name = "black", marker = "extra == 'dev'", specifier = ">=23.0" }, { name = "black", marker = "extra == 'dev'", specifier = ">=23.0" },
{ name = "cryptography", specifier = ">=41.0" },
{ name = "openai", marker = "extra == 'llm'", specifier = ">=1.0" }, { name = "openai", marker = "extra == 'llm'", specifier = ">=1.0" },
{ name = "pydantic", specifier = ">=2.0" }, { name = "pydantic", specifier = ">=2.0" },
{ name = "pydantic-settings", specifier = ">=2.0" }, { name = "pydantic-settings", specifier = ">=2.0" },
{ name = "pymysql", specifier = ">=1.1" },
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=7.0" }, { name = "pytest", marker = "extra == 'dev'", specifier = ">=7.0" },
{ name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.21" }, { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.21" },
{ name = "ruff", marker = "extra == 'dev'", specifier = ">=0.1" }, { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.1" },
{ name = "sqlalchemy", specifier = ">=2.0" },
{ name = "ylhp-boss-hr", specifier = ">=1.37" }, { name = "ylhp-boss-hr", specifier = ">=1.37" },
] ]
provides-extras = ["llm", "dev"] provides-extras = ["llm", "dev"]