This commit is contained in:
fengchuanhn@gmail.com
2026-05-21 02:40:10 +08:00
parent 5ee6a57dd1
commit a5e9e8c0d4
8 changed files with 321 additions and 12 deletions

View File

@@ -82,7 +82,7 @@ async def _validate_owner_ids(
raise AdminUserError("所选代理不存在")
async def create_user(db: AsyncSession, body: AdminUserCreate) -> User:
async def create_user(db: AsyncSession, body: AdminUserCreate,oem: User) -> User:
existing = await db.scalar(select(User).where(User.username == body.username))
if existing is not None:
raise AdminUserError("用户名已存在")
@@ -92,14 +92,14 @@ async def create_user(db: AsyncSession, body: AdminUserCreate) -> User:
if phone_taken is not None:
raise AdminUserError("手机号已被使用")
await _validate_owner_ids(db, oem_id=body.oem_id, agent_id=body.agent_id)
await _validate_owner_ids(db, oem_id=oem.id, agent_id=body.agent_id)
user = User(
username=body.username,
password_hash=hash_password(body.password),
phone=body.phone,
role_id=body.role_id,
oem_id=body.oem_id,
oem_id=oem.id,
agent_id=body.agent_id,
vip_end_time=body.vip_end_time,
)
@@ -111,6 +111,7 @@ async def create_user(db: AsyncSession, body: AdminUserCreate) -> User:
async def update_user(
db: AsyncSession,
oem: User,
user_id: int,
body: AdminUserUpdate,
*,
@@ -119,7 +120,7 @@ async def update_user(
user = await db.get(User, user_id)
if user is None:
raise AdminUserError("用户不存在")
body.oem_id=oem.id
if body.username is not None and body.username != user.username:
taken = await db.scalar(select(User).where(User.username == body.username))
if taken is not None:
@@ -162,12 +163,13 @@ async def update_user(
return user
async def delete_user(db: AsyncSession, user_id: int, *, actor_id: int) -> None:
async def delete_user(db: AsyncSession, oem: User, user_id: int, *, actor_id: int) -> None:
if user_id == actor_id:
raise AdminUserError("不能删除当前登录账号")
user = await db.get(User, user_id)
if user is None:
raise AdminUserError("用户不存在")
if user.oem_id != oem.id:
raise AdminUserError("无权限删除该用户")
await db.delete(user)

View File

@@ -5,10 +5,10 @@ from app.models.oem import Oem
from app.schemas.oem_public import OemPublicBrandingOut
def _logo_url(logo_path: str | None, base_url: str) -> str | None:
if not logo_path or not logo_path.strip():
def _image_url(image_path: str | None, base_url: str) -> str | None:
if not image_path or not image_path.strip():
return None
path = logo_path.strip().replace("\\", "/").lstrip("/")
path = image_path.strip().replace("\\", "/").lstrip("/")
if path.startswith("http://") or path.startswith("https://"):
return path
if not path.startswith("images/"):
@@ -21,11 +21,18 @@ async def get_oem_public_branding(db: AsyncSession, oem_id: int) -> OemPublicBra
base_url = settings.api_public_base.rstrip("/")
oem = await db.get(Oem, oem_id)
if oem is None:
return OemPublicBrandingOut(oem_id=oem_id, software_name="", logo_url=None, wechat=None)
return OemPublicBrandingOut(
oem_id=oem_id,
software_name="",
logo_url=None,
wechat=None,
wechat_qrcode_url=None,
)
return OemPublicBrandingOut(
oem_id=oem.id,
software_name=oem.software_name or "",
logo_url=_logo_url(oem.logo_path, base_url),
logo_url=_image_url(oem.logo_path, base_url),
wechat=oem.wechat,
wechat_qrcode_url=_image_url(oem.wechat_qrcode, base_url),
)

173
app/services/oem_user.py Normal file
View File

@@ -0,0 +1,173 @@
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.roles import ROLE_ADMIN, ROLE_AGENT, ROLE_OEM
from app.core.security import hash_password
from app.models.user import User
from app.schemas.admin_user import AdminUserCreate, AdminUserUpdate
DEFAULT_PAGE_SIZE = 20
MAX_PAGE_SIZE = 200
class AdminUserError(Exception):
def __init__(self, message: str) -> None:
self.message = message
super().__init__(message)
def _list_users_filters(
*,
username: str | None = None,
oem_id: int | None = None,
agent_id: int | None = None,
role_id: int | None = None,
) -> list:
conditions = []
if username and username.strip():
conditions.append(User.username.ilike(f"%{username.strip()}%"))
if oem_id is not None:
conditions.append(User.oem_id == oem_id)
if agent_id is not None:
conditions.append(User.agent_id == agent_id)
if role_id is not None:
conditions.append(User.role_id == role_id)
return conditions
async def list_users(
db: AsyncSession,
*,
page: int = 1,
page_size: int = DEFAULT_PAGE_SIZE,
username: str | None = None,
oem_id: int | None = None,
agent_id: int | None = None,
role_id: int | None = None,
) -> tuple[list[User], int]:
page_size = min(max(page_size, 1), MAX_PAGE_SIZE)
page = max(page, 1)
offset = (page - 1) * page_size
conditions = _list_users_filters(
username=username,
oem_id=oem_id,
agent_id=agent_id,
role_id=role_id,
)
count_stmt = select(func.count()).select_from(User)
list_stmt = select(User).order_by(User.id.desc())
if conditions:
count_stmt = count_stmt.where(*conditions)
list_stmt = list_stmt.where(*conditions)
total = await db.scalar(count_stmt) or 0
result = await db.scalars(list_stmt.offset(offset).limit(page_size))
return list(result.all()), total
async def _validate_owner_ids(
db: AsyncSession,
*,
oem_id: int | None,
agent_id: int | None,
) -> None:
if oem_id is not None:
oem = await db.get(User, oem_id)
if oem is None or oem.role_id != ROLE_OEM:
raise AdminUserError("所选 OEM 不存在")
if agent_id is not None:
agent = await db.get(User, agent_id)
if agent is None or agent.role_id != ROLE_AGENT:
raise AdminUserError("所选代理不存在")
async def create_user(db: AsyncSession, body: AdminUserCreate) -> User:
existing = await db.scalar(select(User).where(User.username == body.username))
if existing is not None:
raise AdminUserError("用户名已存在")
if body.phone:
phone_taken = await db.scalar(select(User).where(User.phone == body.phone))
if phone_taken is not None:
raise AdminUserError("手机号已被使用")
await _validate_owner_ids(db, oem_id=body.oem_id, agent_id=body.agent_id)
user = User(
username=body.username,
password_hash=hash_password(body.password),
phone=body.phone,
role_id=body.role_id,
oem_id=body.oem_id,
agent_id=body.agent_id,
vip_end_time=body.vip_end_time,
)
db.add(user)
await db.flush()
await db.refresh(user)
return user
async def update_user(
db: AsyncSession,
user_id: int,
body: AdminUserUpdate,
*,
actor_id: int,
) -> User:
user = await db.get(User, user_id)
if user is None:
raise AdminUserError("用户不存在")
if body.username is not None and body.username != user.username:
taken = await db.scalar(select(User).where(User.username == body.username))
if taken is not None:
raise AdminUserError("用户名已存在")
user.username = body.username
if body.phone is not None:
if body.phone != user.phone:
taken = await db.scalar(select(User).where(User.phone == body.phone))
if taken is not None:
raise AdminUserError("手机号已被使用")
user.phone = body.phone
if body.role_id is not None:
if user_id == actor_id and body.role_id != ROLE_ADMIN:
raise AdminUserError("不能修改自己的管理员角色")
user.role_id = body.role_id
if body.oem_id is not None or body.agent_id is not None:
await _validate_owner_ids(
db,
oem_id=body.oem_id if body.oem_id is not None else user.oem_id,
agent_id=body.agent_id if body.agent_id is not None else user.agent_id,
)
if body.oem_id is not None:
user.oem_id = body.oem_id
if body.agent_id is not None:
user.agent_id = body.agent_id
if body.password:
user.password_hash = hash_password(body.password)
if body.clear_vip_end_time:
user.vip_end_time = None
elif body.vip_end_time is not None:
user.vip_end_time = body.vip_end_time
await db.flush()
await db.refresh(user)
return user
async def delete_user(db: AsyncSession, user_id: int, *, actor_id: int) -> None:
if user_id == actor_id:
raise AdminUserError("不能删除当前登录账号")
user = await db.get(User, user_id)
if user is None:
raise AdminUserError("用户不存在")
await db.delete(user)