150 lines
5.9 KiB
Markdown
150 lines
5.9 KiB
Markdown
:::tip 用户管理
|
|
用户管理是系统的核心模块,属于复杂业务模块。包含用户 CRUD、角色关联、密码加密、行政区划等功能。与简单 CRUD 模块不同,用户管理使用自定义 service 函数。
|
|
:::
|
|
|
|
## 模块结构
|
|
|
|
```
|
|
application/user/
|
|
├── models.py # 用户模型
|
|
├── forms.py # 表单验证
|
|
├── services.py # 业务逻辑层(自定义函数)
|
|
├── views.py # 视图层
|
|
└── urls.py # 路由配置
|
|
|
|
application/user_role/
|
|
├── models.py # 用户-角色关联模型
|
|
├── services.py # 关联业务逻辑
|
|
└── urls.py # 路由配置
|
|
```
|
|
|
|
## 用户模型
|
|
|
|
```python
|
|
# application/user/models.py
|
|
from application.models import BaseModel
|
|
|
|
class User(BaseModel):
|
|
"""用户模型类"""
|
|
class Meta:
|
|
db_table = 'django_user'
|
|
|
|
code = models.CharField(max_length=100, default='', verbose_name='用户编码')
|
|
realname = models.CharField(max_length=150, verbose_name='用户姓名')
|
|
gender = models.IntegerField(default=1, verbose_name='性别:1-男 2-女 3-保密')
|
|
avatar = models.CharField(max_length=255, default='', verbose_name='用户头像')
|
|
mobile = models.CharField(max_length=30, default='', verbose_name='手机号')
|
|
email = models.CharField(max_length=30, default='', verbose_name='邮箱')
|
|
dept_id = models.IntegerField(default=0, verbose_name='部门ID')
|
|
level_id = models.IntegerField(default=0, verbose_name='职级ID')
|
|
position_id = models.IntegerField(default=0, verbose_name='岗位ID')
|
|
province_code = models.CharField(max_length=30, default='', verbose_name='省份编码')
|
|
city_code = models.CharField(max_length=30, default='', verbose_name='城市编码')
|
|
district_code = models.CharField(max_length=30, default='', verbose_name='县区编码')
|
|
street_code = models.CharField(max_length=30, default='', verbose_name='街道编码')
|
|
city_info = models.CharField(max_length=255, null=True, blank=True, verbose_name='省市区信息')
|
|
address = models.CharField(max_length=255, default='', verbose_name='详细地址')
|
|
username = models.CharField(max_length=30, null=True, blank=True, verbose_name='用户名')
|
|
password = models.CharField(max_length=255, null=True, blank=True, verbose_name='密码')
|
|
salt = models.CharField(max_length=30, null=True, blank=True, verbose_name='加密盐')
|
|
intro = models.CharField(max_length=255, null=True, blank=True, verbose_name='个人简介')
|
|
status = models.IntegerField(default=1, verbose_name='状态:1-正常 2-禁用')
|
|
sort = models.IntegerField(default=0, verbose_name='排序')
|
|
note = models.CharField(max_length=255, null=True, blank=True, verbose_name='个人备注')
|
|
```
|
|
|
|
## 密码加密
|
|
|
|
使用 bcrypt 双重加盐方案,由 `utils/password.py` 提供:
|
|
|
|
```python
|
|
# utils/password.py
|
|
import secrets
|
|
import bcrypt
|
|
|
|
BCRYPT_ROUNDS = 12
|
|
|
|
def generate_salt(length=10):
|
|
"""生成随机盐值"""
|
|
return secrets.token_urlsafe(length)[:length]
|
|
|
|
def encrypt_password(password, salt):
|
|
"""使用 bcrypt 加密密码(外部 salt 双重加盐)"""
|
|
combined = (password + salt).encode('utf-8')
|
|
hashed = bcrypt.hashpw(combined, bcrypt.gensalt(rounds=BCRYPT_ROUNDS))
|
|
return hashed.decode('utf-8')
|
|
|
|
def verify_password(password, salt, hashed):
|
|
"""验证密码是否正确"""
|
|
combined = (password + salt).encode('utf-8')
|
|
return bcrypt.checkpw(combined, hashed.encode('utf-8'))
|
|
```
|
|
|
|
:::tip 双重加盐
|
|
密码加密采用双重加盐:外部 salt(存储在 user 表)+ bcrypt 内部 salt。即使数据库泄露,攻击者也无法通过彩虹表破解密码。工作因子 12,单次加密约 250ms。
|
|
:::
|
|
|
|
## 角色关联
|
|
|
|
用户与角色通过 `django_user_role` 关联表实现多对多关系:
|
|
|
|
```python
|
|
# application/user_role/models.py
|
|
class UserRole(BaseModel):
|
|
"""用户角色关联模型类"""
|
|
class Meta:
|
|
db_table = 'django_user_role'
|
|
|
|
user_id = models.IntegerField(default=0, verbose_name='用户ID')
|
|
role_id = models.IntegerField(default=0, verbose_name='角色ID')
|
|
```
|
|
|
|
## 关联数据富化
|
|
|
|
用户列表和详情需要富化部门、职级、岗位、角色等关联数据:
|
|
|
|
```python
|
|
# application/user/services.py
|
|
def _enrich_user_records(records):
|
|
"""为用户记录补充关联数据"""
|
|
dept_map = {d.id: d.name for d in Dept.objects.filter(is_delete=False)}
|
|
level_map = {l.id: l.name for l in Level.objects.filter(is_delete=False)}
|
|
position_map = {p.id: p.name for p in Position.objects.filter(is_delete=False)}
|
|
|
|
for data in records:
|
|
data["deptName"] = dept_map.get(data.get("deptId"))
|
|
data["levelName"] = level_map.get(data.get("levelId"))
|
|
data["positionName"] = position_map.get(data.get("positionId"))
|
|
|
|
return records
|
|
```
|
|
|
|
## API 接口
|
|
|
|
| 接口 | 方法 | 权限节点 | 说明 |
|
|
|------|------|---------|------|
|
|
| `/user/page` | GET | `sys:user:page` | 用户分页列表 |
|
|
| `/user/detail/<int:user_id>` | GET | `sys:user:detail` | 用户详情 |
|
|
| `/user/add` | POST | `sys:user:add` | 新增用户 |
|
|
| `/user/update` | POST | `sys:user:update` | 编辑用户 |
|
|
| `/user/delete/<int:user_id>` | POST | `sys:user:delete` | 删除用户 |
|
|
| `/user/status` | POST | `sys:user:status` | 设置状态 |
|
|
| `/user/resetPwd` | POST | `sys:user:resetPwd` | 重置密码 |
|
|
|
|
:::warning 内置超管保护
|
|
用户 ID 为 1 的内置超管账号禁止通过管理接口修改、删除、禁用、重置密码。这些操作会在 service 层提前拦截并返回失败提示。
|
|
:::
|
|
|
|
## 总结
|
|
|
|
用户管理模块具备以下特点:
|
|
|
|
```
|
|
1. 复杂业务:自定义 service 函数,处理关联数据
|
|
2. 密码安全:bcrypt 双重加盐,工作因子12
|
|
3. 角色关联:多对多关系,通过 user_role 关联表
|
|
4. 关联富化:列表/详情补充部门、职级、岗位、角色等展示名称
|
|
5. 行政区划:省/市/区/街道四级编码
|
|
6. 超管保护:ID=1 账号禁止修改/删除/禁用/重置密码
|
|
```
|