Repository navigation
Expand file tree
/
Copy pathuser_controller.py
More file actions
283 lines (242 loc) · 9.27 KB
/
Copy pathuser_controller.py
File metadata and controls
283 lines (242 loc) · 9.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
"""
app/controllers/user_controller.py — 用户 Controller(路由层)
接口清单:
公开接口(无需 Token):
POST /api/v1/auth/login 登录,返回 JWT
POST /api/v1/users 注册/新增用户
需要 Token(Authorization: Bearer <token>):
GET /api/v1/users 查询所有用户
GET /api/v1/users/page/list 分页查询
GET /api/v1/users/{user_id} 根据 ID 查询
GET /api/v1/users/name/{user_name} 根据用户名查询
GET /api/v1/users/other-db/page/list 从其他库分页查询(演示多库)
PUT /api/v1/users/{user_id} 修改用户名
DELETE /api/v1/users/{user_id} 删除用户
DELETE /api/v1/users/batch/delete 批量删除
PATCH /api/v1/users/{user_id}/password 修改密码
"""
from typing import List
from fastapi import APIRouter, Depends, Query
from sqlmodel import Session, select
from app.core.database import get_session_read, get_session_write, get_session_other
from app.core.response import ResponseModel, PageResult, PaginatedResponse
from app.core.security import get_current_user, TokenPayload
from app.entity.user_entity import (
User, UserCreate, UserUpdate, UserResponse,
UserPageQuery, LoginRequest, LoginResponse, ChangePasswordRequest,
)
from app.services.user_service import UserService
router = APIRouter()
# ------------------------------------------------------------------ #
# 依赖注入
# ------------------------------------------------------------------ #
def get_user_service(
session_read: Session = Depends(get_session_read),
session_write: Session = Depends(get_session_write),
) -> UserService:
"""创建 UserService 实例,支持读写分离"""
return UserService(session_write=session_write, session_read=session_read)
# ================================================================== #
# Auth — 登录(公开,无需 Token)
# ================================================================== #
@router.post(
"/auth/login",
response_model=ResponseModel[LoginResponse],
tags=["认证"],
summary="用户登录",
description="传入用户名和密码,校验通过后返回 JWT access token。",
)
async def login(
dto: LoginRequest,
service: UserService = Depends(get_user_service),
):
data = await service.login(dto)
return ResponseModel.ok(data=data, message="登录成功")
# ================================================================== #
# 基础 CRUD
# ================================================================== #
@router.post(
"/users",
response_model=ResponseModel[UserResponse],
tags=["用户管理"],
summary="新增用户",
description="传入 user_name 和 password 创建用户,密码自动 bcrypt 加密存储。",
)
async def create_user(
dto: UserCreate,
service: UserService = Depends(get_user_service),
_: TokenPayload = Depends(get_current_user), # 需要登录
):
data = await service.create(dto)
return ResponseModel.ok(data=data, message="用户创建成功")
@router.get(
"/users/{user_id}",
response_model=ResponseModel[UserResponse],
tags=["用户管理"],
summary="根据 ID 查询用户",
)
async def get_user(
user_id: int,
service: UserService = Depends(get_user_service),
_: TokenPayload = Depends(get_current_user), # 需要登录
):
data = await service.get_by_id(user_id)
return ResponseModel.ok(data=data)
@router.put(
"/users/{user_id}",
response_model=ResponseModel[UserResponse],
tags=["用户管理"],
summary="修改用户名",
)
async def update_user(
user_id: int,
dto: UserUpdate,
service: UserService = Depends(get_user_service),
_: TokenPayload = Depends(get_current_user),
):
data = await service.update(user_id, dto)
return ResponseModel.ok(data=data, message="用户更新成功")
@router.delete(
"/users/{user_id}",
response_model=ResponseModel[bool],
tags=["用户管理"],
summary="删除用户",
)
async def delete_user(
user_id: int,
service: UserService = Depends(get_user_service),
_: TokenPayload = Depends(get_current_user),
):
result = await service.delete(user_id)
return ResponseModel.ok(data=result, message="用户删除成功")
@router.get(
"/users",
response_model=ResponseModel[List[UserResponse]],
tags=["用户管理"],
summary="查询所有用户(不分页)",
)
async def list_users(
service: UserService = Depends(get_user_service),
_: TokenPayload = Depends(get_current_user),
):
data = await service.list_all()
return ResponseModel.ok(data=data)
# ================================================================== #
# 扩展接口
# ================================================================== #
@router.get(
"/users/page/list",
response_model=ResponseModel[PageResult[UserResponse]],
tags=["用户管理"],
summary="分页查询用户",
description="支持按 user_name 模糊搜索,返回分页结构。",
)
async def page_query(
page: int = Query(default=1, ge=1, description="页码"),
page_size: int = Query(default=10, ge=1, le=100, description="每页条数"),
keyword: str = Query(default=None, description="用户名模糊搜索"),
service: UserService = Depends(get_user_service),
_: TokenPayload = Depends(get_current_user),
):
query = UserPageQuery(page=page, page_size=page_size, keyword=keyword)
data = await service.page_query(query)
return ResponseModel.ok(data=data)
@router.get(
"/users/name/{user_name}",
response_model=ResponseModel[UserResponse],
tags=["用户管理"],
summary="根据用户名精确查询",
)
async def get_by_username(
user_name: str,
service: UserService = Depends(get_user_service),
_: TokenPayload = Depends(get_current_user),
):
data = await service.get_by_username(user_name)
return ResponseModel.ok(data=data)
@router.patch(
"/users/{user_id}/password",
response_model=ResponseModel[bool],
tags=["用户管理"],
summary="修改密码",
description="传入原密码和新密码,校验原密码正确后更新。",
)
async def change_password(
user_id: int,
dto: ChangePasswordRequest,
service: UserService = Depends(get_user_service),
current_user: TokenPayload = Depends(get_current_user),
):
# 只允许修改自己的密码
if str(user_id) != current_user.sub:
from fastapi import HTTPException
raise HTTPException(status_code=403, detail="只能修改自己的密码")
result = await service.change_password(user_id, dto)
return ResponseModel.ok(data=result, message="密码修改成功")
@router.delete(
"/users/batch/delete",
response_model=ResponseModel[int],
tags=["用户管理"],
summary="批量删除用户",
description="传入 user_id 列表,返回成功删除数量。",
)
async def batch_delete(
ids: List[int],
service: UserService = Depends(get_user_service),
_: TokenPayload = Depends(get_current_user),
):
count = await service.batch_delete(ids)
return ResponseModel.ok(data=count, message=f"成功删除 {count} 个用户")
# ================================================================== #
# 其他库操作 — 从分析库/日志库查询数据
# ================================================================== #
@router.get(
"/users/other-db/page/list",
response_model=PaginatedResponse[UserResponse],
tags=["用户管理"],
summary="从其他库分页查询用户",
description="从分析库或日志库分页查询用户数据,支持模糊搜索。用于演示多库连接的使用。",
)
async def page_query_from_other_db(
page: int = Query(default=1, ge=1, description="页码"),
page_size: int = Query(default=10, ge=1, le=100, description="每页条数"),
keyword: str = Query(default=None, description="用户名模糊搜索"),
session_other: Session = Depends(get_session_other),
_: TokenPayload = Depends(get_current_user),
):
"""
从其他数据库(如分析库)分页查询用户数据。
应用场景:
- 从分析库分页查询用户的统计信息
- 从日志库分页查询用户的操作记录
- 从审计库分页查询用户的变更历史
"""
from sqlmodel import func
try:
# 构建查询语句
stmt = select(User)
if keyword:
stmt = stmt.where(User.user_name.contains(keyword))
# 获取总数
count_stmt = select(func.count()).select_from(stmt.subquery())
total = session_other.exec(count_stmt).one()
# 分页查询
offset = (page - 1) * page_size
users = session_other.exec(stmt.offset(offset).limit(page_size)).all()
# 返回分页结果
result = [UserResponse.model_validate(u) for u in users]
return PaginatedResponse.ok(
data=result,
total=total,
page=page,
page_size=page_size,
)
except Exception as e:
# 如果其他库查询失败,返回空列表
return PaginatedResponse.ok(
data=[],
total=0,
page=page,
page_size=page_size,
message=f"从其他库查询失败: {str(e)}"
)