Merge branch 'main' of https://git.1024tool.vip/zfc/guzhi
This commit is contained in:
commit
513e6ab0e4
54
.github/copilot-instructions.md
vendored
Normal file
54
.github/copilot-instructions.md
vendored
Normal 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`(默认 SQLite,db 文件在项目根 `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
3
.gitignore
vendored
@ -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
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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])
|
||||
|
||||
0
app/api/v1/calculation/__init__.py
Normal file
0
app/api/v1/calculation/__init__.py
Normal file
325
app/api/v1/calculation/calcuation.py
Normal file
325
app/api/v1/calculation/calcuation.py
Normal 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
|
||||
}
|
||||
@ -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)}")
|
||||
5
app/api/v1/upload/__init__.py
Normal file
5
app/api/v1/upload/__init__.py
Normal 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=["文件上传"])
|
||||
14
app/api/v1/upload/upload.py
Normal file
14
app/api/v1/upload/upload.py
Normal 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)
|
||||
@ -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地址(支持jpg,png,jpeg,1M以内)
|
||||
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
51
app/controllers/upload.py
Normal 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
|
||||
)
|
||||
@ -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
|
||||
|
||||
@ -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"
|
||||
@ -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地址(支持jpg,png,jpeg,1M以内)")
|
||||
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
6
app/schemas/upload.py
Normal file
@ -0,0 +1,6 @@
|
||||
from pydantic import BaseModel
|
||||
|
||||
class ImageUploadResponse(BaseModel):
|
||||
"""图片上传响应模型"""
|
||||
url: str
|
||||
filename: str
|
||||
0
app/utils/__init__.py
Normal file
0
app/utils/__init__.py
Normal 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()
|
||||
|
||||
53
app/utils/calculation_engine/__init__.py
Normal file
53
app/utils/calculation_engine/__init__.py
Normal 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"
|
||||
]
|
||||
11
app/utils/calculation_engine/cultural_value_b2/__init__.py
Normal file
11
app/utils/calculation_engine/cultural_value_b2/__init__.py
Normal file
@ -0,0 +1,11 @@
|
||||
from .cultural_value_b2 import CulturalValueB2Calculator
|
||||
from .sub_formulas import (
|
||||
LivingHeritageB21Calculator,
|
||||
PatternGeneB22Calculator
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"CulturalValueB2Calculator",
|
||||
"LivingHeritageB21Calculator",
|
||||
"PatternGeneB22Calculator"
|
||||
]
|
||||
@ -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}")
|
||||
@ -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"
|
||||
]
|
||||
@ -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}")
|
||||
|
||||
|
||||
@ -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}")
|
||||
18
app/utils/calculation_engine/economic_value_b1/__init__.py
Normal file
18
app/utils/calculation_engine/economic_value_b1/__init__.py
Normal 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"
|
||||
]
|
||||
@ -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}")
|
||||
@ -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"
|
||||
]
|
||||
@ -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}")
|
||||
@ -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}")
|
||||
@ -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}")
|
||||
18
app/utils/calculation_engine/final_value_ab/__init__.py
Normal file
18
app/utils/calculation_engine/final_value_ab/__init__.py
Normal file
@ -0,0 +1,18 @@
|
||||
"""
|
||||
最终估值A计算包
|
||||
|
||||
最终估值A = 模型估值B × 0.7 + 市场估值C × 0.3
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
||||
"""
|
||||
|
||||
from .final_value_a import FinalValueACalculator
|
||||
|
||||
__all__ = [
|
||||
"FinalValueACalculator"
|
||||
]
|
||||
|
||||
|
||||
166
app/utils/calculation_engine/final_value_ab/final_value_a.py
Normal file
166
app/utils/calculation_engine/final_value_ab/final_value_a.py
Normal 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}")
|
||||
|
||||
|
||||
127
app/utils/calculation_engine/final_value_ab/model_value_b.py
Normal file
127
app/utils/calculation_engine/final_value_ab/model_value_b.py
Normal 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}")
|
||||
16
app/utils/calculation_engine/market_value_c/__init__.py
Normal file
16
app/utils/calculation_engine/market_value_c/__init__.py
Normal 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"
|
||||
]
|
||||
|
||||
150
app/utils/calculation_engine/market_value_c/market_value_c.py
Normal file
150
app/utils/calculation_engine/market_value_c/market_value_c.py
Normal 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}")
|
||||
|
||||
|
||||
@ -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"
|
||||
]
|
||||
|
||||
|
||||
@ -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}")
|
||||
@ -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}")
|
||||
|
||||
@ -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}")
|
||||
|
||||
@ -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}")
|
||||
|
||||
@ -0,0 +1,6 @@
|
||||
from .sub_formulas.risk_adjustment_b3 import RiskAdjustmentB3Calculator
|
||||
|
||||
__all__ = [
|
||||
"RiskAdjustmentB3Calculator"
|
||||
]
|
||||
|
||||
@ -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"
|
||||
]
|
||||
|
||||
@ -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}")
|
||||
@ -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
384
demo_api.py
Normal 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()
|
||||
@ -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
8415
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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
2464
web/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user