用户管理
多用户支持
本节完成多用户功能
- 功能目标:支持多个用户使用系统,并按账号归属隔离私有资源,并按明确的公有模型、管理员和分享规则访问。
- 实现效果:
- 每个用户拥有独立的知识库、文档、片段及应用配置。
- 提供用户角色和权限管理功能,确保多用户环境下的数据安全和操作灵活性。
创建、查询、编辑、启停及重置密码接口
所有请求携带管理员 Authorization: Bearer <JWT_TOKEN>。普通用户不能调用账号管理接口。
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/user_manage/1/20?email_or_username=reader | 分页及搜索 |
| POST | /api/user_manage | 创建普通账号 |
| PUT | /api/user_manage/{id} | 编辑资料、启用或停用 |
| PUT | /api/user_manage/{id}/re_password | 重置密码 |
| DELETE | /api/user_manage/{id} | 逻辑删除 |
创建请求:
{"username":"reader01","password":"<6至20字符密码>","nick_name":"资料阅读用户","email":"[email protected]","phone":""}
停用请求:
{"is_active":false}
重置密码请求:
{"password":"<新密码>","re_password":"<相同的新密码>"}
响应使用 {code,message,data}。保存的密码哈希不返回前端,停用、重置或删除会使旧令牌不能继续使用。用户名、密码限制与字段说明见第 34 章。
ApiUserManage 控制器
package nexus.io.mosskb.controller;
import nexus.io.annotation.Get;
import nexus.io.annotation.Post;
import nexus.io.annotation.Put;
import nexus.io.annotation.Delete;
import nexus.io.mosskb.service.UserManagementService;
import nexus.io.tio.http.common.HttpRequest;
import com.alibaba.fastjson2.JSON;
import nexus.io.annotation.RequestPath;
import nexus.io.jfinal.aop.Aop;
import nexus.io.mosskb.service.KbUserService;
import nexus.io.model.result.ResultVo;
@RequestPath("/api/user_manage")
public class ApiUserManage {
@Post("")
public ResultVo create(HttpRequest request) {
return Aop.get(UserManagementService.class).create(JSON.parseObject(request.getBodyString()));
}
@Put("/{id}")
public ResultVo update(Long id, HttpRequest request) {
return Aop.get(UserManagementService.class).update(id, JSON.parseObject(request.getBodyString()));
}
@Get("/{pageNo}/{pageSize}")
public ResultVo page(Integer pageNo, Integer pageSize, String email_or_username) {
return Aop.get(KbUserService.class).page(pageNo, pageSize, email_or_username);
}
@Put("/{id}/re_password")
public ResultVo resetPassword(Long id, HttpRequest request) {
return Aop.get(UserManagementService.class).resetPassword(id, JSON.parseObject(request.getBodyString()));
}
@Delete("/{id}")
public ResultVo delete(Long id) {
return Aop.get(UserManagementService.class).delete(id);
}
}
UserManagementService 服务实现
package nexus.io.mosskb.service;
import com.alibaba.fastjson2.JSONObject;
import nexus.io.db.activerecord.Db;
import nexus.io.db.activerecord.Row;
import nexus.io.model.result.ResultVo;
import nexus.io.tio.utils.snowflake.SnowflakeIdUtils;
/** Administrator-managed local accounts; never accept roles or ownership from request bodies. */
public class UserManagementService {
public ResultVo create(JSONObject input) {
String username = input.getString("username");
String password = input.getString("password");
if (username == null || !username.matches("[A-Za-z0-9_.@-]{6,20}") || password == null || password.length() < 6 || password.length() > 20) {
return ResultVo.fail("用户名需为 6~20 位字母、数字或 _ . @ -,密码需为 6~20 位");
}
if (Db.queryLong("select count(*) from moss_kb_user where username=?", username) > 0) {
return ResultVo.fail("用户名已存在");
}
Row user = Row.by("id", SnowflakeIdUtils.id()).set("username", username).set("password", UserPassword.hash(password))
.set("role", "USER").set("is_active", true).set("source", "LOCAL").set("deleted", 0);
for (String field : new String[] {"email", "phone", "nick_name"}) {
String value = input.getString(field);
if (value != null && value.length() > 254) {
return ResultVo.fail("用户资料过长");
}
user.set(field, value == null ? "" : value);
}
// Serialize account creation across JVM instances to prevent duplicate names without changing existing records.
boolean[] duplicate = {false};
Db.tx(() -> {
Db.query("select pg_advisory_xact_lock(7335647387278176)");
if (Db.queryLong("select count(*) from moss_kb_user where username=?", username) > 0) {
duplicate[0] = true;
} else {
Db.save("moss_kb_user", user);
}
return true;
});
if (duplicate[0]) {
return ResultVo.fail("用户名已存在");
}
return ResultVo.ok(JSONObject.of("id", user.getLong("id").toString(), "username", username, "role", "USER", "is_active", true));
}
public ResultVo update(Long id, JSONObject input) {
Row existing = Db.findFirst("select id,role from moss_kb_user where id=? and deleted=0", id);
if (existing == null) {
return ResultVo.fail("用户不存在");
}
Row update = Row.by("id", id);
for (String field : new String[] {"email", "phone", "nick_name"}) {
if (input.containsKey(field)) {
String value = input.getString(field);
if (value == null || value.length() > 254) {
return ResultVo.fail("用户资料无效");
}
update.set(field, value);
}
}
if (input.containsKey("is_active")) {
if (id == 1L || "ADMIN".equalsIgnoreCase(existing.getStr("role"))) {
return ResultVo.fail("不能禁用管理员");
}
update.set("is_active", input.getBooleanValue("is_active"));
}
update.set("update_time", new java.util.Date());
Db.update("moss_kb_user", update);
if (input.containsKey("is_active")) {
Db.update("update moss_kb_user set token_version=token_version+1 where id=?", id);
}
return ResultVo.ok();
}
public ResultVo resetPassword(Long id, JSONObject input) {
String password = input.getString("password");
if (password == null || password.length() < 6 || password.length() > 20 || !password.equals(input.getString("re_password"))) {
return ResultVo.fail("密码需为 6~20 位,且两次输入一致");
}
int updated = Db.update("update moss_kb_user set password=?,token_version=token_version+1,update_time=now() where id=? and deleted=0", UserPassword.hash(password), id);
return updated == 1 ? ResultVo.ok() : ResultVo.fail("用户不存在");
}
public ResultVo delete(Long id) {
int updated = Db.update("update moss_kb_user set deleted=1,is_active=false,token_version=token_version+1,update_time=now() where id<>1 and upper(role)<>'ADMIN' and id=? and deleted=0", id);
return updated == 1 ? ResultVo.ok() : ResultVo.fail("用户不存在或不能删除管理员");
}
}
隔离范围和界面操作
管理员通过“系统 → 用户 → 创建用户”创建独立账号。新增用户使用自己的用户名和密码登录,其应用、知识库及私有模型按账号隔离。
管理接口支持分页、创建、资料修改、启用/停用、重置密码及逻辑删除。停用、密码重置或删除后旧令牌不能继续使用。公共模型可供普通用户选择,管理员保留管理权限;这属于同一实例的账号隔离,不是数据库或进程物理隔离。
操作步骤、字段限制及接口清单见独立用户与资源权限。
