upload project source code
This commit is contained in:
@@ -0,0 +1 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
@@ -0,0 +1,102 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from app.core.dependencies import AuthPermission
|
||||
from app.common.response import SuccessResponse
|
||||
from app.core.base_params import PaginationQueryParam
|
||||
from app.core.logger import log
|
||||
from app.api.v1.module_yifan.yifan_affinity.service import YifanAffinityService
|
||||
from app.api.v1.module_yifan.yifan_affinity.schema import (
|
||||
YifanAffinityCreateSchema, YifanAffinityUpdateSchema, YifanAffinityQueryParam,
|
||||
AffinityCalculateRequestSchema
|
||||
)
|
||||
|
||||
YifanAffinityRouter = APIRouter(prefix="/yifan_affinity", tags=["缘分合盘"])
|
||||
|
||||
|
||||
@YifanAffinityRouter.post("/calculate", summary="缘分合盘测算", description="缘分合盘测算,创建任务并异步生成分析结果")
|
||||
async def calculate_affinity_controller(
|
||||
data: AffinityCalculateRequestSchema,
|
||||
auth: AuthSchema = Depends(AuthPermission([]))
|
||||
) -> JSONResponse:
|
||||
"""缘分合盘测算接口 - 创建任务,后台异步生成分析结果"""
|
||||
result_dict = await YifanAffinityService.calculate_affinity_service(auth=auth, data=data)
|
||||
log.info(f"缘分合盘任务已创建: {data.person1.name} & {data.person2.name} - {data.relationship}")
|
||||
return SuccessResponse(data=result_dict, msg="缘分合盘任务已创建,正在测算中")
|
||||
|
||||
|
||||
@YifanAffinityRouter.get("/result/{affinity_id}", summary="获取缘分合盘结果", description="根据ID获取缘分合盘测算结果")
|
||||
async def get_affinity_result_controller(
|
||||
affinity_id: int,
|
||||
auth: AuthSchema = Depends(AuthPermission([]))
|
||||
) -> JSONResponse:
|
||||
"""获取缘分合盘结果接口"""
|
||||
result_dict = await YifanAffinityService.get_affinity_result_service(auth=auth, affinity_id=affinity_id)
|
||||
log.info(f"获取缘分合盘结果成功: ID={affinity_id}")
|
||||
return SuccessResponse(data=result_dict.model_dump(), msg="获取缘分合盘结果成功")
|
||||
|
||||
|
||||
@YifanAffinityRouter.get("/list", summary="查询缘分合盘列表", description="查询缘分合盘列表")
|
||||
async def get_yifan_affinity_list_controller(
|
||||
page: PaginationQueryParam = Depends(),
|
||||
search: YifanAffinityQueryParam = Depends(),
|
||||
auth: AuthSchema = Depends(AuthPermission([]))
|
||||
) -> JSONResponse:
|
||||
"""查询缘分合盘列表接口(数据库分页)"""
|
||||
result_dict = await YifanAffinityService.page_yifan_affinity_service(
|
||||
auth=auth,
|
||||
page_no=page.page_no if page.page_no is not None else 1,
|
||||
page_size=page.page_size if page.page_size is not None else 10,
|
||||
search=search,
|
||||
order_by=page.order_by
|
||||
)
|
||||
log.info("查询缘分合盘列表成功")
|
||||
return SuccessResponse(data=result_dict, msg="查询缘分合盘列表成功")
|
||||
|
||||
|
||||
@YifanAffinityRouter.get("/{affinity_id}", summary="获取缘分合盘详情", description="根据ID获取缘分合盘详情")
|
||||
async def get_yifan_affinity_controller(
|
||||
affinity_id: int,
|
||||
auth: AuthSchema = Depends(AuthPermission([]))
|
||||
) -> JSONResponse:
|
||||
"""获取缘分合盘详情接口"""
|
||||
result_dict = await YifanAffinityService.get_yifan_affinity_service(auth=auth, affinity_id=affinity_id)
|
||||
log.info("获取缘分合盘详情成功")
|
||||
return SuccessResponse(data=result_dict, msg="获取缘分合盘详情成功")
|
||||
|
||||
|
||||
@YifanAffinityRouter.post("/create", summary="创建缘分合盘", description="创建缘分合盘")
|
||||
async def create_yifan_affinity_controller(
|
||||
data: YifanAffinityCreateSchema,
|
||||
auth: AuthSchema = Depends(AuthPermission(["module_yifan:yifan_affinity:create"]))
|
||||
) -> JSONResponse:
|
||||
"""创建缘分合盘接口"""
|
||||
result_dict = await YifanAffinityService.create_yifan_affinity_service(auth=auth, data=data)
|
||||
log.info("创建缘分合盘成功")
|
||||
return SuccessResponse(data=result_dict, msg="创建缘分合盘成功")
|
||||
|
||||
|
||||
@YifanAffinityRouter.put("/{affinity_id}", summary="更新缘分合盘", description="根据ID更新缘分合盘")
|
||||
async def update_yifan_affinity_controller(
|
||||
affinity_id: int,
|
||||
data: YifanAffinityUpdateSchema,
|
||||
auth: AuthSchema = Depends(AuthPermission(["module_yifan:yifan_affinity:update"]))
|
||||
) -> JSONResponse:
|
||||
"""更新缘分合盘接口"""
|
||||
result_dict = await YifanAffinityService.update_yifan_affinity_service(auth=auth, affinity_id=affinity_id, data=data)
|
||||
log.info("更新缘分合盘成功")
|
||||
return SuccessResponse(data=result_dict, msg="更新缘分合盘成功")
|
||||
|
||||
|
||||
@YifanAffinityRouter.delete("/{affinity_id}", summary="删除缘分合盘", description="根据ID删除缘分合盘")
|
||||
async def delete_yifan_affinity_controller(
|
||||
affinity_id: int,
|
||||
auth: AuthSchema = Depends(AuthPermission(["module_yifan:yifan_affinity:delete"]))
|
||||
) -> JSONResponse:
|
||||
"""删除缘分合盘接口"""
|
||||
result = await YifanAffinityService.delete_yifan_affinity_service(auth=auth, affinity_id=affinity_id)
|
||||
log.info("删除缘分合盘成功")
|
||||
return SuccessResponse(data=result, msg="删除缘分合盘成功")
|
||||
@@ -0,0 +1,112 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Sequence
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.core.base_crud import CRUDBase
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from app.api.v1.module_yifan.yifan_affinity.model import YifanAffinityModel
|
||||
from app.api.v1.module_yifan.yifan_affinity.schema import YifanAffinityCreateSchema, YifanAffinityUpdateSchema
|
||||
|
||||
|
||||
class YifanAffinityCRUD(CRUDBase[YifanAffinityModel, YifanAffinityCreateSchema, YifanAffinityUpdateSchema]):
|
||||
"""缘分合盘CRUD操作"""
|
||||
|
||||
def __init__(self, auth: AuthSchema):
|
||||
super().__init__(model=YifanAffinityModel, auth=auth)
|
||||
|
||||
async def create_yifan_affinity_crud(self, data: YifanAffinityCreateSchema) -> YifanAffinityModel:
|
||||
"""
|
||||
创建缘分合盘记录
|
||||
|
||||
参数:
|
||||
- data (YifanAffinityCreateSchema): 创建数据
|
||||
|
||||
返回:
|
||||
- YifanAffinityModel: 模型实例
|
||||
"""
|
||||
return await self.create(data=data)
|
||||
|
||||
async def get_yifan_affinity_crud(self, affinity_id: int, preload: list | None = None) -> YifanAffinityModel | None:
|
||||
"""
|
||||
获取单个缘分合盘记录
|
||||
|
||||
参数:
|
||||
- affinity_id (int): 记录ID
|
||||
- preload (list | None): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- YifanAffinityModel | None: 模型实例或None
|
||||
"""
|
||||
return await self.get(id=affinity_id, preload=preload)
|
||||
|
||||
async def update_yifan_affinity_crud(self, affinity_id: int, data: YifanAffinityUpdateSchema) -> YifanAffinityModel:
|
||||
"""
|
||||
更新缘分合盘记录
|
||||
|
||||
参数:
|
||||
- affinity_id (int): 记录ID
|
||||
- data (YifanAffinityUpdateSchema): 更新数据
|
||||
|
||||
返回:
|
||||
- YifanAffinityModel: 更新后的模型实例
|
||||
"""
|
||||
return await self.update(id=affinity_id, data=data)
|
||||
|
||||
async def delete_yifan_affinity_crud(self, affinity_id: int) -> bool:
|
||||
"""
|
||||
删除缘分合盘记录
|
||||
|
||||
参数:
|
||||
- affinity_id (int): 记录ID
|
||||
|
||||
返回:
|
||||
- bool: 删除是否成功
|
||||
"""
|
||||
return await self.delete(id=affinity_id)
|
||||
|
||||
async def list_yifan_affinity_crud(self, search: dict | None = None, order_by: list[dict] | None = None, preload: list | None = None) -> Sequence[YifanAffinityModel]:
|
||||
"""
|
||||
列表查询
|
||||
|
||||
参数:
|
||||
- search (dict | None): 查询参数
|
||||
- order_by (list[dict] | None): 排序参数,未提供时使用模型默认项
|
||||
- preload (list | None): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- Sequence[YifanAffinityModel]: 模型实例序列
|
||||
"""
|
||||
return await self.list(search=search, order_by=order_by, preload=preload)
|
||||
|
||||
async def page_yifan_affinity_crud(self, page_no: int, page_size: int, search: dict | None = None, order_by: list[dict] | None = None, preload: list | None = None) -> dict:
|
||||
"""
|
||||
分页查询
|
||||
|
||||
参数:
|
||||
- page_no (int): 页码
|
||||
- page_size (int): 每页数量
|
||||
- search (dict | None): 查询参数
|
||||
- order_by (list[dict] | None): 排序参数,未提供时使用模型默认项
|
||||
- preload (list | None): 预加载关系,未提供时使用模型默认项
|
||||
|
||||
返回:
|
||||
- dict: 分页结果字典
|
||||
"""
|
||||
from app.api.v1.module_yifan.yifan_affinity.schema import YifanAffinityOutSchema
|
||||
|
||||
# 将页码转换为偏移量
|
||||
offset = (page_no - 1) * page_size
|
||||
|
||||
# 设置默认排序
|
||||
if order_by is None:
|
||||
order_by = [{"field": "created_time", "order": "desc"}]
|
||||
|
||||
return await self.page(
|
||||
offset=offset,
|
||||
limit=page_size,
|
||||
search=search or {},
|
||||
order_by=order_by,
|
||||
out_schema=YifanAffinityOutSchema,
|
||||
preload=preload
|
||||
)
|
||||
@@ -0,0 +1,59 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import datetime
|
||||
from sqlalchemy import Integer, DateTime, SmallInteger, Text, String, JSON, Numeric
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.core.base_model import ModelMixin, UserMixin
|
||||
|
||||
|
||||
class YifanAffinityModel(ModelMixin, UserMixin):
|
||||
"""
|
||||
缘分合盘表
|
||||
"""
|
||||
__tablename__: str = 'yifan_affinity'
|
||||
__table_args__: dict[str, str] = {'comment': '缘分合盘'}
|
||||
__loader_options__: list[str] = ["created_by", "updated_by"]
|
||||
|
||||
is_deleted: Mapped[int | None] = mapped_column(SmallInteger, nullable=True, comment='是否删除(0否 1是)')
|
||||
task_status: Mapped[int | None] = mapped_column(SmallInteger, nullable=True, comment='任务状态(1已创建 2测算中 5测算成功 3测算超时 0任务失败)')
|
||||
user_id: Mapped[int | None] = mapped_column(Integer, nullable=True, comment='用户ID')
|
||||
|
||||
# 缘分类型
|
||||
relationship: Mapped[str | None] = mapped_column(String(32), nullable=True, comment='缘分类型(couple情侣 married夫妻 crush暗恋 partner合伙 friend知己 family亲缘)')
|
||||
|
||||
# 甲方信息
|
||||
person1_name: Mapped[str | None] = mapped_column(String(100), nullable=True, comment='甲方姓名')
|
||||
person1_gender: Mapped[str | None] = mapped_column(String(10), nullable=True, comment='甲方性别(male男 female女)')
|
||||
person1_birth_date: Mapped[str | None] = mapped_column(String(100), nullable=True, comment='甲方生辰显示格式')
|
||||
person1_birth_date_api: Mapped[datetime.datetime | None] = mapped_column(DateTime, nullable=True, comment='甲方生辰API格式')
|
||||
|
||||
# 乙方信息
|
||||
person2_name: Mapped[str | None] = mapped_column(String(100), nullable=True, comment='乙方姓名')
|
||||
person2_gender: Mapped[str | None] = mapped_column(String(10), nullable=True, comment='乙方性别(male男 female女)')
|
||||
person2_birth_date: Mapped[str | None] = mapped_column(String(100), nullable=True, comment='乙方生辰显示格式')
|
||||
person2_birth_date_api: Mapped[datetime.datetime | None] = mapped_column(DateTime, nullable=True, comment='乙方生辰API格式')
|
||||
|
||||
# 测算结果
|
||||
score: Mapped[int | None] = mapped_column(Integer, nullable=True, comment='默契指数总分(0-100)')
|
||||
score_badge: Mapped[str | None] = mapped_column(String(32), nullable=True, comment='婚配等级(上上婚 上等婚 中等婚 下等婚)')
|
||||
|
||||
# 六维契合度评分(JSON格式存储)
|
||||
six_dimension: Mapped[str | None] = mapped_column(JSON, nullable=True, comment='六维契合度评分JSON')
|
||||
radar_desc: Mapped[str | None] = mapped_column(Text, nullable=True, comment='六维雷达图描述')
|
||||
|
||||
# 分析卡片(JSON格式存储)
|
||||
analysis_cards: Mapped[str | None] = mapped_column(JSON, nullable=True, comment='分析卡片列表JSON')
|
||||
|
||||
# 解锁状态
|
||||
is_unlocked: Mapped[int | None] = mapped_column(SmallInteger, nullable=True, comment='是否已解锁深度报告(0否 1是)')
|
||||
unlock_price: Mapped[str | None] = mapped_column(Numeric(10, 2), nullable=True, comment='解锁价格')
|
||||
|
||||
# 解锁后的深度内容(JSON格式存储)
|
||||
unlocked_content: Mapped[str | None] = mapped_column(JSON, nullable=True, comment='解锁后的深度内容JSON')
|
||||
|
||||
# AI原始数据
|
||||
ai_source_data: Mapped[str | None] = mapped_column(Text, nullable=True, comment='AI原始数据')
|
||||
error_message: Mapped[str | None] = mapped_column(Text, nullable=True, comment='错误信息')
|
||||
|
||||
remark: Mapped[str | None] = mapped_column(String(255), nullable=True, comment='备注')
|
||||
@@ -0,0 +1,244 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import datetime
|
||||
from typing import List, Dict, Any, Optional
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from fastapi import Query
|
||||
|
||||
from app.core.validator import DateTimeStr
|
||||
from app.core.base_schema import BaseSchema, UserBySchema
|
||||
|
||||
|
||||
class PersonSchema(BaseModel):
|
||||
"""人员信息模型"""
|
||||
name: str = Field(..., description='姓名', example='张三')
|
||||
gender: str = Field(..., description='性别', example='male')
|
||||
birth_date: str = Field(..., description='生辰八字(显示格式)', example='1990年1月1日 子时')
|
||||
birth_date_api: datetime.datetime = Field(..., description='生辰八字(API格式)', example='1990-01-01 00:30:00')
|
||||
|
||||
|
||||
class AffinityCalculateRequestSchema(BaseModel):
|
||||
"""缘分合盘测算请求模型"""
|
||||
relationship: str = Field(..., description='缘分类型', example='couple')
|
||||
person1: PersonSchema = Field(..., description='甲方信息(君)')
|
||||
person2: PersonSchema = Field(..., description='乙方信息(卿)')
|
||||
|
||||
|
||||
class SixDimensionSchema(BaseModel):
|
||||
"""六维契合度评分模型"""
|
||||
emotional: int = Field(..., description='情感共鸣', ge=0, le=100, example=92)
|
||||
communication: int = Field(..., description='沟通模式', ge=0, le=100, example=78)
|
||||
trust: int = Field(..., description='信任安全', ge=0, le=100, example=95)
|
||||
values: int = Field(..., description='价值观', ge=0, le=100, example=85)
|
||||
compatibility: int = Field(..., description='互补性', ge=0, le=100, example=88)
|
||||
luck: int = Field(..., description='互旺运势', ge=0, le=100, example=88)
|
||||
|
||||
|
||||
class AnalysisCardSchema(BaseModel):
|
||||
"""分析卡片模型"""
|
||||
id: str = Field(..., description='卡片ID', example='emotional')
|
||||
icon: str = Field(..., description='图标emoji', example='[表情]')
|
||||
icon_bg: str = Field(..., description='图标背景色', example='rgba(236, 72, 153, 0.2)')
|
||||
title: str = Field(..., description='标题', example='情感共鸣')
|
||||
score: str = Field(..., description='评分', example='92')
|
||||
summary: str = Field(..., description='摘要(折叠时显示)')
|
||||
content: str = Field(..., description='详细内容(展开时显示)')
|
||||
|
||||
|
||||
class PastLifeSchema(BaseModel):
|
||||
"""前世羁绊模型"""
|
||||
description: str = Field(..., description='前世关系描述')
|
||||
depth: str = Field(..., description='缘分深浅', example='三生石上旧精魂')
|
||||
relationship: str = Field(..., description='还债关系', example='互不相欠 · 共同成长')
|
||||
|
||||
|
||||
class MatchItemSchema(BaseModel):
|
||||
"""正缘特征验证模型"""
|
||||
label: str = Field(..., description='验证项', example='外貌特征')
|
||||
match: int = Field(..., description='契合度百分比', ge=0, le=100, example=90)
|
||||
desc: str = Field(..., description='描述')
|
||||
|
||||
|
||||
class GuideSchema(BaseModel):
|
||||
"""潜意识与相处指南模型"""
|
||||
real_needs: str = Field(..., description='TA的真实需求')
|
||||
red_zone: str = Field(..., description='绝对雷区')
|
||||
tips: List[str] = Field(..., description='如何拿捏TA')
|
||||
|
||||
|
||||
class MonthlyFortuneSchema(BaseModel):
|
||||
"""未来12个月感情运势模型"""
|
||||
heats: List[int] = Field(..., description='12个月的热度值(0-3)')
|
||||
heat_labels: List[str] = Field(..., description='热度标签')
|
||||
highlights: str = Field(..., description='高光时刻提示')
|
||||
warnings: str = Field(..., description='预警时刻提示')
|
||||
|
||||
|
||||
class TimelineItemSchema(BaseModel):
|
||||
"""未来十年关键节点项模型"""
|
||||
year: str = Field(..., description='年份', example='2025')
|
||||
title: str = Field(..., description='阶段标题', example='升温期')
|
||||
desc: str = Field(..., description='描述')
|
||||
highlight: bool = Field(..., description='是否高亮显示')
|
||||
|
||||
|
||||
class MasterAdviceSchema(BaseModel):
|
||||
"""大师寄语与化解之道模型"""
|
||||
message: str = Field(..., description='寄语')
|
||||
tips: List[str] = Field(..., description='开运建议')
|
||||
|
||||
|
||||
class UnlockedContentSchema(BaseModel):
|
||||
"""解锁后的深度内容模型"""
|
||||
past_life: PastLifeSchema = Field(..., description='前世羁绊')
|
||||
match_items: List[MatchItemSchema] = Field(..., description='正缘特征验证')
|
||||
guide: GuideSchema = Field(..., description='潜意识与相处指南')
|
||||
monthly_fortune: MonthlyFortuneSchema = Field(..., description='未来12个月感情运势')
|
||||
timeline: List[TimelineItemSchema] = Field(..., description='未来十年关键节点')
|
||||
master_advice: MasterAdviceSchema = Field(..., description='大师寄语与化解之道')
|
||||
|
||||
|
||||
class UnlockStatsSchema(BaseModel):
|
||||
"""解锁统计信息模型"""
|
||||
unlock_count: int = Field(..., description='已解锁人数', example=12392)
|
||||
accuracy: str = Field(..., description='准确率', example='98%')
|
||||
|
||||
|
||||
class PersonResponseSchema(BaseModel):
|
||||
"""人员响应信息模型"""
|
||||
name: str = Field(..., description='姓名')
|
||||
gender: str = Field(..., description='性别')
|
||||
birth_date: str = Field(..., description='生辰八字')
|
||||
|
||||
|
||||
class AffinityCalculateResponseSchema(BaseModel):
|
||||
"""缘分合盘测算响应模型"""
|
||||
relationship: str = Field(..., description='缘分类型')
|
||||
relationship_label: str = Field(..., description='缘分类型标签')
|
||||
person1: PersonResponseSchema = Field(..., description='甲方信息')
|
||||
person2: PersonResponseSchema = Field(..., description='乙方信息')
|
||||
score: int = Field(..., description='默契指数总分', ge=0, le=100)
|
||||
score_badge: str = Field(..., description='婚配等级')
|
||||
six_dimension: SixDimensionSchema = Field(..., description='六维契合度评分')
|
||||
radar_desc: str = Field(..., description='六维雷达图描述')
|
||||
analysis_cards: List[AnalysisCardSchema] = Field(..., description='分析卡片列表')
|
||||
unlocked: Optional[UnlockedContentSchema] = Field(None, description='解锁后的深度内容')
|
||||
is_unlocked: bool = Field(..., description='是否已解锁深度报告')
|
||||
unlock_price: float = Field(..., description='解锁价格(元)')
|
||||
unlock_stats: UnlockStatsSchema = Field(..., description='解锁统计信息')
|
||||
|
||||
|
||||
class YifanAffinityCreateSchema(BaseModel):
|
||||
"""
|
||||
缘分合盘新增模型
|
||||
"""
|
||||
is_deleted: int = Field(default=0, description='是否删除(0否 1是)')
|
||||
task_status: int = Field(default=1, description='任务状态(1已创建 2测算中 5测算成功 3测算超时 0任务失败)')
|
||||
user_id: int | None = Field(default=None, description='用户ID')
|
||||
relationship: str = Field(default=..., description='缘分类型')
|
||||
person1_name: str = Field(default=..., description='甲方姓名')
|
||||
person1_gender: str = Field(default=..., description='甲方性别')
|
||||
person1_birth_date: str = Field(default=..., description='甲方生辰显示格式')
|
||||
person1_birth_date_api: datetime.datetime = Field(default=..., description='甲方生辰API格式')
|
||||
person2_name: str = Field(default=..., description='乙方姓名')
|
||||
person2_gender: str = Field(default=..., description='乙方性别')
|
||||
person2_birth_date: str = Field(default=..., description='乙方生辰显示格式')
|
||||
person2_birth_date_api: datetime.datetime = Field(default=..., description='乙方生辰API格式')
|
||||
score: int | None = Field(default=None, description='默契指数总分')
|
||||
score_badge: str | None = Field(default=None, description='婚配等级')
|
||||
six_dimension: str | None = Field(default=None, description='六维契合度评分JSON')
|
||||
radar_desc: str | None = Field(default=None, description='六维雷达图描述')
|
||||
analysis_cards: str | None = Field(default=None, description='分析卡片列表JSON')
|
||||
is_unlocked: int = Field(default=0, description='是否已解锁深度报告')
|
||||
unlock_price: float | None = Field(default=9.9, description='解锁价格')
|
||||
unlocked_content: str | None = Field(default=None, description='解锁后的深度内容JSON')
|
||||
ai_source_data: str | None = Field(default=None, description='AI原始数据')
|
||||
error_message: str | None = Field(default=None, description='错误信息')
|
||||
remark: str | None = Field(default=None, description='备注')
|
||||
|
||||
|
||||
class YifanAffinityUpdateSchema(BaseModel):
|
||||
"""
|
||||
缘分合盘更新模型
|
||||
"""
|
||||
is_deleted: int | None = Field(default=None, description='是否删除(0否 1是)')
|
||||
task_status: int | None = Field(default=None, description='任务状态(1已创建 2测算中 5测算成功 3测算超时 0任务失败)')
|
||||
user_id: int | None = Field(default=None, description='用户ID')
|
||||
relationship: str | None = Field(default=None, description='缘分类型')
|
||||
person1_name: str | None = Field(default=None, description='甲方姓名')
|
||||
person1_gender: str | None = Field(default=None, description='甲方性别')
|
||||
person1_birth_date: str | None = Field(default=None, description='甲方生辰显示格式')
|
||||
person1_birth_date_api: datetime.datetime | None = Field(default=None, description='甲方生辰API格式')
|
||||
person2_name: str | None = Field(default=None, description='乙方姓名')
|
||||
person2_gender: str | None = Field(default=None, description='乙方性别')
|
||||
person2_birth_date: str | None = Field(default=None, description='乙方生辰显示格式')
|
||||
person2_birth_date_api: datetime.datetime | None = Field(default=None, description='乙方生辰API格式')
|
||||
score: int | None = Field(default=None, description='默契指数总分(0-100)')
|
||||
score_badge: str | None = Field(default=None, description='婚配等级')
|
||||
six_dimension: str | None = Field(default=None, description='六维契合度评分JSON')
|
||||
radar_desc: str | None = Field(default=None, description='六维雷达图描述')
|
||||
analysis_cards: str | None = Field(default=None, description='分析卡片列表JSON')
|
||||
is_unlocked: int | None = Field(default=None, description='是否已解锁深度报告')
|
||||
unlock_price: float | None = Field(default=None, description='解锁价格')
|
||||
unlocked_content: str | None = Field(default=None, description='解锁后的深度内容JSON')
|
||||
ai_source_data: str | None = Field(default=None, description='AI原始数据')
|
||||
error_message: str | None = Field(default=None, description='错误信息')
|
||||
remark: str | None = Field(default=None, description='备注')
|
||||
|
||||
|
||||
class YifanAffinityOutSchema(YifanAffinityCreateSchema, BaseSchema, UserBySchema):
|
||||
"""
|
||||
缘分合盘响应模型
|
||||
"""
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class YifanAffinityQueryParam:
|
||||
"""缘分合盘查询参数"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
relationship: str | None = Query(None, description="缘分类型"),
|
||||
person1_name: str | None = Query(None, description="甲方姓名"),
|
||||
person2_name: str | None = Query(None, description="乙方姓名"),
|
||||
score_min: int | None = Query(None, description="最低分数"),
|
||||
score_max: int | None = Query(None, description="最高分数"),
|
||||
score_badge: str | None = Query(None, description="婚配等级"),
|
||||
is_unlocked: int | None = Query(None, description="是否已解锁深度报告"),
|
||||
created_id: int | None = Query(None, description="创建人ID"),
|
||||
updated_id: int | None = Query(None, description="更新人ID"),
|
||||
is_deleted: int | None = Query(None, description="是否删除(0否 1是)"),
|
||||
task_status: int | None = Query(None, description="任务状态"),
|
||||
user_id: int | None = Query(None, description="用户ID"),
|
||||
created_time: list[DateTimeStr] | None = Query(None, description="创建时间范围", examples=["2025-01-01 00:00:00", "2025-12-31 23:59:59"]),
|
||||
updated_time: list[DateTimeStr] | None = Query(None, description="更新时间范围", examples=["2025-01-01 00:00:00", "2025-12-31 23:59:59"]),
|
||||
|
||||
) -> None:
|
||||
|
||||
# 精确查询字段
|
||||
self.created_id = created_id
|
||||
self.updated_id = updated_id
|
||||
self.is_deleted = is_deleted
|
||||
self.task_status = task_status
|
||||
self.user_id = user_id
|
||||
self.is_unlocked = is_unlocked
|
||||
|
||||
# 模糊查询字段
|
||||
self.relationship = ("like", relationship)
|
||||
self.person1_name = ("like", person1_name)
|
||||
self.person2_name = ("like", person2_name)
|
||||
self.score_badge = ("like", score_badge)
|
||||
|
||||
# 范围查询字段
|
||||
if score_min is not None and score_max is not None:
|
||||
self.score = ("between", (score_min, score_max))
|
||||
elif score_min is not None:
|
||||
self.score = (">=", score_min)
|
||||
elif score_max is not None:
|
||||
self.score = ("<=", score_max)
|
||||
|
||||
# 时间范围查询
|
||||
if created_time and len(created_time) == 2:
|
||||
self.created_time = ("between", (created_time[0], created_time[1]))
|
||||
if updated_time and len(updated_time) == 2:
|
||||
self.updated_time = ("between", (updated_time[0], updated_time[1]))
|
||||
@@ -0,0 +1,377 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import json
|
||||
import asyncio
|
||||
from typing import Dict, Any
|
||||
|
||||
from app.api.v1.module_system.auth.schema import AuthSchema
|
||||
from app.core.exceptions import CustomException
|
||||
from app.core.logger import log
|
||||
from app.api.v1.module_yifan.yifan_affinity.crud import YifanAffinityCRUD
|
||||
from app.api.v1.module_yifan.yifan_affinity.schema import (
|
||||
YifanAffinityCreateSchema, YifanAffinityUpdateSchema, YifanAffinityOutSchema, YifanAffinityQueryParam,
|
||||
AffinityCalculateRequestSchema, AffinityCalculateResponseSchema
|
||||
)
|
||||
|
||||
|
||||
class YifanAffinityService:
|
||||
"""缘分合盘服务"""
|
||||
|
||||
@classmethod
|
||||
async def calculate_affinity_service(cls, auth: AuthSchema, data: AffinityCalculateRequestSchema) -> Dict[str, Any]:
|
||||
"""
|
||||
缘分合盘测算服务(异步模式)
|
||||
|
||||
1. 校验数据,创建报告记录(status=1 已创建)
|
||||
2. 启动后台任务执行AI测算
|
||||
3. 立即返回 report_id + status
|
||||
"""
|
||||
user_id = auth.user.id if auth.user else 0
|
||||
|
||||
# 创建缘分合盘记录
|
||||
create_data = YifanAffinityCreateSchema(
|
||||
user_id=user_id,
|
||||
relationship=data.relationship,
|
||||
person1_name=data.person1.name,
|
||||
person1_gender=data.person1.gender,
|
||||
person1_birth_date=data.person1.birth_date,
|
||||
person1_birth_date_api=data.person1.birth_date_api,
|
||||
person2_name=data.person2.name,
|
||||
person2_gender=data.person2.gender,
|
||||
person2_birth_date=data.person2.birth_date,
|
||||
person2_birth_date_api=data.person2.birth_date_api,
|
||||
task_status=1,
|
||||
is_deleted=0
|
||||
)
|
||||
|
||||
try:
|
||||
affinity_obj = await YifanAffinityCRUD(auth).create_yifan_affinity_crud(data=create_data)
|
||||
affinity_id = affinity_obj.id
|
||||
log.info(f"[缘分合盘] 报告创建成功,ID: {affinity_id}")
|
||||
except Exception as e:
|
||||
log.error(f"[缘分合盘] 报告创建失败: {str(e)}")
|
||||
raise CustomException(msg=f"保存缘分合盘报告失败: {str(e)}")
|
||||
|
||||
# 启动后台任务执行AI测算
|
||||
asyncio.create_task(
|
||||
cls._process_affinity_calculation(
|
||||
affinity_id=affinity_id,
|
||||
data=data,
|
||||
user_id=user_id,
|
||||
)
|
||||
)
|
||||
|
||||
return {"affinity_id": affinity_id, "status": 1}
|
||||
|
||||
@classmethod
|
||||
async def _process_affinity_calculation(
|
||||
cls,
|
||||
affinity_id: int,
|
||||
data: AffinityCalculateRequestSchema,
|
||||
user_id: int,
|
||||
) -> None:
|
||||
"""后台任务:执行缘分合盘AI测算"""
|
||||
log_prefix = f"[缘分合盘-{affinity_id}]"
|
||||
|
||||
try:
|
||||
# 更新状态为测算中
|
||||
await cls._update_affinity_status(affinity_id, task_status=2)
|
||||
log.info(f"{log_prefix} 开始AI测算")
|
||||
|
||||
# 构建AI输入文本
|
||||
relationship_labels = {
|
||||
"couple": "情侣",
|
||||
"married": "夫妻",
|
||||
"crush": "暗恋",
|
||||
"partner": "合伙",
|
||||
"friend": "知己",
|
||||
"family": "亲缘"
|
||||
}
|
||||
|
||||
relationship_label = relationship_labels.get(data.relationship, data.relationship)
|
||||
|
||||
ai_input_text = f"""缘分合盘测算请求:
|
||||
缘分类型:{relationship_label}
|
||||
甲方信息:
|
||||
- 姓名:{data.person1.name}
|
||||
- 性别:{'男' if data.person1.gender == 'male' else '女'}
|
||||
- 生辰:{data.person1.birth_date}
|
||||
乙方信息:
|
||||
- 姓名:{data.person2.name}
|
||||
- 性别:{'男' if data.person2.gender == 'male' else '女'}
|
||||
- 生辰:{data.person2.birth_date}
|
||||
|
||||
请根据以上信息进行缘分合盘测算,返回详细的分析结果。"""
|
||||
|
||||
# 调用AI服务进行测算
|
||||
ai_response = await cls._call_ai_service(ai_input_text)
|
||||
log.info(f"ai_response is {ai_response}")
|
||||
if not ai_response:
|
||||
raise Exception("AI服务返回空结果")
|
||||
|
||||
# 解析AI响应并构建结果数据
|
||||
result_data = cls._parse_ai_response(ai_response, data, relationship_label)
|
||||
log.info(f"result_data is {result_data}")
|
||||
|
||||
# 更新数据库记录
|
||||
update_data = YifanAffinityUpdateSchema(
|
||||
score=result_data.get('score'),
|
||||
score_badge=result_data.get('score_badge'),
|
||||
six_dimension=json.dumps(result_data.get('six_dimension'), ensure_ascii=False),
|
||||
radar_desc=result_data.get('radar_desc'),
|
||||
analysis_cards=json.dumps(result_data.get('analysis_cards'), ensure_ascii=False),
|
||||
unlocked_content=json.dumps(result_data.get('unlocked'), ensure_ascii=False),
|
||||
ai_source_data=ai_response,
|
||||
task_status=5 # 测算成功
|
||||
)
|
||||
|
||||
await cls._update_affinity_record(affinity_id, update_data)
|
||||
log.info(f"{log_prefix} AI测算完成并保存成功")
|
||||
|
||||
except asyncio.TimeoutError:
|
||||
log.error(f"{log_prefix} AI测算超时")
|
||||
await cls._update_affinity_status(affinity_id, task_status=3, error_message="AI测算超时")
|
||||
except Exception as e:
|
||||
log.error(f"{log_prefix} AI测算失败: {str(e)}")
|
||||
await cls._update_affinity_status(affinity_id, task_status=0, error_message=str(e)[:500])
|
||||
|
||||
@classmethod
|
||||
async def _call_ai_service(cls, text: str) -> str:
|
||||
"""
|
||||
调用AI服务进行缘分合盘测算
|
||||
|
||||
Args:
|
||||
text: 输入文本
|
||||
|
||||
Returns:
|
||||
AI响应结果
|
||||
"""
|
||||
from app.api.v1.module_application.ai.service import AIModelTestService
|
||||
|
||||
log.info(f"[缘分合盘] 调用AI服务,输入长度: {len(text)}")
|
||||
|
||||
try:
|
||||
# 调用实际的AI服务,使用personal_naming模型类型
|
||||
ai_response = await AIModelTestService.test_naming(
|
||||
model_type="yuanfen_hepan",
|
||||
text=text
|
||||
)
|
||||
|
||||
log.info(f"[缘分合盘] AI服务响应成功,响应长度: {len(ai_response)}")
|
||||
return ai_response
|
||||
|
||||
except Exception as e:
|
||||
log.error(f"[缘分合盘] AI服务调用失败: {str(e)}")
|
||||
# 如果AI服务调用失败,返回模拟数据作为降级方案
|
||||
log.warning("[缘分合盘] 使用模拟数据作为降级方案")
|
||||
|
||||
mock_response = {
|
||||
"score": 85,
|
||||
"score_badge": "上等婚",
|
||||
"six_dimension": {
|
||||
"personality": 88,
|
||||
"career": 82,
|
||||
"wealth": 90,
|
||||
"health": 85,
|
||||
"emotion": 87,
|
||||
"family": 83
|
||||
},
|
||||
"radar_desc": "你们在财运和性格方面非常契合,情感交流也很顺畅。",
|
||||
"analysis_cards": [
|
||||
{
|
||||
"title": "性格契合度",
|
||||
"content": "你们的性格互补,能够相互理解和支持。",
|
||||
"score": 88
|
||||
},
|
||||
{
|
||||
"title": "事业发展",
|
||||
"content": "在事业上你们有共同目标,能够相互促进。",
|
||||
"score": 82
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
return json.dumps(mock_response, ensure_ascii=False)
|
||||
|
||||
@classmethod
|
||||
def _parse_ai_response(cls, ai_response: str, request_data: AffinityCalculateRequestSchema, relationship_label: str) -> Dict[str, Any]:
|
||||
"""解析AI响应数据"""
|
||||
try:
|
||||
ai_data = json.loads(ai_response)
|
||||
|
||||
# 构建完整的响应数据
|
||||
result_data = {
|
||||
"relationship": request_data.relationship,
|
||||
"relationship_label": relationship_label,
|
||||
"person1": {
|
||||
"name": request_data.person1.name,
|
||||
"gender": request_data.person1.gender,
|
||||
"birth_date": request_data.person1.birth_date
|
||||
},
|
||||
"person2": {
|
||||
"name": request_data.person2.name,
|
||||
"gender": request_data.person2.gender,
|
||||
"birth_date": request_data.person2.birth_date
|
||||
},
|
||||
"score": ai_data.get("score", 0),
|
||||
"score_badge": ai_data.get("score_badge", "中等婚"),
|
||||
"six_dimension": ai_data.get("six_dimension", {}),
|
||||
"radar_desc": ai_data.get("radar_desc", ""),
|
||||
"analysis_cards": ai_data.get("analysis_cards", []),
|
||||
"unlocked": ai_data.get("unlocked", {}),
|
||||
"is_unlocked": False,
|
||||
"unlock_price": 9.9,
|
||||
"unlock_stats": {
|
||||
"unlock_count": 12392,
|
||||
"accuracy": "98%"
|
||||
}
|
||||
}
|
||||
|
||||
return result_data
|
||||
|
||||
except json.JSONDecodeError as e:
|
||||
log.error(f"AI响应JSON解析失败: {str(e)}")
|
||||
raise Exception(f"AI响应格式错误: {str(e)}")
|
||||
|
||||
@classmethod
|
||||
async def _update_affinity_status(cls, affinity_id: int, task_status: int, error_message: str = None):
|
||||
"""更新缘分合盘状态"""
|
||||
from app.core.database import async_db_session
|
||||
|
||||
async with async_db_session() as session:
|
||||
try:
|
||||
# 创建一个临时的auth对象用于更新操作
|
||||
temp_auth = AuthSchema(user=None, db=session)
|
||||
crud = YifanAffinityCRUD(temp_auth)
|
||||
crud.session = session
|
||||
|
||||
update_data = YifanAffinityUpdateSchema(
|
||||
task_status=task_status,
|
||||
error_message=error_message
|
||||
)
|
||||
|
||||
await crud.update_yifan_affinity_crud(affinity_id, update_data)
|
||||
await session.commit() # 提交事务
|
||||
log.info(f"[缘分合盘] 状态更新成功,ID: {affinity_id}, 状态: {task_status}")
|
||||
except Exception as e:
|
||||
await session.rollback() # 回滚事务
|
||||
log.error(f"[缘分合盘] 状态更新失败,ID: {affinity_id}, 错误: {str(e)}")
|
||||
raise
|
||||
|
||||
@classmethod
|
||||
async def _update_affinity_record(cls, affinity_id: int, update_data: YifanAffinityUpdateSchema):
|
||||
"""更新缘分合盘记录"""
|
||||
from app.core.database import async_db_session
|
||||
|
||||
async with async_db_session() as session:
|
||||
try:
|
||||
# 创建一个临时的auth对象用于更新操作
|
||||
temp_auth = AuthSchema(user=None, db=session)
|
||||
crud = YifanAffinityCRUD(temp_auth)
|
||||
crud.session = session
|
||||
|
||||
await crud.update_yifan_affinity_crud(affinity_id, update_data)
|
||||
await session.commit() # 提交事务
|
||||
log.info(f"[缘分合盘] 记录更新成功,ID: {affinity_id}")
|
||||
except Exception as e:
|
||||
await session.rollback() # 回滚事务
|
||||
log.error(f"[缘分合盘] 记录更新失败,ID: {affinity_id}, 错误: {str(e)}")
|
||||
raise
|
||||
|
||||
@classmethod
|
||||
async def get_affinity_result_service(cls, auth: AuthSchema, affinity_id: int) -> AffinityCalculateResponseSchema:
|
||||
"""获取缘分合盘结果"""
|
||||
affinity_obj = await YifanAffinityCRUD(auth).get_yifan_affinity_crud(affinity_id)
|
||||
|
||||
if not affinity_obj:
|
||||
raise CustomException(msg="缘分合盘记录不存在")
|
||||
|
||||
if affinity_obj.task_status != 5:
|
||||
raise CustomException(msg="缘分合盘测算尚未完成")
|
||||
|
||||
# 构建响应数据
|
||||
response_data = {
|
||||
"relationship": affinity_obj.relationship,
|
||||
"relationship_label": cls._get_relationship_label(affinity_obj.relationship),
|
||||
"person1": {
|
||||
"name": affinity_obj.person1_name,
|
||||
"gender": affinity_obj.person1_gender,
|
||||
"birth_date": affinity_obj.person1_birth_date
|
||||
},
|
||||
"person2": {
|
||||
"name": affinity_obj.person2_name,
|
||||
"gender": affinity_obj.person2_gender,
|
||||
"birth_date": affinity_obj.person2_birth_date
|
||||
},
|
||||
"score": affinity_obj.score or 0,
|
||||
"score_badge": affinity_obj.score_badge or "中等婚",
|
||||
"six_dimension": json.loads(affinity_obj.six_dimension) if affinity_obj.six_dimension else {},
|
||||
"radar_desc": affinity_obj.radar_desc or "",
|
||||
"analysis_cards": json.loads(affinity_obj.analysis_cards) if affinity_obj.analysis_cards else [],
|
||||
"unlocked": json.loads(affinity_obj.unlocked_content) if affinity_obj.unlocked_content and affinity_obj.is_unlocked else None,
|
||||
"is_unlocked": bool(affinity_obj.is_unlocked),
|
||||
"unlock_price": float(affinity_obj.unlock_price or 9.9),
|
||||
"unlock_stats": {
|
||||
"unlock_count": 12392,
|
||||
"accuracy": "98%"
|
||||
}
|
||||
}
|
||||
|
||||
return AffinityCalculateResponseSchema(**response_data)
|
||||
|
||||
@classmethod
|
||||
def _get_relationship_label(cls, relationship: str) -> str:
|
||||
"""获取缘分类型标签"""
|
||||
labels = {
|
||||
"couple": "情侣",
|
||||
"married": "夫妻",
|
||||
"crush": "暗恋",
|
||||
"partner": "合伙",
|
||||
"friend": "知己",
|
||||
"family": "亲缘"
|
||||
}
|
||||
return labels.get(relationship, relationship)
|
||||
|
||||
@classmethod
|
||||
async def create_yifan_affinity_service(cls, auth: AuthSchema, data: YifanAffinityCreateSchema) -> dict:
|
||||
"""创建缘分合盘记录"""
|
||||
result_dict = await YifanAffinityCRUD(auth).create_yifan_affinity_crud(data=data)
|
||||
return YifanAffinityOutSchema.model_validate(result_dict).model_dump()
|
||||
|
||||
@classmethod
|
||||
async def get_yifan_affinity_service(cls, auth: AuthSchema, affinity_id: int) -> dict:
|
||||
"""获取单个缘分合盘记录"""
|
||||
result_dict = await YifanAffinityCRUD(auth).get_yifan_affinity_crud(affinity_id=affinity_id)
|
||||
if not result_dict:
|
||||
raise CustomException(msg="缘分合盘记录不存在")
|
||||
return YifanAffinityOutSchema.model_validate(result_dict).model_dump()
|
||||
|
||||
@classmethod
|
||||
async def update_yifan_affinity_service(cls, auth: AuthSchema, affinity_id: int, data: YifanAffinityUpdateSchema) -> dict:
|
||||
"""更新缘分合盘记录"""
|
||||
result_dict = await YifanAffinityCRUD(auth).update_yifan_affinity_crud(affinity_id=affinity_id, data=data)
|
||||
return YifanAffinityOutSchema.model_validate(result_dict).model_dump()
|
||||
|
||||
@classmethod
|
||||
async def delete_yifan_affinity_service(cls, auth: AuthSchema, affinity_id: int) -> bool:
|
||||
"""删除缘分合盘记录"""
|
||||
return await YifanAffinityCRUD(auth).delete_yifan_affinity_crud(affinity_id=affinity_id)
|
||||
|
||||
@classmethod
|
||||
async def list_yifan_affinity_service(cls, auth: AuthSchema, search: YifanAffinityQueryParam | None = None, order_by: list[dict] | None = None) -> list[dict]:
|
||||
"""列表查询"""
|
||||
search_dict = search.__dict__ if search else None
|
||||
obj_list = await YifanAffinityCRUD(auth).list_yifan_affinity_crud(search=search_dict, order_by=order_by)
|
||||
return [YifanAffinityOutSchema.model_validate(obj).model_dump() for obj in obj_list]
|
||||
|
||||
@classmethod
|
||||
async def page_yifan_affinity_service(cls, auth: AuthSchema, page_no: int, page_size: int, search: YifanAffinityQueryParam | None = None, order_by: list[dict] | None = None) -> dict:
|
||||
"""分页查询"""
|
||||
search_dict = search.__dict__ if search else None
|
||||
result_dict = await YifanAffinityCRUD(auth).page_yifan_affinity_crud(page_no=page_no, page_size=page_size, search=search_dict, order_by=order_by)
|
||||
|
||||
# 基础CRUD返回的是items键,我们需要转换为data键以保持API一致性
|
||||
if "items" in result_dict:
|
||||
result_dict["data"] = result_dict.pop("items")
|
||||
|
||||
return result_dict
|
||||
Reference in New Issue
Block a user