// Package user 定义用户与会话的领域类型,以及 user 模块对外暴露的 Service 接口契约。 // // 本文件仅做类型声明,不包含任何业务实现;Login/Logout/ResolveSession 等 // 方法在后续步骤(E6 等)实装。Service 接口的方法集刻意覆盖了 // transport/http/middleware.SessionResolver 的方法签名,因此 Service 的 // 任意实现都可直接作为 SessionResolver 注入到 Auth 中间件,避免在装配层 // 再写一层适配。 package user import ( "context" "time" "github.com/google/uuid" ) // User 表示一个本地账号。字段与 sqlc 生成的行模型一一对应,但使用领域语义 // 类型(uuid.UUID / time.Time)而非 pgtype.*,便于上层逻辑直接使用而无需 // 关心驱动层的可空封装。 type User struct { ID uuid.UUID Email string DisplayName string IsAdmin bool Enabled bool PasswordHash string CreatedAt time.Time UpdatedAt time.Time } // Session 表示一条已颁发但尚未失效的登录会话。TokenHash 存放原始 token 的 // SHA-256 摘要,原文仅在登录响应时返回一次;服务端不持久化明文 token, // 因此校验时必须重新计算摘要再比对。LastSeenAt 为可空字段(首次创建后 // 尚未被使用过时为 nil)。 type Session struct { ID uuid.UUID UserID uuid.UUID TokenHash []byte // sha256(token) ExpiresAt time.Time LastSeenAt *time.Time CreatedAt time.Time } // Service 是 user 模块对外暴露的应用服务契约。它同时满足 // transport/http/middleware.SessionResolver 接口(参见 ResolveSession 方法), // 因此可以直接被 Auth 中间件复用。 type Service interface { // Login 校验邮箱密码,成功后签发新会话;返回的 token 为原文,仅此一次可见。 Login(ctx context.Context, email, password, ip string) (token string, user *User, err error) // Logout 注销给定 token 对应的会话;token 未知或已过期时返回 nil(幂等)。 Logout(ctx context.Context, token string) error // ResolveSession 将 bearer token 解析为已认证的用户 ID,实现 // middleware.SessionResolver 接口契约:合法且未过期返回 (uid,true,nil); // 语法合法但未知/过期返回 (uuid.Nil,false,nil);底层故障返回非 nil error。 ResolveSession(ctx context.Context, token string) (uuid.UUID, bool, error) // Get 按主键加载用户,未找到时返回 errs.NotFound(在 E6 实装时确定)。 Get(ctx context.Context, id uuid.UUID) (*User, error) // Bootstrap 在系统首次启动时创建初始管理员账号,已存在同邮箱用户时为幂等。 Bootstrap(ctx context.Context, email, password string) (*User, error) // ListAdmins 返回所有管理员用户,供内部模块(如 jobs runner)使用。 ListAdmins(ctx context.Context) ([]*User, error) // ===== 管理员用户管理(需调用方自行做 admin 鉴权)===== // ListUsers 返回全部用户,按创建时间升序,供管理后台展示。 ListUsers(ctx context.Context) ([]*User, error) // CreateUser 由管理员创建一个新账号。email 唯一冲突时返回 CodeConflict。 CreateUser(ctx context.Context, in CreateUserInput) (*User, error) // UpdateProfile 修改用户的 display_name。 UpdateProfile(ctx context.Context, id uuid.UUID, displayName string) (*User, error) // SetAdmin 设置用户的管理员标志。降级最后一个启用管理员时返回 CodeConflict; // actor==target 且降级自身时返回 CodeConflict(防自锁)。 SetAdmin(ctx context.Context, actor, target uuid.UUID, isAdmin bool) (*User, error) // SetEnabled 启用/禁用用户。禁用会撤销其全部会话。禁用最后一个启用管理员或 // 禁用自身时返回 CodeConflict。 SetEnabled(ctx context.Context, actor, target uuid.UUID, enabled bool) (*User, error) // AdminResetPassword 由管理员重置目标用户密码,返回一次性明文临时密码并撤销其会话。 AdminResetPassword(ctx context.Context, id uuid.UUID) (tempPassword string, err error) // ChangePassword 用户自助改密:校验旧密码后更新,并撤销其它会话。 ChangePassword(ctx context.Context, id uuid.UUID, oldPassword, newPassword string) error // DeleteUser 删除用户。删除最后一个启用管理员或删除自身时返回 CodeConflict。 DeleteUser(ctx context.Context, actor, target uuid.UUID) error } // CreateUserInput 是管理员创建用户的入参。 type CreateUserInput struct { Email string DisplayName string Password string IsAdmin bool }