feat(valuation): 优化估值评估流程并添加删除功能
- 将估值计算改为后台任务执行,提高响应速度 - 添加估值评估记录的软删除功能 - 更新评估状态字段值从approved/rejected改为success/fail - 修复注册接口的HTTP状态码问题 - 更新API版本号和服务器配置 - 禁用FastAPI尾部斜杠重定向
This commit is contained in:
parent
50e02dc37d
commit
9d950ba368
@ -33,6 +33,7 @@ def create_app() -> FastAPI:
|
|||||||
openapi_url="/openapi.json",
|
openapi_url="/openapi.json",
|
||||||
middleware=make_middlewares(),
|
middleware=make_middlewares(),
|
||||||
lifespan=lifespan,
|
lifespan=lifespan,
|
||||||
|
redirect_slashes=False, # 禁用尾部斜杠重定向
|
||||||
)
|
)
|
||||||
# 注册静态文件目录
|
# 注册静态文件目录
|
||||||
# app.mount("/static", StaticFiles(directory="app/static"), name="static")
|
# app.mount("/static", StaticFiles(directory="app/static"), name="static")
|
||||||
|
|||||||
@ -38,7 +38,7 @@ async def register(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=200, detail=str(e))
|
||||||
|
|
||||||
|
|
||||||
@router.post("/login", response_model=AppUserJWTOut, summary="用户登录")
|
@router.post("/login", response_model=AppUserJWTOut, summary="用户登录")
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
from random import random
|
from random import random
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from fastapi import APIRouter, Depends, HTTPException, status
|
from fastapi import APIRouter, Depends, HTTPException, status, BackgroundTasks
|
||||||
from typing import Optional, List, Dict, Any
|
from typing import Optional, List, Dict, Any
|
||||||
import json
|
import json
|
||||||
import asyncio
|
import asyncio
|
||||||
@ -33,15 +33,191 @@ from app.utils.wechat_index_calculator import wechat_index_calculator
|
|||||||
app_valuations_router = APIRouter(tags=["用户端估值评估"])
|
app_valuations_router = APIRouter(tags=["用户端估值评估"])
|
||||||
|
|
||||||
|
|
||||||
|
async def _perform_valuation_calculation(user_id: int, data: UserValuationCreate):
|
||||||
|
"""
|
||||||
|
后台任务:执行估值计算
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
|
||||||
|
# 侵权分 默认 6
|
||||||
|
try:
|
||||||
|
judicial_data = universal_api.query_judicial_data(data.institution)
|
||||||
|
_data = judicial_data["data"].get("target",None) # 诉讼标的
|
||||||
|
if _data:
|
||||||
|
infringement_score = 0.0
|
||||||
|
else:
|
||||||
|
infringement_score = 10.0
|
||||||
|
except:
|
||||||
|
infringement_score = 0.0
|
||||||
|
input_data_by_b1["infringement_score"] = infringement_score
|
||||||
|
|
||||||
|
# 获取专利信息 TODO 参数
|
||||||
|
try:
|
||||||
|
patent_data = universal_api.query_patent_info(data.industry)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning("valuation.patent_api_error err={}", repr(e))
|
||||||
|
input_data_by_b1["patent_count"] = 0.0
|
||||||
|
input_data_by_b1["patent_score"] = 0.0
|
||||||
|
|
||||||
|
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 []
|
||||||
|
# 验证 专利剩余年限
|
||||||
|
# 发展潜力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
|
||||||
|
|
||||||
|
patent_score = calculate_patent_score(calculate_total_years(data_list))
|
||||||
|
input_data_by_b1["patent_score"] = patent_score
|
||||||
|
|
||||||
|
# 提取 文化价值B2 计算参数
|
||||||
|
input_data_by_b2 = await _extract_calculation_params_b2(data)
|
||||||
|
# 提取 风险调整系数B3 计算参数
|
||||||
|
input_data_by_b3 = await _extract_calculation_params_b3(data)
|
||||||
|
if infringement_score == 10.0:
|
||||||
|
input_data_by_b3["lawsuit_status"] = "无诉讼状态"
|
||||||
|
if 0 < infringement_score < 4.0:
|
||||||
|
input_data_by_b3["lawsuit_status"] = "已解决诉讼"
|
||||||
|
else:
|
||||||
|
input_data_by_b3["lawsuit_status"] = "未解决诉讼"
|
||||||
|
|
||||||
|
# 提取 市场估值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)
|
||||||
|
|
||||||
|
# 计算动态质押
|
||||||
|
drp_c = DynamicPledgeRateCalculator()
|
||||||
|
'''
|
||||||
|
monthly_amount (float): 月交易额(万元)
|
||||||
|
heritage_level (str): 非遗等级
|
||||||
|
'''
|
||||||
|
# 解析月交易额字符串为数值
|
||||||
|
monthly_amount = drp_c.parse_monthly_transaction_amount(data.monthly_transaction_amount or "")
|
||||||
|
drp_result = drp_c.calculate_dynamic_pledge_rate(monthly_amount, data.heritage_asset_level)
|
||||||
|
|
||||||
|
# 结构化日志:关键分值
|
||||||
|
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,
|
||||||
|
calculation_result=calculation_result,
|
||||||
|
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()),
|
||||||
|
},
|
||||||
|
drp_result=drp_result,
|
||||||
|
status='success' # 计算成功,设置为approved状态
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info("valuation.background_calc_success user_id={} valuation_id={}", user_id, result.id)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
import traceback
|
||||||
|
print(traceback.format_exc())
|
||||||
|
logger.error("valuation.background_calc_failed user_id={} err={}", user_id, repr(e))
|
||||||
|
|
||||||
|
# 计算失败时也创建记录,状态设置为failed
|
||||||
|
try:
|
||||||
|
result = await user_valuation_controller.create_valuation(
|
||||||
|
user_id=user_id,
|
||||||
|
data=data,
|
||||||
|
calculation_result=None,
|
||||||
|
calculation_input=None,
|
||||||
|
drp_result=None,
|
||||||
|
status='rejected' # 计算失败,设置为rejected状态
|
||||||
|
)
|
||||||
|
logger.info("valuation.failed_record_created user_id={} valuation_id={}", user_id, result.id)
|
||||||
|
except Exception as create_error:
|
||||||
|
logger.error("valuation.failed_to_create_record user_id={} err={}", user_id, repr(create_error))
|
||||||
|
|
||||||
|
|
||||||
@app_valuations_router.post("/", summary="创建估值评估")
|
@app_valuations_router.post("/", summary="创建估值评估")
|
||||||
async def calculate_valuation(
|
async def calculate_valuation(
|
||||||
|
background_tasks: BackgroundTasks,
|
||||||
data: UserValuationCreate,
|
data: UserValuationCreate,
|
||||||
user_id: int = Depends(get_current_app_user_id)
|
user_id: int = Depends(get_current_app_user_id)
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
计算估值评估
|
创建估值评估任务
|
||||||
|
|
||||||
根据用户提交的估值评估数据,调用计算引擎进行经济价值B1计算
|
根据用户提交的估值评估数据,启动后台计算任务进行经济价值B1计算
|
||||||
|
|
||||||
请求示例JSON (仅包含用户填写部分):
|
请求示例JSON (仅包含用户填写部分):
|
||||||
{
|
{
|
||||||
@ -101,195 +277,27 @@ async def calculate_valuation(
|
|||||||
- 专利验证: 通过API验证专利有效性
|
- 专利验证: 通过API验证专利有效性
|
||||||
- 侵权记录: 通过API查询侵权诉讼历史
|
- 侵权记录: 通过API查询侵权诉讼历史
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
start_ts = time.monotonic()
|
# 添加后台任务
|
||||||
logger.info("valuation.calc_start user_id={} asset_name={} industry={}", user_id,
|
background_tasks.add_task(_perform_valuation_calculation, user_id, data)
|
||||||
getattr(data, 'asset_name', None), getattr(data, 'industry', None))
|
|
||||||
|
logger.info("valuation.task_queued user_id={} asset_name={} industry={}",
|
||||||
# 根据行业查询 ESG 基准分(优先用行业名称匹配,如用的是行业代码就把 name 改成 code)
|
user_id, getattr(data, 'asset_name', None), getattr(data, 'industry', None))
|
||||||
esg_obj = None
|
|
||||||
industry_obj = None
|
return Success(
|
||||||
policy_obj = None
|
data={
|
||||||
|
"task_status": "queued",
|
||||||
|
"message": "估值计算任务已提交,正在后台处理中",
|
||||||
try:
|
"user_id": user_id,
|
||||||
esg_obj = await asyncio.wait_for(ESG.filter(name=data.industry).first(), timeout=2.0)
|
"asset_name": getattr(data, 'asset_name', None)
|
||||||
except Exception as e:
|
},
|
||||||
logger.warning("valuation.esg_fetch_timeout industry={} err={}", data.industry, repr(e))
|
msg="估值计算任务已启动"
|
||||||
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
|
|
||||||
|
|
||||||
# 侵权分 默认 6
|
|
||||||
try:
|
|
||||||
judicial_data = universal_api.query_judicial_data(data.institution)
|
|
||||||
_data = judicial_data["data"].get("target",None) # 诉讼标的
|
|
||||||
if _data:
|
|
||||||
infringement_score = 0.0
|
|
||||||
else:
|
|
||||||
infringement_score = 10.0
|
|
||||||
except:
|
|
||||||
infringement_score = 0.0
|
|
||||||
input_data_by_b1["infringement_score"] = infringement_score
|
|
||||||
# 获取专利信息 TODO 参数
|
|
||||||
|
|
||||||
try:
|
|
||||||
patent_data = universal_api.query_patent_info(data.industry)
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning("valuation.patent_api_error err={}", repr(e))
|
|
||||||
input_data_by_b1["patent_count"] = 0.0
|
|
||||||
input_data_by_b1["patent_score"] = 0.0
|
|
||||||
|
|
||||||
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 []
|
|
||||||
# 验证 专利剩余年限
|
|
||||||
# 发展潜力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
|
|
||||||
|
|
||||||
patent_score = calculate_patent_score(calculate_total_years(data_list))
|
|
||||||
input_data_by_b1["patent_score"] = patent_score
|
|
||||||
|
|
||||||
# 提取 文化价值B2 计算参数
|
|
||||||
input_data_by_b2 = await _extract_calculation_params_b2(data)
|
|
||||||
# 提取 风险调整系数B3 计算参数
|
|
||||||
input_data_by_b3 = await _extract_calculation_params_b3(data)
|
|
||||||
if infringement_score == 10.0:
|
|
||||||
|
|
||||||
input_data_by_b3["lawsuit_status"] = "无诉讼状态"
|
|
||||||
if 0 < infringement_score < 4.0:
|
|
||||||
input_data_by_b3["lawsuit_status"] = "已解决诉讼"
|
|
||||||
else:
|
|
||||||
input_data_by_b3["lawsuit_status"] = "未解决诉讼"
|
|
||||||
# 提取 市场估值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)
|
|
||||||
|
|
||||||
# 计算动态质押
|
|
||||||
drp_c = DynamicPledgeRateCalculator()
|
|
||||||
'''
|
|
||||||
monthly_amount (float): 月交易额(万元)
|
|
||||||
heritage_level (str): 非遗等级
|
|
||||||
'''
|
|
||||||
# 解析月交易额字符串为数值
|
|
||||||
monthly_amount = drp_c.parse_monthly_transaction_amount(data.monthly_transaction_amount or "")
|
|
||||||
drp_result = drp_c.calculate_dynamic_pledge_rate(monthly_amount, data.heritage_asset_level)
|
|
||||||
|
|
||||||
# 结构化日志:关键分值
|
|
||||||
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,
|
|
||||||
calculation_result=calculation_result,
|
|
||||||
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()),
|
|
||||||
},
|
|
||||||
drp_result=drp_result,
|
|
||||||
status='approved' # 计算成功,设置为approved状态
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# 组装返回
|
|
||||||
result_dict = json.loads(result.model_dump_json())
|
|
||||||
# "calculation_result": {
|
|
||||||
# "model_value_b": 660.1534497474814,
|
|
||||||
# "market_value_c": 8800.0,
|
|
||||||
# "final_value_ab": 3102.107414823237
|
|
||||||
# }
|
|
||||||
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:
|
except Exception as e:
|
||||||
import traceback
|
logger.error("valuation.task_queue_failed user_id={} err={}", user_id, repr(e))
|
||||||
print(traceback.format_exc())
|
raise HTTPException(status_code=500, detail=f"任务提交失败: {str(e)}")
|
||||||
logger.error("valuation.calc_failed user_id={} err={}", user_id, repr(e))
|
|
||||||
|
|
||||||
# 计算失败时也创建记录,状态设置为failed
|
|
||||||
try:
|
|
||||||
result = await user_valuation_controller.create_valuation(
|
|
||||||
user_id=user_id,
|
|
||||||
data=data,
|
|
||||||
calculation_result=None,
|
|
||||||
calculation_input=None,
|
|
||||||
drp_result=None,
|
|
||||||
status='rejected' # 计算失败,设置为rejected状态
|
|
||||||
)
|
|
||||||
logger.info("valuation.failed_record_created user_id={} valuation_id={}", user_id, result.id)
|
|
||||||
except Exception as create_error:
|
|
||||||
logger.error("valuation.failed_to_create_record user_id={} err={}", user_id, repr(create_error))
|
|
||||||
|
|
||||||
raise HTTPException(status_code=400, detail=f"计算失败: {str(e)}")
|
|
||||||
|
|
||||||
|
|
||||||
async def _extract_calculation_params_b1(data: UserValuationCreate) -> Dict[str, Any]:
|
async def _extract_calculation_params_b1(data: UserValuationCreate) -> Dict[str, Any]:
|
||||||
@ -563,6 +571,36 @@ async def get_my_valuation_statistics(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app_valuations_router.delete("/{valuation_id}", summary="删除估值评估")
|
||||||
|
async def delete_valuation(
|
||||||
|
valuation_id: int,
|
||||||
|
current_user: AppUser = Depends(get_current_app_user)
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
删除指定的估值评估记录(软删除)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
result = await user_valuation_controller.delete_user_valuation(
|
||||||
|
user_id=current_user.id,
|
||||||
|
valuation_id=valuation_id
|
||||||
|
)
|
||||||
|
|
||||||
|
if not result:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
|
detail="估值评估记录不存在或已被删除"
|
||||||
|
)
|
||||||
|
|
||||||
|
return Success(data={"deleted": True}, msg="删除估值评估成功")
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
|
detail=f"删除估值评估失败: {str(e)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def calculate_total_years(data_list):
|
def calculate_total_years(data_list):
|
||||||
current_date = datetime.now().date()
|
current_date = datetime.now().date()
|
||||||
total_years = 0
|
total_years = 0
|
||||||
|
|||||||
@ -58,7 +58,7 @@ class ThirdPartyAPIController:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# 站长之家API便捷方法
|
# 站长之家API便捷方法
|
||||||
async def query_copyright_software(self, company_name: str, chinaz_ver: str = "1") -> APIResponse:
|
async def query_copyright_software(self, company_name: str, chinaz_ver: str = "1.0") -> APIResponse:
|
||||||
"""查询企业软件著作权"""
|
"""查询企业软件著作权"""
|
||||||
return await self.make_api_request(
|
return await self.make_api_request(
|
||||||
provider="chinaz",
|
provider="chinaz",
|
||||||
@ -71,7 +71,7 @@ class ThirdPartyAPIController:
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
async def query_patent_info(self, company_name: str, chinaz_ver: str = "1") -> APIResponse:
|
async def query_patent_info(self, company_name: str, chinaz_ver: str = "2.0") -> APIResponse:
|
||||||
"""查询企业专利信息"""
|
"""查询企业专利信息"""
|
||||||
return await self.make_api_request(
|
return await self.make_api_request(
|
||||||
provider="chinaz",
|
provider="chinaz",
|
||||||
|
|||||||
@ -22,7 +22,7 @@ class UserValuationController:
|
|||||||
"""用户创建估值评估"""
|
"""用户创建估值评估"""
|
||||||
valuation_data = data.model_dump()
|
valuation_data = data.model_dump()
|
||||||
valuation_data['user_id'] = user_id
|
valuation_data['user_id'] = user_id
|
||||||
valuation_data['status'] = status # 根据计算结果显示设置状态
|
valuation_data['status'] = "success" # 根据计算结果显示设置状态
|
||||||
|
|
||||||
# 添加计算结果到数据库
|
# 添加计算结果到数据库
|
||||||
if calculation_result:
|
if calculation_result:
|
||||||
@ -97,6 +97,21 @@ class UserValuationController:
|
|||||||
'rejected': rejected
|
'rejected': rejected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async def delete_user_valuation(self, user_id: int, valuation_id: int) -> bool:
|
||||||
|
"""删除用户的估值评估(软删除)"""
|
||||||
|
valuation = await self.model.filter(
|
||||||
|
id=valuation_id,
|
||||||
|
user_id=user_id,
|
||||||
|
is_active=True
|
||||||
|
).first()
|
||||||
|
|
||||||
|
if not valuation:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 软删除:设置 is_active 为 False
|
||||||
|
await valuation.update_from_dict({'is_active': False}).save()
|
||||||
|
return True
|
||||||
|
|
||||||
async def _to_user_out(self, valuation: ValuationAssessment) -> UserValuationOut:
|
async def _to_user_out(self, valuation: ValuationAssessment) -> UserValuationOut:
|
||||||
"""转换为用户端输出模型"""
|
"""转换为用户端输出模型"""
|
||||||
return UserValuationOut.model_validate(valuation)
|
return UserValuationOut.model_validate(valuation)
|
||||||
|
|||||||
@ -78,7 +78,7 @@ class ValuationAssessment(Model):
|
|||||||
|
|
||||||
# 系统字段
|
# 系统字段
|
||||||
user = fields.ForeignKeyField("models.AppUser", related_name="valuations", description="提交用户")
|
user = fields.ForeignKeyField("models.AppUser", related_name="valuations", description="提交用户")
|
||||||
status = fields.CharField(max_length=20, default="pending", description="评估状态: pending(待审核), approved(已通过), rejected(已拒绝)")
|
status = fields.CharField(max_length=20, default="success", description="评估状态: pending(待审核), success(已通过), fail(已拒绝)")
|
||||||
admin_notes = fields.TextField(null=True, description="管理员备注")
|
admin_notes = fields.TextField(null=True, description="管理员备注")
|
||||||
created_at = fields.DatetimeField(auto_now_add=True, description="创建时间")
|
created_at = fields.DatetimeField(auto_now_add=True, description="创建时间")
|
||||||
updated_at = fields.DatetimeField(auto_now=True, description="更新时间")
|
updated_at = fields.DatetimeField(auto_now=True, description="更新时间")
|
||||||
|
|||||||
@ -39,7 +39,7 @@ class ValuationAssessmentBase(BaseModel):
|
|||||||
offline_activities: Optional[str] = Field(None, description="近12个月线下相关宣讲活动次数")
|
offline_activities: Optional[str] = Field(None, description="近12个月线下相关宣讲活动次数")
|
||||||
offline_teaching_count: Optional[int] = Field(None, description="近12个月线下相关演讲活动次数")
|
offline_teaching_count: Optional[int] = Field(None, description="近12个月线下相关演讲活动次数")
|
||||||
online_accounts: Optional[List[Any]] = Field(None, description="线上相关宣传账号信息")
|
online_accounts: Optional[List[Any]] = Field(None, description="线上相关宣传账号信息")
|
||||||
platform_accounts: Optional[Dict[str, Dict[str, int]]] = Field(None, description="线上相关宣传账号信息")
|
platform_accounts: Optional[Dict[str, Dict[str, Union[str, int]]]] = Field(None, description="线上相关宣传账号信息")
|
||||||
|
|
||||||
# 非遗资产衍生商品信息
|
# 非遗资产衍生商品信息
|
||||||
sales_volume: Optional[str] = Field(None, description="该商品近12个月销售量")
|
sales_volume: Optional[str] = Field(None, description="该商品近12个月销售量")
|
||||||
|
|||||||
@ -18,9 +18,9 @@ class Settings(BaseSettings):
|
|||||||
DEBUG: bool = True
|
DEBUG: bool = True
|
||||||
|
|
||||||
# 服务器配置
|
# 服务器配置
|
||||||
SERVER_HOST: str = "124.222.245.240"
|
SERVER_HOST: str = "https://value.cdcee.net"
|
||||||
SERVER_PORT: int = 9999
|
SERVER_PORT: int = 9999
|
||||||
BASE_URL: str = f"http://{SERVER_HOST}:8080"
|
BASE_URL: str = f"{SERVER_HOST}"
|
||||||
|
|
||||||
PROJECT_ROOT: str = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
|
PROJECT_ROOT: str = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
|
||||||
BASE_DIR: str = os.path.abspath(os.path.join(PROJECT_ROOT, os.pardir))
|
BASE_DIR: str = os.path.abspath(os.path.join(PROJECT_ROOT, os.pardir))
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user