对接口进行调整。
This commit is contained in:
parent
038a2df3da
commit
d80555bbad
@ -1,7 +1,10 @@
|
|||||||
from fastapi import APIRouter, Depends
|
from fastapi import APIRouter, Depends
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from typing import List
|
from typing import List, Optional
|
||||||
from app.models.community import CommunityDB, CommunityCreate, CommunityUpdate, CommunityInfo
|
from app.models.community import (
|
||||||
|
CommunityDB, CommunityCreate, CommunityUpdate,
|
||||||
|
CommunityInfo, CommunityStatus
|
||||||
|
)
|
||||||
from app.models.database import get_db
|
from app.models.database import get_db
|
||||||
from app.api.deps import get_admin_user
|
from app.api.deps import get_admin_user
|
||||||
from app.models.user import UserDB
|
from app.models.user import UserDB
|
||||||
@ -22,32 +25,51 @@ async def create_community(
|
|||||||
db.refresh(db_community)
|
db.refresh(db_community)
|
||||||
return success_response(data=CommunityInfo.model_validate(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(
|
async def get_communities(
|
||||||
latitude: float,
|
latitude: Optional[float] = None,
|
||||||
longitude: float,
|
longitude: Optional[float] = None,
|
||||||
|
status: Optional[CommunityStatus] = None,
|
||||||
|
skip: int = 0,
|
||||||
|
limit: int = 10,
|
||||||
db: Session = Depends(get_db)
|
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 = []
|
community_list = []
|
||||||
for community in communities:
|
for community in communities:
|
||||||
# 使用 Haversine 公式计算两点之间的距离
|
|
||||||
distance = calculate_distance(
|
|
||||||
latitude, longitude,
|
|
||||||
community.latitude, community.longitude
|
|
||||||
)
|
|
||||||
|
|
||||||
community_info = CommunityInfo.model_validate(community)
|
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.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:
|
def calculate_distance(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
|
||||||
"""计算两点之间的距离(米)"""
|
"""计算两点之间的距离(米)"""
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
from fastapi import APIRouter, Depends
|
from fastapi import APIRouter, Depends
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from typing import List
|
from typing import List, Optional
|
||||||
from app.models.community_building import (
|
from app.models.community_building import (
|
||||||
CommunityBuildingDB,
|
CommunityBuildingDB,
|
||||||
CommunityBuildingCreate,
|
CommunityBuildingCreate,
|
||||||
@ -14,23 +14,28 @@ from app.core.response import success_response, error_response, ResponseModel
|
|||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@router.get("/list/{community_id}", response_model=ResponseModel)
|
@router.get("/list", response_model=ResponseModel)
|
||||||
async def get_community_buildings(
|
async def get_buildings(
|
||||||
community_id: int,
|
community_id: Optional[int] = None,
|
||||||
skip: int = 0,
|
skip: int = 0,
|
||||||
limit: int = 20,
|
limit: int = 10,
|
||||||
db: Session = Depends(get_db)
|
db: Session = Depends(get_db)
|
||||||
):
|
):
|
||||||
"""获取小区的楼栋列表"""
|
"""获取楼栋列表"""
|
||||||
buildings = db.query(CommunityBuildingDB).filter(
|
query = db.query(CommunityBuildingDB)
|
||||||
CommunityBuildingDB.community_id == community_id
|
if community_id:
|
||||||
).order_by(
|
query = query.filter(CommunityBuildingDB.community_id == community_id)
|
||||||
CommunityBuildingDB.building_number
|
|
||||||
).offset(skip).limit(limit).all()
|
|
||||||
|
|
||||||
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)
|
@router.post("", response_model=ResponseModel)
|
||||||
async def create_building(
|
async def create_building(
|
||||||
|
|||||||
@ -24,9 +24,9 @@ async def create_station(
|
|||||||
|
|
||||||
@router.get("/", response_model=ResponseModel)
|
@router.get("/", response_model=ResponseModel)
|
||||||
async def get_stations(
|
async def get_stations(
|
||||||
|
community_id: Optional[int] = None,
|
||||||
skip: int = 0,
|
skip: int = 0,
|
||||||
limit: int = 10,
|
limit: int = 10,
|
||||||
community_id: Optional[int] = None,
|
|
||||||
db: Session = Depends(get_db)
|
db: Session = Depends(get_db)
|
||||||
):
|
):
|
||||||
"""获取驿站列表"""
|
"""获取驿站列表"""
|
||||||
@ -34,8 +34,16 @@ async def get_stations(
|
|||||||
if community_id:
|
if community_id:
|
||||||
query = query.filter(StationDB.community_id == community_id)
|
query = query.filter(StationDB.community_id == community_id)
|
||||||
|
|
||||||
|
# 获取总数
|
||||||
|
total = query.count()
|
||||||
|
|
||||||
|
# 查询数据
|
||||||
stations = query.offset(skip).limit(limit).all()
|
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)
|
@router.get("/{station_id}", response_model=ResponseModel)
|
||||||
async def get_station(
|
async def get_station(
|
||||||
|
|||||||
@ -35,6 +35,10 @@ client = UniSMS(settings.UNI_APP_ID)
|
|||||||
class MockLoginRequest(BaseModel):
|
class MockLoginRequest(BaseModel):
|
||||||
phone: str = Field(..., pattern="^1[3-9]\d{9}$")
|
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")
|
@router.post("/send-code")
|
||||||
async def send_verify_code(request: VerifyCodeRequest):
|
async def send_verify_code(request: VerifyCodeRequest):
|
||||||
"""发送验证码"""
|
"""发送验证码"""
|
||||||
@ -251,7 +255,7 @@ async def update_user_roles(
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
return error_response(code=500, message=f"更新失败: {str(e)}")
|
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(
|
async def password_login(
|
||||||
login_data: UserPasswordLogin,
|
login_data: UserPasswordLogin,
|
||||||
db: Session = Depends(get_db)
|
db: Session = Depends(get_db)
|
||||||
@ -269,11 +273,12 @@ async def password_login(
|
|||||||
return error_response(code=401, message="密码错误")
|
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(
|
return success_response(
|
||||||
data={
|
data={
|
||||||
"access_token": f"Bearer {access_token}",
|
"access_token": access_token,
|
||||||
|
"token_type": "bearer",
|
||||||
"user": UserInfo.model_validate(user)
|
"user": UserInfo.model_validate(user)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -329,3 +334,65 @@ def issue_register_coupons(db: Session, user_id: int):
|
|||||||
status="unused"
|
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)}")
|
||||||
@ -1,9 +1,14 @@
|
|||||||
from typing import Optional
|
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 sqlalchemy.sql import func
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
from .database import Base
|
from .database import Base
|
||||||
|
|
||||||
|
class CommunityStatus(str, enum.Enum):
|
||||||
|
UNOPEN = "UNOPEN" # 未运营
|
||||||
|
OPENING = "OPENING" # 已运营
|
||||||
|
|
||||||
# 数据库模型
|
# 数据库模型
|
||||||
class CommunityDB(Base):
|
class CommunityDB(Base):
|
||||||
__tablename__ = "communities"
|
__tablename__ = "communities"
|
||||||
@ -13,6 +18,7 @@ class CommunityDB(Base):
|
|||||||
address = Column(String(200), nullable=False)
|
address = Column(String(200), nullable=False)
|
||||||
longitude = Column(DECIMAL(9,6), nullable=False) # 经度,精确到小数点后6位
|
longitude = Column(DECIMAL(9,6), nullable=False) # 经度,精确到小数点后6位
|
||||||
latitude = 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())
|
create_time = Column(DateTime(timezone=True), server_default=func.now())
|
||||||
update_time = Column(DateTime(timezone=True), onupdate=func.now())
|
update_time = Column(DateTime(timezone=True), onupdate=func.now())
|
||||||
|
|
||||||
@ -22,12 +28,14 @@ class CommunityCreate(BaseModel):
|
|||||||
address: str = Field(..., max_length=200)
|
address: str = Field(..., max_length=200)
|
||||||
longitude: float = Field(..., ge=-180, le=180)
|
longitude: float = Field(..., ge=-180, le=180)
|
||||||
latitude: float = Field(..., ge=-90, le=90)
|
latitude: float = Field(..., ge=-90, le=90)
|
||||||
|
status: CommunityStatus = Field(default=CommunityStatus.UNOPEN)
|
||||||
|
|
||||||
class CommunityUpdate(BaseModel):
|
class CommunityUpdate(BaseModel):
|
||||||
name: Optional[str] = Field(None, max_length=100)
|
name: Optional[str] = Field(None, max_length=100)
|
||||||
address: Optional[str] = Field(None, max_length=200)
|
address: Optional[str] = Field(None, max_length=200)
|
||||||
longitude: Optional[float] = Field(None, ge=-180, le=180)
|
longitude: Optional[float] = Field(None, ge=-180, le=180)
|
||||||
latitude: Optional[float] = Field(None, ge=-90, le=90)
|
latitude: Optional[float] = Field(None, ge=-90, le=90)
|
||||||
|
status: Optional[CommunityStatus] = None
|
||||||
|
|
||||||
class CommunityInfo(BaseModel):
|
class CommunityInfo(BaseModel):
|
||||||
id: int
|
id: int
|
||||||
@ -35,6 +43,7 @@ class CommunityInfo(BaseModel):
|
|||||||
address: str
|
address: str
|
||||||
latitude: float
|
latitude: float
|
||||||
longitude: float
|
longitude: float
|
||||||
|
status: CommunityStatus
|
||||||
distance: Optional[float] = None # 距离,单位:米
|
distance: Optional[float] = None # 距离,单位:米
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
|
|||||||
@ -47,11 +47,12 @@ class UserInfo(BaseModel):
|
|||||||
username: str
|
username: str
|
||||||
phone: str
|
phone: str
|
||||||
user_code: str
|
user_code: str
|
||||||
referral_code: Optional[str]
|
referral_code: Optional[str] = None
|
||||||
avatar: Optional[str] = None
|
avatar: Optional[str] = None
|
||||||
gender: Gender
|
gender: Gender = Gender.UNKNOWN
|
||||||
points: float
|
points: int = 0
|
||||||
roles: List[UserRole]
|
roles: List[UserRole]
|
||||||
|
create_time: datetime
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
from_attributes = True
|
from_attributes = True
|
||||||
|
|||||||
@ -11,3 +11,4 @@ pymysql==1.1.0
|
|||||||
SQLAlchemy==2.0.27
|
SQLAlchemy==2.0.27
|
||||||
unisms
|
unisms
|
||||||
cos-python-sdk-v5==1.9.25
|
cos-python-sdk-v5==1.9.25
|
||||||
|
bcrypt
|
||||||
Loading…
Reference in New Issue
Block a user