Files
rxthinkcmf-wiki/RXThinkCMF_ThinkPhp6_AntdVue/zh/modules/user.md
T
2026-09-29 10:14:04 +08:00

222 lines
6.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 6.1 用户管理
:::tip 概述
用户管理是系统核心模块,继承 BaseTenantLogic 实现租户隔离,支持 CRUD、重置密码、Excel 导入导出。
:::
## 模块特点
| 特性 | 说明 |
|------|------|
| 租户隔离 | 继承 BaseTenantLogic,自动按 tenant_id 过滤 |
| 数据权限 | 配置 dataScopeDeptField='dept_id',支持按部门过滤 |
| 编码生成 | beforeAdd 自动生成用户编码(租户编码-6位序号,如 T0001-000020) |
| 文件字段 | avatar(头像),保存到 user/avatar 目录 |
| 唯一校验 | username、mobile 全局唯一 |
| 枚举映射 | gender→gender 字典,status→user_status 字典 |
| 密码加密 | bcrypt 双重加盐,新增时自动加密 |
| 角色关联 | 通过 UserRole 中间表关联,支持多角色 |
## 文件清单
| 文件 | 说明 |
|------|------|
| `app/controller/UserController.php` | 控制器(9 个接口) |
| `app/logic/UserLogic.php` | 逻辑层(含 import/export/resetPassword) |
| `app/model/User.php` | 模型 |
| `app/model/UserRole.php` | 用户角色关联模型 |
| `app/validate/UserValidate.php` | 验证器 |
## API 接口
| 方法 | 路径 | 权限码 | 说明 |
|------|------|--------|------|
| GET | `/api/user/page` | sys:user:page | 分页列表 |
| GET | `/api/user/detail/:id` | sys:user:detail | 详情 |
| POST | `/api/user/add` | sys:user:add | 新增 |
| PUT | `/api/user/update` | sys:user:update | 修改 |
| PUT | `/api/user/resetPwd` | sys:user:resetPwd | 重置密码 |
| DELETE | `/api/user/delete/:id` | sys:user:delete | 删除 |
| DELETE | `/api/user/batchDelete` | sys:user:batchDelete | 批量删除 |
| POST | `/api/user/import` | sys:user:import | 导入 |
| GET | `/api/user/export` | sys:user:export | 导出 |
## 特殊逻辑
### 重置密码
```php
// app/controller/UserController.php
public function resetPwd(): Json
{
$data = $this->getJsonBody();
$id = $data['userId'] ?? 0;
if (empty($id)) {
return $this->fail('缺少id参数');
}
$this->logic->resetPassword($id);
return $this->success(null, '密码重置成功');
}
```
**请求参数:**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `userId` | int | 是 | 用户ID |
:::tip 默认密码
重置密码接口无需传递密码参数,系统自动使用 `PasswordService::getDefaultPasswordPlain()` 作为默认密码。
:::
### 新增用户(beforeAdd 钩子)
```php
// app/logic/UserLogic.php
/**
* 新增前处理:生成编码、处理城市、加密密码
*
* @param array $data 待新增的数据
* @return array 处理后的数据
*/
protected function beforeAdd(array $data): array
{
// 自动生成用户编码:租户编码 + 租户内序号(如 T0001-000020)
$tenantId = $data['tenantId'] ?? request()->tenantId ?? 0;
if ($tenantId == 0) {
$tenantId = config('tenant.default_tenant_id', 1);
}
if ($tenantId > 0) {
$tenant = Tenant::find($tenantId);
$currentUserCount = User::where('tenant_id', $tenantId)->where('is_delete', 0)->count();
$nextSeq = $currentUserCount + 1;
$data['code'] = $tenant->code . '-' . str_pad((string)$nextSeq, 6, '0', STR_PAD_LEFT);
}
// 处理城市字段
if (isset($data['city']) && is_array($data['city'])) {
$data['province_code'] = $data['city'][0] ?? '';
$data['city_code'] = $data['city'][1] ?? '';
$data['district_code'] = $data['city'][2] ?? '';
$data['street_code'] = $data['city'][3] ?? '';
$data['city_info'] = City::resolveCityNames($data['city']);
unset($data['city']);
}
// 密码加密
$password = $data['password'] ?? PasswordService::getDefaultPasswordPlain();
$encrypted = PasswordService::encrypt($password);
$data['password'] = $encrypted['password'];
$data['salt'] = $encrypted['salt'];
return $data;
}
```
:::tip 编码规则
- 用户编码格式:`租户编码-6位序号`,如 `T0001-000020`
- 序号 = 当前租户下未删除用户数 + 1
- 超级管理员(tenantId=0)自动代入默认租户生成编码
- 编码由系统自动生成,前端不传,修改时不可变更
:::
### 新增后保存角色(afterAdd 钩子)
```php
/**
* 新增后处理:保存用户角色关联
*
* @param int $id 新增用户的主键 ID
* @param array $data 新增时提交的原始数据
* @return void
*/
protected function afterAdd(int $id, array $data): void
{
$roleIds = $data['roles'] ?? [];
foreach ($roleIds as $roleId) {
UserRole::create([
'user_id' => $id,
'role_id' => $roleId,
'create_user' => request()->userInfo->username ?? '',
]);
}
}
```
:::info 多角色支持
前端传 `roles: [1, 2, 3]` 数组,`afterAdd` 遍历写入 `UserRole` 中间表。`afterUpdate` 同理,先删后插实现全量覆盖。
:::
### 详情补全(afterDetail 钩子)
```php
/**
* 详情查询后处理:补充关联数据与格式化输出
*
* @param int $id 用户 ID
* @param array &$data 详情数据(引用传递)
* @return void
*/
protected function afterDetail(int $id, array &$data): void
{
// 补充角色信息
$roleIds = UserRole::where('user_id', $id)->column('role_id');
$data['roles'] = $roleIds;
// 补充关联名称
$data['levelName'] = Level::getNameMap()[$data['level_id']] ?? '';
$data['positionName'] = Position::getNameMap()[$data['position_id']] ?? '';
$data['deptName'] = Dept::getNameMap()[$data['dept_id']] ?? '';
// 组装城市数组
$data['city'] = [
$data['province_code'] ?? '',
$data['city_code'] ?? '',
$data['district_code'] ?? '',
$data['street_code'] ?? '',
];
// 移除敏感字段
unset($data['password'], $data['salt']);
}
```
### 导出(export)
导出为分批导出(chunkExport),每批 500 条,避免内存溢出。
| 导出列 | 字段来源 |
|--------|----------|
| 用户编码 | code(系统自动生成) |
| 姓名 | realname |
| 性别 | genderText(DictService 自动转换) |
| 手机号 | mobile |
| 邮箱 | email |
| 部门 | deptName(Dept::getNameMap 映射) |
| 职级 | levelName(Level::getNameMap 映射) |
| 岗位 | positionName(Position::getNameMap 映射) |
| 城市 | cityInfo(4个城市编码拼接为名称字符串) |
| 地址 | address |
| 用户名 | username |
| 简介 | intro |
| 状态 | statusText(DictService 自动转换) |
| 备注 | note |
### 导入(import)
导入时需处理以下反向映射:
| 导入列 | 处理逻辑 |
|--------|----------|
| 性别/状态 | 非数字时 DictService::getValue 反查 |
| 部门/职级/岗位 | 名称通过 array_flip(getNameMap) 反查 ID |
| 城市 | cityInfo 通过 City::resolveCityCodes 拆为4个编码 |
| 用户编码 | 忽略(由 beforeAdd 自动生成) |
:::warning 导入不处理 code
导入时即使 Excel 中有「用户编码」列也会被忽略,编码由 `beforeAdd` 自动生成。
:::