This commit is contained in:
若拙_233 2025-10-10 01:29:40 +08:00
commit 513e6ab0e4
50 changed files with 4985 additions and 10091 deletions

54
.github/copilot-instructions.md vendored Normal file
View File

@ -0,0 +1,54 @@
## 项目(快速)指导 — 供 AI 编码代理使用
下面的要点帮助你快速理解并在本代码库中高效工作。保持简短、具体并以可执行示例为主。
- 项目类型FastAPI 后端 (Python 3.11) + Vue3/Vite 前端(目录 `web/`)。后端使用 Tortoise ORM配置在 `app/settings/config.py`),前端用 pnpm/vite。
- 快速启动(后端):在项目根目录
- 建议 Python venv然后安装依赖`pip install -r requirements.txt`(或使用项目 README 中的 uv/uvenv 过程)。
- 启动:`python run.py`。这会通过 `uvicorn` 运行 `app:app`(见 `run.py`),开启 `reload=True`OpenAPI 在 `/docs`
- 快速启动(前端):进入 `web/`,使用 pnpm或 npm安装并运行`pnpm i``pnpm dev`
- 后端关键入口
- `run.py`:应用启动脚本,设置 uvicorn 日志格式并运行 `app:app`
- `app/__init__.py`:创建 FastAPI app调用 `core/init_app.py` 中的注册函数init 数据、注册中间件、异常处理与路由(路由前缀为 `/api`)。
- `app/core/init_app.py`(注意:此文件包含启动时的路由/中间件/异常注册逻辑,请优先阅读它来理解请求生命周期)。
- 重要配置点
- `app/settings/config.py`:使用 Pydantic Settings包含 `TORTOISE_ORM`(默认 SQLitedb 文件在项目根 `db.sqlite3`、JWT、SECRET_KEY、CORS 等。修改环境变量即可覆盖设置。
- `app/utils/api_config.py`:提供 `api_config` 全局实例,用来存放第三方 API示例`chinaz``xiaohongshu`)。常用方法:`api_config.get_api_key(provider)``get_endpoint_config(provider, endpoint)``add_endpoint(...)``save_config()`
- 路由与模块约定
- API 版本化:`app/api/v1/` 下放置 v1 接口。路由统一由 `core/init_app.py` 通过 `register_routers(..., prefix='/api')` 注册。
- 控制器HTTP handlers位于 `app/controllers/`,数据模型在 `app/models/`Pydantic schemas 在 `app/schemas/`
- 数据库与迁移
- 使用 Tortoise ORM`TORTOISE_ORM``app/settings/config.py`。项目把 `aerich.models` 列入 models见配置repository 中存在 `migrations/` 文件夹。若需变更模型,按项目现有工具链(如 aerich执行迁移在不确定时先检查 `pyproject.toml`/`requirements.txt` 是否包含 aerich 并复核 README。
- 日志与持久化
- 日志目录:`app/logs`(可在 `settings.LOGS_ROOT` 找到)。运行时可根据 `run.py` 中的 LOGGING_CONFIG 调整格式。
- 第三方 API 集成(示例)
- `api_config` 示例用法Python:
```py
from app.utils.api_config import api_config
cfg = api_config.get_endpoint_config('xiaohongshu', 'xiaohongshu_note_detail')
base = api_config.get_base_url('xiaohongshu')
key = api_config.get_api_key('xiaohongshu')
```
- 环境变量覆盖CHINAZ_API_KEY、XIAOHONGSHU_TOKEN、EXAMPLE_API_KEY 等会被 `api_config` 或 settings 读取。
- 编辑/贡献约定(可自动推断的现有模式)
- 新增 API`app/api/v1/...` 添加路由模块,控制器放 `app/controllers/`schema 放 `app/schemas/`,并在 `core/init_app.py` 中确保路由被注册。
- 新增模型:更新 `app/models/` 并生成迁移(项目使用 Tortoise + aerich 风格)。先检查 `migrations/models` 是否有对应变更。
- 调试提示
- 本地运行时使用 `python run.py`reload=True然后访问 `http://localhost:9999/docs` 查看 OpenAPI确认路由/依赖注入是否按预期工作。
- 常见故障点:环境变量未设置(导致 API keys 丢失、Tortoise 连接配置错误(检查 `TORTOISE_ORM.connections`)、以及中间件注册顺序会影响异常处理。
- 其它注意事项(小而具体)
- 前端以 `/api` 为后端前缀,修改后端接口时请同步前端 `web/src/api` 的调用。
- `app/utils/api_config.py` 会在模块导入时创建 `api_config` 单例;修改该文件时注意导入时机(不要在模块顶层做阻塞网络调用)。
如果需要我把 README 中的启动说明转成更精确的 shell 命令(或添加 aerich 的迁移示例命令),我可以继续补充。请告诉我你希望强调的额外部分或需要澄清的地方。

3
.gitignore vendored
View File

@ -11,6 +11,7 @@ db.sqlite3
db.sqlite3-journal
db.sqlite3-shm
db.sqlite3-wal
test-api.py
test_api.py
.DS_Store
._.DS_Store

View File

@ -2,7 +2,7 @@ FROM node:18.12.0-alpine3.16 AS web
WORKDIR /opt/vue-fastapi-admin
COPY /web ./web
RUN cd /opt/vue-fastapi-admin/web && npm i --registry=https://registry.npmmirror.com && npm run build
RUN npm install -g pnpm && cd /opt/vue-fastapi-admin/web && pnpm install --registry=https://registry.npmmirror.com && pnpm run build
FROM python:3.11-slim-bullseye

View File

@ -1,6 +1,7 @@
from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from tortoise import Tortoise
from app.core.exceptions import SettingNotFound
@ -33,6 +34,8 @@ def create_app() -> FastAPI:
middleware=make_middlewares(),
lifespan=lifespan,
)
# 注册静态文件目录
app.mount("/static", StaticFiles(directory="app/static"), name="static")
register_exceptions(app)
register_routers(app, prefix="/api")
return app

View File

@ -1,6 +1,7 @@
from fastapi import APIRouter
from fastapi import APIRouter, Depends
from app.core.dependency import DependPermission
from app.core.dependency import DependAuth, DependPermission
from app.utils.app_user_jwt import get_current_app_user
from .apis import apis_router
from .app_users import app_users_router
@ -15,6 +16,7 @@ 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 .upload import router as upload_router
from .users import users_router
from .valuations import router as valuations_router
@ -23,15 +25,20 @@ v1_router = APIRouter()
v1_router.include_router(base_router, prefix="/base")
v1_router.include_router(app_users_router, prefix="/app-user") # AppUser路由无需权限依赖
v1_router.include_router(app_valuations_router, prefix="/app-valuations") # 用户端估值评估路由,需要认证
v1_router.include_router(users_router, prefix="/user", dependencies=[DependPermission])
v1_router.include_router(roles_router, prefix="/role", dependencies=[DependPermission])
v1_router.include_router(menus_router, prefix="/menu", dependencies=[DependPermission])
v1_router.include_router(apis_router, prefix="/api", dependencies=[DependPermission])
v1_router.include_router(depts_router, prefix="/dept", dependencies=[DependPermission])
v1_router.include_router(auditlog_router, prefix="/auditlog", dependencies=[DependPermission])
v1_router.include_router(esg_router, prefix="/esg", dependencies=[DependPermission])
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])
v1_router.include_router(valuations_router, prefix="/valuations", dependencies=[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(menus_router, prefix="/menu", dependencies=[DependAuth, DependPermission])
v1_router.include_router(apis_router, prefix="/api", dependencies=[DependAuth, DependPermission])
v1_router.include_router(depts_router, prefix="/dept", dependencies=[DependAuth, DependPermission])
v1_router.include_router(auditlog_router, prefix="/auditlog", dependencies=[DependAuth, DependPermission])
v1_router.include_router(esg_router, prefix="/esg")
v1_router.include_router(index_router, prefix="/index")
v1_router.include_router(industry_router, prefix="/industry")
v1_router.include_router(policy_router, prefix="/policy")
v1_router.include_router(upload_router, prefix="/upload") # 文件上传路由
v1_router.include_router(
third_party_api_router,
prefix="/third_party_api",
dependencies=[DependAuth, DependPermission],
)
v1_router.include_router(valuations_router, prefix="/valuations", dependencies=[DependAuth, DependPermission])

View File

View File

@ -0,0 +1,325 @@
from fastapi import APIRouter, Depends, HTTPException, status
from typing import Optional, List, Dict, Any
import json
import asyncio
import time
from app.controllers.user_valuation import user_valuation_controller
from app.schemas.valuation import (
UserValuationCreate,
UserValuationQuery,
UserValuationList,
UserValuationOut,
UserValuationDetail
)
from app.schemas.base import Success, SuccessExtra
from app.utils.app_user_jwt import get_current_app_user_id
from app.utils.calculation_engine import FinalValueACalculator
from app.utils.calculation_engine.economic_value_b1.sub_formulas.basic_value_b11 import calculate_popularity_score, \
calculate_infringement_score, calculate_patent_usage_score
from app.utils.calculation_engine.economic_value_b1.sub_formulas.traffic_factor_b12 import calculate_search_index_s1
from app.log.log import logger
from app.models.esg import ESG
from app.models.industry import Industry
from app.models.policy import Policy
from app.utils.universal_api_manager import universal_api
app_valuations_router = APIRouter(tags=["用户端估值评估"])
@app_valuations_router.post("/calculation", summary="计算估值")
async def calculate_valuation(
data: UserValuationCreate,
user_id: int = Depends(get_current_app_user_id)
):
"""
计算估值评估
根据用户提交的估值评估数据调用计算引擎进行经济价值B1计算
"""
try:
start_ts = time.monotonic()
logger.info("valuation.calc_start user_id={} asset_name={} industry={}", user_id, getattr(data, 'asset_name', None), getattr(data, 'industry', None))
# 根据行业查询 ESG 基准分(优先用行业名称匹配,如用的是行业代码就把 name 改成 code
esg_obj = None
industry_obj = None
policy_obj = None
try:
esg_obj = await asyncio.wait_for(ESG.filter(name=data.industry).first(), timeout=2.0)
except Exception as e:
logger.warning("valuation.esg_fetch_timeout industry={} err={}", data.industry, repr(e))
esg_score = float(getattr(esg_obj, 'number', 0.0) or 0.0)
# 根据行业查询 行业修正系数与ROE
try:
industry_obj = await asyncio.wait_for(Industry.filter(name=data.industry).first(), timeout=2.0)
except Exception as e:
logger.warning("valuation.industry_fetch_timeout industry={} err={}", data.industry, repr(e))
fix_num_score = getattr(industry_obj, 'fix_num', 0.0) or 0.0
# 根据行业查询 政策匹配度
try:
policy_obj = await asyncio.wait_for(Policy.filter(name=data.industry).first(), timeout=2.0)
except Exception as e:
logger.warning("valuation.policy_fetch_timeout industry={} err={}", data.industry, repr(e))
policy_match_score = getattr(policy_obj, 'score', 0.0) or 0.0
# 提取 经济价值B1 计算参数
input_data_by_b1 = await _extract_calculation_params_b1(data)
# ESG关联价值 ESG分 (0-10分)
input_data_by_b1["esg_score"] = esg_score
# 行业修正系数I
input_data_by_b1["industry_coefficient"] = fix_num_score
# 政策匹配度
input_data_by_b1["policy_match_score"] = policy_match_score
# 获取专利信息 TODO 参数
try:
patent_data = universal_api.query_patent_info("未找到 企业名称、企业统代、企业注册号")
patent_dict = patent_data if isinstance(patent_data, dict) else {}
inner_data = patent_dict.get("data", {}) if isinstance(patent_dict.get("data", {}), dict) else {}
data_list = inner_data.get("dataList", [])
data_list = data_list if isinstance(data_list, list) else []
# 验证 专利剩余年限
# TODO 无法验证 专利剩余保护期>10年(10分)5-10年(7分)<5年(3分)
# 发展潜力D相关参数 专利数量
# 查询匹配申请号的记录集合
matched = [item for item in data_list if isinstance(item, dict) and item.get("SQH") == getattr(data, 'patent_application_no', None)]
if matched:
patent_count = calculate_patent_usage_score(len(matched))
input_data_by_b1["patent_count"] = float(patent_count)
else:
input_data_by_b1["patent_count"] = 0.0
except Exception as e:
logger.warning("valuation.patent_api_error err={}", repr(e))
input_data_by_b1["patent_count"] = 0.0
# 提取 文化价值B2 计算参数
input_data_by_b2 = await _extract_calculation_params_b2(data)
# 提取 风险调整系数B3 计算参数
input_data_by_b3 = await _extract_calculation_params_b3(data)
# 提取 市场估值C 参数
input_data_by_c = await _extract_calculation_params_c(data)
input_data = {
# 模型估值B 相关参数
"model_data": {
# 经济价值B1 参数
"economic_data": input_data_by_b1,
# 文化价值B2 参数
"cultural_data": input_data_by_b2,
# 风险调整参数 B3
"risky_data": input_data_by_b3,
},
# 市场估值C 参数
"market_data": input_data_by_c,
}
calculator = FinalValueACalculator()
# 计算最终估值A统一计算
calculation_result = calculator.calculate_complete_final_value_a(input_data)
# 结构化日志:关键分值
try:
duration_ms = int((time.monotonic() - start_ts) * 1000)
logger.info(
"valuation.calc_done user_id={} duration_ms={} model_value_b={} market_value_c={} final_value_ab={}",
user_id,
duration_ms,
calculation_result.get('model_value_b'),
calculation_result.get('market_value_c'),
calculation_result.get('final_value_ab'),
)
except Exception:
pass
# 创建估值评估记录
result = await user_valuation_controller.create_valuation(
user_id=user_id,
data=data
)
# 组装返回
result_dict = json.loads(result.model_dump_json())
result_dict['calculation_result'] = calculation_result
result_dict['calculation_input'] = {
'model_data': {
'economic_data': list(input_data.get('model_data', {}).get('economic_data', {}).keys()),
'cultural_data': list(input_data.get('model_data', {}).get('cultural_data', {}).keys()),
'risky_data': list(input_data.get('model_data', {}).get('risky_data', {}).keys()),
},
'market_data': list(input_data.get('market_data', {}).keys()),
}
return Success(data=result_dict, msg="估值计算完成")
except Exception as e:
logger.error("valuation.calc_failed user_id={} err={}", user_id, repr(e))
raise HTTPException(status_code=400, detail=f"计算失败: {str(e)}")
async def _extract_calculation_params_b1(data: UserValuationCreate) -> Dict[str, Any]:
"""
从用户提交的数据中提取计算所需的参数
Args:
data: 用户提交的估值评估数据
Returns:
Dict: 计算所需的参数字典
"""
# 基础价值B11相关参数
# 财务价值所需数据 从近三年收益计算
three_year_income = data.three_year_income or [0, 0, 0]
# 法律强度L相关参数
# 普及地域分值 默认 7分
popularity_score = calculate_popularity_score(data.application_coverage)
# 侵权分 默认 6 TODO 需要使用第三方API 无侵权记录(10分),历史侵权已解决(6分),现存纠纷(2分)
infringement_score = calculate_infringement_score("无侵权信息")
infringement_score = 0
# 创新投入比 = (研发费用/营收) * 100
try:
rd_investment = float(data.rd_investment or 0)
annual_revenue = float(data.annual_revenue or 1) # 避免除零
innovation_ratio = (rd_investment / annual_revenue) * 100 if annual_revenue > 0 else 0
except (ValueError, TypeError):
innovation_ratio = 0.0
# 流量因子B12相关参数
# 近30天搜索指数S1 - 从社交媒体数据计算 TODO 需要使用第三方API
baidu_index = "暂无"
wechat_index = universal_api.wx_index(data.asset_name) # 通过资产信息获取微信指数 TODO 这里返回的没确认指数参数,有可能返回的图示是指数信息
weibo_index = "暂无"
search_index_s1 = calculate_search_index_s1(baidu_index,wechat_index,weibo_index) # 默认值实际应从API获取
# 行业均值S2 TODO 系统内置 未找到相关内容
industry_average_s2 = 0.0
# 社交媒体传播度S3 - TODO 需要使用第三方API,click_count view_count 未找到对应参数
# likes: 点赞数(API获取)
# comments: 评论数(API获取)
# shares: 转发数(API获取)
# followers: 粉丝数
# click_count: 商品链接点击量(用户填写) sales_volume 使用 sales_volume 销售量
# view_count: 内容浏览量(用户填写) link_views
# 政策乘数B13相关参数
# 政策契合度评分P - 根据行业和资助情况计算
# 实施阶段
implementation_stage = data.application_maturity
# 资金支持
funding_support = data.funding_status
return {
# 基础价值B11相关参数
'three_year_income': three_year_income,
'popularity_score': popularity_score,
'infringement_score': infringement_score,
'innovation_ratio': innovation_ratio,
# 流量因子B12相关参数
'search_index_s1': search_index_s1,
'industry_average_s2': industry_average_s2,
# 'social_media_spread_s3': social_media_spread_s3,
# 这些社交数据暂未来源置为0后续接入API再填充
'likes': 0,
'comments': 0,
'shares': 0,
# followers 非当前计算用键,先移除避免干扰
# click_count 与 view_count 目前未参与计算,先移除
# 政策乘数B13相关参数
'implementation_stage': implementation_stage,
'funding_support':funding_support
}
# 获取 文化价值B2 相关参数
async def _extract_calculation_params_b2(data: UserValuationCreate) -> Dict[str, Any]:
"""
argrg:
data: 用户提交的估值评估数据
retus:
Dict: 计算所需的参数字典
"""
# 活态传承系数B21 县官参数
inheritor_level_coefficient = data.inheritor_level # 传承人等级
offline_sessions = int(data.offline_activities) #线下传习次数
# 以下调用API douyin\bilibili\kuaishou
douyin_views = 0
kuaishou_views= 0
bilibili_views= 0
# 跨界合作深度 品牌联名0.3科技载体0.5国家外交礼品1.0
cross_border_depth = float(data.cooperation_depth)
# 纹样基因值B22相关参数
# 以下三项需由后续模型/服务计算;此处提供默认可计算占位
historical_inheritance = 0.8
structure_complexity = 0.75
normalized_entropy = 0.85
return {
"inheritor_level_coefficient": inheritor_level_coefficient,
"offline_sessions": offline_sessions,
"douyin_views": douyin_views,
"kuaishou_views": kuaishou_views,
"bilibili_views": bilibili_views,
"cross_border_depth": cross_border_depth,
"historical_inheritance": historical_inheritance,
"structure_complexity": structure_complexity,
"normalized_entropy": normalized_entropy,
}
# 获取 文化价值B2 相关参数
async def _extract_calculation_params_b3(data: UserValuationCreate) -> Dict[str, Any]:
# 过去30天最高价格 过去30天最低价格 TODO 需要根据字样进行切分获取最高价和最低价 转换成 float 类型
highest_price,lowest_price= data.price_fluctuation
lawsuit_status = "无诉讼" # 诉讼状态 TODO (API获取)
inheritor_ages = data.inheritor_age_count # [45, 60, 75] # 传承人年龄列表
return {
"highest_price": highest_price,
"lowest_price": lowest_price,
"lawsuit_status": lawsuit_status,
"inheritor_ages": inheritor_ages,
}
# 获取 市场估值C 相关参数
async def _extract_calculation_params_c(data: UserValuationCreate) -> Dict[str, Any]:
# 市场竞价C1 TODO 暂无
# transaction_data: 交易数据字典(API获取)
# manual_bids: 手动收集的竞价列表(用户填写)
# expert_valuations: 专家估值列表(系统配置)
transaction_data: Dict = None
manual_bids: List[float] = None
expert_valuations: List[float] = None
# 浏览热度分 TODO 需要先确定平台信息
daily_browse_volume = 500.0 # 近7日日均浏览量(默认占位)
collection_count = 50 # 收藏数(默认占位)
# 稀缺性乘数C3 发行量
circulation = data.circulation or '限量'
# 时效性衰减C4 参数 用户选择距离最近一次市场活动(交易、报价、评估)的相距时间
recent_market_activity = data.last_market_activity or '2024-01-15'
return {
# 计算市场竞价C1
# C1 的实现接受 transaction_data={'weighted_average_price': x}
"weighted_average_price": transaction_data,
"manual_bids": manual_bids, # 手动收集的竞价列表 (用户填写)
"expert_valuations": expert_valuations, # 专家估值列表 (系统配置)
# 计算热度系数C2
"daily_browse_volume": daily_browse_volume, # 近7日日均浏览量 (API获取)
"collection_count": collection_count, # 收藏数
"issuance_level": circulation, # 默认 限量发行 计算稀缺性乘数C3
"recent_market_activity": recent_market_activity, # 默认 '近一月' 计算市场估值C
}

View File

@ -8,7 +8,9 @@ from app.schemas.base import Success, Fail
from app.schemas.third_party_api import (
BaseAPIRequest,
ChinazAPIRequest,
OCRRequest,
XiaohongshuNoteRequest,
JizhiliaoSearchRequest,
APIResponse,
APIProviderInfo,
APIEndpointInfo,
@ -38,8 +40,7 @@ async def query_copyright_software(request: ChinazAPIRequest):
logger.error(f"查询企业软件著作权失败: {e}")
return Fail(message=f"查询企业软件著作权失败: {str(e)}")
@router.post("/chinaz/patent_info", summary="企业专利信息查询")
@router.post("/chinaz/patent", summary="企业专利信息查询")
async def query_patent_info(request: ChinazAPIRequest):
"""查询企业专利信息"""
try:
@ -56,13 +57,12 @@ async def query_patent_info(request: ChinazAPIRequest):
logger.error(f"查询企业专利信息失败: {e}")
return Fail(message=f"查询企业专利信息失败: {str(e)}")
@router.post("/chinaz/judicial_data", summary="司法综合数据查询")
async def query_judicial_data(request: ChinazAPIRequest):
"""查询司法综合数据"""
@router.post("/chinaz/ocr", summary="OCR图片识别")
async def recognize_ocr(request: OCRRequest):
"""OCR图片识别接口"""
try:
result = await third_party_api_controller.query_judicial_data(
company_name=request.company_name,
result = await third_party_api_controller.recognize_ocr(
image_url=request.url,
chinaz_ver=request.chinaz_ver
)
@ -71,11 +71,11 @@ async def query_judicial_data(request: ChinazAPIRequest):
else:
return Fail(message=result.message)
except Exception as e:
logger.error(f"查询司法综合数据失败: {e}")
return Fail(message=f"查询司法综合数据失败: {str(e)}")
logger.error(f"OCR识别失败: {e}")
return Fail(message=f"OCR识别失败: {str(e)}")
# 小红书API端点
@router.post("/xiaohongshu/note", summary="获取小红书笔记详情")
async def get_xiaohongshu_note(request: XiaohongshuNoteRequest):
"""获取小红书笔记详情"""
@ -89,5 +89,25 @@ async def get_xiaohongshu_note(request: XiaohongshuNoteRequest):
else:
return Fail(message=result.message)
except Exception as e:
logger.error(f"获取小红书笔记失败: {e}")
return Fail(message=f"获取小红书笔记失败: {str(e)}")
logger.error(f"获取小红书笔记详情失败: {e}")
return Fail(message=f"获取小红书笔记详情失败: {str(e)}")
@router.post("/jizhiliao/search", summary="极致聊指数搜索")
async def search_jizhiliao_index(request: JizhiliaoSearchRequest):
"""执行极致聊指数搜索"""
try:
params = {
"keyword": request.keyword,
"page": request.page,
"size": request.size
}
result = await third_party_api_controller.search_jizhiliao_index(params)
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,5 @@
from fastapi import APIRouter
from .upload import router as upload_router
router = APIRouter()
router.include_router(upload_router, prefix="/upload", tags=["文件上传"])

View File

@ -0,0 +1,14 @@
from fastapi import APIRouter, UploadFile, File
from app.controllers.upload import UploadController
from app.schemas.upload import ImageUploadResponse
router = APIRouter()
@router.post("/image", response_model=ImageUploadResponse, summary="上传图片")
async def upload_image(file: UploadFile = File(...)) -> ImageUploadResponse:
"""
上传图片接口
:param file: 图片文件
:return: 图片URL和文件名
"""
return await UploadController.upload_image(file)

View File

@ -1,53 +1,60 @@
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
)
from typing import Dict, Any, Optional
from app.utils.universal_api_manager import UniversalAPIManager
from app.schemas.third_party_api import APIResponse
logger = logging.getLogger(__name__)
class ThirdPartyAPIController:
"""第三方API控制器"""
def __init__(self):
self.api_manager = universal_api
"""初始化控制器"""
self.api_manager = UniversalAPIManager()
async def make_api_request(
self,
provider: str,
endpoint: str,
params: Dict[str, Any] = None,
timeout: int = 30
self,
provider: str,
endpoint: str,
params: Dict[str, Any],
timeout: Optional[int] = None
) -> APIResponse:
"""通用API请求方法"""
try:
result = self.api_manager.make_request(
provider=provider,
endpoint=endpoint,
params=params or {},
timeout=timeout
)
"""
发送API请求
Args:
provider: API提供商
endpoint: API端点
params: 请求参数
timeout: 超时时间()
return APIResponse(
success=True,
data=result,
message="请求成功",
provider=provider,
endpoint=endpoint
)
Returns:
APIResponse: API响应
"""
try:
# 发送请求
result = self.api_manager.make_request(provider, endpoint, params)
# 检查响应
if isinstance(result, dict) and result.get('code') == '200':
return APIResponse(
success=True,
message="请求成功",
data=result
)
else:
return APIResponse(
success=False,
message=f"请求失败: {result.get('msg', '未知错误')}",
data=result
)
except Exception as e:
logger.error(f"API请求失败: {e}")
logger.error(f"API请求失败: {str(e)}")
return APIResponse(
success=False,
data={},
message=f"请求失败: {str(e)}",
provider=provider,
endpoint=endpoint
message=f"API请求失败: {str(e)}",
data=None
)
# 站长之家API便捷方法
@ -73,19 +80,27 @@ class ThirdPartyAPIController:
"searchKey": company_name,
"pageNo": 1,
"range": 100,
"searchType": "0",
"searchType": 0,
"ChinazVer": chinaz_ver
}
)
async def query_judicial_data(self, company_name: str, chinaz_ver: str = "1") -> APIResponse:
"""查询司法综合数据"""
async def recognize_ocr(self, image_url: str, chinaz_ver: str = "1.0") -> APIResponse:
"""
OCR图片识别
Args:
image_url: 图片URL地址(支持jpgpngjpeg1M以内)
chinaz_ver: API版本号
Returns:
APIResponse: OCR识别结果
"""
return await self.make_api_request(
provider="chinaz",
endpoint="judgement",
endpoint="recognition_ocr",
params={
"q": company_name,
"pageNo": 1,
"url": image_url,
"ChinazVer": chinaz_ver
}
)
@ -96,10 +111,19 @@ class ThirdPartyAPIController:
params = {"noteId": note_id}
return await self.make_api_request(
provider="justoneapi",
provider="xiaohongshu",
endpoint="xiaohongshu_note_detail",
params=params
)
async def search_jizhiliao_index(self, params: Dict[str, Any], timeout: int = 30) -> APIResponse:
"""执行极致聊指数搜索"""
return await self.make_api_request(
provider="jizhiliao",
endpoint="index_search",
params=params,
timeout=timeout
)
# 创建全局控制器实例

51
app/controllers/upload.py Normal file
View File

@ -0,0 +1,51 @@
import os
from pathlib import Path
from typing import List
from fastapi import UploadFile
from app.schemas.upload import ImageUploadResponse
class UploadController:
"""文件上传控制器"""
@staticmethod
async def upload_image(file: UploadFile) -> ImageUploadResponse:
"""
上传图片
:param file: 上传的图片文件
:return: 图片URL和文件名
"""
# 检查文件类型
if not file.content_type.startswith('image/'):
raise ValueError("只支持上传图片文件")
# 获取项目根目录
base_dir = Path(__file__).resolve().parent.parent
# 图片保存目录
upload_dir = base_dir / "static" / "images"
# 确保目录存在
if not upload_dir.exists():
upload_dir.mkdir(parents=True, exist_ok=True)
# 生成文件名
filename = file.filename
file_path = upload_dir / filename
# 如果文件已存在,重命名
counter = 1
while file_path.exists():
name, ext = os.path.splitext(filename)
filename = f"{name}_{counter}{ext}"
file_path = upload_dir / filename
counter += 1
# 保存文件
content = await file.read()
with open(file_path, "wb") as f:
f.write(content)
# 返回文件URL
return ImageUploadResponse(
url=f"/static/images/{filename}",
filename=filename
)

View File

@ -122,6 +122,35 @@ class HttpAuditLogMiddleware(BaseHTTPMiddleware):
pass
return v
def normalize_json_field(self, value: Any) -> Any:
"""确保写入 JSONField 的值合法。"""
if value is None:
return None
if isinstance(value, (bytes, bytearray)):
try:
value = value.decode("utf-8")
except Exception:
value = value.decode("utf-8", errors="ignore")
if isinstance(value, str):
stripped = value.strip()
if not stripped:
return None
try:
return json.loads(stripped)
except (ValueError, TypeError):
return stripped
if isinstance(value, (dict, list, int, float, bool)):
return value
try:
json.dumps(value)
return value
except (TypeError, ValueError):
return str(value)
async def _async_iter(self, items: list[bytes]) -> AsyncGenerator[bytes, None]:
for item in items:
yield item
@ -166,8 +195,11 @@ class HttpAuditLogMiddleware(BaseHTTPMiddleware):
data: dict = await self.get_request_log(request=request, response=response)
data["response_time"] = process_time
data["request_args"] = request.state.request_args
data["response_body"] = await self.get_response_body(request, response)
request_args = getattr(request.state, "request_args", None)
response_body = await self.get_response_body(request, response)
data["request_args"] = self.normalize_json_field(request_args)
data["response_body"] = self.normalize_json_field(response_body)
await AuditLog.create(**data)
return response

View File

@ -11,6 +11,8 @@ class Policy(BaseModel, TimestampMixin):
name = fields.CharField(max_length=20, description="行业名称")
level = fields.CharEnumField(PolicyType, description="政策扶持等级")
score = fields.IntField( description="政策匹配度基准分",default=0)
remark = fields.TextField(default="备注")
class Meta:
table = "policy"

View File

@ -1,55 +1,53 @@
from typing import Dict, Any, Optional, List
import logging
from typing import List, Optional
from pydantic import BaseModel, Field
logger = logging.getLogger(__name__)
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="超时时间(秒)")
pass
class ChinazAPIRequest(BaseModel):
class ChinazAPIRequest(BaseAPIRequest):
"""站长之家API请求模型"""
company_name: str = Field(..., description="公司名称", example="百度在线网络技术(北京)有限公司")
chinaz_ver: Optional[str] = Field("1", description="API版本", example="1")
timeout: Optional[int] = Field(30, description="超时时间(秒)")
company_name: Optional[str] = Field(None, description="公司名称")
chinaz_ver: str = Field("1.0", description="API版本号")
class OCRRequest(BaseAPIRequest):
"""OCR识别请求模型"""
url: str = Field(..., description="图片URL地址(支持jpgpngjpeg1M以内)")
chinaz_ver: str = Field("1.0", description="API版本号")
class XiaohongshuNoteRequest(BaseModel):
"""小红书笔记详情请求"""
note_id: str = Field(..., description="笔记ID", example="68d2c71d000000000e00e9ea")
class XiaohongshuNoteRequest(BaseAPIRequest):
"""小红书笔记请求模型"""
note_id: str = Field(..., description="笔记ID")
class JizhiliaoSearchRequest(BaseAPIRequest):
"""极致聊搜索请求模型"""
keyword: str = Field(..., description="搜索关键词")
page: int = Field(1, description="页码")
size: int = Field(10, description="每页数量")
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="可用端点")
success: bool
message: str
data: Optional[dict] = None
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="可选参数")
path: str
method: str
description: str
required_params: List[str]
optional_params: Optional[List[str]] = None
class APIProviderInfo(BaseModel):
"""API提供商信息"""
name: str
base_url: str
endpoints: dict[str, APIEndpointInfo]
class APIListResponse(BaseModel):
"""API列表响应"""
providers: List[APIProviderInfo] = Field(..., description="API提供商列表")
total_providers: int = Field(..., description="提供商总数")
providers: List[APIProviderInfo]

