签入版本

This commit is contained in:
小蚂蚁云
2026-10-25 17:24:27 +08:00
parent 48911cc08f
commit fdbeae092a
1010 changed files with 30682 additions and 95274 deletions
+27 -208
View File
@@ -1,222 +1,41 @@
# 6.1 用户管理
:::tip 概述
用户管理是系统核心模块,继承 BaseTenantLogic 实现租户隔离,支持 CRUD、重置密码、Excel 导入导出。
用户是组织权限体系的核心表(gin_user,租户级)。除标准 CRUD 外,承载角色分配、密码重置、导入导出与(租户场景的)开户逻辑,并挂接数据权限过滤。
:::
## 模块特点
## 功能清单
| 特性 | 说明 |
|------|------|
| 租户隔离 | 继承 BaseTenantLogic,自动按 tenant_id 过滤 |
| 数据权限 | 配置 dataScopeDeptField='dept_id',支持按部门过滤 |
| 编码生成 | beforeAdd 自动生成用户编码(租户编码-6位序号,如 T0001-000020) |
| 文件字段 | avatar(头像),保存到 user/avatar 目录 |
| 唯一校验 | username、mobile 全局唯一 |
| 枚举映射 | gender→gender 字典,status→user_status 字典 |
| 密码加密 | bcrypt 双重加盐,新增时自动加密 |
| 角色关联 | 通过 UserRole 中间表关联,支持多角色 |
| 接口 | 说明 |
|---|---|
| Page / List / Detail | 标准查询(分页支持关键字/部门/状态过滤) |
| Add | 新增(租户场景经 addWithTenant 归属租户) |
| Update | 修改(全量表单语义) |
| Status | 启停单列更新 |
| ResetPwd | 重置密码(手写逻辑,管理员操作留痕) |
| Delete / BatchDelete | 软删除 |
| Import / Export | Excel 导入导出 |
| Document | 用户打印信息(打印视图数据) |
## 文件清单
## 特色机制
| 文件 | 说明 |
|------|------|
| `app/controller/UserController.php` | 控制器(9 个接口) |
| `app/logic/UserLogic.php` | 逻辑层(含 import/export/resetPassword) |
| `app/model/User.php` | 模型 |
| `app/model/UserRole.php` | 用户角色关联模型 |
| `app/validate/UserValidate.php` | 验证器 |
- **角色随表单写入**:Add/Update 请求带 `roles []int64`(json 名 `roles`),事务内 rewriteUserRoles 先清后写 gin_user_role——没有独立的"分配角色"接口;service 层 AssignRoles 是事务复用的内部方法
- **数据权限挂接**:userSpec 声明 `DataScope: ApplyDataScope("dept_id")`(user.go:77 注释:角色数据权限(Page/List/导出生效))——本模块是数据权限的典型示范(行级部门过滤)
- **租户级表**:写侧自动补 tenant_id,读侧自动隔离
- **密码安全**:重置与新增均走 crypto 哈希(外部 salt + bcrypt),永不落明文
- **导入导出**:标准 Excel 契约(列头、逐行校验、结果汇总)
## 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 | 导出 |
- 用户名在租户内唯一(UniqueFields 预检 + 数据库 uk 兜底)
- 部门列 dept_id 与 gin_dept 关联,数据权限与组织展示都依赖它
- 状态非 1 视为禁用(登录校验同口径)
## 特殊逻辑
## 管理界面
### 重置密码
`system/user`:列表 + 搜索 + 新增/编辑弹窗 + 分配角色 + 重置密码 + 导入导出。
```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` 自动生成。
:::
- 机制:[5.4 权限管理](/zh/core/rbac.html)、[5.10 数据权限](/zh/core/data-scope.html)
- 开发:[第 3 章 模块开发实战](/zh/guide/overview.html)