This commit is contained in:
若拙_233 2025-10-10 15:48:42 +08:00
commit 42b5ce887c
27 changed files with 5230 additions and 250 deletions

83
aaa.json Normal file
View File

@ -0,0 +1,83 @@
{
"asset_name": "资产名称",
"institution": "所属机构",
"industry": "农业",
"annual_revenue": "22",
"rd_investment": "33",
"three_year_income": [
"11",
"22",
"33"
],
"funding_status": "国家级资助",
"sales_volume": "22",
"link_views": "22",
"circulation": "0",
"last_market_activity": "0",
"monthly_transaction": "0",
"price_fluctuation": [
"2",
"3"
],
"application_maturity": "0",
"application_coverage": "0",
"cooperation_depth": "1",
"offline_activities": "3",
"online_accounts": [
"0",
"333"
],
"inheritor_level": "国家级传承人",
"inheritor_age_count": [
"55",
"66",
"77"
],
"inheritor_certificates": [
"http://example.com/国家级非遗传承人证书.jpg"
],
"heritage_level": "0",
"historical_evidence": {
"artifacts": "22",
"ancient_literature": "33",
"inheritor_testimony": "66"
},
"patent_certificates": [
"http://example.com/专利证书1.jpg",
"http://example.com/专利证书2.jpg"
],
"pattern_images": [
"pattern1.jpg"
],
"patent_application_no": "22",
"heritage_asset_level": "国家级非遗",
"inheritor_ages": [
"55",
"66",
"77"
],
"implementation_stage": "成熟应用",
"coverage_area": "全球覆盖",
"collaboration_type": "品牌联名",
"platform_accounts": {
"bilibili": {
"followers_count": 8000,
"likes": 1000,
"comments": 500,
"shares": 500
},
"douyin": {
"followers_count": 8000,
"likes": 1000,
"comments": 500,
"shares": 500
}
},
"scarcity_level": "孤品:全球唯一,不可复制(如特定版权、唯一实物)",
"market_activity_time": "近一周",
"price_range": {
"highest": "2",
"lowest": "3"
},
"monthly_transaction_amount": "月交易额<100万元"
}

View File

@ -35,7 +35,7 @@ def create_app() -> FastAPI:
lifespan=lifespan, lifespan=lifespan,
) )
# 注册静态文件目录 # 注册静态文件目录
app.mount("/static", StaticFiles(directory="app/static"), name="static") # app.mount("/static", StaticFiles(directory="app/static"), name="static")
register_exceptions(app) register_exceptions(app)
register_routers(app, prefix="/api") register_routers(app, prefix="/api")
return app return app

View File

@ -24,7 +24,9 @@ v1_router = APIRouter()
v1_router.include_router(base_router, prefix="/base") v1_router.include_router(base_router, prefix="/base")
v1_router.include_router(app_users_router, prefix="/app-user") # AppUser路由无需权限依赖 v1_router.include_router(app_users_router, prefix="/app-user") # AppUser路由无需权限依赖
v1_router.include_router(app_valuations_router, prefix="/app-valuations") # 用户端估值评估路由,需要认证 # 注意app-valuations 路由在各自的端点内部使用 get_current_app_user 进行认证
# 这样可以保持App用户认证系统的独立性不与后台管理权限系统混合
v1_router.include_router(app_valuations_router, prefix="/app-valuations") # 用户端估值评估路由
v1_router.include_router(users_router, prefix="/user", dependencies=[DependAuth, DependPermission]) v1_router.include_router(users_router, prefix="/user", dependencies=[DependAuth, DependPermission])
v1_router.include_router(roles_router, prefix="/role", dependencies=[DependAuth, DependPermission]) v1_router.include_router(roles_router, prefix="/role", dependencies=[DependAuth, DependPermission])
v1_router.include_router(menus_router, prefix="/menu", dependencies=[DependAuth, DependPermission]) v1_router.include_router(menus_router, prefix="/menu", dependencies=[DependAuth, DependPermission])

View File

