v0.0.4 - 修复寻配号API,添加user_name字段

This commit is contained in:
甲辰生产 2026-04-20 21:42:52 +08:00
parent 65d5603f9a
commit c70a4f1221
5 changed files with 57 additions and 1721 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,384 +1,195 @@
# seek - 寻号匹配路由 # seek - 寻号匹配路由
# Version: 0.0.1 # Version: 0.0.1
# 更新:
from fastapi import APIRouter, Depends, Query, HTTPException from fastapi import APIRouter, Depends, Query, HTTPException
# 更新:
# Version: 1.2.x # Version: 1.2.x
# 更新:
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
# 更新:
from pydantic import BaseModel from pydantic import BaseModel
# 更新: from typing import Optional, List
from typing import Optional
# 更新:
from datetime import datetime from datetime import datetime
# 更新:
from app.core.database import get_db from app.core.database import get_db
# 更新:
from app.core.auth import get_current_user from app.core.auth import get_current_user
# 更新:
from app.models.seek_info import SeekInfo from app.models.seek_info import SeekInfo
# 更新: from app.models.models import User
# 更新:
router = APIRouter(prefix="/api/seek", tags=["寻配号"]) router = APIRouter(prefix="/api/seek", tags=["寻配号"])
# 更新:
# 更新:
# ============ Schema ============ # ============ Schema ============
# 更新:
class SeekInfoCreate(BaseModel): class SeekInfoCreate(BaseModel):
# 更新:
title: str title: str
# 更新:
content: Optional[str] = None content: Optional[str] = None
# 更新:
expect_category: Optional[str] = None expect_category: Optional[str] = None
# 更新:
expect_version: Optional[str] = None expect_version: Optional[str] = None
# 更新:
expect_packaging: Optional[str] = None expect_packaging: Optional[str] = None
# 更新:
expect_number: Optional[str] = None expect_number: Optional[str] = None
# 更新:
expect_price_min: Optional[float] = None expect_price_min: Optional[float] = None
# 更新:
expect_price_max: Optional[float] = None expect_price_max: Optional[float] = None
# 更新:
# 更新:
class SeekInfoUpdate(BaseModel): class SeekInfoUpdate(BaseModel):
# 更新:
title: Optional[str] = None title: Optional[str] = None
# 更新:
content: Optional[str] = None content: Optional[str] = None
# 更新:
expect_category: Optional[str] = None expect_category: Optional[str] = None
# 更新:
expect_version: Optional[str] = None expect_version: Optional[str] = None
# 更新:
expect_packaging: Optional[str] = None expect_packaging: Optional[str] = None
# 更新:
expect_number: Optional[str] = None expect_number: Optional[str] = None
# 更新:
expect_price_min: Optional[float] = None expect_price_min: Optional[float] = None
# 更新:
expect_price_max: Optional[float] = None expect_price_max: Optional[float] = None
# 更新:
status: Optional[str] = None status: Optional[str] = None
# 更新:
# 更新:
class SeekInfoResponse(BaseModel): class SeekInfoResponse(BaseModel):
# 更新:
id: str id: str
# 更新:
user_id: str user_id: str
# 更新: user_name: Optional[str] = None
title: str title: str
# 更新:
content: Optional[str] content: Optional[str]
# 更新:
expect_category: Optional[str] expect_category: Optional[str]
# 更新:
expect_version: Optional[str] expect_version: Optional[str]
# 更新:
expect_packaging: Optional[str] expect_packaging: Optional[str]
# 更新:
expect_number: Optional[str] expect_number: Optional[str]
# 更新:
expect_price_min: Optional[float] expect_price_min: Optional[float]
# 更新:
expect_price_max: Optional[float] expect_price_max: Optional[float]
# 更新:
status: str status: str
# 更新:
is_matched: Optional[str] is_matched: Optional[str]
# 更新:
matched_user_id: Optional[str] matched_user_id: Optional[str]
# 更新:
matched_contact: Optional[str] matched_contact: Optional[str]
# 更新:
view_count: int view_count: int
# 更新:
contact_count: int contact_count: int
# 更新:
created_at: Optional[datetime] created_at: Optional[datetime]
# 更新:
updated_at: Optional[datetime] updated_at: Optional[datetime]
# 更新:
# 更新:
class Config: class Config:
# 更新:
from_attributes = True from_attributes = True
# 更新:
# 更新:
# ============ API ============ # ============ API ============
# 更新:
@router.get("/list", response_model=list[SeekInfoResponse]) @router.get("/list", response_model=list[SeekInfoResponse])
# 更新:
def get_seek_list( def get_seek_list(
# 更新:
status: str = Query("active"), status: str = Query("active"),
# 更新:
page: int = Query(1, ge=1), page: int = Query(1, ge=1),
# 更新:
page_size: int = Query(20, ge=1, le=1000), page_size: int = Query(20, ge=1, le=1000),
# 更新:
user_only: bool = Query(False), user_only: bool = Query(False),
# 更新:
current_user: Optional = Depends(get_current_user), current_user: Optional = Depends(get_current_user),
# 更新:
db: Session = Depends(get_db) db: Session = Depends(get_db)
# 更新:
): ):
# 更新:
"""获取寻配号列表""" """获取寻配号列表"""
# 更新:
query = db.query(SeekInfo).filter(SeekInfo.status == status) query = db.query(SeekInfo).filter(SeekInfo.status == status)
# 更新:
# 更新:
# 我的寻配号:只查看自己的 # 我的寻配号:只查看自己的
# 更新:
if user_only and current_user: if user_only and current_user:
# 更新:
query = query.filter(SeekInfo.user_id == current_user.f99_90_id) query = query.filter(SeekInfo.user_id == current_user.f99_90_id)
# 更新:
# 更新:
# 排序 # 排序
# 更新:
query = query.order_by(SeekInfo.created_at.desc()) query = query.order_by(SeekInfo.created_at.desc())
# 更新:
# 更新:
# 分页 # 分页
# 更新:
offset = (page - 1) * page_size offset = (page - 1) * page_size
# 更新:
items = query.offset(offset).limit(page_size).all() items = query.offset(offset).limit(page_size).all()
# 更新:
# 更新:
return items return items
# 更新:
# 更新:
@router.get("/stats") @router.get("/stats")
# 更新:
def get_seek_stats( def get_seek_stats(
# 更新:
current_user = Depends(get_current_user), current_user = Depends(get_current_user),
# 更新:
db: Session = Depends(get_db) db: Session = Depends(get_db)
# 更新:
): ):
# 更新:
"""获取寻配号统计""" """获取寻配号统计"""
# 更新:
total = db.query(SeekInfo).filter(SeekInfo.status == "active").count() total = db.query(SeekInfo).filter(SeekInfo.status == "active").count()
# 更新:
matched = db.query(SeekInfo).filter(SeekInfo.is_matched == "true").count() matched = db.query(SeekInfo).filter(SeekInfo.is_matched == "true").count()
# 更新:
# 更新:
return { return {
# 更新:
"total": total, "total": total,
# 更新:
"matched": matched, "matched": matched,
# 更新:
"unmatched": total - matched "unmatched": total - matched
# 更新:
} }
# 更新:
# 更新:
@router.post("", response_model=SeekInfoResponse) @router.post("", response_model=SeekInfoResponse)
# 更新:
def create_seek( def create_seek(
# 更新:
data: SeekInfoCreate, data: SeekInfoCreate,
# 更新:
current_user = Depends(get_current_user), current_user = Depends(get_current_user),
# 更新:
db: Session = Depends(get_db) db: Session = Depends(get_db)
# 更新:
): ):
# 更新:
"""创建寻配号""" """创建寻配号"""
# 更新:
if not current_user: if not current_user:
# 更新:
raise HTTPException(status_code=401, detail="请先登录") raise HTTPException(status_code=401, detail="请先登录")
# 更新:
# 更新:
seek = SeekInfo( seek = SeekInfo(
# 更新:
user_id=current_user.f99_90_id, user_id=current_user.f99_90_id,
# 更新:
title=data.title, title=data.title,
# 更新:
content=data.content, content=data.content,
# 更新:
expect_category=data.expect_category, expect_category=data.expect_category,
# 更新:
expect_version=data.expect_version, expect_version=data.expect_version,
# 更新:
expect_packaging=data.expect_packaging, expect_packaging=data.expect_packaging,
# 更新:
expect_number=data.expect_number, expect_number=data.expect_number,
# 更新:
expect_price_min=data.expect_price_min, expect_price_min=data.expect_price_min,
# 更新:
expect_price_max=data.expect_price_max, expect_price_max=data.expect_price_max,
# 更新:
status="active" status="active"
# 更新:
) )
# 更新:
db.add(seek) db.add(seek)
# 更新:
db.commit() db.commit()
# 更新:
db.refresh(seek) db.refresh(seek)
# 更新:
return seek return seek
# 更新:
# 更新:
@router.get("/{seek_id}", response_model=SeekInfoResponse) @router.get("/{seek_id}", response_model=SeekInfoResponse)
# 更新:
def get_seek( def get_seek(
# 更新:
seek_id: str, seek_id: str,
# 更新:
current_user = Depends(get_current_user), current_user = Depends(get_current_user),
# 更新:
db: Session = Depends(get_db) db: Session = Depends(get_db)
# 更新:
): ):
# 更新:
"""获取寻配号详情""" """获取寻配号详情"""
# 更新:
seek = db.query(SeekInfo).filter(SeekInfo.id == seek_id).first() seek = db.query(SeekInfo).filter(SeekInfo.id == seek_id).first()
# 更新:
if not seek: if not seek:
# 更新:
raise HTTPException(status_code=404, detail="寻配号不存在") raise HTTPException(status_code=404, detail="寻配号不存在")
# 更新:
# 更新:
# 增加浏览数 # 增加浏览数
# 更新:
seek.view_count += 1 seek.view_count += 1
# 更新:
db.commit() db.commit()
# 更新:
# 更新:
return seek return seek
# 更新:
# 更新:
@router.put("/{seek_id}", response_model=SeekInfoResponse) @router.put("/{seek_id}", response_model=SeekInfoResponse)
# 更新:
def update_seek( def update_seek(
# 更新:
seek_id: str, seek_id: str,
# 更新:
data: SeekInfoUpdate, data: SeekInfoUpdate,
# 更新:
current_user = Depends(get_current_user), current_user = Depends(get_current_user),
# 更新:
db: Session = Depends(get_db) db: Session = Depends(get_db)
# 更新:
): ):
# 更新:
"""更新寻配号""" """更新寻配号"""
# 更新:
if not current_user: if not current_user:
# 更新:
raise HTTPException(status_code=401, detail="请先登录") raise HTTPException(status_code=401, detail="请先登录")
# 更新:
# 更新:
seek = db.query(SeekInfo).filter( seek = db.query(SeekInfo).filter(
# 更新:
SeekInfo.id == seek_id, SeekInfo.id == seek_id,
# 更新:
SeekInfo.user_id == current_user.f99_90_id SeekInfo.user_id == current_user.f99_90_id
# 更新:
).first() ).first()
# 更新:
# 更新:
if not seek: if not seek:
# 更新:
raise HTTPException(status_code=404, detail="寻配号不存在") raise HTTPException(status_code=404, detail="寻配号不存在")
# 更新:
# 更新:
for key, value in data.model_dump(exclude_unset=True).items(): for key, value in data.model_dump(exclude_unset=True).items():
# 更新:
setattr(seek, key, value) setattr(seek, key, value)
# 更新:
# 更新:
db.commit() db.commit()
# 更新:
db.refresh(seek) db.refresh(seek)
# 更新:
return seek return seek
# 更新:
# 更新:
@router.delete("/{seek_id}") @router.delete("/{seek_id}")
# 更新:
def delete_seek( def delete_seek(
# 更新:
seek_id: str, seek_id: str,
# 更新:
current_user = Depends(get_current_user), current_user = Depends(get_current_user),
# 更新:
db: Session = Depends(get_db) db: Session = Depends(get_db)
# 更新:
): ):
# 更新:
"""删除寻配号""" """删除寻配号"""
# 更新:
if not current_user: if not current_user:
# 更新:
raise HTTPException(status_code=401, detail="请先登录") raise HTTPException(status_code=401, detail="请先登录")
# 更新:
# 更新:
seek = db.query(SeekInfo).filter( seek = db.query(SeekInfo).filter(
# 更新:
SeekInfo.id == seek_id, SeekInfo.id == seek_id,
# 更新:
SeekInfo.user_id == current_user.f99_90_id SeekInfo.user_id == current_user.f99_90_id
# 更新:
).first() ).first()
# 更新:
# 更新:
if not seek: if not seek:
# 更新:
raise HTTPException(status_code=404, detail="寻配号不存在") raise HTTPException(status_code=404, detail="寻配号不存在")
# 更新:
# 更新:
seek.status = "deleted" seek.status = "deleted"
# 更新:
db.commit() db.commit()
# 更新:
# 更新:
return {"message": "删除成功"} return {"message": "删除成功"}
# 更新:

