diff --git a/openspec/changes/online-session-management/.openspec.yaml b/openspec/changes/archive/2026-10-11-online-session-management/.openspec.yaml similarity index 100% rename from openspec/changes/online-session-management/.openspec.yaml rename to openspec/changes/archive/2026-10-11-online-session-management/.openspec.yaml diff --git a/openspec/changes/online-session-management/design.md b/openspec/changes/archive/2026-10-11-online-session-management/design.md similarity index 100% rename from openspec/changes/online-session-management/design.md rename to openspec/changes/archive/2026-10-11-online-session-management/design.md diff --git a/openspec/changes/online-session-management/proposal.md b/openspec/changes/archive/2026-10-11-online-session-management/proposal.md similarity index 100% rename from openspec/changes/online-session-management/proposal.md rename to openspec/changes/archive/2026-10-11-online-session-management/proposal.md diff --git a/openspec/changes/online-session-management/specs/backend/session/spec.md b/openspec/changes/archive/2026-10-11-online-session-management/specs/backend/session/spec.md similarity index 100% rename from openspec/changes/online-session-management/specs/backend/session/spec.md rename to openspec/changes/archive/2026-10-11-online-session-management/specs/backend/session/spec.md diff --git a/openspec/changes/online-session-management/specs/backend/users/spec.md b/openspec/changes/archive/2026-10-11-online-session-management/specs/backend/users/spec.md similarity index 100% rename from openspec/changes/online-session-management/specs/backend/users/spec.md rename to openspec/changes/archive/2026-10-11-online-session-management/specs/backend/users/spec.md diff --git a/openspec/changes/online-session-management/specs/frontend/AD/session/spec.md b/openspec/changes/archive/2026-10-11-online-session-management/specs/frontend/AD/session/spec.md similarity index 100% rename from openspec/changes/online-session-management/specs/frontend/AD/session/spec.md rename to openspec/changes/archive/2026-10-11-online-session-management/specs/frontend/AD/session/spec.md diff --git a/openspec/changes/online-session-management/specs/frontend/AD/user/spec.md b/openspec/changes/archive/2026-10-11-online-session-management/specs/frontend/AD/user/spec.md similarity index 100% rename from openspec/changes/online-session-management/specs/frontend/AD/user/spec.md rename to openspec/changes/archive/2026-10-11-online-session-management/specs/frontend/AD/user/spec.md diff --git a/openspec/changes/online-session-management/specs/shared/api-contract/spec.md b/openspec/changes/archive/2026-10-11-online-session-management/specs/shared/api-contract/spec.md similarity index 100% rename from openspec/changes/online-session-management/specs/shared/api-contract/spec.md rename to openspec/changes/archive/2026-10-11-online-session-management/specs/shared/api-contract/spec.md diff --git a/openspec/changes/online-session-management/specs/shared/data-model/spec.md b/openspec/changes/archive/2026-10-11-online-session-management/specs/shared/data-model/spec.md similarity index 100% rename from openspec/changes/online-session-management/specs/shared/data-model/spec.md rename to openspec/changes/archive/2026-10-11-online-session-management/specs/shared/data-model/spec.md diff --git a/openspec/changes/online-session-management/tasks.md b/openspec/changes/archive/2026-10-11-online-session-management/tasks.md similarity index 100% rename from openspec/changes/online-session-management/tasks.md rename to openspec/changes/archive/2026-10-11-online-session-management/tasks.md diff --git a/openspec/specs/backend/session/spec.md b/openspec/specs/backend/session/spec.md new file mode 100644 index 0000000..db82a49 --- /dev/null +++ b/openspec/specs/backend/session/spec.md @@ -0,0 +1,32 @@ +# Session Specification + +## Purpose + +定义在线会话域的行为:列出活跃会话、按单个会话强退、按用户踢全部会话。 + +## Requirements + +### Requirement: 活跃会话列表 +系统 SHALL 支持按用户名/昵称/IP 分页查询当前租户的活跃会话,返回用户名、昵称、设备 IP、User-Agent、登录时间、过期时间与会话标识;列表按数据权限过滤(仅可见本人权限范围内用户的会话,超管可见全部)。 + +#### Scenario: 分页查询活跃会话 +- **WHEN** 管理员请求会话列表并传 page/pageSize 与筛选条件 +- **THEN** 系统返回满足条件的活跃会话分页结果(仅含未过期、未吊销的 refresh token 对应会话) + +#### Scenario: 数据权限过滤 +- **WHEN** 数据权限为「本部门」的管理员查询会话列表 +- **THEN** 仅返回本部门用户的活跃会话 + +### Requirement: 单会话强退 +系统 SHALL 支持指定会话标识强退单个会话:吊销该会话的 refresh token,并使其 access token 在下次请求时被拒绝(401)。 + +#### Scenario: 强退单个会话 +- **WHEN** 管理员对某会话标识执行强退 +- **THEN** 该会话的 refresh token 被吊销,其 access token 在下次请求时返回 401 + +### Requirement: 踢全部会话 +系统 SHALL 支持按用户踢出其全部活跃会话:吊销该用户所有 refresh token,并使其全部 access token 失效。 + +#### Scenario: 踢出用户全部会话 +- **WHEN** 管理员对某用户执行踢全部会话 +- **THEN** 该用户所有活跃会话的 refresh token 被吊销,全部 access token 失效 diff --git a/openspec/specs/backend/users/spec.md b/openspec/specs/backend/users/spec.md index 8603a21..2b7377b 100644 --- a/openspec/specs/backend/users/spec.md +++ b/openspec/specs/backend/users/spec.md @@ -33,3 +33,17 @@ #### Scenario: 分配角色 - **WHEN** 管理员为用户分配角色 - **THEN** 该用户下次请求按新角色聚合权限 + +### Requirement: 账号锁定 +系统 SHALL 支持管理员锁定用户(可附原因);锁定后该用户无法登录,且其全部活跃会话被吊销。 + +#### Scenario: 锁定用户 +- **WHEN** 管理员锁定某用户 +- **THEN** 系统记录锁定时间与原因,吊销该用户全部会话,该用户后续登录被拒绝 + +### Requirement: 账号解锁 +系统 SHALL 支持管理员解锁用户;解锁后清空锁定时间与原因,并清除该用户的登录失败计数。 + +#### Scenario: 解锁用户 +- **WHEN** 管理员解锁某锁定用户 +- **THEN** 系统清空锁定状态与登录失败计数,该用户恢复登录能力(若 status 为启用) diff --git a/openspec/specs/frontend/AD/session/spec.md b/openspec/specs/frontend/AD/session/spec.md new file mode 100644 index 0000000..4c241e8 --- /dev/null +++ b/openspec/specs/frontend/AD/session/spec.md @@ -0,0 +1,28 @@ +# Session Management Specification + +## Purpose + +定义在线会话管理页面的行为:分页展示活跃会话、单个会话强退、按用户踢全部。 + +## Requirements + +### Requirement: 会话列表展示 +页面 SHALL 分页展示活跃会话(用户名/昵称/设备 IP/User-Agent/登录时间/过期时间),支持按用户名与 IP 筛选。 + +#### Scenario: 展示会话列表 +- **WHEN** 用户进入在线会话页面 +- **THEN** 页面展示当前租户活跃会话的分页列表 + +### Requirement: 单个会话强退 +页面 SHALL 支持对单个会话执行强退操作,强退前需确认,成功后刷新列表。 + +#### Scenario: 强退单个会话 +- **WHEN** 用户对某会话点击强退并确认 +- **THEN** 该会话被强退,列表刷新且该会话消失 + +### Requirement: 踢全部会话 +页面 SHALL 支持对某用户执行「踢全部会话」操作,强退前需确认。 + +#### Scenario: 踢出用户全部会话 +- **WHEN** 用户对某用户执行踢全部并确认 +- **THEN** 该用户所有活跃会话被强退,列表刷新 diff --git a/openspec/specs/frontend/AD/user/spec.md b/openspec/specs/frontend/AD/user/spec.md index f351371..ffb287d 100644 --- a/openspec/specs/frontend/AD/user/spec.md +++ b/openspec/specs/frontend/AD/user/spec.md @@ -58,3 +58,14 @@ #### Scenario: 后续接后端 - **WHEN** 占位数据替换为后端用户接口 - **THEN** 页面无需改动数据结构即可直接渲染 + +### Requirement: 锁定/解锁操作 +用户管理页 SHALL 支持对用户执行锁定(可填原因)与解锁操作;锁定/解锁后刷新列表并提示结果。 + +#### Scenario: 锁定用户 +- **WHEN** 管理员对某启用用户执行锁定并(可选)填写原因 +- **THEN** 系统锁定该用户并刷新列表,状态显示为锁定 + +#### Scenario: 解锁用户 +- **WHEN** 管理员对某锁定用户执行解锁 +- **THEN** 系统解锁该用户并刷新列表 diff --git a/openspec/specs/shared/api-contract/spec.md b/openspec/specs/shared/api-contract/spec.md index bfbe0f3..ff06b4f 100644 --- a/openspec/specs/shared/api-contract/spec.md +++ b/openspec/specs/shared/api-contract/spec.md @@ -54,7 +54,7 @@ - **THEN** 系统签发新 access 与 refresh,并作废旧 refresh ### Requirement: 核心域接口目录 -系统 SHALL 为每个核心域提供 REST 资源接口(复数、kebab-case):/users、/roles、/resources、/orgs、/dict-types、/configs、/audit-logs、/login-logs。 +系统 SHALL 为每个核心域提供 REST 资源接口(复数、kebab-case):/users、/roles、/resources、/orgs、/dict-types、/configs、/audit-logs、/login-logs、/sessions。 #### Scenario: 资源 CRUD - **WHEN** 管理端操作某核心域 @@ -70,3 +70,18 @@ #### Scenario: 停用过滤 - **WHEN** 某字典类型或字典项被停用 - **THEN** GET /dicts 不返回该类型或该项 + +### Requirement: 会话与账号安全接口 +系统 SHALL 提供以下接口:`GET /sessions`(分页查询活跃会话)、`POST /sessions/:sid/kick`(单会话强退)、`POST /users/:id/kick-sessions`(踢用户全部会话)、`POST /users/:id/lock`(锁定用户,body 可含 reason)、`POST /users/:id/unlock`(解锁用户)。 + +#### Scenario: 查询活跃会话 +- **WHEN** 客户端请求 GET /sessions +- **THEN** 返回活跃会话分页数据(list/total/page/pageSize) + +#### Scenario: 单会话强退 +- **WHEN** 客户端请求 POST /sessions/:sid/kick +- **THEN** 该会话被强退 + +#### Scenario: 锁定与解锁 +- **WHEN** 客户端请求 POST /users/:id/lock 或 /users/:id/unlock +- **THEN** 用户被锁定或解锁 diff --git a/openspec/specs/shared/data-model/spec.md b/openspec/specs/shared/data-model/spec.md index 4b11197..7802e5b 100644 --- a/openspec/specs/shared/data-model/spec.md +++ b/openspec/specs/shared/data-model/spec.md @@ -53,12 +53,16 @@ role 实体 SHALL 通过 data_scope 字段支持全部/本部门/本部门及以 - **THEN** 表名以 sys_ 开头,列名使用 snake_case ### Requirement: 用户状态机 -user 实体 SHALL 支持启用/停用/锁定状态;停用与锁定的用户 SHALL 无法登录。 +user 实体 SHALL 通过 `status` 字段支持启用/停用(管理员手动启停),并通过独立的 `locked_at`(锁定时间,可空)与 `lock_reason`(锁定原因,可空)表示安全锁定;「锁定」由 `locked_at` 非空推导,`status` 仅存 enabled/disabled。停用或锁定的用户 SHALL 均无法登录。 #### Scenario: 锁定登录 -- **WHEN** 锁定用户尝试登录 +- **WHEN** 锁定用户(locked_at 非空)尝试登录 - **THEN** 系统拒绝登录并提示账号已锁定 +#### Scenario: 停用登录 +- **WHEN** 停用用户(status=disabled)尝试登录 +- **THEN** 系统拒绝登录并提示账号已停用 + ### Requirement: 字典实体扩展 dict_type 实体 SHALL 新增 built_in 字段(布尔,标记内置字典类型);dict_item 实体 SHALL 新增 built_in(布尔,标记内置字典项)与 color_type(字符串,取值 primary/success/warning/danger/info,可空)字段,用于前端标签渲染与内置保护。