@ -9,8 +9,10 @@ from app.schemas.valuation import (
UserValuationOut, UserValuationOut,
UserValuationDetail UserValuationDetail
) )
from app.models.user import AppUser
from app.schemas.base import Success, SuccessExtra from app.schemas.base import Success, SuccessExtra
from app.utils.app_user_jwt import get_current_app_user_id from app.utils.app_user_jwt import get_current_app_user
app_valuations_router = APIRouter(tags=["用户端估值评估"]) app_valuations_router = APIRouter(tags=["用户端估值评估"])
@ -18,19 +20,70 @@ app_valuations_router = APIRouter(tags=["用户端估值评估"])
@app_valuations_router.post("/", summary="创建估值评估") @app_valuations_router.post("/", summary="创建估值评估")
async def create_valuation( async def create_valuation(
data: UserValuationCreate, data: UserValuationCreate,
user_id: int = Depends(get_current_app_user_id) current_user: AppUser = Depends(get_current_app_user)
): ):
""" """
用户创建估值评估申请 用户创建估值评估申请
""" """
try: try:
result = await user_valuation_controller.create_valuation( result = await user_valuation_controller.create_valuation(
user_id=user_id, user_id=current_user.id,
data=data data=data
) )
# 使用model_dump_json()来正确序列化datetime然后解析为dict # 使用model_dump_json()来正确序列化datetime然后解析为dict
import json import json
result_dict = json.loads(result.model_dump_json()) result_dict = json.loads(result.model_dump_json())
# 开始计算 估值 信息
# 1 # 经济价值B1模块: EconomicValueB1Calculator | BasicValueB11Calculator | TrafficFactorB12Calculator | PolicyMultiplierB13Calculator
# 1.1 EconomicValueB1Calculator
# input_data = {
# # 基础价值B11相关参数
# 'three_year_income': data.three_year_income,
# 'patent_score': data.pa, # 专利分
# 'popularity_score': data.popularity_score, # 普及地域分值
# 'infringement_score': data.infringement_score, # 侵权分
# 'innovation_ratio': data.innovation_ratio,
# 'esg_score':data.esg_score,
# 'industry_coefficient':data.industry_coefficient,
# # 流量因子B12相关参数
# 'search_index_s1': 4500.0,
# 'industry_average_s2': 5000.0,
# # 'social_media_spread_s3': social_media_spread_s3,
# 'likes': 4, # 点赞
# 'comments': 5, # 评论
# 'shares': 6, # 转发
# 'followers': 7, # 粉丝数
# 'click_count': 1000,# 点击量
# 'view_count': 100, # 内容浏览量
# # 政策乘数B13相关参数
# 'policy_match_score': 10.0, # 政策匹配度
# 'implementation_stage': 10.0, # 实施阶段评分
# 'funding_support': 10.0 # 资金支持度
# }
# 1.2 BasicValueB11Calculator
# 1.3 TrafficFactorB12Calculator
# 1.4 PolicyMultiplierB13Calculator
# 2 # 文化价值B2模块: CulturalValueB2Calculator | LivingHeritageB21Calculator | PatternGeneB22Calculator
# 2.1 CulturalValueB2Calculator
# 2.2 LivingHeritageB21Calculator
# 2.3 PatternGeneB22Calculator
# 3 # 风险调整系数B3模块: RiskAdjustmentB3Calculator
# 3.1 RiskAdjustmentB3Calculator
# 4 # 市场估值C模块: MarketValueCCalculator | MarketBiddingC1Calculator | HeatCoefficientC2Calculator | ScarcityMultiplierC3Calculator | TemporalDecayC4Calculator
# 4.1 MarketValueCCalculator
# 4.2 MarketBiddingC1Calculator
# 4.3 HeatCoefficientC2Calculator
# 4.4 ScarcityMultiplierC3Calculator
# 4.5 TemporalDecayC4Calculator
# 5 # 最终估值A模块: FinalValueACalculator
# 5.1 FinalValueACalculator
return Success(data=result_dict, msg="估值评估申请提交成功") return Success(data=result_dict, msg="估值评估申请提交成功")
except Exception as e: except Exception as e:
raise HTTPException( raise HTTPException(
@ -42,14 +95,15 @@ async def create_valuation(
@app_valuations_router.get("/", summary="获取我的估值评估列表") @app_valuations_router.get("/", summary="获取我的估值评估列表")
async def get_my_valuations( async def get_my_valuations(
query: UserValuationQuery = Depends(), query: UserValuationQuery = Depends(),
user_id: int = Depends(get_current_app_user_id) current_user: AppUser = Depends(get_current_app_user)
): ):
""" """
获取当前用户的估值评估列表 获取当前用户的估值评估列表
""" """
try: try:
result = await user_valuation_controller.get_user_valuations( result = await user_valuation_controller.get_user_valuations(
user_id=user_id, user_id=current_user.id,
query=query query=query
) )
# 使用model_dump_json()来正确序列化datetime然后解析为dict列表 # 使用model_dump_json()来正确序列化datetime然后解析为dict列表
@ -59,7 +113,7 @@ async def get_my_valuations(
data=serialized_items, data=serialized_items,
total=result.total, total=result.total,
page=result.page, page=result.page,
size=result.size, page_size=result.size,
pages=result.pages, pages=result.pages,
msg="获取估值评估列表成功" msg="获取估值评估列表成功"
) )
@ -73,14 +127,14 @@ async def get_my_valuations(
@app_valuations_router.get("/{valuation_id}", summary="获取估值评估详情") @app_valuations_router.get("/{valuation_id}", summary="获取估值评估详情")
async def get_valuation_detail( async def get_valuation_detail(
valuation_id: int, valuation_id: int,
user_id: int = Depends(get_current_app_user_id) current_user: AppUser = Depends(get_current_app_user)
): ):
""" """
获取指定估值评估的详细信息 获取指定估值评估的详细信息
""" """
try: try:
result = await user_valuation_controller.get_user_valuation_detail( result = await user_valuation_controller.get_user_valuation_detail(
user_id=user_id, user_id=current_user.id,
valuation_id=valuation_id valuation_id=valuation_id
) )
@ -105,14 +159,14 @@ async def get_valuation_detail(
@app_valuations_router.get("/statistics/overview", summary="获取我的估值评估统计") @app_valuations_router.get("/statistics/overview", summary="获取我的估值评估统计")
async def get_my_valuation_statistics( async def get_my_valuation_statistics(
user_id: int = Depends(get_current_app_user_id) current_user: AppUser = Depends(get_current_app_user)
): ):
""" """
获取当前用户的估值评估统计信息 获取当前用户的估值评估统计信息
""" """
try: try:
result = await user_valuation_controller.get_user_valuation_statistics( result = await user_valuation_controller.get_user_valuation_statistics(
user_id=user_id user_id=current_user.id
) )
return Success(data=result, msg="获取统计信息成功") return Success(data=result, msg="获取统计信息成功")
except Exception as e: except Exception as e:

View File

@ -15,6 +15,7 @@ from app.schemas.valuation import (
from app.schemas.base import Success, SuccessExtra from app.schemas.base import Success, SuccessExtra
from app.utils.app_user_jwt import get_current_app_user_id from app.utils.app_user_jwt import get_current_app_user_id
from app.utils.calculation_engine import FinalValueACalculator from app.utils.calculation_engine import FinalValueACalculator
from app.utils.calculation_engine.drp import DynamicPledgeRateCalculator
from app.utils.calculation_engine.economic_value_b1.sub_formulas.basic_value_b11 import calculate_popularity_score, \ from app.utils.calculation_engine.economic_value_b1.sub_formulas.basic_value_b11 import calculate_popularity_score, \
calculate_infringement_score, calculate_patent_usage_score calculate_infringement_score, calculate_patent_usage_score
@ -40,6 +41,64 @@ async def calculate_valuation(
计算估值评估 计算估值评估
根据用户提交的估值评估数据调用计算引擎进行经济价值B1计算 根据用户提交的估值评估数据调用计算引擎进行经济价值B1计算
请求示例JSON (仅包含用户填写部分):
{
"asset_name": "传统刺绣工艺",
"institution": "某文化传承机构",
"industry": "传统手工艺",
// 财务状况 (用户填写)
"annual_revenue": "500", // 近12个月机构营收/万元
"rd_investment": "50", // 近12个月机构研发投入/万元
"three_year_income": [400, 450, 500], // 近三年机构收益/万元
// 非遗等级与技术 (用户填写)
"inheritor_level": "国家级传承人", // 非遗传承人等级
"inheritor_ages": [45, 60, 75], // 传承人年龄列表
"heritage_level": "国家级", // 非遗等级
"patent_application_no": "CN202310123456.7", // 专利申请号
"patent_remaining_years": "15", // 专利剩余年限
"historical_evidence": { // 历史证明证据及数量
"历史文献": 3,
"考古发现": 2,
"传承谱系": 5
},
"pattern_images": ["demo.jpg"], // 非遗纹样图片
// 非遗应用与推广 (用户填写)
"application_maturity": "成熟应用", // 应用成熟度
"application_coverage": "全国覆盖", // 应用覆盖范围
"cooperation_depth": "0.5", // 跨界合作深度
"offline_activities": "12", // 近12个月线下宣讲活动次数
"online_accounts": [ // 线上宣传账号信息
{"platform": "抖音", "account": "传统刺绣大师"},
{"platform": "微博", "account": "非遗传承人"}
],
// 市场信息 (用户填写)
"sales_volume": "1000", // 近12个月销售量
"link_views": "5000", // 近12个月链接浏览量
"circulation": "限量", // 发行量
"last_market_activity": "2024-01-15", // 最近一次市场活动时间
"price_fluctuation": [95.0, 105.0], // 近30天价格波动区间
"manual_bids": [48000.0, 50000.0, 52000.0], // 手动收集的竞价列表
// 政策相关 (用户填写)
"funding_status": "国家级资助", // 资金支持情况
"implementation_stage": "成熟应用" // 实施阶段
}
API获取参数 (系统自动获取无需用户填写):
- 搜索指数: 百度微信微博搜索指数
- 社交媒体数据: 点赞数评论数转发数粉丝数
- 交易数据: 近3个月加权平均价格
- 热度数据: 近7日日均浏览量收藏数
- ESG评分: 根据行业自动匹配
- 行业系数: 根据行业ROE计算
- 政策匹配度: 根据行业自动匹配
- 专利验证: 通过API验证专利有效性
- 侵权记录: 通过API查询侵权诉讼历史
""" """
try: try:
start_ts = time.monotonic() start_ts = time.monotonic()
@ -126,6 +185,14 @@ async def calculate_valuation(
# 计算最终估值A统一计算 # 计算最终估值A统一计算
calculation_result = calculator.calculate_complete_final_value_a(input_data) calculation_result = calculator.calculate_complete_final_value_a(input_data)
# 计算动态质押
drp_c = DynamicPledgeRateCalculator()
'''
monthly_amount (float): 月交易额万元
heritage_level (str): 非遗等级
'''
drp_result = drp_c.calculate_dynamic_pledge_rate(float(data.monthly_transaction_amount),data.heritage_asset_level)
# 结构化日志:关键分值 # 结构化日志:关键分值
try: try:
duration_ms = int((time.monotonic() - start_ts) * 1000) duration_ms = int((time.monotonic() - start_ts) * 1000)

View File

@ -11,6 +11,7 @@ from app.schemas.third_party_api import (
OCRRequest, OCRRequest,
XiaohongshuNoteRequest, XiaohongshuNoteRequest,
JizhiliaoSearchRequest, JizhiliaoSearchRequest,
DifyWorkflowRequest,
APIResponse, APIResponse,
APIProviderInfo, APIProviderInfo,
APIEndpointInfo, APIEndpointInfo,
@ -110,4 +111,22 @@ async def search_jizhiliao_index(request: JizhiliaoSearchRequest):
return Fail(message=result.message) return Fail(message=result.message)
except Exception as e: except Exception as e:
logger.error(f"极致聊指数搜索失败: {e}") logger.error(f"极致聊指数搜索失败: {e}")
return Fail(message=f"极致聊指数搜索失败: {str(e)}") return Fail(message=f"极致聊指数搜索失败: {str(e)}")
@router.post("/dify/workflows/run", summary="运行Dify工作流")
async def run_dify_workflow(request: DifyWorkflowRequest):
"""运行Dify工作流"""
try:
result = await third_party_api_controller.run_dify_workflow(
zl_img=request.zl_img,
response_mode=request.response_mode,
user=request.user
)
if result.success:
return Success(data=result.data, message=result.message)
else:
return Fail(message=result.message)
except Exception as e:
logger.error(f"运行Dify工作流失败: {e}")
return Fail(message=f"运行Dify工作流失败: {str(e)}")

View File

@ -2,4 +2,4 @@ from fastapi import APIRouter
from .upload import router as upload_router from .upload import router as upload_router
router = APIRouter() router = APIRouter()
router.include_router(upload_router, prefix="/upload", tags=["文件上传"]) router.include_router(upload_router, tags=["文件上传"])

View File

@ -21,7 +21,10 @@ valuations_router = APIRouter(tags=["估值评估"])
async def create_valuation(data: ValuationAssessmentCreate): async def create_valuation(data: ValuationAssessmentCreate):
"""创建新的估值评估记录""" """创建新的估值评估记录"""
try: try:
result = await valuation_controller.create(data) # 获取当前用户ID
user_id = CTX_USER_ID.get()
print(user_id)
result = await valuation_controller.create(data, user_id)
return Success(data=result, msg="创建成功") return Success(data=result, msg="创建成功")
except Exception as e: except Exception as e:
raise HTTPException(status_code=400, detail=f"创建失败: {str(e)}") raise HTTPException(status_code=400, detail=f"创建失败: {str(e)}")
@ -88,9 +91,9 @@ async def get_valuations(
data=result.items, data=result.items,
total=result.total, total=result.total,
page=result.page, page=result.page,
size=result.size, page_size=result.size,
pages=result.pages, pages=result.pages,
msg="获取成功" msg="获取估值评估列表成功"
) )
@ -106,7 +109,7 @@ async def search_valuations(
data=result.items, data=result.items,
total=result.total, total=result.total,
page=result.page, page=result.page,
size=result.size, page_size=result.size,
pages=result.pages, pages=result.pages,
msg="搜索成功" msg="搜索成功"
) )

View File

@ -78,9 +78,9 @@ class ThirdPartyAPIController:
endpoint="patent", endpoint="patent",
params={ params={
"searchKey": company_name, "searchKey": company_name,
"pageNo": 1, "pageNo": "1",
"range": 100, "range": "100",
"searchType": 0, "searchType": '1',
"ChinazVer": chinaz_ver "ChinazVer": chinaz_ver
} }
) )
@ -125,6 +125,20 @@ class ThirdPartyAPIController:
timeout=timeout timeout=timeout
) )
async def run_dify_workflow(
self,
zl_img: str,
response_mode: str = "blocking",
user: str = "default_user"
) -> APIResponse:
"""运行Dify工作流"""
params = {
"inputs": {"zl_img": zl_img},
"response_mode": response_mode,
"user": user
}
return await self.make_api_request("dify", "workflows_run", params)
# 创建全局控制器实例 # 创建全局控制器实例
third_party_api_controller = ThirdPartyAPIController() third_party_api_controller = ThirdPartyAPIController()

View File

@ -3,6 +3,7 @@ from pathlib import Path
from typing import List from typing import List
from fastapi import UploadFile from fastapi import UploadFile
from app.schemas.upload import ImageUploadResponse from app.schemas.upload import ImageUploadResponse
from app.settings.config import settings
class UploadController: class UploadController:
"""文件上传控制器""" """文件上传控制器"""
@ -44,8 +45,8 @@ class UploadController:
with open(file_path, "wb") as f: with open(file_path, "wb") as f:
f.write(content) f.write(content)
# 返回文件URL # 返回完整的可访问URL
return ImageUploadResponse( return ImageUploadResponse(
url=f"/static/images/{filename}", url=f"{settings.BASE_URL}/static/images/{filename}",
filename=filename filename=filename
) )

View File

@ -86,16 +86,7 @@ class UserValuationController:
async def _to_user_out(self, valuation: ValuationAssessment) -> UserValuationOut: async def _to_user_out(self, valuation: ValuationAssessment) -> UserValuationOut:
"""转换为用户端输出模型""" """转换为用户端输出模型"""
return UserValuationOut( return UserValuationOut.model_validate(valuation)
id=valuation.id,
asset_name=valuation.asset_name,
institution=valuation.institution,
industry=valuation.industry,
status=valuation.status,
admin_notes=valuation.admin_notes,
created_at=valuation.created_at,
updated_at=valuation.updated_at
)
async def _to_user_detail(self, valuation: ValuationAssessment) -> UserValuationDetail: async def _to_user_detail(self, valuation: ValuationAssessment) -> UserValuationDetail:
"""转换为用户端详细模型""" """转换为用户端详细模型"""
@ -109,24 +100,47 @@ class UserValuationController:
three_year_income=valuation.three_year_income, three_year_income=valuation.three_year_income,
funding_status=valuation.funding_status, funding_status=valuation.funding_status,
inheritor_level=valuation.inheritor_level, inheritor_level=valuation.inheritor_level,
inheritor_ages=valuation.inheritor_ages,
inheritor_age_count=valuation.inheritor_age_count, inheritor_age_count=valuation.inheritor_age_count,
inheritor_certificates=valuation.inheritor_certificates, inheritor_certificates=valuation.inheritor_certificates,
heritage_level=valuation.heritage_level, heritage_level=valuation.heritage_level,
heritage_asset_level=valuation.heritage_asset_level,
patent_application_no=valuation.patent_application_no, patent_application_no=valuation.patent_application_no,
patent_remaining_years=valuation.patent_remaining_years,
historical_evidence=valuation.historical_evidence, historical_evidence=valuation.historical_evidence,
patent_certificates=valuation.patent_certificates, patent_certificates=valuation.patent_certificates,
pattern_images=valuation.pattern_images, pattern_images=valuation.pattern_images,
application_maturity=valuation.application_maturity, application_maturity=valuation.application_maturity,
implementation_stage=valuation.implementation_stage,
application_coverage=valuation.application_coverage, application_coverage=valuation.application_coverage,
coverage_area=valuation.coverage_area,
cooperation_depth=valuation.cooperation_depth, cooperation_depth=valuation.cooperation_depth,
collaboration_type=valuation.collaboration_type,
offline_activities=valuation.offline_activities, offline_activities=valuation.offline_activities,
offline_teaching_count=valuation.offline_teaching_count,
online_accounts=valuation.online_accounts, online_accounts=valuation.online_accounts,
platform_accounts=valuation.platform_accounts,
sales_volume=valuation.sales_volume, sales_volume=valuation.sales_volume,
link_views=valuation.link_views, link_views=valuation.link_views,
circulation=valuation.circulation, circulation=valuation.circulation,
scarcity_level=valuation.scarcity_level,
last_market_activity=valuation.last_market_activity, last_market_activity=valuation.last_market_activity,
market_activity_time=valuation.market_activity_time,
monthly_transaction=valuation.monthly_transaction, monthly_transaction=valuation.monthly_transaction,
monthly_transaction_amount=valuation.monthly_transaction_amount,
price_fluctuation=valuation.price_fluctuation, price_fluctuation=valuation.price_fluctuation,
price_range=valuation.price_range,
market_price=valuation.market_price,
infringement_record=valuation.infringement_record,
patent_count=valuation.patent_count,
esg_value=valuation.esg_value,
policy_matching=valuation.policy_matching,
online_course_views=valuation.online_course_views,
pattern_complexity=valuation.pattern_complexity,
normalized_entropy=valuation.normalized_entropy,
legal_risk=valuation.legal_risk,
base_pledge_rate=valuation.base_pledge_rate,
flow_correction=valuation.flow_correction,
status=valuation.status, status=valuation.status,
admin_notes=valuation.admin_notes, admin_notes=valuation.admin_notes,
created_at=valuation.created_at, created_at=valuation.created_at,

View File

@ -18,9 +18,12 @@ class ValuationController:
model = ValuationAssessment model = ValuationAssessment
async def create(self, data: ValuationAssessmentCreate) -> ValuationAssessmentOut: async def create(self, data: ValuationAssessmentCreate, user_id: int) -> ValuationAssessmentOut:
"""创建估值评估""" """创建估值评估"""
valuation = await self.model.create(**data.model_dump()) # 将用户ID添加到数据中
create_data = data.model_dump()
create_data['user_id'] = user_id
valuation = await self.model.create(**create_data)
return ValuationAssessmentOut.model_validate(valuation) return ValuationAssessmentOut.model_validate(valuation)
async def get_by_id(self, valuation_id: int) -> Optional[ValuationAssessmentOut]: async def get_by_id(self, valuation_id: int) -> Optional[ValuationAssessmentOut]:
@ -66,7 +69,6 @@ class ValuationController:
# 转换为输出模型 # 转换为输出模型
items = [ValuationAssessmentOut.model_validate(v) for v in valuations] items = [ValuationAssessmentOut.model_validate(v) for v in valuations]
# 计算总页数 # 计算总页数
pages = (total + query.size - 1) // query.size pages = (total + query.size - 1) // query.size

View File

@ -46,6 +46,7 @@ def make_middlewares():
"/api/v1/base/access_token", "/api/v1/base/access_token",
"/docs", "/docs",
"/openapi.json", "/openapi.json",
"/static", # 排除静态文件路径
], ],
), ),
] ]