View File

@ -1,6 +1,6 @@
# Pydantic Schema - 使用字段编码并支持 camelCase # Pydantic Schema - 使用字段编码并支持 camelCase
from typing import Optional, List from typing import Optional, List
from pydantic import BaseModel, EmailStr, Field, ConfigDict from pydantic import BaseModel, EmailStr, Field
from datetime import datetime from datetime import datetime
@ -14,7 +14,9 @@ class UserBase(BaseModel):
address: Optional[str] = None address: Optional[str] = None
bio: Optional[str] = None bio: Optional[str] = None
model_config = ConfigDict(from_attributes=True, populate_by_name=True) class Config:
from_attributes = True
populate_by_name = True
class UserCreate(UserBase): class UserCreate(UserBase):
@ -31,7 +33,9 @@ class UserUpdate(BaseModel):
bio: Optional[str] = None bio: Optional[str] = None
password: Optional[str] = None password: Optional[str] = None
model_config = ConfigDict(from_attributes=True, populate_by_name=True) class Config:
from_attributes = True
populate_by_name = True
class UserResponse(UserBase): class UserResponse(UserBase):
@ -59,7 +63,9 @@ class UserResponse(UserBase):
f99_92_created_at: Optional[datetime] = Field(None, alias="created_at") f99_92_created_at: Optional[datetime] = Field(None, alias="created_at")
f99_93_updated_at: Optional[datetime] = Field(None, alias="updated_at") f99_93_updated_at: Optional[datetime] = Field(None, alias="updated_at")
model_config = ConfigDict(from_attributes=True, populate_by_name=True) class Config:
from_attributes = True
populate_by_name = True
# ============ 藏品相关 ============ # ============ 藏品相关 ============
@ -104,7 +110,9 @@ class CollectionBase(BaseModel):
# f06 其他信息 # f06 其他信息
f06_50_purpose: Optional[str] = Field(None, alias="purpose") f06_50_purpose: Optional[str] = Field(None, alias="purpose")
model_config = ConfigDict(from_attributes=True, populate_by_name=True) class Config:
from_attributes = True
populate_by_name = True
class CollectionCreate(CollectionBase): class CollectionCreate(CollectionBase):
@ -151,7 +159,9 @@ class CollectionUpdate(BaseModel):
# f06 其他信息 # f06 其他信息
f06_50_purpose: Optional[str] = Field(None, alias="purpose") f06_50_purpose: Optional[str] = Field(None, alias="purpose")
model_config = ConfigDict(from_attributes=True, populate_by_name=True) class Config:
from_attributes = True
populate_by_name = True
class CollectionImageResponse(BaseModel): class CollectionImageResponse(BaseModel):
@ -161,7 +171,8 @@ class CollectionImageResponse(BaseModel):
path: Optional[str] = None path: Optional[str] = None
f99_92_created_at: Optional[datetime] = None f99_92_created_at: Optional[datetime] = None
model_config = ConfigDict(from_attributes=True) class Config:
from_attributes = True
class CollectionResponse(CollectionBase): class CollectionResponse(CollectionBase):
@ -171,7 +182,9 @@ class CollectionResponse(CollectionBase):
f99_93_updated_at: Optional[datetime] = Field(None, alias="updatedAt") f99_93_updated_at: Optional[datetime] = Field(None, alias="updatedAt")
images: List[CollectionImageResponse] = [] images: List[CollectionImageResponse] = []
model_config = ConfigDict(from_attributes=True, populate_by_name=True) class Config:
from_attributes = True
populate_by_name = True
class CollectionListResponse(BaseModel): class CollectionListResponse(BaseModel):
@ -196,7 +209,8 @@ class OperationResponse(OperationBase):
f99_91_user_id: str f99_91_user_id: str
f99_93_created_at: Optional[datetime] = None f99_93_created_at: Optional[datetime] = None
model_config = ConfigDict(from_attributes=True) class Config:
from_attributes = True
# ============ OCR 相关 ============ # ============ OCR 相关 ============

