api-第三方

This commit is contained in:
邹方成 2025-10-01 20:27:20 +08:00
parent 25681c16a4
commit 8dc8e13019
14 changed files with 2558 additions and 3 deletions

View File

@ -63,6 +63,7 @@ uvicorn = "==0.34.0"
uvloop = "==0.21.0"
watchfiles = "==1.0.4"
websockets = "==14.1"
requests = "*"
[dev-packages]

1407
Pipfile.lock generated Normal file

File diff suppressed because it is too large Load Diff

302
aaa.txt Normal file
View File

@ -0,0 +1,302 @@
企业软件著作权查询:
接口地址 : https://openapi.chinaz.net/v1/1036/copyrightsoftware
返回格式 : JSON
请求方式 : POST
请求参数:
名称
类型
必填
说明
sign string 是 填写由"634xz年月日"生成的32位md5加密值"年月日"指调用api的日期。 举例如果是2021年5月6日调用api则对634xz20210506进行md5加密得到的32位加密值就是sign
entName string 是 企业名称
pageNo string 是 页码值从1开始表示第1页
range string 是 每页条数取值介于在1到300之间含1和300
APIKey string 是 申请接口时获取的APIKey值
ChinazVer string 是 接口版本号取值2.0
返回参数说明:
名称
类型
说明
rc string 状态码
msg string 状态信息
data object 数据对象
data.total string 总数量
data.totalPage string 总页数
data.dataList object 软件著作权列表
data.dataList[].SNAME string 软件全称
data.dataList[].SHORTNAME string 软件简称
data.dataList[].ENTNAME string 著作权人
data.dataList[].SNUM string 登记号
data.dataList[].REGDATE string 首次发表日期
data.dataList[].ANNDATE string 登记批准日期
data.dataList[].ANNTYPE string 分类号名称
data.dataList[].TYPENUM string 分类号编号
data.dataList[].VNUM string 版本号
data.dataList[].cdate string 完成日期
正确的返回示例:
{
"orderNo":"202308281517124830015",
"data":{
"total":12,
"totalPage":25,
"dataList":[
{
"cdate":"2023-08-30",
"ANNDATE":"2023-11-29",
"SNUM":"2023SR1531019",
"SHORTNAME":"ModelMate",
"REGDATE":"2023-11-29",
"SNAME":"AI模型使能平台",
"VNUM":"24.0",
"ENTNAME":"****有限公司",
"ANNTYPE":"****",
"TYPENUM":"438951"
}
]
},
"rc":"0000",
"msg":"查询成功"
}
错误的返回示例:
{
"rc":"1002",
"msg":"sign校验失败"
}
企业专利信息
接口地址 : https://openapi.chinaz.net/v1/1036/patent
返回格式 : JSON
请求方式 : POST
请求参数:
名称
类型
必填
说明
sign string 是 填写由"634xz年月日"生成的32位md5加密值"年月日"指调用api的日期。 举例如果是2021年5月6日调用api则对634xz20210506进行md5加密得到的32位加密值就是sign
searchKey string 是 查询关键词(企业名称、企业统代、企业注册号)
searchType string 否 查询关键字类型默认为0支持的所有类型1企业名称2统一社会信用代码3注册号
pageNo string 是 页码值从1开始表示第1页
range string 是 每页条数取值介于在1到300之间含1和300
APIKey string 是 申请接口时获取的APIKey值
ChinazVer string 是 接口版本号取值2.0
返回参数说明:
名称
类型
说明
rc string 状态码
msg string 状态信息
data object 数据列表
data.total string 总数量
data.totalPage string 总页数
data.dataList string 专利信息列表
data.dataList[].patentflls string 专利法律状态信息列表
data.dataList[].patentflls[].flzt string 法律状态
data.dataList[].patentflls[].flztxq string 法律状态详情
data.dataList[].patentflls[].flztggrq string 法律状态公告日期
data.dataList[].SQH string 专利申请号
data.dataList[].PATNAME string 专利标题
data.dataList[].SQR string 申请/专利权人
data.dataList[].SQRQ string 申请日期
data.dataList[].GKGGH string 公开/公告号
data.dataList[].FMR string 发明/设计人
data.dataList[].GKGGR string 公开/公告日
data.dataList[].FCFL string 范畴分类
data.dataList[].FLH string 分类号
data.dataList[].PTYPE string 专利分类
data.dataList[].DLR string 代理人
data.dataList[].DZ string 地址
data.dataList[].FASQ string 分案申请
data.dataList[].GJGB string 国际公布
data.dataList[].GJSQ string 国际申请
data.dataList[].GSDM string 国省代码
data.dataList[].JRGJRQ string 进入国家日期
data.dataList[].YXQ string 优先权
data.dataList[].YZWX string 引证文献
data.dataList[].ZFLH string 主分类号
data.dataList[].ZLDLJG string 专利代理机构
data.dataList[].ZQX string 主权项
data.dataList[].ZY string 摘要
data.dataList[].IPC string 专利行业分类
data.dataList[].PID string 专利ID
data.dataList[].PIC string 代表图片
data.dataList[].SQGKGGH string 授权公告号
data.dataList[].SQGKGGR string 授权公开日期
data.dataList[].ENTNAME string 申请企业
data.dataList[].ZFLNAME string 主分类名称
司法综合数据查询
接口地址 : https://openapi.chinaz.net/v1/1036/judgementdetailv4
返回格式 : JSON
请求方式 : POST
调用说明 : 司法数据综合查询接口数据非实时,可能存在部分数据延迟。该数据仅供参考,不作为判定具体结果使用。
请求参数:
名称
类型
必填
说明
sign string 是 填写由"634xz年月日"生成的32位md5加密值"年月日"指调用api的日期。 举例如果是2021年5月6日调用api则对634xz20210506进行md5加密得到的32位加密值就是sign
q string 否 名称选填列表检索时即查详情也即id不填时必填
idCardNo string 否 身份证号,选填,一般查个人时填写查询会更加精确
datatype string 否 查询数据类型,多个以',' 分割共有zxgg(被执行人)、sxgg(失信被执行人)、ktgg(开庭公告)、fygg(法院公告)、cpws(裁判文书)、splc(审判流程)六种数据类型,如果列表检索时,该字段默认为六种数据类型都填
id string 否 详情id,该id从检索结果中获取请配合查询数据类型使用。如查询某条被执行人详情时datatype必须填'zxgg'
pageNo string 否 页码列表检索时输入的字段默认为1
APIKey string 是 申请接口时获取的APIKey值
ChinazVer string 是 接口版本号,取值1.0
返回参数说明:
名称
类型
说明
rc string 状态码
msg string 状态信息
orderNo string 订单号
data string 返回查询结果
count string 检索出每种数据类型的总量统计
count[].bzxrCount string 被执行人总量
count[].sxrCount string 失信被执行人总量
count[].ktggCount string 开庭公告总量
count[].fyggCount string 法院公告数量
count[].cpwsCount string 裁判文书数量
count[].splcCount string 审判流程数量
list string 检索出每种数据类型数据列表
list[].bzxrList string 被执行人列表
bzxrList[].id string 被执行人ID
bzxrList[].entityName string 主体名称
bzxrList[].entityId string 主体代码
bzxrList[].courtName string 法院
bzxrList[].caseCode string 案号
bzxrList[].regDate string 立案时间
bzxrList[].relateType string 数据类别
list[].sxrList string 失信被执行人列表
sxrList[].id string 失信被执行人ID
sxrList[].entityName string 主体名称
sxrList[].entityId string 主体代码
sxrList[].courtName string 法院
sxrList[].caseCode string 案号
sxrList[].regDate string 立案时间
sxrList[].relateType string 数据类别
list[].ktggList string 开庭公告列表
ktggList[].id string 开庭公告ID
ktggList[].content string 开庭公告文书内容
list[].fyggList string 法院公告列表
fyggList[].id string 法院公告ID
fyggList[].content string 法院公告文书内容
list[].cpwsList string 裁判文书列表
cpwsList[].id string 裁判文书ID
cpwsList[].title string 标题
cpwsList[].caseCode string 案号
cpwsList[].court string 法院
cpwsList[].standPoint string 查询主体立场
cpwsList[].judgeDate string 裁判日期
cpwsList[].content string 文书缩略内容
cpwsList[].resultMatch string 个人查询匹配度,个人查询时有值,企业查询返回是空值
list[].splcList string 审判流程列表
splcList[].id string 审判流程ID
splcList[].litigant string 当事人
splcList[].courtName string 法院
splcList[].caseCode string 案号
entityName string 主体名称
entityId string 主体代码
courtName string 法院
caseState string 案件状态
caseCode string 案号
execMoney string 执行标的
regDate string 立案时间
gistId string 依据文号
entityName string 主体名称
entityId string 主体代码
caseCode string 案号
age string 年龄
sex string 性别
bussinessEntity string 法定代表人或者负责人姓名
courtName string 法院
areaName string 省份
partyTypeName string 类型
gistId string 执行依据文号
regDate string 立案时间
gistUnit string 做出执行依据单位
duty string 生效法律文书确定的义务
performance string 被执行人的履行情况
performedPart string 已履行
unPerformPart string 未履行
disruptTypeName string 失信类型
publishDate string 发布日期
status string 下架状态:已下架,未下架
ktggId string 开庭公告ID
areaName string 地区名称
court string 审理法院
caseNo string 案号
content string 公告内容
openTime string 开庭时间
litigant string 当事人,多个以顿号分开
plaintiff string 原告,多个以顿号分开
defendant string 被告,多个以顿号分开
caseCause string 案由
type string 公告类型
announcer string 公告人
litigant string 当事人
content string 公告内容
publishDate string 公告时间
baseInfo string 案件详情
baseInfo.caseCode string 案号
baseInfo.judgeDate string 裁判日期
baseInfo.title string 标题
baseInfo.publishDate string 发布日期
baseInfo.caseType string 案件类型
baseInfo.docType string 文书类型
baseInfo.caseCause string 案由
baseInfo.court string 法院
baseInfo.province string 省份
baseInfo.program string 审判程序
baseInfo.clauseContent string 审判依据
baseInfo.resultContent string 审判结果
baseInfo.content string 文书缩略内容
baseInfo.id string 裁判文书ID
roleList[] string 角色详情
roleList[].roleName string 角色
roleList[].standpoint string 立场
roleList[].entityName string 名称
roleList[].birthday string 生日
roleList[].gender string 性别
roleList[].nativePlace string 籍贯
roleList[].address string 地址
roleList[].workUnit string 工作单位
roleList[].nation string 民族
roleList[].organizationCode string 组织机构代码
roleList[].socialCreditCode string 统一社会信用代码
roleList[].idcard string 身份证
roleList[].jobTitle string 职位
judgeResultList[] string 判决结果
judgeResultList[].price string 涉案金额
judgeResultList[].realPrice string 实际金额
judgeResultList[].fromRole string 负担角色
judgeResultList[].way string 处置方式
judgeResultList[].toRole string 受让角色
judgeResultList[].priceType string 金额类型
judgeResultList[].priceUnit string 金额单位
judgeResultList[].currency string 币种
relationCaseList[] string 案件关联
relationCaseList[].caseCode string 案号
relationCaseList[].court string 法院
relationCaseList[].realationCaseCode string 关联案号
relationCaseList[].relationCaseId string 关联文书ID
areaName string 区域名称
litigant string 当事人
accuser string 原告
defender string 被告
others string 其他
courtName string 法院
caseCode string 案号
caseStat string 案件状态
openTime string 立案时间
trialTime string 开庭时间
closeTime string 结案时间
program string 审判程序
reason string 案由
caseType string 案件类型
target string 诉讼标的