View File

@ -62,22 +62,31 @@ class HttpAuditLogMiddleware(BaseHTTPMiddleware):
# 获取请求体 # 获取请求体
if request.method in ["POST", "PUT", "PATCH"]: if request.method in ["POST", "PUT", "PATCH"]:
try: # 检查Content-Type来决定如何解析请求体
body = await request.json() content_type = request.headers.get("content-type", "")
args.update(body)
except json.JSONDecodeError: if "multipart/form-data" in content_type or "application/x-www-form-urlencoded" in content_type:
# 处理表单数据(包括文件上传)
try: try:
body = await request.form() # 对于文件上传不要在中间件中消费request.form()
# args.update(body) # 因为这会导致FastAPI无法再次读取请求体
for k, v in body.items(): pass
if hasattr(v, "filename"): # 文件上传行为
args[k] = v.filename
elif isinstance(v, list) and v and hasattr(v[0], "filename"):
args[k] = [file.filename for file in v]
else:
args[k] = v
except Exception: except Exception:
pass pass
elif "application/json" in content_type:
# 处理JSON数据
try:
body = await request.json()
args.update(body)
except (json.JSONDecodeError, UnicodeDecodeError):
pass
else:
# 尝试解析为JSON如果失败则跳过
try:
body = await request.json()
args.update(body)
except (json.JSONDecodeError, UnicodeDecodeError):
pass
return args return args

