feat(valuation): 扩展非遗资产评估模型并完善相关功能

- 在valuation模型中新增多个评估字段,包括稀缺等级、市场活动时间等
- 完善用户端输出模型,确保所有字段正确序列化
- 修复文件上传返回URL缺少BASE_URL的问题
- 更新Docker镜像版本至v1.2
- 添加静态文件路径到中间件排除列表
- 优化估值评估创建接口,自动关联当前用户ID
This commit is contained in:
邹方成 2025-10-10 08:55:17 +08:00
parent 4656d4b96c
commit 48b93fdddb
15 changed files with 236 additions and 53 deletions

83
aaa.json Normal file
View File

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

View File

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

View File

@ -59,7 +59,7 @@ async def get_my_valuations(
data=serialized_items, data=serialized_items,
total=result.total, total=result.total,
page=result.page, page=result.page,
size=result.size, page_size=result.size,
pages=result.pages, pages=result.pages,
msg="获取估值评估列表成功" msg="获取估值评估列表成功"
) )

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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