Skip to content

用户管理 ​

多用户支持 ​

本节完成多用户功能

  • 功能目标:支持多个用户使用系统,并按账号归属隔离私有资源,并按明确的公有模型、管理员和分享规则访问。
  • 实现效果:
    • 每个用户拥有独立的知识库、文档、片段及应用配置。
    • 提供用户角色和权限管理功能,确保多用户环境下的数据安全和操作灵活性。

创建、查询、编辑、启停及重置密码接口 ​

所有请求携带管理员 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}逻辑删除

创建请求:

json
{"username":"reader01","password":"<6至20字符密码>","nick_name":"资料阅读用户","email":"reader@example.invalid","phone":""}

停用请求:

json
{"is_active":false}

重置密码请求:

json
{"password":"<新密码>","re_password":"<相同的新密码>"}

响应使用 {code,message,data}。保存的密码哈希不返回前端,停用、重置或删除会使旧令牌不能继续使用。用户名、密码限制与字段说明见第 34 章。

ApiUserManage 控制器 ​

java
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 服务实现 ​

java
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("用户不存在或不能删除管理员");
  }
}

隔离范围和界面操作 ​

管理员通过“系统 → 用户 → 创建用户”创建独立账号。新增用户使用自己的用户名和密码登录,其应用、知识库及私有模型按账号隔离。

管理接口支持分页、创建、资料修改、启用/停用、重置密码及逻辑删除。停用、密码重置或删除后旧令牌不能继续使用。公共模型可供普通用户选择,管理员保留管理权限;这属于同一实例的账号隔离,不是数据库或进程物理隔离。

操作步骤、字段限制及接口清单见独立用户与资源权限。

MIT Licensed