View File

@ -20,28 +20,53 @@ class ValuationAssessment(Model):
# 非遗等级与技术 # 非遗等级与技术
inheritor_level = fields.CharField(max_length=50, null=True, description="非遗传承人等级") inheritor_level = fields.CharField(max_length=50, null=True, description="非遗传承人等级")
inheritor_ages = fields.JSONField(null=True, description="传承人年龄列表")
inheritor_age_count = fields.JSONField(null=True, description="非遗传承人年龄水平及数量") inheritor_age_count = fields.JSONField(null=True, description="非遗传承人年龄水平及数量")
inheritor_certificates = fields.JSONField(null=True, description="非遗传承人等级证书") inheritor_certificates = fields.JSONField(null=True, description="非遗传承人等级证书")
heritage_level = fields.CharField(max_length=50, null=True, description="非遗等级") heritage_level = fields.CharField(max_length=50, null=True, description="非遗等级")
heritage_asset_level = fields.CharField(max_length=50, null=True, description="非遗资产等级")
patent_application_no = fields.CharField(max_length=100, null=True, description="非遗资产所用专利的申请号") patent_application_no = fields.CharField(max_length=100, null=True, description="非遗资产所用专利的申请号")
patent_remaining_years = fields.CharField(max_length=50, null=True, description="专利剩余年限")
historical_evidence = fields.JSONField(null=True, description="非遗资产历史证明证据及数量") historical_evidence = fields.JSONField(null=True, description="非遗资产历史证明证据及数量")
patent_certificates = fields.JSONField(null=True, description="非遗资产所用专利的证书") patent_certificates = fields.JSONField(null=True, description="非遗资产所用专利的证书")
pattern_images = fields.JSONField(null=True, description="非遗纹样图片") pattern_images = fields.JSONField(null=True, description="非遗纹样图片")
# 非遗应用与推广 # 非遗应用与推广
application_maturity = fields.CharField(max_length=100, null=True, description="非遗资产应用成熟度") application_maturity = fields.CharField(max_length=100, null=True, description="非遗资产应用成熟度")
implementation_stage = fields.CharField(max_length=100, null=True, description="非遗资产应用成熟度")
application_coverage = fields.CharField(max_length=100, null=True, description="非遗资产应用覆盖范围") application_coverage = fields.CharField(max_length=100, null=True, description="非遗资产应用覆盖范围")
coverage_area = fields.CharField(max_length=100, null=True, description="非遗资产应用覆盖范围")
cooperation_depth = fields.CharField(max_length=100, null=True, description="非遗资产跨界合作深度") cooperation_depth = fields.CharField(max_length=100, null=True, description="非遗资产跨界合作深度")
collaboration_type = fields.CharField(max_length=100, null=True, description="非遗资产跨界合作深度")
offline_activities = fields.CharField(max_length=50, null=True, description="近12个月线下相关宣讲活动次数") offline_activities = fields.CharField(max_length=50, null=True, description="近12个月线下相关宣讲活动次数")
offline_teaching_count = fields.IntField(null=True, description="近12个月线下相关演讲活动次数")
online_accounts = fields.JSONField(null=True, description="线上相关宣传账号信息") online_accounts = fields.JSONField(null=True, description="线上相关宣传账号信息")
platform_accounts = fields.JSONField(null=True, description="线上相关宣传账号信息")
# 非遗资产衍生商品信息 # 非遗资产衍生商品信息
sales_volume = fields.CharField(max_length=50, null=True, description="该商品近12个月销售量") sales_volume = fields.CharField(max_length=50, null=True, description="该商品近12个月销售量")
link_views = fields.CharField(max_length=50, null=True, description="该商品近12个月的链接浏览量") link_views = fields.CharField(max_length=50, null=True, description="该商品近12个月的链接浏览量")
circulation = fields.CharField(max_length=50, null=True, description="该商品的发行量") circulation = fields.CharField(max_length=50, null=True, description="该商品的发行量")
scarcity_level = fields.CharField(max_length=50, null=True, description="稀缺等级")
last_market_activity = fields.CharField(max_length=100, null=True, description="该商品最近一次市场活动时间") last_market_activity = fields.CharField(max_length=100, null=True, description="该商品最近一次市场活动时间")
market_activity_time = fields.CharField(max_length=100, null=True, description="市场活动的时间")
monthly_transaction = fields.CharField(max_length=50, null=True, description="月交易额") monthly_transaction = fields.CharField(max_length=50, null=True, description="月交易额")
price_fluctuation = fields.CharField(max_length=100, null=True, description="该商品近30天价格波动区间") monthly_transaction_amount = fields.CharField(max_length=50, null=True, description="月交易额")
price_fluctuation = fields.JSONField(null=True, description="该商品近30天价格波动区间")
price_range = fields.JSONField(null=True, description="资产商品的价格波动率")
market_price = fields.FloatField(null=True, description="市场价格(单位:万元)")
# 内置API计算字段
infringement_record = fields.CharField(max_length=100, null=True, description="侵权记录")
patent_count = fields.CharField(max_length=50, null=True, description="专利使用量")
esg_value = fields.CharField(max_length=50, null=True, description="ESG关联价值")
policy_matching = fields.CharField(max_length=50, null=True, description="政策匹配度")
online_course_views = fields.IntField(null=True, description="线上课程点击量")
pattern_complexity = fields.CharField(max_length=50, null=True, description="结构复杂度")
normalized_entropy = fields.CharField(max_length=50, null=True, description="归一化信息熵")
legal_risk = fields.CharField(max_length=100, null=True, description="法律风险-侵权诉讼历史")
base_pledge_rate = fields.CharField(max_length=50, null=True, description="基础质押率")
flow_correction = fields.CharField(max_length=50, null=True, description="流量修正系数")
# 系统字段 # 系统字段
user = fields.ForeignKeyField("models.AppUser", related_name="valuations", description="提交用户") user = fields.ForeignKeyField("models.AppUser", related_name="valuations", description="提交用户")

View File

@ -28,6 +28,12 @@ class JizhiliaoSearchRequest(BaseAPIRequest):
page: int = Field(1, description="页码") page: int = Field(1, description="页码")
size: int = Field(10, description="每页数量") size: int = Field(10, description="每页数量")
class DifyWorkflowRequest(BaseAPIRequest):
"""Dify工作流请求模型"""
zl_img: str = Field(..., description="资料图片字段")
response_mode: str = Field("blocking", description="响应模式blocking或streaming")
user: str = Field(..., description="用户标识")
class APIResponse(BaseModel): class APIResponse(BaseModel):
"""API响应模型""" """API响应模型"""
success: bool success: bool

View File