View File

@ -12,6 +12,7 @@ from .industry.industry import router as industry_router
from .menus import menus_router
from .policy.policy import router as policy_router
from .roles import roles_router
from .third_party_api import third_party_api_router
from .users import users_router
v1_router = APIRouter()
@ -27,3 +28,4 @@ v1_router.include_router(esg_router, prefix="/esg", dependencies=[DependPermissi
v1_router.include_router(index_router, prefix="/index", dependencies=[DependPermission])
v1_router.include_router(industry_router, prefix="/industry", dependencies=[DependPermission])
v1_router.include_router(policy_router, prefix="/policy", dependencies=[DependPermission])
v1_router.include_router(third_party_api_router, prefix="/third_party_api", dependencies=[DependPermission])

View File

@ -0,0 +1,3 @@
from .third_party_api import router as third_party_api_router
__all__ = ["third_party_api_router"]

View File

@ -0,0 +1,93 @@
import logging
from typing import List
from fastapi import APIRouter, Query, Body, HTTPException
from app.controllers.third_party_api import third_party_api_controller
from app.schemas.base import Success, Fail
from app.schemas.third_party_api import (
BaseAPIRequest,
ChinazAPIRequest,
XiaohongshuNoteRequest,
APIResponse,
APIProviderInfo,
APIEndpointInfo,
APIListResponse
)
logger = logging.getLogger(__name__)
router = APIRouter(tags=["内置接口"])
# 站长之家API端点
@router.post("/chinaz/copyright_software", summary="企业软件著作权查询")
async def query_copyright_software(request: ChinazAPIRequest):
"""查询企业软件著作权信息"""
try:
result = await third_party_api_controller.query_copyright_software(
company_name=request.company_name,
chinaz_ver=request.chinaz_ver
)
if result.success:
return Success(data=result.data, message=result.message)
else:
return Fail(message=result.message)
except Exception as e:
logger.error(f"查询企业软件著作权失败: {e}")
return Fail(message=f"查询企业软件著作权失败: {str(e)}")
@router.post("/chinaz/patent_info", summary="企业专利信息查询")
async def query_patent_info(request: ChinazAPIRequest):
"""查询企业专利信息"""
try:
result = await third_party_api_controller.query_patent_info(
company_name=request.company_name,
chinaz_ver=request.chinaz_ver
)
if result.success:
return Success(data=result.data, message=result.message)
else:
return Fail(message=result.message)
except Exception as e:
logger.error(f"查询企业专利信息失败: {e}")
return Fail(message=f"查询企业专利信息失败: {str(e)}")
@router.post("/chinaz/judicial_data", summary="司法综合数据查询")
async def query_judicial_data(request: ChinazAPIRequest):
"""查询司法综合数据"""
try:
result = await third_party_api_controller.query_judicial_data(
company_name=request.company_name,
chinaz_ver=request.chinaz_ver
)
if result.success:
return Success(data=result.data, message=result.message)
else:
return Fail(message=result.message)
except Exception as e:
logger.error(f"查询司法综合数据失败: {e}")
return Fail(message=f"查询司法综合数据失败: {str(e)}")
# 小红书API端点
@router.post("/xiaohongshu/note", summary="获取小红书笔记详情")
async def get_xiaohongshu_note(request: XiaohongshuNoteRequest):
"""获取小红书笔记详情"""
try:
result = await third_party_api_controller.get_xiaohongshu_note(
note_id=request.note_id
)
if result.success:
return Success(data=result.data, message=result.message)
else:
return Fail(message=result.message)
except Exception as e:
logger.error(f"获取小红书笔记失败: {e}")
return Fail(message=f"获取小红书笔记失败: {str(e)}")

View File

@ -0,0 +1,106 @@
import logging
from typing import Dict, Any, List, Optional
from app.utils.universal_api_manager import universal_api
from app.schemas.third_party_api import (
APIProviderInfo,
APIEndpointInfo,
APIResponse
)
logger = logging.getLogger(__name__)
class ThirdPartyAPIController:
"""第三方API控制器"""
def __init__(self):
self.api_manager = universal_api
async def make_api_request(
self,
provider: str,
endpoint: str,
params: Dict[str, Any] = None,
timeout: int = 30
) -> APIResponse:
"""通用API请求方法"""
try:
result = self.api_manager.make_request(
provider=provider,
endpoint=endpoint,
params=params or {},
timeout=timeout
)
return APIResponse(
success=True,
data=result,
message="请求成功",
provider=provider,
endpoint=endpoint
)
except Exception as e:
logger.error(f"API请求失败: {e}")
return APIResponse(
success=False,
data={},
message=f"请求失败: {str(e)}",
provider=provider,
endpoint=endpoint
)
# 站长之家API便捷方法
async def query_copyright_software(self, company_name: str, chinaz_ver: str = "1") -> APIResponse:
"""查询企业软件著作权"""
return await self.make_api_request(
provider="chinaz",
endpoint="copyright_software",
params={
"entName": company_name,
"pageNo": 1,
"range": 100,
"ChinazVer": chinaz_ver
}
)
async def query_patent_info(self, company_name: str, chinaz_ver: str = "1") -> APIResponse:
"""查询企业专利信息"""
return await self.make_api_request(
provider="chinaz",
endpoint="patent",
params={
"searchKey": company_name,
"pageNo": 1,
"range": 100,
"searchType": "0",
"ChinazVer": chinaz_ver
}
)
async def query_judicial_data(self, company_name: str, chinaz_ver: str = "1") -> APIResponse:
"""查询司法综合数据"""
return await self.make_api_request(
provider="chinaz",
endpoint="judgement",
params={
"q": company_name,
"pageNo": 1,
"ChinazVer": chinaz_ver
}
)
# 小红书API便捷方法
async def get_xiaohongshu_note(self, note_id: str) -> APIResponse:
"""获取小红书笔记详情"""
params = {"noteId": note_id}
return await self.make_api_request(
provider="justoneapi",
endpoint="xiaohongshu_note_detail",
params=params
)
# 创建全局控制器实例
third_party_api_controller = ThirdPartyAPIController()

View File

@ -1,5 +1,6 @@
import asyncio
from datetime import datetime
from typing import List, Optional, Union
from tortoise import fields, models
@ -9,7 +10,7 @@ from app.settings import settings
class BaseModel(models.Model):
id = fields.BigIntField(pk=True, index=True)
async def to_dict(self, m2m: bool = False, exclude_fields: list[str] | None = None):
async def to_dict(self, m2m: bool = False, exclude_fields: Optional[List[str]] = None):
if exclude_fields is None:
exclude_fields = []

View File

@ -1,4 +1,11 @@
from enum import Enum, StrEnum
import sys
from enum import Enum
if sys.version_info >= (3, 11):
from enum import StrEnum
else:
class StrEnum(str, Enum):
pass
class EnumBase(Enum):

View File

@ -1,8 +1,15 @@
from enum import StrEnum
import sys
from typing import Optional
from pydantic import BaseModel, Field
if sys.version_info >= (3, 11):
from enum import StrEnum
else:
from enum import Enum
class StrEnum(str, Enum):
pass
class MenuType(StrEnum):
CATALOG = "catalog" # 目录

View File

@ -0,0 +1,55 @@
from typing import Dict, Any, Optional, List
from pydantic import BaseModel, Field
class BaseAPIRequest(BaseModel):
"""基础API请求模型"""
provider: str = Field(..., description="API提供商", example="chinaz")
endpoint: str = Field(..., description="端点名称", example="icp_info")
params: Dict[str, Any] = Field(default_factory=dict, description="请求参数")
timeout: Optional[int] = Field(30, description="超时时间(秒)")
class ChinazAPIRequest(BaseModel):
"""站长之家API请求模型"""
company_name: str = Field(..., description="公司名称", example="百度在线网络技术(北京)有限公司")
chinaz_ver: Optional[str] = Field("1", description="API版本", example="1")
timeout: Optional[int] = Field(30, description="超时时间(秒)")
class XiaohongshuNoteRequest(BaseModel):
"""小红书笔记详情请求"""
note_id: str = Field(..., description="笔记ID", example="68d2c71d000000000e00e9ea")
class APIResponse(BaseModel):
"""API响应模型"""
success: bool = Field(..., description="请求是否成功")
data: Dict[str, Any] = Field(..., description="响应数据")
message: Optional[str] = Field(None, description="响应消息")
provider: Optional[str] = Field(None, description="API提供商")
endpoint: Optional[str] = Field(None, description="端点名称")
class APIProviderInfo(BaseModel):
"""API提供商信息"""
name: str = Field(..., description="提供商名称")
base_url: str = Field(..., description="基础URL")
description: Optional[str] = Field(None, description="描述")
endpoints: List[str] = Field(default_factory=list, description="可用端点")
class APIEndpointInfo(BaseModel):
"""API端点信息"""
name: str = Field(..., description="端点名称")
path: str = Field(..., description="请求路径")
method: str = Field(..., description="HTTP方法")
description: Optional[str] = Field(None, description="描述")
required_params: List[str] = Field(default_factory=list, description="必需参数")
optional_params: List[str] = Field(default_factory=list, description="可选参数")
class APIListResponse(BaseModel):
"""API列表响应"""
providers: List[APIProviderInfo] = Field(..., description="API提供商列表")
total_providers: int = Field(..., description="提供商总数")

259
app/utils/api_config.py Normal file
View File

@ -0,0 +1,259 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
第三方API配置管理
支持从配置文件或环境变量加载API配置信息
"""
import os
import json
from typing import Dict, Any, Optional
from pathlib import Path
class APIConfig:
"""API配置管理器"""
def __init__(self, config_file: str = None):
"""
初始化配置管理器
Args:
config_file: 配置文件路径
"""
self.config_file = config_file or os.path.join(os.path.dirname(__file__), 'api_config.json')
self.config = self._load_config()
def _load_config(self) -> Dict[str, Any]:
"""加载配置"""
# 默认配置
default_config = {
"chinaz": {
"api_key": os.getenv("CHINAZ_API_KEY", "YOUR_API_KEY"),
"base_url": "https://openapi.chinaz.net",
"endpoints": {
"copyright_software": {
"path": "/v1/1036/copyrightsoftware",
"method": "POST",
"description": "企业软件著作权查询",
"required_params": ["entName", "pageNo", "range", "APIKey", "ChinazVer"],
"optional_params": ["sign"]
},
"patent": {
"path": "/v1/1036/patent",
"method": "POST",
"description": "企业专利信息查询",
"required_params": ["searchKey", "pageNo", "range", "APIKey", "ChinazVer"],
"optional_params": ["sign", "searchType"]
},
"judgement": {
"path": "/v1/1036/judgementdetailv4",
"method": "POST",
"description": "司法综合数据查询",
"required_params": ["APIKey", "ChinazVer"],
"optional_params": ["sign", "q", "idCardNo", "datatype", "id", "pageNo"]
}
}
},
"justoneapi": {
"api_key": os.getenv("JUSTONEAPI_TOKEN", "YNSbIjdU"),
"base_url": "https://api.justoneapi.com",
"timeout": 30,
"retries": 3,
"endpoints": {
"xiaohongshu_note_detail": {
"path": "/api/xiaohongshu/get-note-detail/v7",
"method": "GET",
"description": "小红书笔记详情查询",
"required_params": ["noteId"],
"optional_params": ["token"]
},
"xiaohongshu_user_info": {
"path": "/api/xiaohongshu/get-user-info/v7",
"method": "GET",
"description": "小红书用户信息查询",
"required_params": ["userId"],
"optional_params": ["token"]
},
"xiaohongshu_search_notes": {
"path": "/api/xiaohongshu/search-notes/v7",
"method": "GET",
"description": "小红书笔记搜索",
"required_params": ["keyword"],
"optional_params": ["page", "size", "token"]
}
}
},
"other_apis": {
# 可以添加其他第三方API配置
"example_api": {
"api_key": os.getenv("EXAMPLE_API_KEY", ""),
"base_url": "https://api.example.com",
"endpoints": {}
}
},
"common_settings": {
"timeout": 60,
"retries": 3,
"sign_prefix": "634xz"
}
}
# 尝试从文件加载配置
if os.path.exists(self.config_file):
try:
with open(self.config_file, 'r', encoding='utf-8') as f:
file_config = json.load(f)
# 合并配置
self._merge_config(default_config, file_config)
except Exception as e:
print(f"加载配置文件失败: {e}")
return default_config
def _merge_config(self, default: Dict[str, Any], custom: Dict[str, Any]) -> None:
"""递归合并配置"""
for key, value in custom.items():
if key in default and isinstance(default[key], dict) and isinstance(value, dict):
self._merge_config(default[key], value)
else:
default[key] = value
def get_api_config(self, provider: str) -> Optional[Dict[str, Any]]:
"""
获取指定提供商的API配置
Args:
provider: API提供商名称
Returns:
Optional[Dict[str, Any]]: API配置信息
"""
return self.config.get(provider)
def get_endpoint_config(self, provider: str, endpoint: str) -> Optional[Dict[str, Any]]:
"""
获取指定端点的配置
Args:
provider: API提供商名称
endpoint: 端点名称
Returns:
Optional[Dict[str, Any]]: 端点配置信息
"""
api_config = self.get_api_config(provider)
if api_config and 'endpoints' in api_config:
return api_config['endpoints'].get(endpoint)
return None
def get_api_key(self, provider: str) -> Optional[str]:
"""
获取API密钥
Args:
provider: API提供商名称
Returns:
Optional[str]: API密钥
"""
api_config = self.get_api_config(provider)
return api_config.get('api_key') if api_config else None
def get_base_url(self, provider: str) -> Optional[str]:
"""
获取基础URL
Args:
provider: API提供商名称
Returns:
Optional[str]: 基础URL
"""
api_config = self.get_api_config(provider)
return api_config.get('base_url') if api_config else None
def set_api_key(self, provider: str, api_key: str) -> None:
"""
设置API密钥
Args:
provider: API提供商名称
api_key: API密钥
"""
if provider not in self.config:
self.config[provider] = {}
self.config[provider]['api_key'] = api_key
def add_endpoint(self,
provider: str,
endpoint_name: str,
path: str,
method: str = 'POST',
description: str = '',
required_params: list = None,
optional_params: list = None) -> None:
"""
添加新的API端点
Args:
provider: API提供商名称
endpoint_name: 端点名称
path: API路径
method: HTTP方法
description: 描述信息
required_params: 必需参数列表
optional_params: 可选参数列表
"""
if provider not in self.config:
self.config[provider] = {'endpoints': {}}
if 'endpoints' not in self.config[provider]:
self.config[provider]['endpoints'] = {}
self.config[provider]['endpoints'][endpoint_name] = {
'path': path,
'method': method.upper(),
'description': description,
'required_params': required_params or [],
'optional_params': optional_params or []
}
def save_config(self) -> None:
"""保存配置到文件"""
try:
# 创建目录(如果不存在)
os.makedirs(os.path.dirname(self.config_file), exist_ok=True)
with open(self.config_file, 'w', encoding='utf-8') as f:
json.dump(self.config, f, indent=2, ensure_ascii=False)
print(f"配置已保存到: {self.config_file}")
except Exception as e:
print(f"保存配置失败: {e}")
def get_common_settings(self) -> Dict[str, Any]:
"""获取通用设置"""
return self.config.get('common_settings', {})
def list_providers(self) -> list:
"""列出所有API提供商"""
return [key for key in self.config.keys() if key != 'common_settings']
def list_endpoints(self, provider: str) -> list:
"""
列出指定提供商的所有端点
Args:
provider: API提供商名称
Returns:
list: 端点名称列表
"""
api_config = self.get_api_config(provider)
if api_config and 'endpoints' in api_config:
return list(api_config['endpoints'].keys())
return []
# 创建全局配置实例
api_config = APIConfig()

View File

View File

@ -0,0 +1,312 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
通用第三方API管理器
支持多种不同类型的第三方API包括需要签名和不需要签名的API
"""
import os
import hashlib
import requests
import logging
from datetime import datetime
from typing import Dict, Any, Optional, Union
import json
import time
import urllib.parse
from .api_config import api_config
# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
class UniversalAPIManager:
"""通用第三方API管理器"""
def __init__(self):
"""初始化API管理器"""
self.config = api_config
self.session = requests.Session()
self.session.headers.update({
'User-Agent': 'Universal-API-Manager/1.0',
'Accept': 'application/json',
'Content-Type': 'application/json'
})
def _get_provider_config(self, provider: str) -> Dict[str, Any]:
"""获取API提供商配置"""
config = self.config.get_api_config(provider)
if not config:
raise ValueError(f"未找到API提供商配置: {provider}")
return config
def _get_endpoint_config(self, provider: str, endpoint: str) -> Dict[str, Any]:
"""获取端点配置"""
endpoint_config = self.config.get_endpoint_config(provider, endpoint)
if not endpoint_config:
raise ValueError(f"未找到端点配置: {provider}.{endpoint}")
return endpoint_config
def _generate_chinaz_sign(self, date: datetime = None) -> str:
"""
生成站长之家API签名
Args:
date: 指定日期默认为当前日期
Returns:
str: 32位MD5签名
"""
if date is None:
date = datetime.now()
# 格式化日期为YYYYMMDD
date_str = date.strftime("%Y%m%d")
# 构建签名字符串
sign_str = f"634xz{date_str}"
# 生成MD5哈希
md5_hash = hashlib.md5(sign_str.encode('utf-8')).hexdigest()
return md5_hash
def _build_url(self, provider: str, endpoint: str, params: Dict[str, Any]) -> str:
"""
构建完整的API请求URL
Args:
provider: API提供商
endpoint: 端点名称
params: 请求参数
Returns:
str: 完整的URL
"""
provider_config = self._get_provider_config(provider)
endpoint_config = self._get_endpoint_config(provider, endpoint)
base_url = provider_config['base_url']
path = endpoint_config['path']
method = endpoint_config['method']
# 构建完整URL
full_url = f"{base_url}{path}"
# 处理参数
if method.upper() == 'GET' and params:
# 对于GET请求将参数添加到URL中
query_string = urllib.parse.urlencode(params)
full_url = f"{full_url}?{query_string}"
return full_url
def _validate_params(self, provider: str, endpoint: str, params: Dict[str, Any]) -> None:
"""
验证请求参数
Args:
provider: API提供商
endpoint: 端点名称
params: 请求参数
Raises:
ValueError: 缺少必需参数时抛出异常
"""
endpoint_config = self._get_endpoint_config(provider, endpoint)
required_params = endpoint_config.get('required_params', [])
missing_params = []
for param in required_params:
if param not in params or params[param] is None:
missing_params.append(param)
if missing_params:
raise ValueError(f"缺少必需参数: {', '.join(missing_params)}")
def _prepare_params(self, provider: str, endpoint: str, params: Dict[str, Any]) -> Dict[str, Any]:
"""
准备请求参数根据不同的API类型添加必要的参数
Args:
provider: API提供商
endpoint: 端点名称
params: 原始参数
Returns:
Dict[str, Any]: 处理后的参数
"""
provider_config = self._get_provider_config(provider)
prepared_params = params.copy()
# 根据不同的API提供商添加特定参数
if provider == 'chinaz':
# 站长之家API需要签名
if 'sign' not in prepared_params:
prepared_params['sign'] = self._generate_chinaz_sign()
elif provider == 'justoneapi':
# JustOneAPI需要token参数
if 'token' not in prepared_params or not prepared_params['token']:
api_key = provider_config.get('api_key')
if api_key:
prepared_params['token'] = api_key
return prepared_params
def make_request(self,
provider: str,
endpoint: str,
params: Dict[str, Any] = None,
timeout: int = 60,
retries: int = 3) -> Dict[str, Any]:
"""
发送API请求
Args:
provider: API提供商名称
endpoint: 端点名称
params: 请求参数
timeout: 超时时间
retries: 重试次数
Returns:
Dict[str, Any]: API响应数据
"""
params = params or {}
# 验证参数
self._validate_params(provider, endpoint, params)
# 准备参数
prepared_params = self._prepare_params(provider, endpoint, params)
# 获取端点配置
endpoint_config = self._get_endpoint_config(provider, endpoint)
method = endpoint_config['method'].upper()
# 构建URL
url = self._build_url(provider, endpoint, prepared_params)
logger.info(f"发送{method}请求到: {url}")
# 发送请求
for attempt in range(retries + 1):
try:
if method == 'GET':
response = self.session.get(url, timeout=timeout)
elif method == 'POST':
response = self.session.post(url, json=prepared_params, timeout=timeout)
else:
raise ValueError(f"不支持的HTTP方法: {method}")
# 检查响应状态
response.raise_for_status()
# 解析响应
try:
result = response.json()
except json.JSONDecodeError:
result = {'raw_response': response.text}
logger.info(f"API请求成功: {provider}.{endpoint}")
return result
except requests.exceptions.RequestException as e:
logger.warning(f"API请求失败 (尝试 {attempt + 1}/{retries + 1}): {e}")
if attempt == retries:
logger.error(f"API请求最终失败: {provider}.{endpoint}")
return {
'error': True,
'message': f"API请求失败: {str(e)}",
'provider': provider,
'endpoint': endpoint
}
time.sleep(2 ** attempt) # 指数退避
# 站长之家API的便捷方法
def query_copyright_software(self, company_name: str, chinaz_ver: str = "1") -> Dict[str, Any]:
"""查询企业软件著作权"""
params = {
'entName': company_name,
'pageNo': '1',
'range': '100',
'ChinazVer': chinaz_ver
}
return self.make_request('chinaz', 'copyright_software', params)
def query_patent_info(self, company_name: str, chinaz_ver: str = "1") -> Dict[str, Any]:
"""查询企业专利信息"""
params = {
'searchKey': company_name,
'pageNo': '1',
'range': '100',
'searchType': '0',
'ChinazVer': chinaz_ver
}
return self.make_request('chinaz', 'patent', params)
def query_judicial_data(self, company_name: str, chinaz_ver: str = "1") -> Dict[str, Any]:
"""查询司法综合数据"""
params = {
'q': company_name,
'pageNo': '1',
'ChinazVer': chinaz_ver
}
return self.make_request('chinaz', 'judgement', params)
# JustOneAPI的便捷方法
def get_xiaohongshu_note_detail(self, note_id: str) -> Dict[str, Any]:
"""
获取小红书笔记详情
Args:
note_id: 笔记ID
Returns:
Dict[str, Any]: 笔记详情数据
"""
params = {'noteId': note_id}
return self.make_request('justoneapi', 'xiaohongshu_note_detail', params)
# 通用方法
def list_providers(self) -> list:
"""列出所有可用的API提供商"""
return self.config.list_providers()
def list_endpoints(self, provider: str) -> list:
"""列出指定提供商的所有端点"""
return self.config.list_endpoints(provider)
def get_endpoint_info(self, provider: str, endpoint: str) -> Dict[str, Any]:
"""获取端点详细信息"""
return self._get_endpoint_config(provider, endpoint)
def set_api_key(self, provider: str, api_key: str) -> None:
"""设置API密钥"""
self.config.set_api_key(provider, api_key)
def add_endpoint(self,
provider: str,
endpoint_name: str,
path: str,
method: str = 'GET',
description: str = '',
required_params: list = None,
optional_params: list = None) -> None:
"""添加新的API端点"""
self.config.add_endpoint(
provider=provider,
endpoint_name=endpoint_name,
path=path,
method=method,
description=description,
required_params=required_params,
optional_params=optional_params
)
# 创建全局实例
universal_api = UniversalAPIManager()