6
app/schemas/upload.py Normal file
View File

@ -0,0 +1,6 @@
from pydantic import BaseModel
class ImageUploadResponse(BaseModel):
"""图片上传响应模型"""
url: str
filename: str

0
app/utils/__init__.py Normal file
View File

View File

@ -13,7 +13,7 @@ from pathlib import Path
class APIConfig:
"""API配置管理器"""
def __init__(self, config_file: str = None):
"""
初始化配置管理器
@ -23,16 +23,16 @@ class APIConfig:
"""
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": {
"api_key": os.getenv("CHINAZ_COPYRIGHT_API_KEY", "YOUR_API_KEY"),
"path": "/v1/1036/copyrightsoftware",
"method": "POST",
"description": "企业软件著作权查询",
@ -40,6 +40,7 @@ class APIConfig:
"optional_params": ["sign"]
},
"patent": {
"api_key": os.getenv("CHINAZ_PATENT_API_KEY", "YOUR_API_KEY"),
"path": "/v1/1036/patent",
"method": "POST",
"description": "企业专利信息查询",
@ -47,58 +48,223 @@ class APIConfig:
"optional_params": ["sign", "searchType"]
},
"judgement": {
"api_key": os.getenv("CHINAZ_JUDGEMENT_API_KEY", "YOUR_API_KEY"),
"path": "/v1/1036/judgementdetailv4",
"method": "POST",
"description": "司法综合数据查询",
"required_params": ["APIKey", "ChinazVer"],
"optional_params": ["sign", "q", "idCardNo", "datatype", "id", "pageNo"]
},
"recognition_ocr": {
"api_key": os.getenv("CHINAZ_OCR_API_KEY", "YOUR_API_KEY"),
"path": "/v1/1024/recognition_ocr",
"method": "POST",
"description": "图片OCR识别",
"required_params": ["url", "APIKey", "ChinazVer"],
"optional_params": ["sign"]
},
}
},
"justoneapi": {
"api_key": os.getenv("JUSTONEAPI_TOKEN", "YNSbIjdU"),
"base_url": "https://api.justoneapi.com",
"timeout": 30,
"retries": 3,
"endpoints": {
"xiaohongshu": {
"api_key": os.getenv("XIAOHONGSHU_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"]
},
"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": {
"api_key": os.getenv("XIAOHONGSHU_TOKEN", "YNSbIjdU"),
"base_url": "https://api.justoneapi.com",
"timeout": 30,
"retries": 3,
"endpoints": {
"weibo_user_detail": {
"path": "/api/weibo/get-user-detail/v1", # 微博只有粉丝数接口
"method": "GET",
"description": "获取用户信息 包括昵称、头像、用户ID、粉丝数、关注数、简介、认证状态等公开信息",
"required_params": ["token", "uid"],
"optional_params": ["token", "uid"]
},
}
},
"weixin": {
"api_key": os.getenv("XIAOHONGSHU_TOKEN", "YNSbIjdU"),
"base_url": "https://api.justoneapi.com",
"timeout": 30,
"retries": 3,
"endpoints": {
"weixin_user_detail": {
"path": "/api/weixin/get-user-post/v1",
"method": "GET",
"description": "公众号发布的文章内容,包括标题、作者、发布时间、内容摘要以及阅读数、点赞数与转发数等互动数据",
"required_params": ["token", "wxid"],
"optional_params": ["token", "wxid"]
}
}
},
"douyin": {
"api_key": os.getenv("XIAOHONGSHU_TOKEN", "YNSbIjdU"),
"base_url": "https://api.justoneapi.com",
"timeout": 30,
"retries": 3,
"endpoints": {
"douyin_video_detail": {
"path": "/api/douyin/get-video-detail/v2",
"method": "GET",
"description": "该接口用于获取指定抖音视频的详细信息,包括视频地址、描述文案、作者信息、发布时间、播放量、点赞数、评论数与分享数等",
"required_params": ["token", "videoId"],
"optional_params": ["token", "videoId"]
}
}
},
"bilibili": {
"api_key": os.getenv("XIAOHONGSHU_TOKEN", "YNSbIjdU"),
"base_url": "https://api.justoneapi.com",
"timeout": 30,
"retries": 3,
"endpoints": {
"bilibili_video_detail": {
"path": "/api/bilibili/get-video-detail/v2",
"method": "GET",
"description": "该接口用于获取指定抖音视频的详细信息,包括视频地址、描述文案、作者信息、发布时间、播放量、点赞数、评论数与分享数等",
"required_params": ["token", "bvid"],
"optional_params": ["token", "bvid"]
}
}
},
"kuaishou": {
"api_key": os.getenv("XIAOHONGSHU_TOKEN", "YNSbIjdU"),
"base_url": "https://api.justoneapi.com",
"timeout": 30,
"retries": 3,
"endpoints": {
"kuaishou_video_detail": {
"path": "/api/kuaishou/get-video-detail/v2",
"method": "GET",
"description": "该接口用于获取指定抖音视频的详细信息,包括视频地址、描述文案、作者信息、发布时间、播放量、点赞数、评论数与分享数等",
"required_params": ["token", "videoId"],
"optional_params": ["token", "videoId"]
}
}
},
"other_apis": {
"jizhiliao": {
"api_key": os.getenv("JIZHILIAO_API_KEY", "JZL089ef0b7d0315d96"),
"base_url": "https://api.justoneapi.com",
"timeout": 30,
"retries": 3,
"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": {
"timeout": 60,
"retries": 3,
"sign_prefix": "634xz"
}
}
}
},
# 微信指数
"dajiala": {
"api_key": "",
"base_url": "https://www.dajiala.com",
"timeout": 30,
"retries": 3,
"endpoints": {
"web_search": {
"path": "/fbmain/monitor/v3/web_search",
"method": "POST",
"description": "获取微信指数",
"required_params": ["keyword", "key"],
"optional_params": []
}
}
},
"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:
@ -108,9 +274,9 @@ class APIConfig:
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():
@ -118,7 +284,7 @@ class APIConfig:
self._merge_config(default[key], value)
else:
default[key] = value
def get_api_config(self, provider: str) -> Optional[Dict[str, Any]]:
"""
获取指定提供商的API配置
@ -130,7 +296,7 @@ class APIConfig:
Optional[Dict[str, Any]]: API配置信息
"""
return self.config.get(provider)
def get_endpoint_config(self, provider: str, endpoint: str) -> Optional[Dict[str, Any]]:
"""
获取指定端点的配置
@ -146,7 +312,7 @@ class APIConfig:
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密钥
@ -159,7 +325,7 @@ class APIConfig:
"""
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
@ -172,7 +338,7 @@ class APIConfig:
"""
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密钥
@ -184,15 +350,15 @@ class APIConfig:
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:
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端点
@ -207,10 +373,10 @@ class APIConfig:
"""
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(),
@ -218,27 +384,27 @@ class APIConfig:
'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:
"""
列出指定提供商的所有端点
@ -256,4 +422,4 @@ class APIConfig:
# 创建全局配置实例
api_config = APIConfig()
api_config = APIConfig()

View File

@ -0,0 +1,53 @@
'''
这是非物质文化遗产IP知识产权评估系统的核心计算引擎包
'''
from app.utils.calculation_engine.economic_value_b1 import EconomicValueB1Calculator
from app.utils.calculation_engine.economic_value_b1.sub_formulas import (
BasicValueB11Calculator,
TrafficFactorB12Calculator,
PolicyMultiplierB13Calculator
)
from app.utils.calculation_engine.cultural_value_b2 import CulturalValueB2Calculator
from app.utils.calculation_engine.cultural_value_b2.sub_formulas import (
LivingHeritageB21Calculator,
PatternGeneB22Calculator
)
from app.utils.calculation_engine.risk_adjustment_b3 import RiskAdjustmentB3Calculator
from app.utils.calculation_engine.market_value_c import MarketValueCCalculator
from app.utils.calculation_engine.market_value_c.sub_formulas import (
MarketBiddingC1Calculator,
HeatCoefficientC2Calculator,
ScarcityMultiplierC3Calculator,
TemporalDecayC4Calculator
)
from app.utils.calculation_engine.final_value_ab import FinalValueACalculator
__version__ = "1.0.0"
__author__ = "Assessment Team"
__all__ = [
# 经济价值B1模块
"EconomicValueB1Calculator",
"BasicValueB11Calculator",
"TrafficFactorB12Calculator",
"PolicyMultiplierB13Calculator",
# 文化价值B2模块
"CulturalValueB2Calculator",
"LivingHeritageB21Calculator",
"PatternGeneB22Calculator",
# 风险调整系数B3模块
"RiskAdjustmentB3Calculator",
# 市场估值C模块
"MarketValueCCalculator",
"MarketBiddingC1Calculator",
"HeatCoefficientC2Calculator",
"ScarcityMultiplierC3Calculator",
"TemporalDecayC4Calculator",
# 最终估值A模块
"FinalValueACalculator"
]

View File

@ -0,0 +1,11 @@
from .cultural_value_b2 import CulturalValueB2Calculator
from .sub_formulas import (
LivingHeritageB21Calculator,
PatternGeneB22Calculator
)
__all__ = [
"CulturalValueB2Calculator",
"LivingHeritageB21Calculator",
"PatternGeneB22Calculator"
]

View File

@ -0,0 +1,116 @@
"""
文化价值B2
文化价值B2 = 活态传承系数B21 × 0.6 + (纹样基因值B22 / 10) × 0.4
"""
from typing import Dict, List, Optional
try:
# 相对导入(当作为包使用时)
from .sub_formulas.living_heritage_b21 import LivingHeritageB21Calculator
from .sub_formulas.pattern_gene_b22 import PatternGeneB22Calculator
except ImportError:
# 绝对导入(当直接运行时)
from sub_formulas.living_heritage_b21 import LivingHeritageB21Calculator
from sub_formulas.pattern_gene_b22 import PatternGeneB22Calculator
class CulturalValueB2Calculator:
"""文化价值B2计算器"""
def __init__(self):
"""初始化计算器"""
self.living_heritage_calculator = LivingHeritageB21Calculator()
self.pattern_gene_calculator = PatternGeneB22Calculator()
def calculate_cultural_value_b2(self,
living_heritage_b21: float,
pattern_gene_b22: float) -> float:
"""
计算文化价值B2
文化价值B2 = 活态传承系数B21 × 0.6 + (纹样基因值B22 / 10) × 0.4
args:
living_heritage_b21: 活态传承系数B21 (系统计算)
pattern_gene_b22: 纹样基因值B22 (系统计算)
return:
float: 文化价值B2
"""
cultural_value = living_heritage_b21 * 0.6 + (pattern_gene_b22 / 10) * 0.4
return cultural_value
def calculate_complete_cultural_value_b2(self, input_data: Dict) -> Dict:
"""
计算完整的文化价值B2包含所有子公式
args:
input_data: 输入数据字典包含所有必要的参数
return:
Dict: 包含所有中间计算结果和最终结果的字典
"""
# 计算活态传承系数B21
teaching_frequency = self.living_heritage_calculator.calculate_teaching_frequency(
input_data["offline_sessions"],
input_data["douyin_views"],
input_data["kuaishou_views"],
input_data["bilibili_views"]
)
living_heritage_b21 = self.living_heritage_calculator.calculate_living_heritage_b21(
input_data['inheritor_level_coefficient'],
teaching_frequency,
input_data['cross_border_depth']
)
# 计算纹样基因值B22
pattern_gene_b22 = self.pattern_gene_calculator.calculate_pattern_gene_b22(
input_data['structure_complexity'],
input_data['normalized_entropy'],
input_data['historical_inheritance']
)
# 计算文化价值B2
cultural_value_b2 = self.calculate_cultural_value_b2(
living_heritage_b21,
pattern_gene_b22
)
return {
'living_heritage_b21': living_heritage_b21,
'pattern_gene_b22': pattern_gene_b22,
'cultural_value_b2': cultural_value_b2
}
# 示例使用
if __name__ == "__main__":
calculator = CulturalValueB2Calculator()
# 示例数据
input_data = {
# 活态传承系数B21相关参数
'inheritor_level_coefficient': 10.0, # 传承人等级系数
'teaching_frequency': 7.0, # 教学传播频次
'cross_border_depth': 7.0, # 跨界合作深度
"offline_sessions": 1,
"douyin_views": 2,
"kuaishou_views": 3,
"bilibili_views": 4,
# 纹样基因值B22相关参数
'structure_complexity': 0.75, # 结构复杂度SC
'normalized_entropy': 0.85, # 归一化信息熵H
'historical_inheritance': 0.80 # 历史传承度HI
}
# 计算文化价值B2
result = calculator.calculate_complete_cultural_value_b2(input_data)
print("文化价值B2计算结果:")
print(f"活态传承系数B21: {result['living_heritage_b21']:.4f}")
print(f"纹样基因值B22: {result['pattern_gene_b22']:.4f}")
print(f"文化价值B2: {result['cultural_value_b2']:.4f}")

View File

@ -0,0 +1,24 @@
"""
文化价值B2子公式包
1. living_heritage_b21: 活态传承系数B21计算
- 传承人等级系数国家级(10)省级(7)市级(4)县级(2)
- 教学传播频次每月>10(10)5-10(7)1-4(4)
- 跨界合作深度多类型高频(10)多类型中频(7)单类型高频(5)
- 活态传承系数B21 = 传承人等级系数 × 0.4 + 教学传播频次 × 0.3 + 跨界合作深度 × 0.3
2. pattern_gene_b22: 纹样基因值B22计算
- 结构复杂度SC = Σ(元素权重 × 复杂度系数) / 总元素数
- 归一化信息熵H = -Σ(p_i × log2(p_i)) / log2(n)
- 历史传承度HI = 传承年限权重 × 0.4 + 文化意义权重 × 0.3 + 保护状况权重 × 0.3
- 纹样基因值B22 = (结构复杂度SC × 0.6 + 归一化信息熵H × 0.4) × 历史传承度HI × 10
- 文化价值B2 = 活态传承系数B21 × 0.6 + (纹样基因值B22 / 10) × 0.4
"""
from .living_heritage_b21 import LivingHeritageB21Calculator
from .pattern_gene_b22 import PatternGeneB22Calculator
__all__ = [
"LivingHeritageB21Calculator",
"PatternGeneB22Calculator"
]

View File

@ -0,0 +1,155 @@
"""
活态传承系数B21计算模块
活态传承系数B21 = 传承人等级系数 × 0.4 + 教学传播频次 × 0.3 + 跨界合作深度 × 0.3
"""
class LivingHeritageB21Calculator:
"""活态传承系数B21计算器"""
def __init__(self):
"""初始化计算器"""
pass
def calculate_living_heritage_b21(self,
inheritor_level_coefficient: float,
teaching_frequency: float,
cross_border_depth: float) -> float:
"""
计算活态传承系数B21
活态传承系数B21 = 传承人等级系数 × 0.4 + 教学传播频次 × 0.3 + 跨界合作深度 × 0.3
args:
inheritor_level_coefficient: 传承人等级系数 (用户填写)
teaching_frequency: 教学传播频次 (用户填写)
cross_border_depth: 跨界合作深度 (用户填写)
return:
float: 活态传承系数B21
"""
#
living_heritage = (inheritor_level_coefficient * 0.4 +
teaching_frequency * 0.3 +
cross_border_depth * 0.3)
return living_heritage
def calculate_inheritor_level_coefficient(self, inheritor_level: str) -> float:
"""
计算传承人等级系数
传承人等级评分标准
- 国家级传承人: 1
- 省级传承人: 0.7
- 市级传承人: .44
args:
inheritor_level: 传承人等级 (用户填写)
return:
float: 传承人等级系数
"""
level_scores = {
"国家级传承人": 1.0,
"省级传承人": 0.7,
"市级传承人": 0.4,
}
return level_scores.get(inheritor_level, 0.4)
def calculate_teaching_frequency(self,
offline_sessions: int,
douyin_views: int = 0,
kuaishou_views: int = 0,
bilibili_views: int = 0) -> float:
"""
计算教学传播频次
教学传播频次 = 线下传习次数 × 0.6 + 线上课程点击量 × 0.4
线下传习次数统计规范
1) 单次活动标准传承人主导时长2小时参与人数5
2) 频次计算按自然年度累计同一内容重复培训不计入
线上课程折算
- 抖音/快手播放量按100:1折算为学习人次
- B站课程按50:1折算
args:
offline_sessions: 线下传习次数符合标准的活动次数
douyin_views: 抖音播放量
kuaishou_views: 快手播放量
bilibili_views: B站播放量
returns:
float: 教学传播频次评分
"""
# 线下传习次数权重计算
offline_score = offline_sessions * 0.6
# 线上课程点击量折算
# 抖音/快手按100:1折算
douyin_kuaishou_learning_sessions = (douyin_views + kuaishou_views) / 100
# B站按50:1折算
bilibili_learning_sessions = bilibili_views / 50
# 线上总学习人次(万)
online_learning_sessions_10k = (douyin_kuaishou_learning_sessions + bilibili_learning_sessions) / 10000
# 线上课程权重计算
online_score = online_learning_sessions_10k * 0.4
# 总教学传播频次
teaching_frequency = offline_score + online_score
return teaching_frequency
# 示例使用
if __name__ == "__main__":
calculator = LivingHeritageB21Calculator()
# 示例数据
inheritor_level = "国家级传承人" # 传承人等级 (用户填写)
cross_border_depth = 50.0
# 教学传播频次数据
offline_sessions = 10 # 线下传习次数符合标准传承人主导、时长≥2小时、参与人数≥5人
douyin_views = 1000000 # 抖音播放量
kuaishou_views = 500000 # 快手播放量
bilibili_views = 200000 # B站播放量
# 计算各项指标
inheritor_level_coefficient = calculator.calculate_inheritor_level_coefficient(inheritor_level)
teaching_frequency = calculator.calculate_teaching_frequency(
offline_sessions=offline_sessions,
douyin_views=douyin_views,
kuaishou_views=kuaishou_views,
bilibili_views=bilibili_views
)
# 计算活态传承系数B21
living_heritage_b21 = calculator.calculate_living_heritage_b21(
inheritor_level_coefficient, teaching_frequency, cross_border_depth
)
print(f"传承人等级系数: {inheritor_level_coefficient:.2f}")
print(f"教学传播频次: {teaching_frequency:.2f}")
print(f" - 线下传习次数: {offline_sessions}")
print(f" - 抖音播放量: {douyin_views:,}")
print(f" - 快手播放量: {kuaishou_views:,}")
print(f" - B站播放量: {bilibili_views:,}")
print(f"跨界合作深度: {cross_border_depth:.2f}")
print(f"活态传承系数B21: {living_heritage_b21:.4f}")

View File

@ -0,0 +1,143 @@
"""
纹样基因值B22计算模块
纹样基因值B22 = (结构复杂度SC × 0.6 + 归一化信息熵H × 0.4) × 历史传承度HI × 10
"""
import math
from typing import Dict, List
class PatternGeneB22Calculator:
"""纹样基因值B22计算器"""
def __init__(self):
"""初始化计算器"""
pass
def calculate_pattern_gene_b22(self,
structure_complexity: float,
normalized_entropy: float,
historical_inheritance: float) -> float:
"""
计算纹样基因值B22
纹样基因值B22 = (结构复杂度SC × 0.6 + 归一化信息熵H × 0.4) × 历史传承度HI × 10
args:
structure_complexity: 结构复杂度SC (系统计算)
normalized_entropy: 归一化信息熵H (系统计算)
historical_inheritance: 历史传承度HI (用户填写)
return:
float: 纹样基因值B22
"""
pattern_gene = ((structure_complexity * 0.6 +
normalized_entropy * 0.4) *
historical_inheritance * 10)
return pattern_gene
def calculate_structure_complexity(self, pattern_elements: List[Dict]) -> float:
"""
计算结构复杂度SC
结构复杂度 = Σ(元素权重 × 复杂度系数) / 总元素数
args:
pattern_elements: 纹样元素列表每个元素包含 {type: str, weight: float, complexity: float} (用户填写)
return:
float: 结构复杂度SC
"""
if not pattern_elements:
return 0.0
total_weighted_complexity = 0.0
total_weight = 0.0
for element in pattern_elements:
weight = element.get('weight', 1.0)
complexity = element.get('complexity', 0.0)
total_weighted_complexity += weight * complexity
total_weight += weight
if total_weight == 0:
return 0.0
structure_complexity = total_weighted_complexity / total_weight
return structure_complexity
def calculate_normalized_entropy(self, pattern_data: List[float]) -> float:
"""
计算归一化信息熵H
归一化信息熵 = -Σ(p_i × log2(p_i)) / log2(n)
args:
pattern_data: 纹样数据列表 (用户填写)
return:
float: 归一化信息熵H
"""
if not pattern_data or len(pattern_data) <= 1:
return 0.0
# 计算概率分布
total = sum(pattern_data)
if total == 0:
return 0.0
probabilities = [x / total for x in pattern_data if x > 0]
# 计算信息熵
entropy = 0.0
for p in probabilities:
if p > 0:
entropy -= p * math.log2(p)
# 归一化
n = len(probabilities)
if n <= 1:
return 0.0
normalized_entropy = entropy / math.log2(n)
return normalized_entropy
# 示例使用
if __name__ == "__main__":
calculator = PatternGeneB22Calculator()
# 示例数据
pattern_elements = [
{'type': '几何图形', 'weight': 0.3, 'complexity': 0.7},
{'type': '植物纹样', 'weight': 0.4, 'complexity': 0.8},
{'type': '动物纹样', 'weight': 0.3, 'complexity': 0.6}
]
entropy_data = [0.3, 0.4, 0.3]
inheritance_years = 500 # 传承年数 (用户填写)
cultural_significance = "国家级" # 文化意义等级 (用户填写)
preservation_status = "良好" # 保护状况 (用户填写)
historical_inheritance = 100.0
# 计算各项指标
structure_complexity = calculator.calculate_structure_complexity(pattern_elements)
normalized_entropy = calculator.calculate_normalized_entropy(entropy_data)
# 计算纹样基因值B22
pattern_gene_b22 = calculator.calculate_pattern_gene_b22(
structure_complexity, normalized_entropy, historical_inheritance
)
print(f"结构复杂度SC: {structure_complexity:.4f}")
print(f"归一化信息熵H: {normalized_entropy:.4f}")
print(f"历史传承度HI: {historical_inheritance:.4f}")
print(f"纹样基因值B22: {pattern_gene_b22:.4f}")

View File

@ -0,0 +1,18 @@
"""
经济价值B1计算包
经济价值B1 = 基础价值B11 × (1 + 流量因子B12) × 政策乘数B13
"""
from .economic_value_b1 import EconomicValueB1Calculator
from .sub_formulas import (
BasicValueB11Calculator,
TrafficFactorB12Calculator,
PolicyMultiplierB13Calculator
)
__all__ = [
"EconomicValueB1Calculator",
"BasicValueB11Calculator",
"TrafficFactorB12Calculator",
"PolicyMultiplierB13Calculator"
]

View File

@ -0,0 +1,214 @@
"""
经济价值B1计算模块
经济价值B1 = 基础价值B11 × (1 + 流量因子B12) × 政策乘数B13
"""
from typing import Dict
try:
# 相对导入(当作为包使用时)
from .sub_formulas.basic_value_b11 import BasicValueB11Calculator, calculate_popularity_score
from .sub_formulas.traffic_factor_b12 import TrafficFactorB12Calculator
from .sub_formulas.policy_multiplier_b13 import PolicyMultiplierB13Calculator
except ImportError:
# 绝对导入(当直接运行时)
from sub_formulas.basic_value_b11 import BasicValueB11Calculator
from sub_formulas.traffic_factor_b12 import TrafficFactorB12Calculator
from sub_formulas.policy_multiplier_b13 import PolicyMultiplierB13Calculator
class EconomicValueB1Calculator:
"""经济价值B1计算器"""
def __init__(self):
"""初始化计算器"""
self.basic_value_calculator = BasicValueB11Calculator()
self.traffic_factor_calculator = TrafficFactorB12Calculator()
self.policy_multiplier_calculator = PolicyMultiplierB13Calculator()
def calculate_economic_value_b1(self,
basic_value_b11: float,
traffic_factor_b12: float,
policy_multiplier_b13: float) -> float:
"""
计算经济价值B1
经济价值B1 = 基础价值B11 × (1 + 流量因子B12) × 政策乘数B13
args:
basic_value_b11: 基础价值B11 (系统计算)
traffic_factor_b12: 流量因子B12 (系统计算)
policy_multiplier_b13: 政策乘数B13 (系统计算)
returns:
float: 经济价值B1
"""
economic_value = basic_value_b11 * (1 + traffic_factor_b12) * policy_multiplier_b13
return economic_value
def calculate_complete_economic_value_b1(self, input_data: Dict) -> Dict:
"""
计算完整的经济价值B1包含所有子公式
args:
input_data: 输入数据字典包含所有必要的参数
returns:
Dict: 包含所有中间计算结果和最终结果的字典
"""
# 财务价值F 近三年年均收益列表 [1,2,3]
financial_value = self.basic_value_calculator.calculate_financial_value_f(input_data["three_year_income"])
# 计算法律强度L patent_score: 专利分 (0-10分) (用户填写)
# popularity_score: 普及地域分 (0-10分) (用户填写)
# infringement_score: 侵权分 (0-10分) (用户填写)
legal_strength = self.basic_value_calculator.calculate_legal_strength_l(
input_data["patent_score"],
input_data["popularity_score"],
input_data["infringement_score"],
)
# 发展潜力 patent_count: 专利分 (0-10分) (用户填写)
# esg_score: ESG分 (0-10分) (用户填写)
# innovation_ratio: 创新投入比 (研发费用/营收) * 100 (用户填写)
development_potential = self.basic_value_calculator.calculate_development_potential_d(
input_data["esg_score"],
input_data["patent_count"],
input_data["innovation_ratio"],
)
# 计算行业系数I target_industry_roe: 目标行业平均ROE (系统配置)
# benchmark_industry_roe: 基准行业ROE (系统配置)
# industry_coefficient = self.basic_value_calculator.calculate_industry_coefficient_i(
#
# )
# 计算基础价值B11
basic_value_b11 = self.basic_value_calculator.calculate_basic_value_b11(
financial_value,
legal_strength,
development_potential,
input_data["industry_coefficient"]
)
# 计算流量因子B12
social_media_spread_s3 = self.traffic_factor_calculator.calculate_interaction_index(
input_data["likes"],
input_data["comments"],
input_data["shares"],
)
traffic_factor_b12 = self.traffic_factor_calculator.calculate_traffic_factor_b12(
input_data['search_index_s1'],
input_data['industry_average_s2'],
social_media_spread_s3
)
# 计算政策乘数B13
policy_compatibility_score = self.policy_multiplier_calculator.calculate_policy_compatibility_score(
input_data["policy_match_score"],
input_data["implementation_stage"],
input_data["funding_support"])
policy_multiplier_b13 = self.policy_multiplier_calculator.calculate_policy_multiplier_b13(
policy_compatibility_score
)
# 计算经济价值B1
economic_value_b1 = self.calculate_economic_value_b1(
basic_value_b11,
traffic_factor_b12,
policy_multiplier_b13
)
return {
'basic_value_b11': basic_value_b11,
'traffic_factor_b12': traffic_factor_b12,
'policy_multiplier_b13': policy_multiplier_b13,
'economic_value_b1': economic_value_b1
}
# 示例使用
if __name__ == "__main__":
calculator = EconomicValueB1Calculator()
# 示例数据
'''
{
# 基础价值B11相关参数
'three_year_income': three_year_income,
'patent_score': patent_score,
'popularity_score': popularity_score,
'infringement_score': infringement_score,
'innovation_ratio': innovation_ratio,
# 流量因子B12相关参数
'search_index_s1': search_index_s1,
'industry_average_s2': industry_average_s2,
# 'social_media_spread_s3': social_media_spread_s3,
'likes':likes,
'comments':comments,
'shares':shares,
'followers':followers,
'click_count': int(data.click_count) or 100,
'view_count': int(data.link_views) or 100,
# 政策乘数B13相关参数
'implementation_stage': implementation_stage,
'funding_support':funding_support
}
'''
input_data = {
# 基础价值B11相关参数
'three_year_income': [1000, 2000, 2222],
'patent_score': 1, # 专利分
'popularity_score': 4.0, # 普及地域分值
'infringement_score': 1.0, # 侵权分
'innovation_ratio': 600.0,
'esg_score':10.0,
'industry_coefficient':12.0,
# 流量因子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 # 资金支持度
}
# input_data = {
# # 基础价值B11相关参数
# 'financial_value': 68.04, # 财务价值F
# 'legal_strength': 7.90, # 法律强度L
# 'development_potential': 6.00, # 发展潜力D
# 'industry_coefficient': 0.25, # 行业系数I
#
# # 流量因子B12相关参数
# 'search_index_s1': 4500.0, # 近30天搜索指数S1
# 'industry_average_s2': 5000.0, # 行业均值S2
# 'social_media_spread_s3': 1.07, # 社交媒体传播度S3
#
# # 政策乘数B13相关参数
# 'policy_compatibility_score': 9.1 # 政策契合度评分P
# }
# 计算经济价值B1
result = calculator.calculate_complete_economic_value_b1(input_data)
print("经济价值B1计算结果:")
print(f"基础价值B11: {result['basic_value_b11']:.2f}")
print(f"流量因子B12: {result['traffic_factor_b12']:.4f}")
print(f"政策乘数B13: {result['policy_multiplier_b13']:.4f}")
print(f"经济价值B1: {result['economic_value_b1']:.2f}")

View File

@ -0,0 +1,34 @@
"""
经济价值B1子公式包
包含经济价值B1的所有子公式计算模块
1. basic_value_b11: 基础价值B11计算
- 财务价值F = [3年内年均收益 × (1 + 增长率)^5] / 5
- 法律强度L = 专利分 × 0.4 + 普及分 × 0.3 + 侵权分 × 0.3
- 发展潜力D = 专利分 × 0.5 + ESG分 × 0.2 + 创新投入比 × 0.3
- 行业系数I = (目标行业平均ROE - 基准行业ROE) / 基准行业ROE
- 基础价值B11 = 财务价值F × (0.45 + 0.05 × 行业系数I) + 法律强度L × (0.35 + 0.05 × 行业系数I) + 发展潜力D × 0.2
2. traffic_factor_b12: 流量因子B12计算
- 近30天搜索指数S1 = 百度搜索指数 × 0.4 + 微信搜索指数 × 0.3 + 微博搜索指数 × 0.3
- 社交媒体传播度S3 = 互动量指数 × 0.4 + 覆盖人群指数 × 0.3 + 转化效率 × 0.3
- 行业均值S2 = 系统内根据行业自动匹配行业均值S2
- 流量因子B12 = ln(近30天搜索指数S1/行业均值S2) × 0.3 + 社交媒体传播度S3 × 0.7
3. policy_multiplier_b13: 政策乘数B13计算
- 政策契合度P = 政策匹配度 × 0.4 + 实施阶段评分 × 0.3 + 资金支持度 × 0.3
- 政策乘数B13 = 1 + (政策契合度评分P × 0.15)
"""
from .basic_value_b11 import BasicValueB11Calculator
from .traffic_factor_b12 import TrafficFactorB12Calculator
from .policy_multiplier_b13 import PolicyMultiplierB13Calculator
__all__ = [
"BasicValueB11Calculator",
"TrafficFactorB12Calculator",
"PolicyMultiplierB13Calculator"
]

View File

@ -0,0 +1,296 @@
import math
from typing import List, Optional
class BasicValueB11Calculator:
"""基础价值B11计算器"""
def __init__(self):
"""初始化计算器"""
pass
def calculate_basic_value_b11(self,
financial_value: float,
legal_strength: float,
development_potential: float,
industry_coefficient: float) -> float:
"""
计算基础价值B11
基础价值B11 = 财务价值F × (0.45 + 0.05 × 行业系数I) + 法律强度L × (0.35 + 0.05 × 行业系数I) + 发展潜力D × 0.2
args:
financial_value: 财务价值F (用户填写)
legal_strength: 法律强度L (用户填写)
development_potential: 发展潜力D (用户填写)
industry_coefficient: 行业系数I (系统配置)
returns:
float: 基础价值B11
"""
basic_value = (financial_value * (0.45 + 0.05 * industry_coefficient) +
legal_strength * (0.35 + 0.05 * industry_coefficient) +
development_potential * 0.2)
return basic_value
def growth_rate(self, annual_revenue_3_years: List[float]):
"""
"""
g1, g2 = (annual_revenue_3_years[1] - annual_revenue_3_years[0]) / annual_revenue_3_years[0] * 100, (
annual_revenue_3_years[2] - annual_revenue_3_years[1]) / annual_revenue_3_years[1] * 100
predicted_growth = g2 + (g2 - g1)
return predicted_growth
def calculate_financial_value_f(self,
annual_revenue_3_years: List[float],
growth_rate: Optional[float] = None) -> float:
"""
计算财务价值F
财务价值F = [3年内年均收益 × (1 + 增长率)^5] / 5
args:
annual_revenue_3_years: 近三年年均收益列表单位万元 (用户填写)
growth_rate: 增长率如果为None则自动计算 (用户填写)
returns:
float: 财务价值F
"""
if len(annual_revenue_3_years) != 3:
raise ValueError("必须提供近三年的收益数据")
# 计算年均收益
avg_annual_revenue = sum(annual_revenue_3_years) / 3
# 如果没有提供增长率,则根据三年数据计算
if growth_rate is None:
growth_rate = self._calculate_growth_rate(annual_revenue_3_years)
financial_value = (avg_annual_revenue * math.pow(1 + growth_rate, 5)) / 5
return financial_value
def _calculate_growth_rate(self, revenue_data: List[float]) -> float:
"""
根据三年收益数据计算增长率
args:
revenue_data: 三年收益数据 (用户填写)
returns:
float: 增长率
"""
if len(revenue_data) != 3:
raise ValueError("必须提供三年的收益数据")
# 计算两个增长率差值作为预测的增长率差值
growth_rate_1 = (revenue_data[1] - revenue_data[0]) / revenue_data[0] if revenue_data[0] != 0 else 0
growth_rate_2 = (revenue_data[2] - revenue_data[1]) / revenue_data[1] if revenue_data[1] != 0 else 0
# 使用两个增长率的平均值
avg_growth_rate = (growth_rate_1 + growth_rate_2) / 2
return avg_growth_rate
def calculate_legal_strength_l(self,
patent_score: float,
popularity_score: float,
infringement_score: float) -> float:
"""
计算法律强度L
法律强度L = 专利分 × 0.4 + 普及分 × 0.3 + 侵权分 × 0.3
args:
patent_score: 专利分 (0-10) (用户填写)
popularity_score: 普及地域分 (0-10) (用户填写)
infringement_score: 侵权分 (0-10) (用户填写)
returns:
float: 法律强度L
"""
legal_strength = (patent_score * 0.4 +
popularity_score * 0.3 +
infringement_score * 0.3)
return legal_strength
def calculate_development_potential_d(self,
patent_score: float,
esg_score: float,
innovation_ratio: float) -> float:
"""
计算发展潜力D
发展潜力D = 专利分 × 0.5 + ESG分 × 0.2 + 创新投入比 × 0.3
args:
patent_score: 专利分 (0-10) (用户填写)
esg_score: ESG分 (0-10) (用户填写)
innovation_ratio: 创新投入比 (研发费用/营收) * 100 (用户填写)
returns:
float: 发展潜力D
"""
development_potential = (patent_score * 0.5 +
esg_score * 0.2 +
innovation_ratio * 0.3)
return development_potential
def calculate_industry_coefficient_i(self,
target_industry_roe: float,
benchmark_industry_roe: float) -> float:
"""
计算行业系数I
行业系数I = (目标行业平均ROE - 基准行业ROE) / 基准行业ROE
args:
target_industry_roe: 目标行业平均ROE (系统配置)
benchmark_industry_roe: 基准行业ROE (系统配置)
returns:
float: 行业系数I
"""
if benchmark_industry_roe == 0:
raise ValueError("基准行业ROE不能为0")
industry_coefficient = (target_industry_roe - benchmark_industry_roe) / benchmark_industry_roe
return industry_coefficient
# 专利相关计算函数
def calculate_patent_score(patent_remaining_years: int) -> float:
"""
计算专利分
专利剩余保护期评分标准
- >10: 10
- 5-10: 7
- <5: 3
args:
patent_remaining_years: 专利剩余保护期 (用户填写)
returns:
float: 专利分
"""
if patent_remaining_years > 10:
return 10.0
elif patent_remaining_years >= 5:
return 7.0
else:
return 3.0
# 识别用户所上传的图像中的专利号通过API验证专利是否存在按所用专利数量赋分未引用0分每引用一项+2.5分10分封顶0-10分
def calculate_patent_usage_score(patent_count: int) -> float:
"""
计算专利使用量分
专利使用量评分标准
- 未引用: 0
- 每引用一项: +2.5
- 10分封顶
args:
patent_count: 专利数量 (用户填写)
returns:
float: 专利使用量分
"""
score = min(patent_count * 2.5, 10.0)
return score
# 普及地域评分
def calculate_popularity_score(region_coverage: str) -> float:
"""
计算普及地域分
全球覆盖(10)全国覆盖(7)区域覆盖(4)
args:
region_coverage: 普及地域类型 (用户填写)
returns:
float: 普及地域分
"""
coverage_scores = {
"全球覆盖": 10.0,
"全国覆盖": 7.0,
"区域覆盖": 4.0
}
return coverage_scores.get(region_coverage, 7.0)
# 侵权记录评分
def calculate_infringement_score(infringement_status: str) -> float:
"""
计算侵权记录分
无侵权记录(10)历史侵权已解决(6)现存纠纷(2)
args:
infringement_status: 侵权记录状态 (用户填写)
returns:
float: 侵权记录分
"""
infringement_scores = {
"无侵权记录": 10.0,
"历史侵权已解决": 6.0,
"现存纠纷": 2.0
}
return infringement_scores.get(infringement_status, 6.0)
# 示例使用
if __name__ == "__main__":
calculator = BasicValueB11Calculator()
# 示例数据
annual_revenue = [100, 120, 150] # 近三年收益,单位:万元 (用户填写)
patent_remaining_years = 8 # 专利剩余年限 (用户填写)
region_coverage = "全国覆盖" # 普及地域 (用户填写)
infringement_status = "无侵权记录" # 侵权记录 (用户填写)
patent_count = 2 # 专利数量 (用户填写)
esg_score = 10.0 # ESG分 (用户填写)
innovation_ratio = 5.0 # 创新投入比 (用户填写) 创新投入比(研发费用/营收)*100
target_industry_roe = 0.15 # 目标行业ROE (系统配置)
benchmark_industry_roe = 0.12 # 基准行业ROE (系统配置)
# 计算各项指标
financial_value = calculator.calculate_financial_value_f(annual_revenue)
patent_score = calculate_patent_score(patent_remaining_years)
popularity_score = calculate_popularity_score(region_coverage)
infringement_score = calculate_infringement_score(infringement_status)
legal_strength = calculator.calculate_legal_strength_l(patent_score, popularity_score, infringement_score)
patent_usage_score = calculate_patent_usage_score(patent_count)
development_potential = calculator.calculate_development_potential_d(patent_usage_score, esg_score,
innovation_ratio)
industry_coefficient = calculator.calculate_industry_coefficient_i(target_industry_roe, benchmark_industry_roe)
# 计算基础价值B11
basic_value = calculator.calculate_basic_value_b11(
financial_value, legal_strength, development_potential, industry_coefficient
)
print(f"财务价值F: {financial_value:.2f}")
print(f"法律强度L: {legal_strength:.2f}")
print(f"发展潜力D: {development_potential:.2f}")
print(f"行业系数I: {industry_coefficient:.4f}")
print(f"基础价值B11: {basic_value:.2f}")

View File

@ -0,0 +1,134 @@
class PolicyMultiplierB13Calculator:
"""政策乘数B13计算器"""
def __init__(self):
"""初始化计算器"""
pass
def calculate_policy_multiplier_b13(self, policy_compatibility_score: float) -> float:
"""
计算政策乘数B13
政策乘数B13 = 1 + (政策契合度评分P × 0.15)
Args:
policy_compatibility_score: 政策契合度评分P (系统计算)
returns:
float: 政策乘数B13
"""
#
policy_multiplier = 1 + (policy_compatibility_score * 0.15)
return policy_multiplier
def calculate_policy_compatibility_score(self,
policy_match_score: float,
implementation_stage_score: float,
funding_support_score: float) -> float:
"""
计算政策契合度评分P
政策契合度P = 政策匹配度 × 0.4 + 实施阶段评分 × 0.3 + 资金支持度 × 0.3
Args:
policy_match_score: 政策匹配度 (系统配置)
implementation_stage_score: 实施阶段评分 (用户填写)
funding_support_score: 资金支持度 (用户填写)
returns:
float: 政策契合度评分P
"""
#
policy_compatibility = (policy_match_score * 0.4 +
implementation_stage_score * 0.3 +
funding_support_score * 0.3)
return policy_compatibility
def calculate_policy_match_score(self, industry: str) -> float:
"""
计算政策匹配度
系统内根据行业自动匹配政策匹配度
Args:
industry: 所属行业 (用户填写)
returns:
float: 政策匹配度
"""
return 5
def calculate_implementation_stage_score(self, implementation_stage: str) -> float:
"""
计算实施阶段评分
用户选择目前资产的实施阶段成熟应用(10)推广阶段(7)试点阶段(4)
Args:
implementation_stage: 实施阶段 (用户填写)
returns:
float: 实施阶段评分
"""
stage_scores = {
"成熟应用": 10.0,
"推广阶段": 7.0,
"试点阶段": 4.0
}
return stage_scores.get(implementation_stage, 10.0)
def calculate_funding_support_score(self, funding_support: str) -> float:
"""
计算资金支持度
用户选择目前资产的资金支持情况国家级资助(10)省级资助(7)无资助(0)
Args:
funding_support: 资金支持情况 (用户填写)
returns:
float: 资金支持度
"""
funding_scores = {
"国家级资助": 10.0,
"省级资助": 7.0,
"无资助": 0.0
}
return funding_scores.get(funding_support, 0.0)
# 示例使用
if __name__ == "__main__":
calculator = PolicyMultiplierB13Calculator()
# 示例数据
industry = "文化艺术" # 所属行业 (用户填写)
implementation_stage = "成熟应用" # 实施阶段 (用户填写)
funding_support = "国家级资助" # 资金支持 (用户填写)
# 计算各项指标
policy_match_score = calculator.calculate_policy_match_score(industry)
implementation_stage_score = calculator.calculate_implementation_stage_score(implementation_stage)
funding_support_score = calculator.calculate_funding_support_score(funding_support)
# 计算政策契合度评分P
policy_compatibility_score = calculator.calculate_policy_compatibility_score(
policy_match_score, implementation_stage_score, funding_support_score
)
# 计算政策乘数B13
policy_multiplier = calculator.calculate_policy_multiplier_b13(policy_compatibility_score)
print(f"政策匹配度: {policy_match_score:.2f}")
print(f"实施阶段评分: {implementation_stage_score:.2f}")
print(f"资金支持度: {funding_support_score:.2f}")
print(f"政策契合度评分P: {policy_compatibility_score:.2f}")
print(f"政策乘数B13: {policy_multiplier:.4f}")

View File

@ -0,0 +1,305 @@
import math
from typing import Dict, Tuple
class TrafficFactorB12Calculator:
"""流量因子B12计算器"""
def __init__(self):
"""初始化计算器"""
pass
def calculate_traffic_factor_b12(self,
search_index_s1: float,
industry_average_s2: float,
social_media_spread_s3: float) -> float:
"""
计算流量因子B12
流量因子B12 = ln(近30天搜索指数S1/行业均值S2) × 0.3 + 社交媒体传播度S3 × 0.7
args:
search_index_s1: 近30天搜索指数S1 (API获取)
industry_average_s2: 行业均值S2 (系统配置)
social_media_spread_s3: 社交媒体传播度S3 (API获取)
returns:
float: 流量因子B12
"""
# 避免除零和对数计算错误
if industry_average_s2 <= 0:
raise ValueError("行业均值S2必须大于0")
if search_index_s1 <= 0:
# 如果搜索指数为0或负数使用最小值避免对数计算错误
search_index_s1 = 1.0
# ,不进行任何拆分
traffic_factor = (math.log(search_index_s1 / industry_average_s2) * 0.3 +
social_media_spread_s3 * 0.7)
return traffic_factor
def calculate_social_media_spread_s3(self,
interaction_index: float,
coverage_index: float,
conversion_efficiency: float) -> float:
"""
计算社交媒体传播度S3
社交媒体传播度S3 = 互动量指数 × 0.4 + 覆盖人群指数 × 0.3 + 转化效率 × 0.3
args:
interaction_index: 互动量指数 (API获取)
coverage_index: 覆盖人群指数 (API获取)
conversion_efficiency: 转化效率 (用户填写)
returns:
float: 社交媒体传播度S3
"""
#
social_media_spread = (interaction_index * 0.4 +
coverage_index * 0.3 +
conversion_efficiency * 0.3)
return social_media_spread
def calculate_interaction_index(self,
likes: int,
comments: int,
shares: int) -> float:
"""
计算互动量指数
互动量指数 = (点赞 + 评论 + 转发) / 1000
args:
likes: 点赞数 (API获取)
comments: 评论数 (API获取)
shares: 转发数 (API获取)
returns:
float: 互动量指数
"""
#
interaction_index = (likes + comments + shares) / 1000.0
return interaction_index
def calculate_coverage_index(self, followers: int) -> float:
"""
计算覆盖人群指数
覆盖人群指数 = 粉丝数 / 10000
args:
followers: 粉丝数 (API获取)
returns:
float: 覆盖人群指数
"""
#
coverage_index = followers / 10000.0
return coverage_index
def calculate_conversion_efficiency(self,
click_count: int,
view_count: int) -> float:
"""
计算转化效率
转化效率 = 商品链接点击量 / 内容浏览量
args:
click_count: 商品链接点击量 (用户填写)
view_count: 内容浏览量 (用户填写)
returns:
float: 转化效率
"""
if view_count == 0:
return 0.0
#
conversion_efficiency = click_count / view_count
return conversion_efficiency
class PlatformDataProcessor:
"""平台数据处理类"""
@staticmethod
def parse_platform_accounts(platform_data: str) -> Dict[str, str]:
"""
解析平台账号数据
输入格式B站12345678\n抖音22334455
args:
platform_data: 平台账号数据字符串 (用户填写)
returns:
Dict[str, str]: 平台账号字典
"""
accounts = {}
lines = platform_data.strip().split('\n')
for line in lines:
if '' in line or ':' in line:
separator = '' if '' in line else ':'
parts = line.split(separator, 1)
if len(parts) == 2:
platform = parts[0].strip()
account = parts[1].strip()
accounts[platform] = account
return accounts
@staticmethod
def calculate_multi_platform_interaction(platform_data: Dict[str, Dict]) -> Tuple[float, float]:
"""
计算多平台互动数据
args:
platform_data: 平台数据字典格式为 {平台名: {likes: int, comments: int, shares: int, followers: int}} (API获取)
returns:
Tuple[float, float]: (互动量指数, 覆盖人群指数)
"""
total_interactions = 0
total_followers = 0
for platform, data in platform_data.items():
# 计算互动量
interactions = data.get('likes', 0) + data.get('comments', 0) + data.get('shares', 0)
total_interactions += interactions
# 计算粉丝数
followers = data.get('followers', 0)
total_followers += followers
# 计算指数
calculator = TrafficFactorB12Calculator()
interaction_index = calculator.calculate_interaction_index(
total_interactions, 0, 0 # 已经包含了所有互动数据
)
coverage_index = calculator.calculate_coverage_index(total_followers)
return interaction_index, coverage_index
# 热度评分相关函数
def calculate_heat_score(daily_views: float, favorites: int) -> float:
"""
计算热度分
热度评分标准
- 高热度近7日日均浏览量 > 1000或收藏数 > 100 1.0
- 中热度近7日日均浏览量 [100, 1000]或收藏数 [20, 100] 0.6
- 低热度近7日日均浏览量 < 100且收藏数 < 20 0.2
- 无数据无任何浏览与互动数据 0.0
args:
daily_views: 近7日日均浏览量 (API获取)
favorites: 收藏数 (API获取)
returns:
float: 热度分
"""
if daily_views > 1000 or favorites > 100:
return 1.0
elif (daily_views >= 100 and daily_views <= 1000) or (favorites >= 20 and favorites <= 100):
return 0.6
elif daily_views < 100 and favorites < 20:
return 0.2
else:
return 0.0
# 30天搜索指数S1
def calculate_search_index_s1(baidu_index: float,
wechat_index: float,
weibo_index: float) -> float:
"""
计算近30天搜索指数S1
近30天搜索指数S1 = 百度搜索指数 × 0.4 + 微信搜索指数 × 0.3 + 微博搜索指数 × 0.3
args:
baidu_index: 百度搜索指数 (API获取)
wechat_index: 微信搜索指数 (API获取)
weibo_index: 微博搜索指数 (API获取)
returns:
float: 近30天搜索指数S1
"""
#
search_index = (baidu_index * 0.4 +
wechat_index * 0.3 +
weibo_index * 0.3)
return search_index
# 示例使用
if __name__ == "__main__":
calculator = TrafficFactorB12Calculator()
processor = PlatformDataProcessor()
# 示例数据
# 搜索指数数据 (API获取)
baidu_index = 6000.0
wechat_index = 4500.0
weibo_index = 3000.0
# 行业均值 (系统配置)
industry_average = 5000.0
# 平台账号数据 (用户填写)
platform_accounts = "B站12345678\n抖音22334455"
accounts = processor.parse_platform_accounts(platform_accounts)
# 平台互动数据 (API获取)
platform_data = {
"B站": {
"likes": 1000,
"comments": 200,
"shares": 50,
"followers": 50000
},
"抖音": {
"likes": 2000,
"comments": 300,
"shares": 100,
"followers": 30000
}
}
# 转化效率数据 (用户填写)
click_count = 20
view_count = 200
# 计算各项指标
search_index_s1 = calculator.calculate_search_index_s1(baidu_index, wechat_index, weibo_index)
interaction_index, coverage_index = processor.calculate_multi_platform_interaction(platform_data)
conversion_efficiency = calculator.calculate_conversion_efficiency(click_count, view_count)
social_media_spread_s3 = calculator.calculate_social_media_spread_s3(
interaction_index, coverage_index, conversion_efficiency
)
# 计算流量因子B12
traffic_factor = calculator.calculate_traffic_factor_b12(
search_index_s1, industry_average, social_media_spread_s3
)
print(f"近30天搜索指数S1: {search_index_s1:.2f}")
print(f"行业均值S2: {industry_average:.2f}")
print(f"互动量指数: {interaction_index:.4f}")
print(f"覆盖人群指数: {coverage_index:.4f}")
print(f"转化效率: {conversion_efficiency:.4f}")
print(f"社交媒体传播度S3: {social_media_spread_s3:.4f}")
print(f"流量因子B12: {traffic_factor:.4f}")

View File

@ -0,0 +1,18 @@
"""
最终估值A计算包
最终估值A = 模型估值B × 0.7 + 市场估值C × 0.3
```
"""
from .final_value_a import FinalValueACalculator
__all__ = [
"FinalValueACalculator"
]

View File

@ -0,0 +1,166 @@
"""
最终估值A计算模块
最终估值A = 模型估值B × 0.7 + 市场估值C × 0.3
"""
from typing import Dict
import os
import sys
current_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.abspath(os.path.join(current_dir, os.pardir, os.pardir, os.pardir, os.pardir))
if project_root not in sys.path:
sys.path.append(project_root)
try:
# 包内相对导入
from .model_value_b import ModelValueBCalculator
from ..market_value_c import MarketValueCCalculator
except ImportError:
# 直接运行时的绝对导入
from app.utils.calculation_engine.final_value_ab.model_value_b import ModelValueBCalculator
from app.utils.calculation_engine.market_value_c import MarketValueCCalculator
class FinalValueACalculator:
"""最终估值A计算器"""
def __init__(self):
"""初始化计算器"""
self.model_value_calculator = ModelValueBCalculator()
self.market_value_calculator = MarketValueCCalculator()
def calculate_final_value_a(self,
model_value_b: float,
market_value_c: float) -> float:
"""
计算最终估值A
最终估值A = 模型估值B × 0.7 + 市场估值C × 0.3
model_value_b: 模型估值B (系统计算)
market_value_c: 市场估值C (系统计算)
float: 最终估值A
"""
final_value = model_value_b * 0.7 + market_value_c * 0.3
return final_value
def calculate_complete_final_value_a(self, input_data: Dict) -> Dict:
"""
计算完整的最终估值A包含所有子模块
input_data: 输入数据字典包含所有必要的参数
包含所有中间计算结果和最终结果的字典
"""
# 计算模型估值B
model_result = self.model_value_calculator.calculate_complete_model_value_b(
input_data['model_data']
)
model_value_b = model_result['model_value_b']
# 计算市场估值C
market_result = self.market_value_calculator.calculate_complete_market_value_c(
input_data['market_data']
)
market_value_c = market_result['market_value_c']
# 计算最终估值A
final_value_a = self.calculate_final_value_a(
model_value_b,
market_value_c
)
return {
'model_value_b': model_value_b,
'market_value_c': market_value_c,
'final_value_ab': final_value_a,
}
# 示例使用
if __name__ == "__main__":
# 创建计算器实例
calculator = FinalValueACalculator()
# 示例数据
input_data = {
# 模型估值B相关数据
'model_data': {
'economic_data': {
# 基础价值B11相关参数
'three_year_income': [1000, 2000, 2222],
'patent_score': 1, # 专利分 (0-10)
'popularity_score': 4.0, # 普及地域分 (0-10)
'infringement_score': 1.0, # 侵权分 (0-10)
'innovation_ratio': 600.0, # 创新投入比 (×100)
'esg_score': 10.0, # ESG分 (0-10)
'patent_count':0.8,
'industry_coefficient': 12.0,
# 流量因子B12相关参数
'search_index_s1': 4500.0,
'industry_average_s2': 5000.0,
'likes': 4,
'comments': 5,
'shares': 6,
# 政策乘数B13相关参数
'policy_match_score': 10.0,
'implementation_stage': 10.0,
'funding_support': 10.0
},
'cultural_data': {
# 活态传承系数B21相关参数教学频次由以下数据计算
'inheritor_level_coefficient': 10.0,
'cross_border_depth': 7.0,
'offline_sessions': 1,
'douyin_views': 2,
'kuaishou_views': 3,
'bilibili_views': 4,
# 纹样基因值B22相关参数
'structure_complexity': 0.75,
'normalized_entropy': 0.85,
'historical_inheritance': 0.80
},
'risky_data': {
# 风险调整系数B3相关参数
'highest_price': 100.0,
'lowest_price': 95.0,
'lawsuit_status': '无诉讼',
'inheritor_ages': [45, 60, 75]
}
},
# 市场估值C相关数据
'market_data': {
'average_transaction_price': 50000.0,
'manual_bids': [48000.0, 50000.0, 52000.0],
'expert_valuations': [49000.0, 51000.0, 50000.0],
'daily_browse_volume': 500.0,
'collection_count': 50,
'issuance_level': '限量',
'recent_market_activity': '2024-01-15'
}
}
# 计算最终估值A
result = calculator.calculate_complete_final_value_a(input_data)
print("最终估值A计算结果:")
print(f"模型估值B: {result['model_value_b']:.2f}")
print(f"市场估值C: {result['market_value_c']:.2f}")
print(f"最终估值A: {result['final_value_ab']:.2f}")

View File

@ -0,0 +1,127 @@
"""
模型估值B计算模块
模型估值B = 经济价值B1*0.7+文化价值B2*0.3*风险调整系数B3
"""
from typing import Dict
from app.utils.calculation_engine import RiskAdjustmentB3Calculator
try:
# 相对导入(当作为包使用时)
from ..economic_value_b1.economic_value_b1 import EconomicValueB1Calculator
from ..cultural_value_b2.cultural_value_b2 import CulturalValueB2Calculator
except ImportError:
# 绝对导入(当直接运行时)
from app.utils.calculation_engine.economic_value_b1.economic_value_b1 import EconomicValueB1Calculator
from app.utils.calculation_engine.cultural_value_b2.cultural_value_b2 import CulturalValueB2Calculator
class ModelValueBCalculator:
"""模型估值B计算器"""
def __init__(self):
"""初始化计算器"""
self.economic_value_calculator = EconomicValueB1Calculator()
self.cultural_value_calculator = CulturalValueB2Calculator()
self.risk_adjustment_calculator = RiskAdjustmentB3Calculator()
def calculate_model_value_b(self,
economic_value_b1: float,
cultural_value_b2: float,
risk_value_b3: float) -> float:
"""
模型估值B = 经济价值B1*0.7+文化价值B2*0.3*风险调整系数B3
Args:
economic_value_b1: 经济价值B1
cultural_value_b2: 文化价值B2
risk_value_b3 : 风险调整系数B3
Returns:
float: 模型估值B
"""
model_value = (economic_value_b1 * 0.7 + cultural_value_b2 * 0.3) * risk_value_b3
return model_value
def calculate_complete_model_value_b(self, input_data: Dict) -> Dict:
"""
计算完整的模型估值B包含所有子公式
Args:
input_data: 输入数据字典包含所有必要的参数
Returns:
Dict: 包含所有中间计算结果和最终结果的字典
"""
# 计算经济价值B1
economic_result = self.economic_value_calculator.calculate_complete_economic_value_b1(
input_data['economic_data']
)
economic_value_b1 = economic_result['economic_value_b1']
# 计算文化价值B2
cultural_result = self.cultural_value_calculator.calculate_complete_cultural_value_b2(
input_data['cultural_data']
)
cultural_value_b2 = cultural_result['cultural_value_b2']
risk_value_result = self.risk_adjustment_calculator.calculate_complete_risky_value_b3(
input_data['risky_data']
)
risk_value_b3 = risk_value_result['risk_adjustment_b3']
# 计算模型估值B
model_value_b = self.calculate_model_value_b(
economic_value_b1,
cultural_value_b2,
risk_value_b3
)
return {
'economic_value_b1': economic_value_b1,
'cultural_value_b2': cultural_value_b2,
'risk_value_b3': risk_value_b3,
'model_value_b': model_value_b,
}
# 示例使用
if __name__ == "__main__":
# 创建计算器实例
calculator = ModelValueBCalculator()
# 示例数据
input_data = {
# 经济价值B1相关数据
'economic_data': {
'financial_value': 68.04,
'legal_strength': 7.90,
'development_potential': 6.00,
'industry_coefficient': 0.25,
'search_index_s1': 4500.0,
'industry_average_s2': 5000.0,
'social_media_spread_s3': 1.07,
'policy_compatibility_score': 9.1
},
# 文化价值B2相关数据
'cultural_data': {
'inheritor_level_coefficient': 10.0,
'teaching_frequency': 7.0,
'cross_border_depth': 7.0,
'structure_complexity': 0.75,
'normalized_entropy': 0.85,
'historical_inheritance': 0.80
}
}
# 计算模型估值B
result = calculator.calculate_complete_model_value_b(input_data)
print("模型估值B计算结果:")
print(f"经济价值B1: {result['economic_value_b1']:.2f}")
print(f"文化价值B2: {result['cultural_value_b2']:.4f}")
print(f"模型估值B: {result['model_value_b']:.2f}")

View File

@ -0,0 +1,16 @@
from .market_value_c import MarketValueCCalculator
from .sub_formulas import (
MarketBiddingC1Calculator,
HeatCoefficientC2Calculator,
ScarcityMultiplierC3Calculator,
TemporalDecayC4Calculator
)
__all__ = [
"MarketValueCCalculator",
"MarketBiddingC1Calculator",
"HeatCoefficientC2Calculator",
"ScarcityMultiplierC3Calculator",
"TemporalDecayC4Calculator"
]

View File

@ -0,0 +1,150 @@
from typing import Dict, List, Optional
import sys
import os
# 添加当前目录到Python路径确保能正确导入子模块
current_dir = os.path.dirname(os.path.abspath(__file__))
if current_dir not in sys.path:
sys.path.append(current_dir)
try:
# 相对导入(当作为包使用时)
from .sub_formulas.market_bidding_c1 import MarketBiddingC1Calculator
from .sub_formulas.heat_coefficient_c2 import HeatCoefficientC2Calculator
from .sub_formulas.scarcity_multiplier_c3 import ScarcityMultiplierC3Calculator
from .sub_formulas.temporal_decay_c4 import TemporalDecayC4Calculator
except ImportError:
# 绝对导入(当直接运行时)
from sub_formulas.market_bidding_c1 import MarketBiddingC1Calculator
from sub_formulas.heat_coefficient_c2 import HeatCoefficientC2Calculator
from sub_formulas.scarcity_multiplier_c3 import ScarcityMultiplierC3Calculator
from sub_formulas.temporal_decay_c4 import TemporalDecayC4Calculator
class MarketValueCCalculator:
"""市场估值C计算器"""
def __init__(self):
"""初始化计算器"""
self.market_bidding_calculator = MarketBiddingC1Calculator()
self.heat_coefficient_calculator = HeatCoefficientC2Calculator()
self.scarcity_multiplier_calculator = ScarcityMultiplierC3Calculator()
self.temporal_decay_calculator = TemporalDecayC4Calculator()
def calculate_market_value_c(self,
market_bidding_c1: float,
heat_coefficient_c2: float,
scarcity_multiplier_c3: float,
temporal_decay_c4: float) -> float:
"""
计算市场估值C
市场估值C = 市场竞价C1 × 热度系数C2 × 稀缺性乘数C3 × 时效性衰减C4
args:
market_bidding_c1: 市场竞价C1 (系统计算)
heat_coefficient_c2: 热度系数C2 (系统计算)
scarcity_multiplier_c3: 稀缺性乘数C3 (系统计算)
temporal_decay_c4: 时效性衰减C4 (系统计算)
return:
float: 市场估值C
"""
market_value = (market_bidding_c1 * heat_coefficient_c2 *
scarcity_multiplier_c3 * temporal_decay_c4)
return market_value
def calculate_complete_market_value_c(self, input_data: Dict) -> Dict:
"""
计算完整的市场估值C包含所有子公式
args:
input_data: 输入数据字典包含所有必要的参数
参数来源标记用户填写/系统配置/API获取/系统计算
- average_transaction_price: 系统计算(基于用户填写/API获取)
- market_activity_coefficient: 系统计算(基于用户填写)
- daily_browse_volume: API获取/系统估算
- collection_count: API获取/系统估算
- issuance_level: 用户填写
- recent_market_activity: 用户填写
- issuance_scarcity/circulation_scarcity/uniqueness_scarcity: 系统配置/系统计算保留向后兼容
return:
Dict: 包含所有中间计算结果和最终结果的字典
"""
# 计算市场竞价C1
market_bidding_c1 = self.market_bidding_calculator.calculate_market_bidding_c1(
transaction_data={'weighted_average_price': input_data.get('average_transaction_price', 50000.0)},
manual_bids=input_data.get('manual_bids', [input_data.get('average_transaction_price', 50000.0)]),
expert_valuations=input_data.get('expert_valuations', [input_data.get('average_transaction_price', 50000.0)])
)
# 计算热度系数C2
heat_coefficient_c2 = self.heat_coefficient_calculator.calculate_heat_coefficient_c2(
input_data.get('daily_browse_volume', 500.0),
input_data.get('collection_count', 50)
)
# 计算稀缺性乘数C3
scarcity_multiplier_c3 = self.scarcity_multiplier_calculator.calculate_scarcity_multiplier_c3(
input_data.get('issuance_level', '限量')
)
# 计算时效性衰减C4
temporal_decay_c4 = self.temporal_decay_calculator.calculate_temporal_decay_c4(
input_data.get('recent_market_activity', '2024-01-15')
)
# 计算市场估值C
market_value_c = self.calculate_market_value_c(
market_bidding_c1,
heat_coefficient_c2,
scarcity_multiplier_c3,
temporal_decay_c4
)
return {
'market_bidding_c1': market_bidding_c1,
'heat_coefficient_c2': heat_coefficient_c2,
'scarcity_multiplier_c3': scarcity_multiplier_c3,
'temporal_decay_c4': temporal_decay_c4,
'market_value_c': market_value_c
}
# 示例使用
if __name__ == "__main__":
# 创建计算器实例
calculator = MarketValueCCalculator()
# 示例数据
input_data = {
# 市场竞价C1相关参数
'average_transaction_price': 50000.0, # 平均交易价格
'manual_bids': [48000.0, 50000.0, 52000.0], # 手动收集的竞价
'expert_valuations': [49000.0, 51000.0, 50000.0], # 专家估值
# 热度系数C2相关参数
'daily_browse_volume': 500.0, # 近7日日均浏览量
'collection_count': 50, # 收藏数
# 稀缺性乘数C3相关参数
'issuance_level': '限量', # 资产发行等级
# 时效性衰减C4相关参数
'recent_market_activity': '2024-01-15' # 最近市场活动时间
}
# 计算市场估值C
result = calculator.calculate_complete_market_value_c(input_data)
print("市场估值C计算结果:")
print(f"市场竞价C1: {result['market_bidding_c1']:.2f}")
print(f"热度系数C2: {result['heat_coefficient_c2']:.4f}")
print(f"稀缺性乘数C3: {result['scarcity_multiplier_c3']:.4f}")
print(f"时效性衰减C4: {result['temporal_decay_c4']:.4f}")
print(f"市场估值C: {result['market_value_c']:.2f}")

View File

@ -0,0 +1,13 @@
from .market_bidding_c1 import MarketBiddingC1Calculator
from .heat_coefficient_c2 import HeatCoefficientC2Calculator
from .scarcity_multiplier_c3 import ScarcityMultiplierC3Calculator
from .temporal_decay_c4 import TemporalDecayC4Calculator
__all__ = [
"MarketBiddingC1Calculator",
"HeatCoefficientC2Calculator",
"ScarcityMultiplierC3Calculator",
"TemporalDecayC4Calculator"
]

View File

@ -0,0 +1,87 @@
class HeatCoefficientC2Calculator:
"""热度系数C2计算器"""
def __init__(self):
"""初始化计算器"""
pass
def calculate_heat_coefficient_c2(self,
daily_browse_volume: float,
collection_count: int) -> float:
"""
计算热度系数C2
热度系数C2 = 1 + 浏览热度分
args:
daily_browse_volume: 近7日日均浏览量 (API获取)
collection_count: 收藏数 (API获取)
return:
float: 热度系数C2
"""
# 计算浏览热度分
browse_heat_score = self.calculate_browse_heat_score(daily_browse_volume, collection_count)
heat_coefficient = 1 + browse_heat_score
return heat_coefficient
def calculate_browse_heat_score(self, daily_browse_volume: float, collection_count: int) -> float:
"""
计算浏览热度分
根据所查询到的浏览量匹配热度分
高热度 近7日日均浏览量 > 1000或收藏数 > 100 1.0
中热度 近7日日均浏览量 [100, 1000]或收藏数 [20, 100] 0.6
低热度 近7日日均浏览量 < 100且收藏数 < 20 0.2
无数据 无任何浏览与互动数据 0.0默认值
args:
daily_browse_volume: 近7日日均浏览量 (API获取)
collection_count: 收藏数 (API获取)
return:
float: 浏览热度分
"""
# 高热度条件
if daily_browse_volume > 1000 or collection_count > 100:
return 1.0
# 中热度条件
elif (daily_browse_volume >= 100 and daily_browse_volume <= 1000) or \
(collection_count >= 20 and collection_count <= 100):
return 0.6
# 低热度条件
elif daily_browse_volume < 100 and collection_count < 20:
return 0.2
# 无数据
else:
return 0.0
# 示例使用
if __name__ == "__main__":
# 创建计算器实例
calculator = HeatCoefficientC2Calculator()
# 示例数据
daily_browse_volume = 500.0 # 近7日日均浏览量 (API获取)
collection_count = 50 # 收藏数 (API获取)
# 计算热度系数C2
heat_coefficient_c2 = calculator.calculate_heat_coefficient_c2(
daily_browse_volume, collection_count
)
print(f"近7日日均浏览量: {daily_browse_volume}")
print(f"收藏数: {collection_count}")
print(f"浏览热度分: {calculator.calculate_browse_heat_score(daily_browse_volume, collection_count):.1f}")
print(f"热度系数C2: {heat_coefficient_c2:.4f}")

View File

@ -0,0 +1,92 @@
from typing import Dict, List
class MarketBiddingC1Calculator:
"""市场竞价C1计算器"""
def __init__(self):
"""初始化计算器"""
pass
def calculate_market_bidding_c1(self,
transaction_data: Dict = None,
manual_bids: List[float] = None,
expert_valuations: List[float] = None) -> float:
"""
计算市场竞价C1
1)取同一资产或同品类同等级资产最近3个月内成功交易的加权平均价格
2)人工采集主流文化产权交易所拍卖平台如阿里拍卖京东拍卖上相似资产的当前报价或起拍价取其中位数
3)若以上数据均缺失由系统内置专家库中随机指派3位专家独立给出估值取平均值
args:
transaction_data: 交易数据字典 (API获取)
manual_bids: 手动收集的竞价列表 (用户填写)
expert_valuations: 专家估值列表 (系统配置)
retusn:
float: 市场竞价C1
"""
# 优先级1近3个月交易数据
if transaction_data and transaction_data.get('weighted_average_price'):
return transaction_data['weighted_average_price']
# 优先级2手动收集的竞价数据
if manual_bids and len(manual_bids) > 0:
# 取中位数
sorted_bids = sorted(manual_bids)
n = len(sorted_bids)
if n % 2 == 0:
return (sorted_bids[n//2-1] + sorted_bids[n//2]) / 2
else:
return sorted_bids[n//2]
# 优先级3专家估值
if expert_valuations and len(expert_valuations) > 0:
return sum(expert_valuations) / len(expert_valuations)
# 默认值
return 0.0
# 示例使用
if __name__ == "__main__":
# 创建计算器实例
calculator = MarketBiddingC1Calculator()
# 示例数据
asset_type = "传统工艺" # 资产类型 (用户填写)
time_period = "近一年" # 时间周期 (用户填写)
transaction_volume = 500 # 交易量 (API获取)
transaction_frequency = "中频" # 交易频率 (用户填写)
market_liquidity = "" # 市场流动性 (用户填写)
# 获取平均交易价格
average_transaction_price = calculator.get_average_transaction_price(asset_type, time_period)
# 计算市场活跃度系数
market_activity_coefficient = calculator.calculate_market_activity_coefficient(
transaction_volume, transaction_frequency, market_liquidity
)
# 示例数据
# 优先级1近3个月交易数据
transaction_data = {"weighted_average_price": 1000.0} # API获取
# 优先级2手动收集的竞价数据
manual_bids = [950.0, 1000.0, 1050.0, 1100.0] # 用户填写
# 优先级3专家估值
expert_valuations = [980.0, 1020.0, 990.0] # 系统配置
# 计算市场竞价C1
market_bidding_c1 = calculator.calculate_market_bidding_c1(
transaction_data, manual_bids, expert_valuations
)
print(f"近3个月交易数据: {transaction_data}")
print(f"手动收集竞价: {manual_bids}")
print(f"专家估值: {expert_valuations}")
print(f"市场竞价C1: {market_bidding_c1:.2f}")

View File

@ -0,0 +1,71 @@
class ScarcityMultiplierC3Calculator:
"""稀缺性乘数C3计算器"""
def __init__(self):
"""初始化计算器"""
pass
def calculate_scarcity_multiplier_c3(self, issuance_level: str) -> float:
"""
计算稀缺性乘数C3
稀缺性乘数C3 = 1 + 稀缺等级分
args:
issuance_level: 资产发行等级 (用户选择)
Returns:
float: 稀缺性乘数C3
"""
# 计算稀缺等级分
scarcity_level_score = self.calculate_scarcity_level_score(issuance_level)
scarcity_multiplier = 1 + scarcity_level_score
return scarcity_multiplier
def calculate_scarcity_level_score(self, issuance_level: str) -> float:
"""
计算稀缺等级分
根据用户所选择的资产发行量匹配稀缺等级
孤品 全球唯一不可复制如特定版权唯一实物 1.0
限量 总发行份数 100 0.7
稀有 总发行份数 (100, 1000]或二级市场流通率 < 5% 0.4
流通 总发行份数 > 1000或二级市场流通率 5% 0.1
args:
issuance_level: 资产发行等级 (用户选择)
Returns:
float: 稀缺等级分
"""
scarcity_scores = {
"孤品": 1.0,
"限量": 0.7,
"稀有": 0.4,
"流通": 0.1
}
return scarcity_scores.get(issuance_level, 0.1)
# 示例使用
if __name__ == "__main__":
# 创建计算器实例
calculator = ScarcityMultiplierC3Calculator()
# 示例数据
issuance_level = "限量" # 资产发行等级 (用户选择)
# 计算稀缺性乘数C3
scarcity_multiplier_c3 = calculator.calculate_scarcity_multiplier_c3(issuance_level)
print(f"资产发行等级: {issuance_level}")
print(f"稀缺等级分: {calculator.calculate_scarcity_level_score(issuance_level):.1f}")
print(f"稀缺性乘数C3: {scarcity_multiplier_c3:.4f}")

View File

@ -0,0 +1,89 @@
from datetime import datetime, timedelta
class TemporalDecayC4Calculator:
"""时效性衰减C4计算器"""
def __init__(self):
"""初始化计算器"""
pass
def calculate_temporal_decay_c4(self, time_elapsed: str) -> float:
"""
计算时效性衰减C4
近一周1.0
近一月0.7
近一年0.4
其他0.1
基于最近一次市场活动交易报价鉴定距今天数
args:
time_elapsed: 距离最近市场活动的时间 (用户选择)
retuen:
float: 时效性衰减C4
"""
timeliness_coefficients = {
"近一周": 1.0,
"近一月": 0.7,
"近一年": 0.4,
"其他": 0.1
}
return timeliness_coefficients.get(self.classify_date_distance(time_elapsed), 0.1)
@staticmethod
def classify_date_distance(target_date):
"""
计算日期距离今天是近一周近一月近一年还是其他
target_date: 目标日期可以是字符串或datetime对象
字符串格式支持'YYYY-MM-DD', 'YYYY/MM/DD', 'YYYYMMDD'
"""
# 获取当前日期
today = datetime.now().date()
# 处理输入日期
if isinstance(target_date, str):
# 尝试不同的日期格式
formats = ['%Y-%m-%d', '%Y/%m/%d', '%Y%m%d']
date_obj = None
for fmt in formats:
try:
date_obj = datetime.strptime(target_date, fmt).date()
break
except ValueError:
continue
if date_obj is None:
raise ValueError(f"无法解析日期: {target_date}")
elif isinstance(target_date, datetime):
date_obj = target_date.date()
else:
date_obj = target_date
# 计算日期差
delta = today - date_obj
# 分类判断
if delta.days <= 7:
return "近一周"
elif delta.days <= 30:
return "近一月"
elif delta.days <= 365:
return "近一年"
else:
return "其他"
# 示例使用
if __name__ == "__main__":
# 创建计算器实例
calculator = TemporalDecayC4Calculator()
# 示例数据
time_elapsed = "2024-01-15" # 距离最近市场活动的时间 (用户选择)
# 计算时效性衰减C4
temporal_decay_c4 = calculator.calculate_temporal_decay_c4(time_elapsed)
print(f"距离最近市场活动时间: {time_elapsed}")
print(f"时效性衰减C4: {temporal_decay_c4:.4f}")

View File

@ -0,0 +1,6 @@
from .sub_formulas.risk_adjustment_b3 import RiskAdjustmentB3Calculator
__all__ = [
"RiskAdjustmentB3Calculator"
]

View File

@ -0,0 +1,17 @@
"""
1. risk_adjustment_b3: 风险调整系数B3计算
- 风险评分总和R = 市场风险 × 0.3 + 法律风险 × 0.4 + 传承风险 × 0.3
- 市场风险资产商品价格波动率 (波动率5%:10, 5-15%:5, >15%:0)
- 法律风险侵权诉讼历史 (无诉讼:10, 已解决:7, 未解决:0)
- 传承风险传承人年龄 (50:10, 50-70:5, >70:0)
- 风险调整系数B3 = 0.8 + 风险评分总和R × 0.4
"""
from .risk_adjustment_b3 import RiskAdjustmentB3Calculator
__all__ = [
"RiskAdjustmentB3Calculator"
]

View File

@ -0,0 +1,206 @@
"""
风险调整系数B3计算模块
风险调整系数B3 = 0.8 + 风险评分总和R × 0.4
"""
from typing import Dict, List
class RiskAdjustmentB3Calculator:
"""风险调整系数B3计算器"""
def __init__(self):
pass
def calculate_risk_adjustment_b3(self, risk_score_sum: float) -> float:
"""
计算风险调整系数B3
风险调整系数B3 = 0.8 + 风险评分总和R × 0.4
args:
risk_score_sum: 风险评分总和R (系统计算)
return:
float: 风险调整系数B3
"""
risk_adjustment = 0.8 + risk_score_sum * 0.4
return risk_adjustment
def calculate_risk_score_sum(self, market_risk: float, legal_risk: float, inheritance_risk: float) -> float:
"""
计算风险评分总和R
风险评分总和R = 市场风险 × 0.3 + 法律风险 × 0.4 + 传承风险 × 0.3
args:
market_risk: 市场风险 (用户填写)
legal_risk: 法律风险 (用户填写)
inheritance_risk: 传承风险 (用户填写)
return:
float: 风险评分总和R
"""
risk_score_sum = market_risk * 0.3 + legal_risk * 0.4 + inheritance_risk * 0.3
return risk_score_sum
def calculate_market_risk(self, highest_price: float, lowest_price: float) -> float:
"""
计算市场风险
用户填写近30天资产商品的最高价与最低价系统自动计算波动率波动率=近30日价格极差/均值5%(10)5-15%(5)>15%(0)
市场风险 = 资产商品的价格波动率
波动率 = 过去30天价格区间 / 过去30天平均价格
评分标准
- 波动率 5%: 10
- 波动率 5-15%: 5
- 波动率 >15%: 0
args:
highest_price: 过去30天最高价格 (用户填写)
lowest_price: 过去30天最低价格 (用户填写)
return:
float: 市场风险评分 (0-10)
"""
if highest_price <= 0 or lowest_price <= 0:
return 0.0
# 计算平均价格
average_price = (highest_price + lowest_price) / 2
# 计算价格区间
price_range = highest_price - lowest_price
# 计算波动率
volatility = price_range / average_price
# 根据波动率评分
if volatility <= 0.05: # ≤5%
return 10.0
elif volatility <= 0.15: # 5-15%
return 5.0
else: # >15%
return 0.0
def calculate_legal_risk(self, lawsuit_status: str) -> float:
"""
计算法律风险
法律风险 = 侵权诉讼历史
评分标准
- 无诉讼: 10
- 已解决诉讼: 7
- 未解决诉讼: 0
args:
lawsuit_status: 诉讼状态 (API获取)
return:
float: 法律风险评分 (0-10)
"""
lawsuit_scores = {
"无诉讼": 10.0,
"已解决诉讼": 7.0,
"未解决诉讼": 0.0
}
return lawsuit_scores.get(lawsuit_status, 0.0)
def calculate_inheritance_risk(self, inheritor_ages: List[int]) -> float:
"""
计算传承风险
用户填写该资产的所有受认证传承人年龄若有多位受认证传承人则填写多位传承人年龄50(10)50-70(5)>70(0)
传承风险 = 传承人年龄
评分标准
- 传承人年龄 50: 10
- 传承人年龄 50-70: 5
- 传承人年龄 >70: 0
如果有多个传承人取最高分
args:
inheritor_ages: 传承人年龄列表 (用户填写)
return:
float: 传承风险评分 (0-10)
"""
if not inheritor_ages:
return 0.0
max_score = 0.0
for age in inheritor_ages:
if age <= 50:
score = 10.0
elif age <= 70:
score = 5.0
else:
score = 0.0
max_score = max(max_score, score)
return max_score
def calculate_complete_risky_value_b3(self, input_data: Dict) -> Dict:
# 计算各项风险评分
market_risk = self.calculate_market_risk(input_data["highest_price"], input_data["lowest_price"])
legal_risk = self.calculate_legal_risk(input_data["lawsuit_status"])
inheritance_risk = self.calculate_inheritance_risk(input_data["inheritor_ages"])
# 计算风险评分总和R
risk_score_sum = self.calculate_risk_score_sum(market_risk, legal_risk, inheritance_risk)
# 计算风险调整系数B3
risk_adjustment_b3 = self.calculate_risk_adjustment_b3(risk_score_sum)
return {
'risk_score_sum': risk_score_sum,
'risk_adjustment_b3': risk_adjustment_b3
}
# 示例使用
if __name__ == "__main__":
# 创建计算器实例
calculator = RiskAdjustmentB3Calculator()
# 示例数据
# 市场风险过去30天价格数据
highest_price = 100.0 # 过去30天最高价格 (用户填写)
lowest_price = 95.0 # 过去30天最低价格 (用户填写)
# 法律风险:诉讼状态
lawsuit_status = "无诉讼" # 诉讼状态 (API获取)
# 传承风险:传承人年龄
inheritor_ages = [45, 60, 75] # 传承人年龄列表 (用户填写)
# 计算各项风险评分
market_risk = calculator.calculate_market_risk(highest_price, lowest_price)
legal_risk = calculator.calculate_legal_risk(lawsuit_status)
inheritance_risk = calculator.calculate_inheritance_risk(inheritor_ages)
# 计算风险评分总和R
risk_score_sum = calculator.calculate_risk_score_sum(market_risk, legal_risk, inheritance_risk)
# 计算风险调整系数B3
risk_adjustment_b3 = calculator.calculate_risk_adjustment_b3(risk_score_sum)
print(f"市场风险评分: {market_risk:.1f}")
print(f"法律风险评分: {legal_risk:.1f}")
print(f"传承风险评分: {inheritance_risk:.1f}")
print(f"风险评分总和R: {risk_score_sum:.4f}")
print(f"风险调整系数B3: {risk_adjustment_b3:.4f}")

View File

@ -41,12 +41,69 @@ class UniversalAPIManager:
raise ValueError(f"未找到API提供商配置: {provider}")
return config
def _get_endpoint_config(self, provider: str, endpoint: str) -> Dict[str, Any]:
def _get_endpoint_config(self, provider: str, endpoint: str) -> Optional[Dict[str, Any]]:
"""获取端点配置"""
endpoint_config = self.config.get_endpoint_config(provider, endpoint)
provider_config = self.config.get_api_config(provider)
if not provider_config or 'endpoints' not in provider_config:
return None
return provider_config['endpoints'].get(endpoint)
def _get_api_key(self, provider: str, endpoint: str) -> Optional[str]:
"""获取API密钥"""
endpoint_config = self._get_endpoint_config(provider, endpoint)
if not endpoint_config:
raise ValueError(f"未找到端点配置: {provider}.{endpoint}")
return endpoint_config
return None
return endpoint_config.get('api_key')
def make_request(self, provider: str, endpoint: str, params: Dict[str, Any], timeout: Optional[int] = None) -> Dict[str, Any]:
"""
发送API请求
Args:
provider: API提供商
endpoint: API端点
params: 请求参数
timeout: 超时时间()
Returns:
Dict[str, Any]: API响应
"""
# 获取API配置
endpoint_config = self._get_endpoint_config(provider, endpoint)
if not endpoint_config:
raise ValueError(f"未找到API配置: {provider}/{endpoint}")
# 获取API密钥
api_key = self._get_api_key(provider, endpoint)
if not api_key:
raise ValueError(f"未找到API密钥: {provider}/{endpoint}")
# 获取基础URL
provider_config = self.config.get_api_config(provider)
base_url = provider_config.get('base_url')
if not base_url:
raise ValueError(f"未找到基础URL: {provider}")
# 构建完整URL
url = f"{base_url.rstrip('/')}{endpoint_config['path']}"
# 添加API密钥到参数中
params['APIKey'] = api_key
# 发送请求
try:
response = self.session.request(
method=endpoint_config['method'],
url=url,
json=params if endpoint_config['method'] == 'POST' else None,
params=params if endpoint_config['method'] == 'GET' else None,
timeout=timeout or provider_config.get('timeout', 30)
)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
logger.error(f"API请求失败: {str(e)}")
raise
def _generate_chinaz_sign(self, date: datetime = None) -> str:
"""
@ -146,12 +203,31 @@ class UniversalAPIManager:
if 'sign' not in prepared_params:
prepared_params['sign'] = self._generate_chinaz_sign()
elif provider == 'justoneapi':
# JustOneAPI需要token参数
elif provider == 'xiaohongshu':
# 小红书接口使用 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
elif provider == 'dajiala':
# 微信指数
# JustOneAPI需要token参数
api_key = provider_config.get('api_key')
if api_key:
prepared_params['key'] = api_key
elif provider == 'jizhiliao':
# 极致聊接口需要 key默认从配置读取
if 'key' not in prepared_params or not prepared_params['key']:
api_key = provider_config.get('api_key')
if api_key:
prepared_params['key'] = api_key
if 'verifycode' not in prepared_params:
default_verifycode = provider_config.get('verifycode')
if default_verifycode is not None:
prepared_params['verifycode'] = default_verifycode
return prepared_params
@ -224,6 +300,37 @@ class UniversalAPIManager:
'endpoint': endpoint
}
time.sleep(2 ** attempt) # 指数退避
def wx_index(self,keyword):
"""
微信指数
"""
data = {
"keyword": keyword,
"mode": 2,
"BusinessType": 8192,
"sub_search_type": 0,
"verifycode": ""
}
return self.make_request('dajiala', 'web_search', data)
def video_views(self,platform_type,video_id):
"""
平台视频播放量
args:
platform_type: douyin\bilibili\kuaishou
"""
if platform_type == 'bilibili':
params = {
"bvid": video_id
}
else:
params = {
"videoId": video_id
}
return self.make_request('justoneapi', 'platform_type', params)
# 站长之家API的便捷方法
def query_copyright_software(self, company_name: str, chinaz_ver: str = "1") -> Dict[str, Any]:
@ -256,7 +363,7 @@ class UniversalAPIManager:
}
return self.make_request('chinaz', 'judgement', params)
# JustOneAPI的便捷方法
# 小红书便捷方法
def get_xiaohongshu_note_detail(self, note_id: str) -> Dict[str, Any]:
"""
获取小红书笔记详情
@ -269,7 +376,31 @@ class UniversalAPIManager:
"""
params = {'noteId': note_id}
return self.make_request('justoneapi', 'xiaohongshu_note_detail', params)
return self.make_request('xiaohongshu', 'xiaohongshu_note_detail', params)
# 极致聊便捷方法
def search_jizhiliao_index(
self,
keyword: str,
mode: int = 2,
business_type: int = 8192,
sub_search_type: int = 0,
key: Optional[str] = None,
verifycode: Optional[str] = None,
) -> Dict[str, Any]:
"""执行极致聊指数搜索"""
params: Dict[str, Any] = {
'keyword': keyword,
'mode': mode,
'BusinessType': business_type,
'sub_search_type': sub_search_type,
}
if key is not None:
params['key'] = key
if verifycode is not None:
params['verifycode'] = verifycode
return self.make_request('jizhiliao', 'index_search', params)
# 通用方法
def list_providers(self) -> list:

384
demo_api.py Normal file
View File

@ -0,0 +1,384 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import requests
import json
import random
import time
# API基础URL
BASE_URL = "http://127.0.0.1:9999/api/v1"
# 测试数据
test_phone = f"1380000{random.randint(1000, 9999)}"
test_password = test_phone[-6:] # 默认密码是手机号后6位
access_token = None
user_id = None
valuation_id = None
def test_register():
"""测试用户注册功能"""
print("\n===== 测试用户注册 =====")
url = f"{BASE_URL}/app-user/register"
data = {
"phone": test_phone
}
response = requests.post(url, json=data)
print(f"请求URL: {url}")
print(f"请求数据: {data}")
print(f"响应状态码: {response.status_code}")
print(f"响应内容: {response.text}")
assert response.status_code == 200, "注册请求失败"
result = response.json()
assert result["code"] == 200, "注册失败"
assert result["data"]["phone"] == test_phone, "返回的手机号不匹配"
assert result["data"]["default_password"] == test_phone[-6:], "默认密码不正确"
print("✅ 用户注册测试通过")
return result
def test_login():
"""测试用户登录功能"""
global access_token
print("\n===== 测试用户登录 =====")
url = f"{BASE_URL}/app-user/login"
data = {
"phone": test_phone,
"password": test_password
}
response = requests.post(url, json=data)
print(f"请求URL: {url}")
print(f"请求数据: {data}")
print(f"响应状态码: {response.status_code}")
print(f"响应内容: {response.text}")
assert response.status_code == 200, "登录请求失败"
result = response.json()
assert "access_token" in result, "登录失败未返回token"
# 保存token供后续请求使用
access_token = result["access_token"]
print("✅ 用户登录测试通过")
return result
def test_create_valuation():
"""测试创建估值评估申请"""
global access_token
print("\n===== 测试创建估值评估申请 =====")
url = f"{BASE_URL}/app-valuations/"
# 准备请求头包含授权token
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
}
# 估值评估申请数据 - 根据估值字段.txt更新
data = {
# 02 - 基础信息 - 非遗IP资产的基本信息
"asset_name": f"蜀绣-{random.randint(1000, 9999)}", # 资产名称:必须是企业全名称
"institution": "有数", # 所属机构拥有或管理该非遗IP资产的机构名称
"industry": "文化艺术", # 所属行业非遗IP资产所属的行业分类
# 03 - 财务状况
"rd_investment": "5", # 近12个月的研发费用单位万元
"annual_revenue": "100", # 近12个月的营收额单位万元用于计算创新投入比
"three_year_income": [100, 120, 150], # 近三年的每年收益资产近3年的年收益数据单位万元用于计算年均收益和增长率
"funding_status": "国家级资助", # 资金支持: 国家级资助(10分)、省级资助(7分)、无资助(0分)
# 04 - 非遗等级与技术
"inheritor_level": "国家级", # 传承人等级
"inheritor_ages": [60, 42, 35], # 传承人年龄
"inheritor_certificates": ["http://example.com/国家级非遗传承人证书.jpg"], # 传承人证书:传承人资质证明材料
"heritage_asset_level": "国家级非遗", # 非遗资产等级
"patent_remaining_years": "8", # [实际上就是专利号 通过API查询到的 ]专利剩余年限资产相关专利的剩余保护期8年对应7分
"historical_evidence": { # 资产历史证据类型+数量:历史传承的证据材料
"artifacts": 1, # 出土实物数量
"ancient_literature": 2, # 古代文献数量
"inheritor_testimony": 3, # 传承人佐证数量
"modern_research": 1 # 现代研究数量
},
# 专利证书:
"patent_certificates": ["http://example.com/专利证书1.jpg", "http://example.com/专利证书2.jpg"],
"pattern_images": ["pattern1.jpg"], # 纹样图片:资产相关的纹样图片文件
# 04 - 非遗应用与推广
"implementation_stage": "成熟应用", # 非遗资产应用成熟度
"coverage_area": "区域覆盖", # 非遗资产应用覆盖范围
"collaboration_type": "品牌联名", # 非遗资产跨界合作深度
"offline_teaching_count": 12, # 近12个月线下相关演讲活动次数
"platform_accounts": { # 线上相关宣传账号信息
"bilibili": {
"followers_count": 8000, # 粉丝数量
"likes": 1000, # 点赞数
"comments": 500, # 评论数
"shares": 500 # 转发数
}, # B站账号
"douyin": {
"followers_count": 8000, # 粉丝数量
"likes": 1000, # 点赞数
"comments": 500, # 评论数
"shares": 500 # 转发数
} # 抖音账号
},
# 06 - 非遗资产衍生商品信息
#该商品近12个月销售量
"sales_volume": "1000", # 近12个月销售量资产衍生商品的近12个月销售量单位 链接购买量
# 该商品近12个月的链接浏览量
"link_views": "10000", # 近12个月链接浏览量资产衍生商品相关链接的近12个月浏览量单位 浏览量
"scarcity_level": "流通", # 稀缺等级:资产的稀缺程度,流通(发行量>1000份对应0.1分
"market_activity_time": "近一月", # 市场活动的时间
"monthly_transaction_amount": "<100万元", # 月交易额:资产衍生商品的月交易额水平,<100万元对应-0.1
"price_range": { # 资产商品的价格波动率近30天商品价格的波动情况
"highest": 239, # 最高价(单位:元)
"lowest": 189 # 最低价(单位:元)
},
"market_price": 0, # 直接提供的市场价格(单位:万元) 用户输入: 专家审核 或者 系统默认 专家审核
# 内置API 计算字段
"infringement_record": "无侵权记录", # 侵权记录资产的侵权历史情况无侵权记录对应10分
"patent_count": "1", # 专利使用量:资产相关的专利数量,每引用一项专利+2.5分
"esg_value": "10", # ESG关联价值根据行业匹配的ESG环境、社会、治理关联价值
"policy_matching": "10", # 政策匹配度:根据行业自动匹配的政策匹配度分值
"online_course_views": 2000, # 线上课程点击量:抖音/快手播放量按100:1折算为学习人次B站课程按50:1折算
"pattern_complexity": "1.459", # 结构复杂度:纹样的结构复杂度值 搞一个默认值: 0.0
"normalized_entropy": "9.01", # 归一化信息熵:纹样的归一化信息熵值 搞一个默认值: 0.0
"legal_risk": "无诉讼", # 法律风险-侵权诉讼历史资产所属机构的诉讼历史无诉讼对应10分
# 动态质押率DPR计算公式=基础质押率*(1+流量修正系数)+政策加成系数-流动性调节因子
"base_pledge_rate": "50%", # 基础质押率基础质押率固定值50%
"flow_correction": "0.3", # 流量修正系数固定值0.3
}
response = requests.post(url, headers=headers, json=data)
print(f"请求URL: {url}")
print(f"请求头: {headers}")
print(f"请求数据: {json.dumps(data, ensure_ascii=False, indent=2)}")
print(f"响应状态码: {response.status_code}")
print(f"响应内容: {response.text}")
assert response.status_code == 200, "创建估值评估申请请求失败"
result = response.json()
assert result["code"] == 200, "创建估值评估申请失败"
print("✅ 创建估值评估申请测试通过")
return result
def test_get_profile():
"""测试获取用户个人信息"""
global access_token, user_id
print("\n===== 测试获取用户个人信息 =====")
url = f"{BASE_URL}/app-user/profile"
headers = {
"Authorization": f"Bearer {access_token}"
}
response = requests.get(url, headers=headers)
print(f"请求URL: {url}")
print(f"请求头: {headers}")
print(f"响应状态码: {response.status_code}")
print(f"响应内容: {response.text}")
assert response.status_code == 200, "获取用户信息请求失败"
result = response.json()
user_id = result["id"] # 保存用户ID供后续使用
print("✅ 获取用户个人信息测试通过")
return result
def test_change_password():
"""测试修改密码"""
global access_token
print("\n===== 测试修改密码 =====")
url = f"{BASE_URL}/app-user/change-password"
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
}
new_password = "new" + test_password
data = {
"old_password": test_password,
"new_password": new_password
}
response = requests.post(url, headers=headers, json=data)
print(f"请求URL: {url}")
print(f"请求头: {headers}")
print(f"请求数据: {data}")
print(f"响应状态码: {response.status_code}")
print(f"响应内容: {response.text}")
assert response.status_code == 200, "修改密码请求失败"
result = response.json()
assert result["code"] == 200, "修改密码失败"
print("✅ 修改密码测试通过")
return result
def test_update_profile():
"""测试更新用户信息"""
global access_token
print("\n===== 测试更新用户信息 =====")
url = f"{BASE_URL}/app-user/profile"
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
}
data = {
"nickname": f"测试用户{random.randint(100, 999)}",
"avatar": "https://example.com/avatar.jpg",
"gender": "male",
"email": f"test{random.randint(100, 999)}@example.com"
}
response = requests.put(url, headers=headers, json=data)
print(f"请求URL: {url}")
print(f"请求头: {headers}")
print(f"请求数据: {data}")
print(f"响应状态码: {response.status_code}")
print(f"响应内容: {response.text}")
assert response.status_code == 200, "更新用户信息请求失败"
result = response.json()
# 更新用户信息接口直接返回用户对象不包含code字段
assert "id" in result, "更新用户信息失败"
print("✅ 更新用户信息测试通过")
return result
def test_logout():
"""测试用户登出"""
global access_token
print("\n===== 测试用户登出 =====")
url = f"{BASE_URL}/app-user/logout"
headers = {
"Authorization": f"Bearer {access_token}"
}
response = requests.post(url, headers=headers)
print(f"请求URL: {url}")
print(f"请求头: {headers}")
print(f"响应状态码: {response.status_code}")
print(f"响应内容: {response.text}")
assert response.status_code == 200, "登出请求失败"
result = response.json()
assert result["code"] == 200, "登出失败"
print("✅ 用户登出测试通过")
return result
def test_get_valuation_list():
"""测试获取用户估值列表"""
global access_token
print("\n===== 测试获取用户估值列表 =====")
url = f"{BASE_URL}/app-valuations/"
headers = {
"Authorization": f"Bearer {access_token}"
}
response = requests.get(url, headers=headers)
print(f"请求URL: {url}")
print(f"请求头: {headers}")
print(f"响应状态码: {response.status_code}")
print(f"响应内容: {response.text}")
assert response.status_code == 200, "获取估值列表请求失败"
result = response.json()
assert result["code"] == 200, "获取估值列表失败"
print("✅ 获取用户估值列表测试通过")
return result
def test_get_valuation_detail():
"""测试获取估值详情"""
global access_token, valuation_id
# 先获取估值列表获取第一个估值ID
if not valuation_id:
list_result = test_get_valuation_list()
if list_result["data"] and len(list_result["data"]) > 0:
valuation_id = list_result["data"][0]["id"]
else:
print("⚠️ 没有可用的估值记录,跳过估值详情测试")
return None
print("\n===== 测试获取估值详情 =====")
url = f"{BASE_URL}/app-valuations/{valuation_id}"
headers = {
"Authorization": f"Bearer {access_token}"
}
response = requests.get(url, headers=headers)
print(f"请求URL: {url}")
print(f"请求头: {headers}")
print(f"响应状态码: {response.status_code}")
print(f"响应内容: {response.text}")
assert response.status_code == 200, "获取估值详情请求失败"
result = response.json()
assert result["code"] == 200, "获取估值详情失败"
print("✅ 获取估值详情测试通过")
return result
def run_tests():
"""运行所有测试"""
try:
# 测试注册
test_register()
# 等待一秒,确保数据已保存
time.sleep(1)
# 测试登录
test_login()
# 测试获取用户个人信息
test_get_profile()
# 测试更新用户信息
test_update_profile()
# 测试创建估值评估申请
test_create_valuation()
# 测试获取估值列表
test_get_valuation_list()
# 测试获取估值详情
test_get_valuation_detail()
# 测试修改密码
test_change_password()
# 测试登出
test_logout()
print("\n===== 所有测试通过 =====")
except AssertionError as e:
print(f"\n❌ 测试失败: {e}")
except Exception as e:
print(f"\n❌ 发生错误: {e}")
if __name__ == "__main__":
run_tests()

View File

@ -4,14 +4,16 @@ annotated-types==0.7.0
anyio==4.8.0
argon2-cffi==23.1.0
argon2-cffi-bindings==21.2.0
asyncclick==8.1.8
asyncclick==8.1.8.0
black==24.10.0
certifi==2024.12.14
cffi==1.17.1
charset-normalizer==3.4.3
click==8.1.8
dictdiffer==0.9.0
dnspython==2.7.0
email-validator==2.2.0
email_validator==2.2.0
exceptiongroup==1.3.0
fastapi==0.111.0
fastapi-cli==0.0.7
h11==0.14.0
@ -21,40 +23,44 @@ httpx==0.28.1
idna==3.10
iso8601==2.1.0
isort==5.13.2
jinja2==3.1.5
Jinja2==3.1.5
loguru==0.7.3
markdown-it-py==3.0.0
markupsafe==3.0.2
MarkupSafe==3.0.2
mdurl==0.1.2
mypy-extensions==1.0.0
orjson==3.10.14
packaging==24.2
passlib==1.7.4
pathspec==0.12.1
pillow==11.3.0
platformdirs==4.3.6
pycparser==2.22
pydantic==2.10.5
pydantic-core==2.27.2
pydantic-settings==2.7.1
pygments==2.19.1
pyjwt==2.10.1
pydantic_core==2.27.2
Pygments==2.19.1
PyJWT==2.10.1
pypika-tortoise==0.3.2
python-dotenv==1.0.1
python-multipart==0.0.20
pytz==2024.2
pyyaml==6.0.2
PyYAML==6.0.2
requests==2.32.5
rich==13.9.4
rich-toolkit==0.13.2
ruff==0.9.1
setuptools==75.8.0
shellingham==1.5.4
sniffio==1.3.1
starlette==0.37.2
tomli==2.2.1
tortoise-orm==0.23.0
tqdm==4.67.1
typer==0.15.1
typing-extensions==4.12.2
typing_extensions==4.12.2
ujson==5.10.0
urllib3==2.5.0
uvicorn==0.34.0
uvloop==0.21.0; sys_platform != 'win32'
uvloop==0.21.0
watchfiles==1.0.4
websockets==14.1

8415
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -16,6 +16,7 @@
"@iconify/json": "^2.2.228",
"@iconify/vue": "^4.1.1",
"@unocss/eslint-config": "^0.55.0",
"@vicons/ionicons5": "^0.13.0",
"@vueuse/core": "^10.3.0",
"@zclzone/eslint-config": "^0.0.4",
"axios": "^1.4.0",
@ -29,9 +30,9 @@
"sass": "^1.65.1",
"typescript": "^5.1.6",
"unocss": "^0.55.0",
"unplugin-auto-import": "^0.15.3",
"unplugin-auto-import": "^0.16.6",
"unplugin-icons": "^0.16.5",
"unplugin-vue-components": "^0.24.1",
"unplugin-vue-components": "^0.25.1",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-html": "^3.2.0",
"vite-plugin-svg-icons": "^2.0.1",

2464
web/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -32,4 +32,10 @@
该商品的发行量: 字符串类型
该商品最近一次市场活动时间: 字符串类型
月交易額: 字符串类型
该商品近30天价格波动区间: 字符串类型
该商品近30天价格波动区间: 字符串类型
export DOCKER_DEFAULT_PLATFORM=linux/amd64
docker build -t zfc931912343/guzhi-fastapi-admin:v1.1 .
docker push zfc931912343/guzhi-fastapi-admin:v1.1