@ -1,5 +1,5 @@
from datetime import datetime from datetime import datetime
from typing import List, Optional, Any from typing import List, Optional, Any, Dict, Union
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
@ -13,33 +13,58 @@ class ValuationAssessmentBase(BaseModel):
# 财务状况 # 财务状况
annual_revenue: Optional[str] = Field(None, description="近12个月机构营收/万元") annual_revenue: Optional[str] = Field(None, description="近12个月机构营收/万元")
rd_investment: Optional[str] = Field(None, description="近12个月机构研发投入/万元") rd_investment: Optional[str] = Field(None, description="近12个月机构研发投入/万元")
three_year_income: Optional[List[Any]] = Field(None, description="近三年机构收益/万元") three_year_income: Optional[List[Union[int, float]]] = Field(None, description="近三年机构收益/万元")
funding_status: Optional[str] = Field(None, description="资产受资助情况") funding_status: Optional[str] = Field(None, description="资产受资助情况")
# 非遗等级与技术 # 非遗等级与技术
inheritor_level: Optional[str] = Field(None, description="非遗传承人等级") inheritor_level: Optional[str] = Field(None, description="非遗传承人等级")
inheritor_ages: Optional[List[int]] = Field(None, description="传承人年龄列表")
inheritor_age_count: Optional[List[Any]] = Field(None, description="非遗传承人年龄水平及数量") inheritor_age_count: Optional[List[Any]] = Field(None, description="非遗传承人年龄水平及数量")
inheritor_certificates: Optional[List[Any]] = Field(None, description="非遗传承人等级证书") inheritor_certificates: Optional[List[str]] = Field(None, description="非遗传承人等级证书")
heritage_level: Optional[str] = Field(None, description="非遗等级") heritage_level: Optional[str] = Field(None, description="非遗等级")
heritage_asset_level: Optional[str] = Field(None, description="非遗资产等级")
patent_application_no: Optional[str] = Field(None, description="非遗资产所用专利的申请号") patent_application_no: Optional[str] = Field(None, description="非遗资产所用专利的申请号")
historical_evidence: Optional[List[Any]] = Field(None, description="非遗资产历史证明证据及数量") patent_remaining_years: Optional[str] = Field(None, description="专利剩余年限")
patent_certificates: Optional[List[Any]] = Field(None, description="非遗资产所用专利的证书") historical_evidence: Optional[Dict[str, int]] = Field(None, description="非遗资产历史证明证据及数量")
pattern_images: Optional[List[Any]] = Field(None, description="非遗纹样图片") patent_certificates: Optional[List[str]] = Field(None, description="非遗资产所用专利的证书")
pattern_images: Optional[List[str]] = Field(None, description="非遗纹样图片")
# 非遗应用与推广 # 非遗应用与推广
application_maturity: Optional[str] = Field(None, description="非遗资产应用成熟度") application_maturity: Optional[str] = Field(None, description="非遗资产应用成熟度")
implementation_stage: Optional[str] = Field(None, description="非遗资产应用成熟度")
application_coverage: Optional[str] = Field(None, description="非遗资产应用覆盖范围") application_coverage: Optional[str] = Field(None, description="非遗资产应用覆盖范围")
coverage_area: Optional[str] = Field(None, description="非遗资产应用覆盖范围")
cooperation_depth: Optional[str] = Field(None, description="非遗资产跨界合作深度") cooperation_depth: Optional[str] = Field(None, description="非遗资产跨界合作深度")
collaboration_type: Optional[str] = Field(None, description="非遗资产跨界合作深度")
offline_activities: Optional[str] = Field(None, description="近12个月线下相关宣讲活动次数") offline_activities: Optional[str] = Field(None, description="近12个月线下相关宣讲活动次数")
offline_teaching_count: Optional[int] = Field(None, description="近12个月线下相关演讲活动次数")
online_accounts: Optional[List[Any]] = Field(None, description="线上相关宣传账号信息") online_accounts: Optional[List[Any]] = Field(None, description="线上相关宣传账号信息")
platform_accounts: Optional[Dict[str, Dict[str, int]]] = Field(None, description="线上相关宣传账号信息")
# 非遗资产衍生商品信息 # 非遗资产衍生商品信息
sales_volume: Optional[str] = Field(None, description="该商品近12个月销售量") sales_volume: Optional[str] = Field(None, description="该商品近12个月销售量")
link_views: Optional[str] = Field(None, description="该商品近12个月的链接浏览量") link_views: Optional[str] = Field(None, description="该商品近12个月的链接浏览量")
circulation: Optional[str] = Field(None, description="该商品的发行量") circulation: Optional[str] = Field(None, description="该商品的发行量")
scarcity_level: Optional[str] = Field(None, description="稀缺等级")
last_market_activity: Optional[str] = Field(None, description="该商品最近一次市场活动时间") last_market_activity: Optional[str] = Field(None, description="该商品最近一次市场活动时间")
market_activity_time: Optional[str] = Field(None, description="市场活动的时间")
monthly_transaction: Optional[str] = Field(None, description="月交易额") monthly_transaction: Optional[str] = Field(None, description="月交易额")
price_fluctuation: Optional[str] = Field(None, description="该商品近30天价格波动区间") monthly_transaction_amount: Optional[str] = Field(None, description="月交易额")
price_fluctuation: Optional[List[Union[str, int, float]]] = Field(None, description="该商品近30天价格波动区间")
price_range: Optional[Dict[str, Union[int, float]]] = Field(None, description="资产商品的价格波动率")
market_price: Optional[Union[int, float]] = Field(None, description="市场价格(单位:万元)")
# 内置API计算字段
infringement_record: Optional[str] = Field(None, description="侵权记录")
patent_count: Optional[str] = Field(None, description="专利使用量")
esg_value: Optional[str] = Field(None, description="ESG关联价值")
policy_matching: Optional[str] = Field(None, description="政策匹配度")
online_course_views: Optional[int] = Field(None, description="线上课程点击量")
pattern_complexity: Optional[str] = Field(None, description="结构复杂度")
normalized_entropy: Optional[str] = Field(None, description="归一化信息熵")
legal_risk: Optional[str] = Field(None, description="法律风险-侵权诉讼历史")
base_pledge_rate: Optional[str] = Field(None, description="基础质押率")
flow_correction: Optional[str] = Field(None, description="流量修正系数")
class ValuationAssessmentCreate(ValuationAssessmentBase): class ValuationAssessmentCreate(ValuationAssessmentBase):
@ -83,13 +108,13 @@ class ValuationAssessmentUpdate(BaseModel):
circulation: Optional[str] = Field(None, description="该商品的发行量") circulation: Optional[str] = Field(None, description="该商品的发行量")
last_market_activity: Optional[str] = Field(None, description="该商品最近一次市场活动时间") last_market_activity: Optional[str] = Field(None, description="该商品最近一次市场活动时间")
monthly_transaction: Optional[str] = Field(None, description="月交易额") monthly_transaction: Optional[str] = Field(None, description="月交易额")
price_fluctuation: Optional[str] = Field(None, description="该商品近30天价格波动区间") price_fluctuation: Optional[List[Union[str, int, float]]] = Field(None, description="该商品近30天价格波动区间")
is_active: Optional[bool] = Field(None, description="是否激活") is_active: Optional[bool] = Field(None, description="是否激活")
class ValuationAssessmentOut(ValuationAssessmentBase): class ValuationAssessmentOut(ValuationAssessmentBase):
"""输出估值评估模型""" """估值评估输出模型"""
id: int = Field(..., description="主键ID") id: int = Field(..., description="主键ID")
user_id: int = Field(..., description="用户ID") user_id: int = Field(..., description="用户ID")
status: str = Field(..., description="评估状态") status: str = Field(..., description="评估状态")
@ -100,6 +125,11 @@ class ValuationAssessmentOut(ValuationAssessmentBase):
class Config: class Config:
from_attributes = True from_attributes = True
json_encoders = {
datetime: lambda v: v.isoformat()
}
# 确保所有字段都被序列化包括None值
exclude_none = False
# 用户端专用模式 # 用户端专用模式
@ -108,22 +138,22 @@ class UserValuationCreate(ValuationAssessmentBase):
pass pass
class UserValuationOut(BaseModel): class UserValuationOut(ValuationAssessmentBase):
"""用户端输出估值评估模型""" """用户端估值评估输出模型"""
id: int = Field(..., description="主键ID") id: int = Field(..., description="主键ID")
asset_name: str = Field(..., description="资产名称") user_id: Optional[int] = Field(None, description="用户ID")
institution: str = Field(..., description="所属机构")
industry: str = Field(..., description="所属行业")
status: str = Field(..., description="评估状态") status: str = Field(..., description="评估状态")
admin_notes: Optional[str] = Field(None, description="管理员备注") admin_notes: Optional[str] = Field(None, description="管理员备注")
created_at: datetime = Field(..., description="创建时间") created_at: datetime = Field(..., description="创建时间")
updated_at: datetime = Field(..., description="更新时间") updated_at: datetime = Field(..., description="更新时间")
is_active: Optional[bool] = Field(None, description="是否激活")
class Config: class Config:
from_attributes = True from_attributes = True
json_encoders = { json_encoders = {
datetime: lambda v: v.isoformat() datetime: lambda v: v.isoformat()
} }
exclude_none = False
class UserValuationDetail(ValuationAssessmentBase): class UserValuationDetail(ValuationAssessmentBase):
@ -148,6 +178,14 @@ class UserValuationList(BaseModel):
page: int = Field(..., description="当前页码") page: int = Field(..., description="当前页码")
size: int = Field(..., description="每页数量") size: int = Field(..., description="每页数量")
pages: int = Field(..., description="总页数") pages: int = Field(..., description="总页数")
class Config:
from_attributes = True
json_encoders = {
datetime: lambda v: v.isoformat()
}
exclude_none = False
class UserValuationQuery(BaseModel): class UserValuationQuery(BaseModel):

