对接口进行调整。

This commit is contained in:
aaron 2025-01-08 00:46:37 +08:00
parent 038a2df3da
commit d80555bbad
7 changed files with 155 additions and 42 deletions

View File

@ -1,7 +1,10 @@
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from typing import List
from app.models.community import CommunityDB, CommunityCreate, CommunityUpdate, CommunityInfo
from typing import List, Optional
from app.models.community import (
CommunityDB, CommunityCreate, CommunityUpdate,
CommunityInfo, CommunityStatus
)
from app.models.database import get_db
from app.api.deps import get_admin_user
from app.models.user import UserDB
@ -22,32 +25,51 @@ async def create_community(
db.refresh(db_community)
return success_response(data=CommunityInfo.model_validate(db_community))
@router.get("/", response_model=ResponseModel)
@router.get("", response_model=ResponseModel)
async def get_communities(
latitude: float,
longitude: float,
latitude: Optional[float] = None,
longitude: Optional[float] = None,
status: Optional[CommunityStatus] = None,
skip: int = 0,
limit: int = 10,
db: Session = Depends(get_db)
):
"""获取社区列表"""
communities = db.query(CommunityDB).all()
# 构建查询
query = db.query(CommunityDB)
# 状态过滤
if status:
query = query.filter(CommunityDB.status == status)
# 获取总数
total = query.count()
# 查询数据
communities = query.offset(skip).limit(limit).all()
# 计算距离并排序
community_list = []
for community in communities:
# 使用 Haversine 公式计算两点之间的距离
distance = calculate_distance(
latitude, longitude,
community.latitude, community.longitude
)
community_info = CommunityInfo.model_validate(community)
community_info.distance = distance
# 如果提供了经纬度,则计算距离
if latitude is not None and longitude is not None:
distance = calculate_distance(
latitude, longitude,
community.latitude, community.longitude
)
community_info.distance = distance
community_list.append(community_info)
# 按距离排序
community_list.sort(key=lambda x: x.distance)
# 如果计算了距离,则按距离排序
if latitude is not None and longitude is not None:
community_list.sort(key=lambda x: x.distance)
return success_response(data=community_list)
return success_response(data={
"total": total,
"items": community_list
})
def calculate_distance(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
"""计算两点之间的距离(米)"""

View File

@ -1,6 +1,6 @@
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from typing import List
from typing import List, Optional
from app.models.community_building import (
CommunityBuildingDB,
CommunityBuildingCreate,
@ -14,23 +14,28 @@ from app.core.response import success_response, error_response, ResponseModel
router = APIRouter()
@router.get("/list/{community_id}", response_model=ResponseModel)
async def get_community_buildings(
community_id: int,
@router.get("/list", response_model=ResponseModel)
async def get_buildings(
community_id: Optional[int] = None,
skip: int = 0,
limit: int = 20,
limit: int = 10,
db: Session = Depends(get_db)
):
"""获取小区的楼栋列表"""
buildings = db.query(CommunityBuildingDB).filter(
CommunityBuildingDB.community_id == community_id
).order_by(
CommunityBuildingDB.building_number
).offset(skip).limit(limit).all()
"""获取楼栋列表"""
query = db.query(CommunityBuildingDB)
if community_id:
query = query.filter(CommunityBuildingDB.community_id == community_id)
return success_response(data=[
CommunityBuildingInfo.model_validate(b) for b in buildings
])
# 获取总数
total = query.count()
# 查询数据
buildings = query.offset(skip).limit(limit).all()
return success_response(data={
"total": total,
"items": [CommunityBuildingInfo.model_validate(b) for b in buildings]
})
@router.post("", response_model=ResponseModel)
async def create_building(

View File

@ -24,9 +24,9 @@ async def create_station(
@router.get("/", response_model=ResponseModel)
async def get_stations(
community_id: Optional[int] = None,
skip: int = 0,
limit: int = 10,
community_id: Optional[int] = None,
db: Session = Depends(get_db)
):
"""获取驿站列表"""
@ -34,8 +34,16 @@ async def get_stations(
if community_id:
query = query.filter(StationDB.community_id == community_id)
# 获取总数
total = query.count()
# 查询数据
stations = query.offset(skip).limit(limit).all()
return success_response(data=[StationInfo.model_validate(s) for s in stations])
return success_response(data={
"total": total,
"items": [StationInfo.model_validate(s) for s in stations]
})
@router.get("/{station_id}", response_model=ResponseModel)
async def get_station(

View File

@ -35,6 +35,10 @@ client = UniSMS(settings.UNI_APP_ID)
class MockLoginRequest(BaseModel):
phone: str = Field(..., pattern="^1[3-9]\d{9}$")
class ResetPasswordRequest(BaseModel):
user_id: int
new_password: str = Field(..., min_length=6, max_length=20)
@router.post("/send-code")
async def send_verify_code(request: VerifyCodeRequest):
"""发送验证码"""
@ -251,7 +255,7 @@ async def update_user_roles(
db.rollback()
return error_response(code=500, message=f"更新失败: {str(e)}")
@router.post("/password_login", response_model=ResponseModel)
@router.post("/password-login", response_model=ResponseModel)
async def password_login(
login_data: UserPasswordLogin,
db: Session = Depends(get_db)
@ -269,11 +273,12 @@ async def password_login(
return error_response(code=401, message="密码错误")
# 生成访问令牌
access_token = create_access_token(user.phone)
access_token = create_access_token(data={"sub": user.phone})
return success_response(
data={
"access_token": f"Bearer {access_token}",
"access_token": access_token,
"token_type": "bearer",
"user": UserInfo.model_validate(user)
}
)
@ -328,4 +333,66 @@ def issue_register_coupons(db: Session, user_id: int):
expire_time=expire_time,
status="unused"
)
db.add(user_coupon)
db.add(user_coupon)
@router.get("/list", response_model=ResponseModel)
async def get_user_list(
skip: int = 0,
limit: int = 10,
db: Session = Depends(get_db),
admin: UserDB = Depends(get_admin_user) # 仅管理员可访问
):
"""获取用户列表(管理员)
Args:
skip: 跳过记录数
limit: 返回记录数
"""
total = db.query(UserDB).count()
users = db.query(UserDB).order_by(
UserDB.create_time.desc()
).offset(skip).limit(limit).all()
# 处理手机号脱敏
def mask_phone(phone: str) -> str:
return f"{phone[:3]}****{phone[7:]}"
user_list = []
for user in users:
user_info = UserInfo.model_validate(user)
user_info.phone = mask_phone(user_info.phone) # 手机号脱敏
user_list.append(user_info)
return success_response(data={
"total": total,
"items": user_list
})
@router.post("/reset-password", response_model=ResponseModel)
async def reset_password(
request: ResetPasswordRequest,
db: Session = Depends(get_db),
admin: UserDB = Depends(get_admin_user) # 仅管理员可操作
):
"""重置用户密码(管理员)"""
# 查找用户
user = db.query(UserDB).filter(UserDB.userid == request.user_id).first()
if not user:
return error_response(code=404, message="用户不存在")
# 重置密码
hashed_password = get_password_hash(request.new_password)
user.password = hashed_password
try:
db.commit()
return success_response(
message="密码重置成功",
data={
"userid": user.userid,
"username": user.username,
"phone": f"{user.phone[:3]}****{user.phone[7:]}" # 手机号脱敏
}
)
except Exception as e:
db.rollback()
return error_response(code=500, message=f"密码重置失败: {str(e)}")

View File

@ -1,9 +1,14 @@
from typing import Optional
from sqlalchemy import Column, Integer, String, DECIMAL, DateTime
import enum
from sqlalchemy import Column, Integer, String, DECIMAL, DateTime, Enum
from sqlalchemy.sql import func
from pydantic import BaseModel, Field
from .database import Base
class CommunityStatus(str, enum.Enum):
UNOPEN = "UNOPEN" # 未运营
OPENING = "OPENING" # 已运营
# 数据库模型
class CommunityDB(Base):
__tablename__ = "communities"
@ -13,6 +18,7 @@ class CommunityDB(Base):
address = Column(String(200), nullable=False)
longitude = Column(DECIMAL(9,6), nullable=False) # 经度精确到小数点后6位
latitude = Column(DECIMAL(9,6), nullable=False) # 纬度精确到小数点后6位
status = Column(Enum(CommunityStatus), nullable=False, default=CommunityStatus.UNOPEN)
create_time = Column(DateTime(timezone=True), server_default=func.now())
update_time = Column(DateTime(timezone=True), onupdate=func.now())
@ -22,12 +28,14 @@ class CommunityCreate(BaseModel):
address: str = Field(..., max_length=200)
longitude: float = Field(..., ge=-180, le=180)
latitude: float = Field(..., ge=-90, le=90)
status: CommunityStatus = Field(default=CommunityStatus.UNOPEN)
class CommunityUpdate(BaseModel):
name: Optional[str] = Field(None, max_length=100)
address: Optional[str] = Field(None, max_length=200)
longitude: Optional[float] = Field(None, ge=-180, le=180)
latitude: Optional[float] = Field(None, ge=-90, le=90)
status: Optional[CommunityStatus] = None
class CommunityInfo(BaseModel):
id: int
@ -35,6 +43,7 @@ class CommunityInfo(BaseModel):
address: str
latitude: float
longitude: float
status: CommunityStatus
distance: Optional[float] = None # 距离,单位:米
class Config:

View File

@ -47,11 +47,12 @@ class UserInfo(BaseModel):
username: str
phone: str
user_code: str
referral_code: Optional[str]
referral_code: Optional[str] = None
avatar: Optional[str] = None
gender: Gender
points: float
gender: Gender = Gender.UNKNOWN
points: int = 0
roles: List[UserRole]
create_time: datetime
class Config:
from_attributes = True

View File

@ -10,4 +10,5 @@ redis==5.0.1
pymysql==1.1.0
SQLAlchemy==2.0.27
unisms
cos-python-sdk-v5==1.9.25
cos-python-sdk-v5==1.9.25
bcrypt