feat (infra): Trae 完成 asset_helper_backend 微服务基础架构 V1 初始化

核心实现:搭建 Monorepo 架构,完成 shared 共享包、gateway、user-service 基础框架开发
技术落地:严格匹配指定技术栈版本,完成 Docker、gRPC、FastAPI、PostgreSQL、Redis 等配置,实现服务间基础连通
配套文件:生成 Makefile、环境变量模板、数据库 / 脚本初始化文件及启动验证文档
架构定位:仅搭建基础架构骨架,无任何业务逻辑、业务规则及业务相关字段,为后续业务开发提供支撑
This commit is contained in:
fish
2026-03-27 20:38:10 +08:00
parent 1ad8ec9be5
commit 3f4165fe78
44 changed files with 1407 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
FROM python:3.13.7-alpine3.22
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app/ .
COPY ../../shared/ /shared/
# 安装共享包
RUN pip install -e /shared
EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD python -c "import socket; s = socket.socket(socket.AF_INET, socket.SOCK_STREAM); s.connect(('localhost', 8000)); s.close(); print('Healthy')" || exit 1
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]

View File

@@ -0,0 +1,40 @@
from fastapi import APIRouter, Depends, HTTPException
from app.grpc_generated import user_pb2, user_pb2_grpc
from app.dependencies import get_user_service_client
from typing import List
router = APIRouter()
@router.get("/users/{user_id}")
async def get_user(user_id: int, client: user_pb2_grpc.UserServiceStub = Depends(get_user_service_client)):
request = user_pb2.GetUserRequest(id=user_id)
response = await client.GetUser(request)
if not response.user.id:
raise HTTPException(status_code=404, detail="User not found")
return response.user
@router.post("/users")
async def create_user(username: str, password_hash: str, client: user_pb2_grpc.UserServiceStub = Depends(get_user_service_client)):
request = user_pb2.CreateUserRequest(username=username, password_hash=password_hash)
response = await client.CreateUser(request)
return response.user
@router.put("/users/{user_id}")
async def update_user(user_id: int, username: str, password_hash: str, client: user_pb2_grpc.UserServiceStub = Depends(get_user_service_client)):
request = user_pb2.UpdateUserRequest(id=user_id, username=username, password_hash=password_hash)
response = await client.UpdateUser(request)
if not response.user.id:
raise HTTPException(status_code=404, detail="User not found")
return response.user
@router.delete("/users/{user_id}")
async def delete_user(user_id: int, client: user_pb2_grpc.UserServiceStub = Depends(get_user_service_client)):
request = user_pb2.DeleteUserRequest(id=user_id)
await client.DeleteUser(request)
return {"message": "User deleted successfully"}
@router.get("/users")
async def list_users(page: int = 1, page_size: int = 10, client: user_pb2_grpc.UserServiceStub = Depends(get_user_service_client)):
request = user_pb2.ListUsersRequest(page=page, page_size=page_size)
response = await client.ListUsers(request)
return {"users": response.users, "total": response.total}

View File

@@ -0,0 +1,7 @@
from shared.utils.config import Settings
class GatewaySettings(Settings):
# 继承基础配置,可添加服务特定配置
service_name: str = "gateway"
settings = GatewaySettings()

View File

@@ -0,0 +1,7 @@
from shared.utils.grpc_client import GrpcClient
from app.grpc_generated import user_pb2_grpc
async def get_user_service_client():
with GrpcClient("user-service", 50051) as channel:
client = user_pb2_grpc.UserServiceStub(channel)
yield client

View File

@@ -0,0 +1,42 @@
from fastapi import FastAPI, WebSocket
from app.api.v1 import users
from app.ws.handlers import websocket_handler
from app.core.config import settings
from shared.middleware import CorrelationIdMiddleware, LoggingMiddleware, ExceptionMiddleware
from loguru import logger
app = FastAPI(
title="Asset Helper Gateway",
version="0.1.0",
description="Asset Helper Backend Gateway"
)
# 添加中间件
app.add_middleware(CorrelationIdMiddleware)
app.add_middleware(LoggingMiddleware)
app.add_middleware(ExceptionMiddleware)
# 注册路由
app.include_router(users.router, prefix="/api/v1")
# WebSocket 端点
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket_handler(websocket)
@app.get("/")
async def root():
return {"message": "Asset Helper Gateway is running"}
@app.get("/health")
async def health_check():
return {"status": "healthy"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"main:app",
host="0.0.0.0",
port=settings.http_port,
reload=True
)

View File

@@ -0,0 +1,22 @@
from fastapi import WebSocket, WebSocketDisconnect
from app.ws.manager import manager
import uuid
async def websocket_handler(websocket: WebSocket):
client_id = str(uuid.uuid4())
await manager.connect(websocket, client_id)
try:
while True:
data = await websocket.receive()
if "text" in data:
message = data["text"]
await manager.send_personal_message(f"You said: {message}", client_id)
await manager.broadcast(f"Client {client_id} said: {message}")
elif "bytes" in data:
# 处理二进制消息
await websocket.send_bytes(data["bytes"])
except WebSocketDisconnect:
manager.disconnect(client_id)
await manager.broadcast(f"Client {client_id} disconnected")

View File

@@ -0,0 +1,24 @@
from fastapi import WebSocket
from typing import Dict, List
class ConnectionManager:
def __init__(self):
self.active_connections: Dict[str, WebSocket] = {}
async def connect(self, websocket: WebSocket, client_id: str):
await websocket.accept()
self.active_connections[client_id] = websocket
def disconnect(self, client_id: str):
if client_id in self.active_connections:
del self.active_connections[client_id]
async def send_personal_message(self, message: str, client_id: str):
if client_id in self.active_connections:
await self.active_connections[client_id].send_text(message)
async def broadcast(self, message: str):
for connection in self.active_connections.values():
await connection.send_text(message)
manager = ConnectionManager()

View File

@@ -0,0 +1,13 @@
fastapi==0.104.1
uvicorn==0.24.0
grpcio==1.59.0
grpcio-tools==1.59.0
pydantic==2.5.0
pydantic-settings==2.1.0
sqlalchemy==2.0.23
asyncpg==0.28.0
redis==5.0.1
python-dotenv==1.0.0
loguru==0.7.2
passlib==1.7.4
bcrypt==4.1.2