View File

@ -16,6 +16,11 @@ class Settings(BaseSettings):
CORS_ALLOW_HEADERS: typing.List = ["*"] CORS_ALLOW_HEADERS: typing.List = ["*"]
DEBUG: bool = True DEBUG: bool = True
# 服务器配置
SERVER_HOST: str = "127.0.0.1"
SERVER_PORT: int = 9999
BASE_URL: str = f"http://{SERVER_HOST}:{SERVER_PORT}"
PROJECT_ROOT: str = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) PROJECT_ROOT: str = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
BASE_DIR: str = os.path.abspath(os.path.join(PROJECT_ROOT, os.pardir)) BASE_DIR: str = os.path.abspath(os.path.join(PROJECT_ROOT, os.pardir))

View File

@ -32,7 +32,7 @@ class APIConfig:
"base_url": "https://openapi.chinaz.net", "base_url": "https://openapi.chinaz.net",
"endpoints": { "endpoints": {
"copyright_software": { "copyright_software": {
"api_key": os.getenv("CHINAZ_COPYRIGHT_API_KEY", "YOUR_API_KEY"), "APIKey": os.getenv("CHINAZ_COPYRIGHT_API_KEY", "YOUR_API_KEY"),
"path": "/v1/1036/copyrightsoftware", "path": "/v1/1036/copyrightsoftware",
"method": "POST", "method": "POST",
"description": "企业软件著作权查询", "description": "企业软件著作权查询",
@ -40,7 +40,7 @@ class APIConfig:
"optional_params": ["sign"] "optional_params": ["sign"]
}, },
"patent": { "patent": {
"api_key": os.getenv("CHINAZ_PATENT_API_KEY", "YOUR_API_KEY"), "APIKey": os.getenv("CHINAZ_PATENT_API_KEY", "apiuser_quantity_045e9c832d253a1fdfd41edd1a85a254_4e9f3a38e384414e97fdbd33cca13124"),
"path": "/v1/1036/patent", "path": "/v1/1036/patent",
"method": "POST", "method": "POST",
"description": "企业专利信息查询", "description": "企业专利信息查询",
@ -48,7 +48,7 @@ class APIConfig:
"optional_params": ["sign", "searchType"] "optional_params": ["sign", "searchType"]
}, },
"judgement": { "judgement": {
"api_key": os.getenv("CHINAZ_JUDGEMENT_API_KEY", "YOUR_API_KEY"), "APIKey": os.getenv("CHINAZ_JUDGEMENT_API_KEY", "YOUR_API_KEY"),
"path": "/v1/1036/judgementdetailv4", "path": "/v1/1036/judgementdetailv4",
"method": "POST", "method": "POST",
"description": "司法综合数据查询", "description": "司法综合数据查询",
@ -56,7 +56,7 @@ class APIConfig:
"optional_params": ["sign", "q", "idCardNo", "datatype", "id", "pageNo"] "optional_params": ["sign", "q", "idCardNo", "datatype", "id", "pageNo"]
}, },
"recognition_ocr": { "recognition_ocr": {
"api_key": os.getenv("CHINAZ_OCR_API_KEY", "YOUR_API_KEY"), "APIKey": os.getenv("CHINAZ_OCR_API_KEY", "apiuser_quantity_d0848e65f7b7ae50ff6f1ae37e413586_31466987e2ef402ba3142b72680a92fc"),
"path": "/v1/1024/recognition_ocr", "path": "/v1/1024/recognition_ocr",
"method": "POST", "method": "POST",
"description": "图片OCR识别", "description": "图片OCR识别",
@ -72,184 +72,75 @@ class APIConfig:
"timeout": 30, "timeout": 30,
"retries": 3, "retries": 3,
"endpoints": { "endpoints": {
"xiaohongshu": { "xiaohongshu_note_detail": {
"api_key": os.getenv("XIAOHONGSHU_TOKEN", "YNSbIjdU"), "path": "/api/xiaohongshu/get-note-detail/v7",
"base_url": "https://api.justoneapi.com", "method": "GET",
"timeout": 30, "description": "小红书笔记详情查询",
"retries": 3, "required_params": ["noteId"],
"endpoints": { "optional_params": ["token"]
"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"]
},
"weibo_user_detail": {
"path": "/api/weibo/get-user-detail/v1", # 微博只有粉丝数接口
"method": "GET",
"description": "获取用户信息 包括昵称、头像、用户ID、粉丝数、关注数、简介、认证状态等公开信息",
"required_params": ["token", "uid"],
"optional_params": ["token", "uid"]
},
"weixin_user_detail": {
"path": "/api/weixin/get-user-post/v1",
"method": "GET",
"description": "公众号发布的文章内容,包括标题、作者、发布时间、内容摘要以及阅读数、点赞数与转发数等互动数据",
"required_params": ["token", "wxid"],
"optional_params": ["token", "wxid"]
},
"douyin_video_detail": {
"path": "/api/douyin/get-video-detail/v2",
"method": "GET",
"description": "该接口用于获取指定抖音视频的详细信息,包括视频地址、描述文案、作者信息、发布时间、播放量、点赞数、评论数与分享数等",
"required_params": ["token", "videoId"],
"optional_params": ["token", "videoId"]
},
"kuaishou_video_detail": {
"path": "/api/kuaishou/get-video-detail/v2",
"method": "GET",
"description": "该接口用于获取指定抖音视频的详细信息,包括视频地址、描述文案、作者信息、发布时间、播放量、点赞数、评论数与分享数等",
"required_params": ["token", "videoId"],
"optional_params": ["token", "videoId"]
},
"bilibili_video_detail": {
"path": "/api/bilibili/get-video-detail/v2",
"method": "GET",
"description": "该接口用于获取指定抖音视频的详细信息,包括视频地址、描述文案、作者信息、发布时间、播放量、点赞数、评论数与分享数等",
"required_params": ["token", "bvid"],
"optional_params": ["token", "bvid"]
}
}
}, },
"weibo": { "xiaohongshu_user_info": {
"api_key": os.getenv("XIAOHONGSHU_TOKEN", "YNSbIjdU"), "path": "/api/xiaohongshu/get-user-info/v7",
"base_url": "https://api.justoneapi.com", "method": "GET",
"timeout": 30, "description": "小红书用户信息查询",
"retries": 3, "required_params": ["userId"],
"endpoints": { "optional_params": ["token"]
"weibo_user_detail": {
"path": "/api/weibo/get-user-detail/v1", # 微博只有粉丝数接口
"method": "GET",
"description": "获取用户信息 包括昵称、头像、用户ID、粉丝数、关注数、简介、认证状态等公开信息",
"required_params": ["token", "uid"],
"optional_params": ["token", "uid"]
},
}
}, },
"weixin": { "xiaohongshu_search_notes": {
"api_key": os.getenv("XIAOHONGSHU_TOKEN", "YNSbIjdU"), "path": "/api/xiaohongshu/search-notes/v7",
"base_url": "https://api.justoneapi.com", "method": "GET",
"timeout": 30, "description": "小红书笔记搜索",
"retries": 3, "required_params": ["keyword"],
"endpoints": { "optional_params": ["page", "size", "token"]
"weixin_user_detail": {
"path": "/api/weixin/get-user-post/v1",
"method": "GET",
"description": "公众号发布的文章内容,包括标题、作者、发布时间、内容摘要以及阅读数、点赞数与转发数等互动数据",
"required_params": ["token", "wxid"],
"optional_params": ["token", "wxid"]
}
}
}, },
"douyin": { "weibo_user_detail": {
"api_key": os.getenv("XIAOHONGSHU_TOKEN", "YNSbIjdU"), "path": "/api/weibo/get-user-detail/v1",
"base_url": "https://api.justoneapi.com", "method": "GET",
"timeout": 30, "description": "获取用户信息 包括昵称、头像、用户ID、粉丝数、关注数、简介、认证状态等公开信息",
"retries": 3, "required_params": ["token", "uid"],
"endpoints": { "optional_params": []
"douyin_video_detail": {
"path": "/api/douyin/get-video-detail/v2",
"method": "GET",
"description": "该接口用于获取指定抖音视频的详细信息,包括视频地址、描述文案、作者信息、发布时间、播放量、点赞数、评论数与分享数等",
"required_params": ["token", "videoId"],
"optional_params": ["token", "videoId"]
}
}
}, },
"bilibili": { "weixin_user_detail": {
"api_key": os.getenv("XIAOHONGSHU_TOKEN", "YNSbIjdU"), "path": "/api/weixin/get-user-post/v1",
"base_url": "https://api.justoneapi.com", "method": "GET",
"timeout": 30, "description": "公众号发布的文章内容,包括标题、作者、发布时间、内容摘要以及阅读数、点赞数与转发数等互动数据",
"retries": 3, "required_params": ["token", "wxid"],
"endpoints": { "optional_params": []
"bilibili_video_detail": {
"path": "/api/bilibili/get-video-detail/v2",
"method": "GET",
"description": "该接口用于获取指定抖音视频的详细信息,包括视频地址、描述文案、作者信息、发布时间、播放量、点赞数、评论数与分享数等",
"required_params": ["token", "bvid"],
"optional_params": ["token", "bvid"]
}
}
}, },
"kuaishou": { "douyin_video_detail": {
"api_key": os.getenv("XIAOHONGSHU_TOKEN", "YNSbIjdU"), "path": "/api/douyin/get-video-detail/v2",
"base_url": "https://api.justoneapi.com", "method": "GET",
"timeout": 30, "description": "该接口用于获取指定抖音视频的详细信息,包括视频地址、描述文案、作者信息、发布时间、播放量、点赞数、评论数与分享数等",
"retries": 3, "required_params": ["token", "videoId"],
"endpoints": { "optional_params": []
"kuaishou_video_detail": {
"path": "/api/kuaishou/get-video-detail/v2",
"method": "GET",
"description": "该接口用于获取指定抖音视频的详细信息,包括视频地址、描述文案、作者信息、发布时间、播放量、点赞数、评论数与分享数等",
"required_params": ["token", "videoId"],
"optional_params": ["token", "videoId"]
}
}
}, },
"other_apis": { "kuaishou_video_detail": {
"jizhiliao": { "path": "/api/kuaishou/get-video-detail/v2",
"api_key": os.getenv("JIZHILIAO_API_KEY", "JZL089ef0b7d0315d96"), "method": "GET",
"base_url": "https://api.justoneapi.com", "description": "该接口用于获取指定快手视频的详细信息,包括视频地址、描述文案、作者信息、发布时间、播放量、点赞数、评论数与分享数等",
"timeout": 30, "required_params": ["token", "videoId"],
"retries": 3, "optional_params": []
"verifycode": os.getenv("JIZHILIAO_VERIFYCODE", ""),
"endpoints": {
"index_search": {
"path": "/fbmain/monitor/v3/web_search",
"method": "POST",
"description": "极致聊指数搜索",
"required_params": ["keyword", "mode", "BusinessType", "sub_search_type"],
"optional_params": ["key", "verifycode"]
},
}
},
"other": {
# 可以添加其他第三方API配置
"example_api": {
"api_key": os.getenv("EXAMPLE_API_KEY", ""),
"base_url": "https://api.example.com",
"endpoints": {}
}
}, },
"common_settings": { "bilibili_video_detail": {
"timeout": 60, "path": "/api/bilibili/get-video-detail/v2",
"retries": 3, "method": "GET",
"sign_prefix": "634xz" "description": "该接口用于获取指定B站视频的详细信息包括视频地址、描述文案、作者信息、发布时间、播放量、点赞数、评论数与分享数等",
"required_params": ["token", "bvid"],
"optional_params": []
},
"jizhiliao_index_search": {
"path": "/fbmain/monitor/v3/web_search",
"method": "POST",
"description": "极致聊指数搜索",
"api_key": os.getenv("JIZHILIAO_API_KEY", "JZL089ef0b7d0315d96"),
"verifycode": os.getenv("JIZHILIAO_VERIFYCODE", ""),
"required_params": ["keyword", "mode", "BusinessType", "sub_search_type"],
"optional_params": ["key", "verifycode"]
} }
} }
}
}, },
# 微信指数 # 微信指数
"dajiala": { "dajiala": {
"api_key": "",
"base_url": "https://www.dajiala.com", "base_url": "https://www.dajiala.com",
"timeout": 30, "timeout": 30,
"retries": 3, "retries": 3,
@ -263,6 +154,26 @@ class APIConfig:
} }
} }
}, },
# dify
"dify": {
"base_url": "http://106.14.211.94",
"api_key": "app-FJXEWdKv63oq1F4rHb4I8kvE",
"timeout": 30,
"retries": 3,
"endpoints": {
"workflows_run": {
"path": "/v1/workflows/run",
"method": "POST",
"description": "运行工作流",
"required_params": ["inputs", "response_mode", "user"],
"optional_params": [],
"headers": {
"Authorization": "Bearer {api_key}",
"Content-Type": "application/json"
}
}
}
}
} }
# 尝试从文件加载配置 # 尝试从文件加载配置