View File

@ -1,7 +1,7 @@
/** /**
* Home - 首页 * Home - 首页
* Version: 0.0.2 * Version: 0.0.3
* 更新快捷操作改为黑色背景2026-04-20 * 更新修复寻配号stats接口调用2026-04-20
*/ */
import React, { useState, useEffect } from 'react' import React, { useState, useEffect } from 'react'

View File

@ -1,7 +1,7 @@
/** /**
* News - 资讯列表页面 * News - 资讯列表页面
* Version: 0.0.1 * Version: 0.0.2
* 更新 * 更新修复寻配号API调用使用/api/seek接口2026-04-20
*/ */
import React, { useState, useEffect } from 'react' import React, { useState, useEffect } from 'react'
@ -330,7 +330,7 @@ export default function News() {
try { try {
// //
const content = (editingSeek.content || '') + '\n联系方式: ' + editingSeek.contact const content = (editingSeek.content || '') + '\n联系方式: ' + editingSeek.contact
await fetch(`${API_BASE}/api/information/${editingSeek.id}`, { await fetch(`${API_BASE}/api/seek/${editingSeek.id}`, {
method: 'PUT', method: 'PUT',
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' }, headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ body: JSON.stringify({
@ -352,16 +352,14 @@ export default function News() {
const content = (seekForm.content ? seekForm.content + '\n' : '') + (seekForm.price ? `价格: ${seekForm.price}` : '') + (seekForm.features ? '\n号码特征: J0' + seekForm.features : '') + '\n联系方式: ' + finalContact const content = (seekForm.content ? seekForm.content + '\n' : '') + (seekForm.price ? `价格: ${seekForm.price}` : '') + (seekForm.features ? '\n号码特征: J0' + seekForm.features : '') + '\n联系方式: ' + finalContact
try { try {
const token = localStorage.getItem('token') const token = localStorage.getItem('token')
// editioncategory // /api/seek
const categoryMap = { '龙钞': '龙钞', '马钞': '马钞', '蛇钞': '蛇钞' } const res = await fetch(`${API_BASE}/api/seek`, {
const res = await fetch(`${API_BASE}/api/information/`, {
method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ body: JSON.stringify({
title: seekForm.title, title: seekForm.title,
content, content,
info_type: 'seek',
expect_category: seekForm.edition, expect_category: seekForm.edition,
expect_number: seekForm.features ? 'J0' + seekForm.features : None expect_number: seekForm.features ? 'J0' + seekForm.features : null
}) })
}) })
const data = await res.json() const data = await res.json()
@ -370,7 +368,7 @@ export default function News() {
setShowSeekPublish(false) setShowSeekPublish(false)
setSeekForm({ edition: '龙钞', type: '', category: '寻号', price: '', matchType: '模糊', features: '', content: '', title: '', contact: getUserPhone() || '' }) setSeekForm({ edition: '龙钞', type: '', category: '寻号', price: '', matchType: '模糊', features: '', content: '', title: '', contact: getUserPhone() || '' })
fetchInfoList() fetchInfoList()
} else { alert(data.message || '发布失败') } } else { alert(data.detail || data.message || '发布失败') }
} catch (e) { alert('发布失败: ' + e.message) } } catch (e) { alert('发布失败: ' + e.message) }
} }