package com.nylc.touyan.auth; import com.nylc.touyan.datasource.DataSourceClient; import com.nylc.touyan.rbac.RbacService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import java.util.Collections; import java.util.Map; /** * 用户业务:注册、登录、按用户名查询。 * 复用 {@link DataSourceClient} 访问底层数据源(当前为 SQLite),未来切换 MySQL 业务代码零改动。 * 登录/注册时通过 {@link RbacService} 加载角色与权限并随 token(AuthUser)下发。 */ @Service public class UserService { private static final Logger log = LoggerFactory.getLogger(UserService.class); private final DataSourceClient dataSource; private final RbacService rbacService; private final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); public UserService(DataSourceClient dataSource, RbacService rbacService) { this.dataSource = dataSource; this.rbacService = rbacService; } public void initSchema() { dataSource.execute( "CREATE TABLE IF NOT EXISTS sys_user (" + "id INTEGER PRIMARY KEY AUTOINCREMENT," + "username TEXT NOT NULL UNIQUE," + "password TEXT NOT NULL," + "nickname TEXT," + "enabled INTEGER NOT NULL DEFAULT 1," + "created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)"); log.info("[auth] 用户表 sys_user 已就绪"); } public AuthUser register(String username, String rawPassword, String nickname) { if (username == null || username.trim().isEmpty()) { throw new AuthException("BAD_REQUEST", "用户名不能为空"); } if (rawPassword == null || rawPassword.length() < 6) { throw new AuthException("BAD_REQUEST", "密码长度至少 6 位"); } Map exists = dataSource.queryForMap( "SELECT id FROM sys_user WHERE username = ?", username); if (exists != null) { throw new AuthException("USER_EXISTS", "用户名已被注册"); } String hash = encoder.encode(rawPassword); int rows = dataSource.update( "INSERT INTO sys_user(username, password, nickname) VALUES(?, ?, ?)", username, hash, nickname); if (rows <= 0) { throw new AuthException("INTERNAL", "注册失败"); } Long id = ((Number) dataSource.queryForObject( "SELECT id FROM sys_user WHERE username = ?", username)).longValue(); assignDefaultRole(id); return buildAuthUser(id, username, nickname); } public AuthUser login(String username, String rawPassword) { Map row = dataSource.queryForMap( "SELECT id, username, password, nickname, enabled FROM sys_user WHERE username = ?", username); if (row == null) { throw new AuthException("BAD_CREDENTIALS", "用户名或密码错误"); } Number enabled = (Number) row.get("enabled"); if (enabled != null && enabled.intValue() == 0) { throw new AuthException("USER_DISABLED", "账号已被禁用"); } String hash = String.valueOf(row.get("password")); if (!encoder.matches(rawPassword, hash)) { throw new AuthException("BAD_CREDENTIALS", "用户名或密码错误"); } Long id = ((Number) row.get("id")).longValue(); String nickname = (String) row.get("nickname"); return buildAuthUser(id, username, nickname); } public AuthUser findByUsername(String username) { Map row = dataSource.queryForMap( "SELECT id, username, nickname FROM sys_user WHERE username = ?", username); if (row == null) return null; Long id = ((Number) row.get("id")).longValue(); return buildAuthUser(id, username, (String) row.get("nickname")); } private AuthUser buildAuthUser(Long id, String username, String nickname) { RbacService.AuthSnapshot snapshot = rbacService.resolve(id); return new AuthUser(id, username, nickname, snapshot.getRoles(), snapshot.getPermissions()); } private void assignDefaultRole(Long userId) { Map role = dataSource.queryForMap( "SELECT id FROM sys_role WHERE code = 'USER'"); if (role != null) { rbacService.assignUserRoles(userId, Collections.singletonList( ((Number) role.get("id")).longValue())); } } }