View File

@ -1,8 +1,7 @@
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Optional from typing import Optional
import jwt import jwt
from fastapi import HTTPException, status, Depends from fastapi import HTTPException, status, Depends, Header
from fastapi.security import HTTPBearer
from app.controllers.app_user import app_user_controller from app.controllers.app_user import app_user_controller
from app.schemas.app_user import AppUserJWTPayload from app.schemas.app_user import AppUserJWTPayload
from app.settings import settings from app.settings import settings
@ -12,8 +11,6 @@ SECRET_KEY = settings.SECRET_KEY
ALGORITHM = "HS256" ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 60 * 24 * 7 # 7天 ACCESS_TOKEN_EXPIRE_MINUTES = 60 * 24 * 7 # 7天
security = HTTPBearer()
def create_app_user_access_token(user_id: int, phone: str) -> str: def create_app_user_access_token(user_id: int, phone: str) -> str:
""" """
@ -51,7 +48,7 @@ def verify_app_user_token(token: str) -> Optional[AppUserJWTPayload]:
return None return None
def get_current_app_user_id(token: str = Depends(security)) -> int: def get_current_app_user_id(token: str = Header(None)) -> int:
""" """
从令牌中获取当前AppUser ID 从令牌中获取当前AppUser ID
""" """
@ -61,7 +58,10 @@ def get_current_app_user_id(token: str = Depends(security)) -> int:
headers={"WWW-Authenticate": "Bearer"}, headers={"WWW-Authenticate": "Bearer"},
) )
payload = verify_app_user_token(token.credentials) if not token:
raise credentials_exception
payload = verify_app_user_token(token)
if payload is None: if payload is None:
raise credentials_exception raise credentials_exception

View File

@ -0,0 +1,9 @@
"""
动态质押率DPR计算包
动态质押率DPR=基础质押率*(1+ 流量修正系数)+ 政策加成系数- 流动性调节因子
"""
from .sub_formulas.dynamic_pledge_rate import DynamicPledgeRateCalculator
__all__ = [
"DynamicPledgeRateCalculator"
]

View File

@ -0,0 +1,7 @@
from .dynamic_pledge_rate import DynamicPledgeRateCalculator
__all__ = [
"DynamicPledgeRateCalculator"
]

View File

