feat(admin): 添加用户管理相关文件

添加用户管理视图、API和状态管理文件
This commit is contained in:
张翔
2026-03-28 14:37:29 +08:00
commit 08ea5fbe98
1643 changed files with 255646 additions and 0 deletions
@@ -0,0 +1,15 @@
"""
测试数据模块
提供测试数据管理和生成功能。
"""
from .factories.user_factory import UserDataFactory
from .factories.role_factory import RoleDataFactory
from .factories.almanac_factory import AlmanacDataFactory
__all__ = [
"UserDataFactory",
"RoleDataFactory",
"AlmanacDataFactory",
]
@@ -0,0 +1,5 @@
"""
数据工厂模块
提供测试数据生成功能。
"""
@@ -0,0 +1,114 @@
"""
黄历数据工厂
提供黄历测试数据生成功能。
"""
from typing import Dict, Any, List
from datetime import datetime, timedelta
class AlmanacDataFactory:
"""黄历数据工厂"""
@staticmethod
def get_test_dates() -> Dict[str, str]:
"""获取测试日期数据"""
return {
"spring_festival": "2026-02-17", # 春节
"lantern_festival": "2026-03-03", # 元宵节
"dragon_boat": "2026-06-19", # 端午节
"mid_autumn": "2026-09-25", # 中秋节
"national_day": "2026-10-01", # 国庆节
"new_year": "2026-01-01", # 元旦
"valentine": "2026-02-14", # 情人节
"labour_day": "2026-05-01", # 劳动节
}
@staticmethod
def get_current_date() -> str:
"""获取当前日期"""
return datetime.now().strftime("%Y-%m-%d")
@staticmethod
def get_previous_date(days: int = 1) -> str:
"""获取前几天日期"""
date = datetime.now() - timedelta(days=days)
return date.strftime("%Y-%m-%d")
@staticmethod
def get_next_date(days: int = 1) -> str:
"""获取后几天日期"""
date = datetime.now() + timedelta(days=days)
return date.strftime("%Y-%m-%d")
@staticmethod
def get_date_range(start_days: int = -7, end_days: int = 7) -> List[str]:
"""获取日期范围"""
dates = []
start_date = datetime.now() + timedelta(days=start_days)
end_date = datetime.now() + timedelta(days=end_days)
current = start_date
while current <= end_date:
dates.append(current.strftime("%Y-%m-%d"))
current += timedelta(days=1)
return dates
@staticmethod
def get_month_dates(year: int, month: int) -> List[str]:
"""获取指定月份的所有日期"""
dates = []
start_date = datetime(year, month, 1)
# 获取下个月的第一天
if month == 12:
next_month = datetime(year + 1, 1, 1)
else:
next_month = datetime(year, month + 1, 1)
current = start_date
while current < next_month:
dates.append(current.strftime("%Y-%m-%d"))
current += timedelta(days=1)
return dates
@staticmethod
def get_expected_almanac_fields() -> List[str]:
"""获取黄历预期字段"""
return [
"solar_date", # 公历日期
"lunar_date", # 农历日期
"ganzhi", # 干支
"shengxiao", # 生肖
"yi", # 宜
"ji", # 忌
"chongsha", # 冲煞
"wuxing", # 五行
"taishen", # 胎神
"caishen", # 财神方位
"shichen", # 时辰
]
@staticmethod
def get_expected_yi_ji_items() -> Dict[str, List[str]]:
"""获取预期的宜忌事项示例"""
return {
"yi": [
"嫁娶", "祭祀", "祈福", "求嗣", "开光", "出行", "解除",
"拆卸", "修造", "动土", "起基", "上梁", "安床", "入宅",
"移徙", "安香", "纳畜", "安葬", "入殓", "破土", "修坟",
],
"ji": [
"开市", "立券", "纳财", "出货", "开仓", "盖屋", "造船",
"掘井", "作灶", "出火", "伐木", "斋醮", "词讼", "行丧",
],
}
@staticmethod
def validate_almanac_data(data: Dict[str, Any]) -> bool:
"""验证黄历数据是否完整"""
required_fields = AlmanacDataFactory.get_expected_almanac_fields()
return all(field in data for field in required_fields)
@@ -0,0 +1,151 @@
"""
数据工厂基类
提供数据工厂的通用功能。
"""
import json
import uuid
from typing import Dict, Any, List, Optional
from abc import ABC, abstractmethod
class BaseDataFactory(ABC):
"""数据工厂基类"""
# 存储生成的数据
_data_store: Dict[str, Dict[str, Any]] = {}
@classmethod
@abstractmethod
def create(cls, **kwargs) -> Dict[str, Any]:
"""创建单个数据"""
pass
@classmethod
def batch_create(cls, count: int, **kwargs) -> List[Dict[str, Any]]:
"""
批量创建数据
Args:
count: 创建数量
**kwargs: 额外参数
Returns:
数据列表
"""
results = []
for i in range(count):
# 添加索引确保唯一性
item_kwargs = kwargs.copy()
item_kwargs['_index'] = i
item = cls.create(**item_kwargs)
results.append(item)
return results
@classmethod
def create_from_template(cls, template: Dict[str, Any], **kwargs) -> Dict[str, Any]:
"""
基于模板创建数据
Args:
template: 模板数据
**kwargs: 额外参数
Returns:
生成的数据
"""
# 先创建基础数据
data = cls.create(**kwargs)
# 应用模板
for key, value in template.items():
data[key] = value
return data
@classmethod
def serialize(cls, data: Dict[str, Any]) -> str:
"""
序列化数据为JSON字符串
Args:
data: 数据字典
Returns:
JSON字符串
"""
return json.dumps(data, ensure_ascii=False, default=str)
@classmethod
def deserialize(cls, json_str: str) -> Dict[str, Any]:
"""
从JSON字符串反序列化数据
Args:
json_str: JSON字符串
Returns:
数据字典
"""
return json.loads(json_str)
@classmethod
def cleanup(cls, data_id: str) -> bool:
"""
清理指定数据
Args:
data_id: 数据ID
Returns:
是否成功清理
"""
if data_id in cls._data_store:
del cls._data_store[data_id]
return True
return False
@classmethod
def exists(cls, data_id: str) -> bool:
"""
检查数据是否存在
Args:
data_id: 数据ID
Returns:
是否存在
"""
return data_id in cls._data_store
@classmethod
def get_all(cls) -> List[Dict[str, Any]]:
"""
获取所有数据
Returns:
数据列表
"""
return list(cls._data_store.values())
@classmethod
def clear_all(cls):
"""清除所有数据"""
cls._data_store.clear()
@classmethod
def _store_data(cls, data: Dict[str, Any]) -> Dict[str, Any]:
"""
存储数据
Args:
data: 数据字典
Returns:
存储的数据
"""
data_id = data.get("id") or str(uuid.uuid4())
data["id"] = data_id
cls._data_store[data_id] = data
return data
@@ -0,0 +1,98 @@
"""
角色数据工厂
提供角色测试数据生成功能。
"""
from faker import Faker
from typing import Dict, Any, List
faker = Faker("zh_CN")
class RoleDataFactory:
"""角色数据工厂"""
@staticmethod
def create_admin_role() -> Dict[str, Any]:
"""创建管理员角色数据"""
return {
"name": f"管理员_{faker.random_int(1000, 9999)}",
"code": f"admin_{faker.random_int(1000, 9999)}",
"description": "系统管理员,拥有所有权限",
"status": "active",
"permissions": ["*"],
}
@staticmethod
def create_user_role() -> Dict[str, Any]:
"""创建普通用户角色数据"""
return {
"name": f"普通用户_{faker.random_int(1000, 9999)}",
"code": f"user_{faker.random_int(1000, 9999)}",
"description": "普通用户,拥有基本权限",
"status": "active",
"permissions": ["user:read", "user:write"],
}
@staticmethod
def create_viewer_role() -> Dict[str, Any]:
"""创建只读角色数据"""
return {
"name": f"只读用户_{faker.random_int(1000, 9999)}",
"code": f"viewer_{faker.random_int(1000, 9999)}",
"description": "只读用户,仅可查看数据",
"status": "active",
"permissions": ["user:read"],
}
@staticmethod
def create_invalid_role() -> Dict[str, Any]:
"""创建无效角色数据(用于测试验证)"""
return {
"name": "", # 空角色名
"code": "", # 空角色编码
"description": "",
"status": "active",
"permissions": [],
}
@staticmethod
def create_duplicate_role(existing_code: str) -> Dict[str, Any]:
"""创建重复角色编码数据"""
return {
"name": faker.job(),
"code": existing_code,
"description": faker.text(max_nb_chars=50),
"status": "active",
"permissions": ["user:read"],
}
@staticmethod
def create_bulk_roles(count: int = 5) -> List[Dict[str, Any]]:
"""批量创建角色数据"""
return [RoleDataFactory.create_user_role() for _ in range(count)]
@staticmethod
def get_test_roles() -> Dict[str, Dict[str, Any]]:
"""获取预定义测试角色"""
return {
"admin": {
"name": "系统管理员",
"code": "admin",
"description": "拥有所有权限",
"status": "active",
},
"user": {
"name": "普通用户",
"code": "user",
"description": "拥有基本权限",
"status": "active",
},
"viewer": {
"name": "只读用户",
"code": "viewer",
"description": "仅可查看数据",
"status": "active",
},
}
@@ -0,0 +1,167 @@
"""
用户数据工厂
提供用户测试数据生成功能。
"""
from faker import Faker
from typing import Dict, Any, List
from .base_factory import BaseDataFactory
faker = Faker("zh_CN")
class UserDataFactory(BaseDataFactory):
"""用户数据工厂"""
@classmethod
def create(cls, **kwargs) -> Dict[str, Any]:
"""
创建用户数据
Args:
**kwargs: 自定义字段
Returns:
用户数据字典
"""
# 获取索引(用于批量创建时确保唯一性)
index = kwargs.get('_index', 0)
# 生成基础数据
data = {
"id": kwargs.get("id") or str(faker.uuid4()),
"username": kwargs.get("username") or f"user_{faker.random_int(1000, 9999)}_{index}",
"nickname": kwargs.get("nickname") or faker.name(),
"password": kwargs.get("password") or "User@123456",
"email": kwargs.get("email") or faker.email(),
"phone": kwargs.get("phone") or faker.phone_number(),
"role": kwargs.get("role") or "user",
"role_id": kwargs.get("role_id") or None,
"status": kwargs.get("status") or "active",
"department": kwargs.get("department") or "",
"avatar": kwargs.get("avatar") or "",
"created_at": kwargs.get("created_at") or faker.date_time_this_year().isoformat(),
}
# 存储数据
return cls._store_data(data)
@staticmethod
def create_admin_user() -> Dict[str, Any]:
"""创建管理员用户数据"""
return {
"id": str(faker.uuid4()),
"username": f"admin_{faker.random_int(1000, 9999)}",
"nickname": f"管理员_{faker.random_int(1000, 9999)}",
"password": "Admin@123456",
"email": faker.email(),
"phone": faker.phone_number(),
"role": "admin",
"role_id": None,
"status": "active",
"department": "",
"avatar": "",
}
@staticmethod
def create_normal_user() -> Dict[str, Any]:
"""创建普通用户数据"""
return {
"id": str(faker.uuid4()),
"username": f"user_{faker.random_int(1000, 9999)}",
"nickname": faker.name(),
"password": "User@123456",
"email": faker.email(),
"phone": faker.phone_number(),
"role": "user",
"role_id": None,
"status": "active",
"department": "",
"avatar": "",
}
@classmethod
def create_with_role(cls, role: Dict[str, Any], **kwargs) -> Dict[str, Any]:
"""
创建带角色的用户
Args:
role: 角色数据
**kwargs: 额外参数
Returns:
用户数据
"""
# 设置角色信息
kwargs['role'] = role.get('name', 'user')
kwargs['role_id'] = role.get('id')
# 创建用户
return cls.create(**kwargs)
@staticmethod
def create_invalid_user() -> Dict[str, Any]:
"""创建无效用户数据(用于测试验证)"""
return {
"id": str(faker.uuid4()),
"username": "", # 空用户名
"nickname": "",
"password": "123", # 密码太短
"email": "invalid-email", # 无效邮箱
"phone": "123", # 无效电话
"role": "user",
"role_id": None,
"status": "active",
"department": "",
"avatar": "",
}
@staticmethod
def create_duplicate_user(existing_username: str) -> Dict[str, Any]:
"""创建重复用户名用户数据"""
return {
"id": str(faker.uuid4()),
"username": existing_username,
"nickname": faker.name(),
"password": "User@123456",
"email": faker.email(),
"phone": faker.phone_number(),
"role": "user",
"role_id": None,
"status": "active",
"department": "",
"avatar": "",
}
@staticmethod
def create_bulk_users(count: int = 10) -> List[Dict[str, Any]]:
"""批量创建用户数据"""
return [UserDataFactory.create_normal_user() for _ in range(count)]
@staticmethod
def get_test_users() -> Dict[str, Dict[str, Any]]:
"""获取预定义测试用户"""
return {
"admin": {
"id": "test-admin-id",
"username": "admin",
"password": "admin123",
"role": "admin",
"role_id": "admin-role-id",
},
"test_user": {
"id": "test-user-id",
"username": "testuser",
"password": "test123",
"role": "user",
"role_id": "user-role-id",
},
"invalid_user": {
"id": "invalid-user-id",
"username": "invalid",
"password": "wrong",
"role": "user",
"role_id": None,
},
}