@ -0,0 +1,106 @@
"""
动态质押率DPR计算模块
动态质押率DPR=基础质押率*(1+ 流量修正系数)+ 政策加成系数- 流动性调节因子
"""
class DynamicPledgeRateCalculator:
"""DRP计算器"""
def __init__(self):
pass
def calculate_dynamic_pledge_rate(self,
monthly_amount: float,
heritage_level: str,
) -> float:
"""
动态质押率 DPR 计算公式
DPR = 基础质押率 * (1 + 流量修正系数) + 政策加成系数 - 流动性调节因子
参数:
monthly_amount (float): 月交易额万元
heritage_level (str): 非遗等级
liquidity_adjustment (float): 流动性调节因子例如 0.05 表示5%
固定参数:
基础质押率 = 0.5
流量修正系数 = 0.3
"""
base_rate = 0.5
flow_correction = 0.3
# ① 交易额得分影响可嵌入流量修正或单独展示,这里保持原式不变
policy_bonus = self.get_heritage_level_score(heritage_level)
liquidity_adjustment = self.get_monthly_transaction_score(monthly_amount)
dpr = base_rate * (1 + flow_correction) + policy_bonus - liquidity_adjustment
return round(dpr, 4)
def get_heritage_level_score(self, level: str) -> float:
"""
根据用户所选的非遗等级匹配系数分数
规则
国家级非遗 => +0.05
省级非遗 => +0.03
纳入国家文化数字化战略清单 => +0.02
=> 0.0
参数:
level (str): 用户选择的非遗等级描述
返回:
float: 对应的分数
"""
level = level.strip()
if "国家级" in level:
return 0.05
elif "省级" in level:
return 0.03
elif "国家文化数字化战略清单" in level:
return 0.02
else:
return 0.0
def get_monthly_transaction_score(self, monthly_amount: float) -> float:
"""
根据用户月交易额区间匹配评分
规则
月交易额 < 100 => -0.1
月交易额 [100, 500) => 0
月交易额 500 => +0.15
参数:
monthly_amount (float): 月交易额单位万元
返回:
float: 对应的评分
"""
if monthly_amount < 100:
return -0.1
elif monthly_amount < 500:
return 0.0
else:
return 0.15
# 示例使用
if __name__ == "__main__":
# 创建计算器实例
calculator = DynamicPledgeRateCalculator()
# 示例1国家级非遗月交易额500万流动性调节因子0.05
print(calculator.calculate_dynamic_pledge_rate(500, "国家级非遗"))
# 结果0.55
# 示例2省级非遗流动性调节因子0.02
print(calculator.calculate_dynamic_pledge_rate(300, "省级非遗"))
# 结果0.68
# 示例3无非遗流动性调节因子0.1
print(calculator.calculate_dynamic_pledge_rate(50, ""))
# 0.75

View File

@ -53,7 +53,14 @@ class UniversalAPIManager:
endpoint_config = self._get_endpoint_config(provider, endpoint) endpoint_config = self._get_endpoint_config(provider, endpoint)
if not endpoint_config: if not endpoint_config:
return None return None
return endpoint_config.get('api_key')
# 优先从端点配置中获取APIKey
if 'APIKey' in endpoint_config:
return endpoint_config['APIKey']
# 如果端点配置中没有则从提供商配置中获取api_key
provider_config = self._get_provider_config(provider)
return provider_config.get('api_key')
def make_request(self, provider: str, endpoint: str, params: Dict[str, Any], timeout: Optional[int] = None) -> Dict[str, Any]: def make_request(self, provider: str, endpoint: str, params: Dict[str, Any], timeout: Optional[int] = None) -> Dict[str, Any]:
""" """
@ -89,7 +96,7 @@ class UniversalAPIManager:
# 添加API密钥到参数中 # 添加API密钥到参数中
params['APIKey'] = api_key params['APIKey'] = api_key
print(params)
# 发送请求 # 发送请求
try: try:
response = self.session.request( response = self.session.request(
@ -156,6 +163,17 @@ class UniversalAPIManager:
# 对于GET请求将参数添加到URL中 # 对于GET请求将参数添加到URL中
query_string = urllib.parse.urlencode(params) query_string = urllib.parse.urlencode(params)
full_url = f"{full_url}?{query_string}" full_url = f"{full_url}?{query_string}"
elif provider == 'chinaz' and method.upper() == 'POST':
# 对于Chinaz POST请求只将APIKey和ChinazVer添加到URL中
url_params = {}
if 'APIKey' in params:
url_params['APIKey'] = params['APIKey']
if 'ChinazVer' in params:
url_params['ChinazVer'] = params['ChinazVer']
if url_params:
query_string = urllib.parse.urlencode(url_params)
full_url = f"{full_url}?{query_string}"
return full_url return full_url
@ -176,6 +194,9 @@ class UniversalAPIManager:
missing_params = [] missing_params = []
for param in required_params: for param in required_params:
# 如果参数在端点配置中已经定义如APIKey则跳过验证
if param in endpoint_config:
continue
if param not in params or params[param] is None: if param not in params or params[param] is None:
missing_params.append(param) missing_params.append(param)
@ -195,6 +216,7 @@ class UniversalAPIManager:
Dict[str, Any]: 处理后的参数 Dict[str, Any]: 处理后的参数
""" """
provider_config = self._get_provider_config(provider) provider_config = self._get_provider_config(provider)
endpoint_config = self._get_endpoint_config(provider, endpoint)
prepared_params = params.copy() prepared_params = params.copy()
# 根据不同的API提供商添加特定参数 # 根据不同的API提供商添加特定参数
@ -202,6 +224,10 @@ class UniversalAPIManager:
# 站长之家API需要签名 # 站长之家API需要签名
if 'sign' not in prepared_params: if 'sign' not in prepared_params:
prepared_params['sign'] = self._generate_chinaz_sign() prepared_params['sign'] = self._generate_chinaz_sign()
# 添加APIKey参数
if 'APIKey' not in prepared_params and endpoint_config and 'APIKey' in endpoint_config:
prepared_params['APIKey'] = endpoint_config['APIKey']
elif provider == 'xiaohongshu': elif provider == 'xiaohongshu':
# 小红书接口使用 JustOneAPI 平台,需要 token 参数 # 小红书接口使用 JustOneAPI 平台,需要 token 参数
@ -228,6 +254,11 @@ class UniversalAPIManager:
if default_verifycode is not None: if default_verifycode is not None:
prepared_params['verifycode'] = default_verifycode prepared_params['verifycode'] = default_verifycode
elif provider == 'dify':
# Dify API需要在请求头中设置Authorization
# 这里不需要修改paramsAuthorization会在make_request中处理
pass
return prepared_params return prepared_params
@ -264,16 +295,38 @@ class UniversalAPIManager:
# 构建URL # 构建URL
url = self._build_url(provider, endpoint, prepared_params) url = self._build_url(provider, endpoint, prepared_params)
logger.info(f"发送{method}请求到: {url}")
# 发送请求 # 发送请求
for attempt in range(retries + 1): for attempt in range(retries + 1):
try: try:
if method == 'GET': if method == 'GET':
response = self.session.get(url, timeout=timeout) response = self.session.get(url, timeout=timeout)
elif method == 'POST': elif method == 'POST':
response = self.session.post(url, json=prepared_params, timeout=timeout) if provider == 'chinaz':
# 对于Chinaz API从prepared_params中移除URL参数只发送body参数
body_params = prepared_params.copy()
body_params.pop('APIKey', None)
body_params.pop('ChinazVer', None)
# Chinaz API使用application/x-www-form-urlencoded格式
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
response = self.session.post(url, data=body_params, headers=headers, timeout=timeout)
print(response.json())
elif provider == 'dify':
# 对于Dify API需要设置Authorization头
provider_config = self._get_provider_config(provider)
endpoint_config = self._get_endpoint_config(provider, endpoint)
# 获取API密钥
api_key = provider_config.get('api_key', 'app-FJXEWdKv63oq1F4rHb4I8kvE')
# 设置请求头
headers = {
'Authorization': f'Bearer {api_key}',
'Content-Type': 'application/json'
}
response = self.session.post(url, json=prepared_params, headers=headers, timeout=timeout)
else:
response = self.session.post(url, json=prepared_params, timeout=timeout)
else: else:
raise ValueError(f"不支持的HTTP方法: {method}") raise ValueError(f"不支持的HTTP方法: {method}")
@ -349,7 +402,7 @@ class UniversalAPIManager:
'searchKey': company_name, 'searchKey': company_name,
'pageNo': '1', 'pageNo': '1',
'range': '100', 'range': '100',
'searchType': '0', 'searchType': '1',
'ChinazVer': chinaz_ver 'ChinazVer': chinaz_ver
} }
return self.make_request('chinaz', 'patent', params) return self.make_request('chinaz', 'patent', params)

4551
app/utils/专利.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -372,7 +372,7 @@ def run_tests():
test_change_password() test_change_password()
# 测试登出 # 测试登出
test_logout() # test_logout()
print("\n===== 所有测试通过 =====") print("\n===== 所有测试通过 =====")
except AssertionError as e: except AssertionError as e:

View File

@ -37,5 +37,5 @@
export DOCKER_DEFAULT_PLATFORM=linux/amd64 export DOCKER_DEFAULT_PLATFORM=linux/amd64
docker build -t zfc931912343/guzhi-fastapi-admin:v1.1 . docker build -t zfc931912343/guzhi-fastapi-admin:v1.2 .
docker push zfc931912343/guzhi-fastapi-admin:v1.1 docker push zfc931912343/guzhi-fastapi-admin:v1.2