在这里插入图片描述

📖 引言

在知识问答学习应用中,用户服务(UserService)是管理用户数据的核心组件。它负责处理用户的登录、注册、数据存储、统计更新等关键操作,是连接用户与应用的桥梁。

本文将详细讲解 UserService 的完整实现,包括用户认证、数据管理、统计更新等核心功能。通过本文,你将掌握:

  • 如何设计用户服务的核心接口
  • 如何实现用户登录和注册流程
  • 如何管理用户数据和统计
  • 如何在实际项目中保证用户数据的安全性

🎯 学习目标

完成本文后,你将能够:

  • ✅ 理解 UserService 的核心职责和功能
  • ✅ 实现用户登录、注册和注销功能
  • ✅ 管理用户数据的增删改查
  • ✅ 更新用户统计和进度
  • ✅ 处理用户设置和偏好

💡 需求分析

功能模块设计

模块 功能描述 技术要点
用户认证 处理用户登录、注册、注销 密码加密、token管理
用户数据管理 CRUD操作用户数据 数据持久化、缓存
用户统计 更新用户学习统计数据 答题数、正确率、积分
用户设置 管理用户偏好设置 主题、通知、语言
进度同步 同步用户学习进度 云端同步、本地存储

🛠️ 核心实现

步骤1: UserService 接口设计

功能说明

定义 UserService 的核心接口,规范服务的方法签名和返回类型。

完整代码
// services/interfaces/UserServiceInterface.ts

import { User, UserSettings, UserStatistics } from '../models/User';
import { ServiceResponse } from './ServiceInterface';

/**
 * 用户服务接口
 */
export interface UserServiceInterface {
  /**
   * 用户登录
   * @param username - 用户名
   * @param password - 密码
   * @returns 用户信息
   */
  login(username: string, password: string): Promise<ServiceResponse<User>>;

  /**
   * 用户注册
   * @param username - 用户名
   * @param password - 密码
   * @param nickname - 用户昵称
   * @returns 用户信息
   */
  register(username: string, password: string, nickname: string): Promise<ServiceResponse<User>>;

  /**
   * 用户注销
   * @returns 操作结果
   */
  logout(): Promise<ServiceResponse<void>>;

  /**
   * 获取当前用户
   * @returns 当前用户信息
   */
  getCurrentUser(): Promise<ServiceResponse<User | null>>;

  /**
   * 根据ID获取用户
   * @param userId - 用户ID
   * @returns 用户信息
   */
  getUserById(userId: string): Promise<ServiceResponse<User | null>>;

  /**
   * 更新用户信息
   * @param userId - 用户ID
   * @param updates - 更新内容
   * @returns 更新后的用户信息
   */
  updateUser(userId: string, updates: Partial<User>): Promise<ServiceResponse<User>>;

  /**
   * 更新用户设置
   * @param userId - 用户ID
   * @param settings - 设置内容
   * @returns 更新后的设置
   */
  updateSettings(userId: string, settings: Partial<UserSettings>): Promise<ServiceResponse<UserSettings>>;

  /**
   * 更新用户统计
   * @param userId - 用户ID
   * @param statistics - 统计数据
   * @returns 更新后的统计
   */
  updateStatistics(userId: string, statistics: Partial<UserStatistics>): Promise<ServiceResponse<UserStatistics>>;

  /**
   * 增加用户积分
   * @param userId - 用户ID
   * @param amount - 积分数量
   * @returns 更新后的用户信息
   */
  addScore(userId: string, amount: number): Promise<ServiceResponse<User>>;

  /**
   * 增加用户经验值
   * @param userId - 用户ID
   * @param amount - 经验值数量
   * @returns 更新后的用户信息
   */
  addExperience(userId: string, amount: number): Promise<ServiceResponse<User>>;

  /**
   * 检查用户名是否存在
   * @param username - 用户名
   * @returns 是否存在
   */
  checkUsernameExists(username: string): Promise<ServiceResponse<boolean>>;

  /**
   * 重置密码
   * @param username - 用户名
   * @param newPassword - 新密码
   * @returns 操作结果
   */
  resetPassword(username: string, newPassword: string): Promise<ServiceResponse<void>>;

  /**
   * 更新用户头像
   * @param userId - 用户ID
   * @param avatarUrl - 头像URL
   * @returns 更新后的用户信息
   */
  updateAvatar(userId: string, avatarUrl: string): Promise<ServiceResponse<User>>;

  /**
   * 更新用户昵称
   * @param userId - 用户ID
   * @param nickname - 新昵称
   * @returns 更新后的用户信息
   */
  updateNickname(userId: string, nickname: string): Promise<ServiceResponse<User>>;
}
代码解析

1. 接口方法分类

分类 方法 功能说明
认证 login(), register(), logout() 用户登录、注册、注销
查询 getCurrentUser(), getUserById() 获取用户信息
更新 updateUser(), updateSettings(), updateStatistics() 更新用户数据
奖励 addScore(), addExperience() 添加积分和经验
辅助 checkUsernameExists(), resetPassword() 辅助操作

2. 返回类型说明

export interface ServiceResponse<T> {
  success: boolean;
  data?: T;
  error?: ServiceError;
  message?: string;
  timestamp: number;
}

步骤2: UserService 实现

功能说明

实现 UserService 的核心逻辑,包括用户认证、数据管理、统计更新等功能。

完整代码
// services/UserService.ts

import { Singleton } from './base/BaseService';
import { BaseService } from './base/BaseService';
import { ServiceResponse, ServiceError, createSuccessResponse, createErrorResponse } from './interfaces/ServiceInterface';
import { UserServiceInterface } from './interfaces/UserServiceInterface';
import { User, UserSettings, UserStatistics, UserTools } from '../models/User';
import { StorageService } from './StorageService';
import { LevelService } from './LevelService';
import { AchievementService } from './AchievementService';

/**
 * 用户服务实现
 */
@Singleton
export class UserService extends BaseService implements UserServiceInterface {
  private storageService: StorageService;
  private levelService: LevelService;
  private achievementService: AchievementService;
  private currentUser: User | null = null;

  constructor() {
    super();
    this.storageService = StorageService.getInstance();
    this.levelService = LevelService.getInstance();
    this.achievementService = AchievementService.getInstance();
  }

  /**
   * 初始化服务
   */
  async initialize(): Promise<void> {
    // 尝试从本地存储恢复用户会话
    await this.restoreSession();
  }

  /**
   * 从本地存储恢复会话
   */
  private async restoreSession(): Promise<void> {
    const storedUser = await this.storageService.getUserData();
    if (storedUser) {
      this.currentUser = storedUser;
    }
  }

  /**
   * 用户登录
   */
  async login(username: string, password: string): Promise<ServiceResponse<User>> {
    try {
      // 验证参数
      if (!username || !password) {
        return createErrorResponse<User>(ServiceError.INVALID_PARAMS, '用户名和密码不能为空');
      }

      // 查询用户
      const users = await this.storageService.getUsers();
      const user = users.find(u => u.username === username);

      if (!user) {
        return createErrorResponse<User>(ServiceError.NOT_FOUND, '用户不存在');
      }

      // 验证密码(实际项目中使用加密验证)
      if (user.password !== password) {
        return createErrorResponse<User>(ServiceError.INVALID_PARAMS, '密码错误');
      }

      // 记录登录日期
      user.lastLoginDate = new Date().toISOString();
      
      // 更新连续登录天数
      await this.updateConsecutiveDays(user);

      // 保存用户数据
      await this.storageService.saveUserData(user);
      this.currentUser = user;

      // 检查成就
      await this.achievementService.checkAndUnlockAchievements(user);

      return createSuccessResponse<User>(user);
    } catch (error) {
      return createErrorResponse<User>(ServiceError.ERROR, (error as Error).message);
    }
  }

  /**
   * 用户注册
   */
  async register(username: string, password: string, nickname: string): Promise<ServiceResponse<User>> {
    try {
      // 验证参数
      if (!username || !password || !nickname) {
        return createErrorResponse<User>(ServiceError.INVALID_PARAMS, '用户名、密码和昵称不能为空');
      }

      // 检查用户名是否已存在
      const exists = await this.checkUsernameExists(username);
      if (exists.data) {
        return createErrorResponse<User>(ServiceError.CONFLICT, '用户名已存在');
      }

      // 创建新用户
      const now = new Date().toISOString();
      const newUser: User = {
        id: this.generateUserId(),
        username,
        password,  // 实际项目中应加密存储
        nickname,
        avatar: this.generateDefaultAvatar(),
        level: 1,
        totalScore: 0,
        totalExperience: 0,
        rank: 0,
        consecutiveDays: 1,
        lastLoginDate: now,
        createdAt: now,
        settings: this.createDefaultSettings(),
        statistics: this.createDefaultStatistics(),
        unlockedLevels: ['math_level_1'],  // 默认解锁第一个关卡
        completedLevels: [],
        levelStars: new Map(),
        achievements: [],
        wrongQuestions: [],
        favorites: [],
        tools: this.createDefaultTools(),
        quizHistory: [],
        dailyRecords: [],
        ownedItems: []
      };

      // 保存用户
      await this.storageService.saveUser(newUser);
      await this.storageService.saveUserData(newUser);
      this.currentUser = newUser;

      return createSuccessResponse<User>(newUser);
    } catch (error) {
      return createErrorResponse<User>(ServiceError.ERROR, (error as Error).message);
    }
  }

  /**
   * 用户注销
   */
  async logout(): Promise<ServiceResponse<void>> {
    try {
      // 清除本地存储
      await this.storageService.clearUserData();
      this.currentUser = null;

      return createSuccessResponse<void>(undefined);
    } catch (error) {
      return createErrorResponse<void>(ServiceError.ERROR, (error as Error).message);
    }
  }

  /**
   * 获取当前用户
   */
  async getCurrentUser(): Promise<ServiceResponse<User | null>> {
    try {
      if (!this.currentUser) {
        await this.restoreSession();
      }
      return createSuccessResponse<User | null>(this.currentUser);
    } catch (error) {
      return createErrorResponse<User | null>(ServiceError.ERROR, (error as Error).message);
    }
  }

  /**
   * 根据ID获取用户
   */
  async getUserById(userId: string): Promise<ServiceResponse<User | null>> {
    try {
      const users = await this.storageService.getUsers();
      const user = users.find(u => u.id === userId);
      return createSuccessResponse<User | null>(user || null);
    } catch (error) {
      return createErrorResponse<User | null>(ServiceError.ERROR, (error as Error).message);
    }
  }

  /**
   * 更新用户信息
   */
  async updateUser(userId: string, updates: Partial<User>): Promise<ServiceResponse<User>> {
    try {
      const users = await this.storageService.getUsers();
      const index = users.findIndex(u => u.id === userId);

      if (index === -1) {
        return createErrorResponse<User>(ServiceError.NOT_FOUND, '用户不存在');
      }

      // 更新用户信息
      users[index] = { ...users[index], ...updates };
      await this.storageService.saveUsers(users);

      // 如果是当前用户,更新本地缓存
      if (this.currentUser?.id === userId) {
        this.currentUser = users[index];
        await this.storageService.saveUserData(this.currentUser);
      }

      return createSuccessResponse<User>(users[index]);
    } catch (error) {
      return createErrorResponse<User>(ServiceError.ERROR, (error as Error).message);
    }
  }

  /**
   * 更新用户设置
   */
  async updateSettings(userId: string, settings: Partial<UserSettings>): Promise<ServiceResponse<UserSettings>> {
    try {
      const users = await this.storageService.getUsers();
      const user = users.find(u => u.id === userId);

      if (!user) {
        return createErrorResponse<UserSettings>(ServiceError.NOT_FOUND, '用户不存在');
      }

      // 更新设置
      user.settings = { ...user.settings, ...settings };
      await this.storageService.saveUsers(users);

      // 更新当前用户缓存
      if (this.currentUser?.id === userId) {
        this.currentUser.settings = user.settings;
        await this.storageService.saveUserData(this.currentUser);
      }

      return createSuccessResponse<UserSettings>(user.settings);
    } catch (error) {
      return createErrorResponse<UserSettings>(ServiceError.ERROR, (error as Error).message);
    }
  }

  /**
   * 更新用户统计
   */
  async updateStatistics(userId: string, statistics: Partial<UserStatistics>): Promise<ServiceResponse<UserStatistics>> {
    try {
      const users = await this.storageService.getUsers();
      const user = users.find(u => u.id === userId);

      if (!user) {
        return createErrorResponse<UserStatistics>(ServiceError.NOT_FOUND, '用户不存在');
      }

      // 更新统计数据
      user.statistics = { ...user.statistics, ...statistics };
      await this.storageService.saveUsers(users);

      // 更新当前用户缓存
      if (this.currentUser?.id === userId) {
        this.currentUser.statistics = user.statistics;
        await this.storageService.saveUserData(this.currentUser);
      }

      return createSuccessResponse<UserStatistics>(user.statistics);
    } catch (error) {
      return createErrorResponse<UserStatistics>(ServiceError.ERROR, (error as Error).message);
    }
  }

  /**
   * 增加用户积分
   */
  async addScore(userId: string, amount: number): Promise<ServiceResponse<User>> {
    try {
      const users = await this.storageService.getUsers();
      const index = users.findIndex(u => u.id === userId);

      if (index === -1) {
        return createErrorResponse<User>(ServiceError.NOT_FOUND, '用户不存在');
      }

      // 增加积分
      users[index].totalScore += amount;
      await this.storageService.saveUsers(users);

      // 更新当前用户缓存
      if (this.currentUser?.id === userId) {
        this.currentUser.totalScore = users[index].totalScore;
        await this.storageService.saveUserData(this.currentUser);
      }

      return createSuccessResponse<User>(users[index]);
    } catch (error) {
      return createErrorResponse<User>(ServiceError.ERROR, (error as Error).message);
    }
  }

  /**
   * 增加用户经验值
   */
  async addExperience(userId: string, amount: number): Promise<ServiceResponse<User>> {
    try {
      const users = await this.storageService.getUsers();
      const index = users.findIndex(u => u.id === userId);

      if (index === -1) {
        return createErrorResponse<User>(ServiceError.NOT_FOUND, '用户不存在');
      }

      // 增加经验值
      users[index].totalExperience += amount;

      // 检查是否升级
      await this.checkLevelUp(users[index]);

      await this.storageService.saveUsers(users);

      // 更新当前用户缓存
      if (this.currentUser?.id === userId) {
        this.currentUser = users[index];
        await this.storageService.saveUserData(this.currentUser);
      }

      return createSuccessResponse<User>(users[index]);
    } catch (error) {
      return createErrorResponse<User>(ServiceError.ERROR, (error as Error).message);
    }
  }

  /**
   * 检查用户名是否存在
   */
  async checkUsernameExists(username: string): Promise<ServiceResponse<boolean>> {
    try {
      const users = await this.storageService.getUsers();
      const exists = users.some(u => u.username === username);
      return createSuccessResponse<boolean>(exists);
    } catch (error) {
      return createErrorResponse<boolean>(ServiceError.ERROR, (error as Error).message);
    }
  }

  /**
   * 重置密码
   */
  async resetPassword(username: string, newPassword: string): Promise<ServiceResponse<void>> {
    try {
      const users = await this.storageService.getUsers();
      const index = users.findIndex(u => u.username === username);

      if (index === -1) {
        return createErrorResponse<void>(ServiceError.NOT_FOUND, '用户不存在');
      }

      // 更新密码(实际项目中应加密)
      users[index].password = newPassword;
      await this.storageService.saveUsers(users);

      return createSuccessResponse<void>(undefined);
    } catch (error) {
      return createErrorResponse<void>(ServiceError.ERROR, (error as Error).message);
    }
  }

  /**
   * 更新用户头像
   */
  async updateAvatar(userId: string, avatarUrl: string): Promise<ServiceResponse<User>> {
    return this.updateUser(userId, { avatar: avatarUrl });
  }

  /**
   * 更新用户昵称
   */
  async updateNickname(userId: string, nickname: string): Promise<ServiceResponse<User>> {
    return this.updateUser(userId, { nickname });
  }

  /**
   * 检查升级
   */
  private async checkLevelUp(user: User): Promise<void> {
    const expForNextLevel = user.level * 100;
    if (user.totalExperience >= expForNextLevel) {
      user.level++;
      // 升级奖励
      user.totalScore += user.level * 50;
    }
  }

  /**
   * 更新连续登录天数
   */
  private async updateConsecutiveDays(user: User): Promise<void> {
    const today = new Date().toDateString();
    const lastLogin = new Date(user.lastLoginDate).toDateString();
    
    // 检查是否连续登录
    const oneDay = 24 * 60 * 60 * 1000;
    const daysDiff = Math.round((new Date(today).getTime() - new Date(lastLogin).getTime()) / oneDay);
    
    if (daysDiff === 1) {
      // 连续登录,天数+1
      user.consecutiveDays++;
    } else if (daysDiff > 1) {
      // 中断登录,重置为1
      user.consecutiveDays = 1;
    }
    // 同一天登录,天数不变
  }

  /**
   * 生成用户ID
   */
  private generateUserId(): string {
    return `user_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  }

  /**
   * 生成默认头像
   */
  private generateDefaultAvatar(): string {
    const colors = ['#4CAF50', '#2196F3', '#FF9800', '#9C27B0', '#F44336'];
    const color = colors[Math.floor(Math.random() * colors.length)];
    return `https://ui-avatars.com/api/?background=${color.replace('#', '')}&color=fff&name=User`;
  }

  /**
   * 创建默认设置
   */
  private createDefaultSettings(): UserSettings {
    return {
      theme: 'light',
      notification: true,
      sound: true,
      autoSave: true,
      dailyReminder: true,
      difficulty: 1,
      language: 'zh-CN',
      fontSize: 16,
      showExplanation: true,
      shuffleQuestions: true
    };
  }

  /**
   * 创建默认统计
   */
  private createDefaultStatistics(): UserStatistics {
    return {
      totalQuestions: 0,
      correctQuestions: 0,
      wrongQuestions: 0,
      averageAccuracy: 0,
      totalTime: 0,
      averageTimePerQuestion: 0,
      streakDays: 0,
      bestStreak: 0,
      completedLevels: 0,
      perfectLevels: 0,
      achievementsUnlocked: 0,
      totalScore: 0,
      rank: 0
    };
  }

  /**
   * 创建默认工具
   */
  private createDefaultTools(): UserTools {
    return {
      hintCount: 3,
      skipCount: 1,
      refreshCount: 1,
      doubleScoreCount: 1
    };
  }
}
代码解析

1. 用户登录流程

async login(username: string, password: string): Promise<ServiceResponse<User>> {
  // 1. 验证参数
  if (!username || !password) {
    return createErrorResponse<User>(ServiceError.INVALID_PARAMS, '用户名和密码不能为空');
  }

  // 2. 查询用户
  const users = await this.storageService.getUsers();
  const user = users.find(u => u.username === username);

  // 3. 验证密码
  if (user.password !== password) {
    return createErrorResponse<User>(ServiceError.INVALID_PARAMS, '密码错误');
  }

  // 4. 更新登录信息
  user.lastLoginDate = new Date().toISOString();
  await this.updateConsecutiveDays(user);

  // 5. 保存会话
  await this.storageService.saveUserData(user);
  this.currentUser = user;

  // 6. 检查成就
  await this.achievementService.checkAndUnlockAchievements(user);

  return createSuccessResponse<User>(user);
}

流程说明:

  1. 参数验证
  2. 查询用户
  3. 密码验证
  4. 更新登录日期和连续登录天数
  5. 保存会话
  6. 检查成就解锁

2. 用户注册流程

async register(username: string, password: string, nickname: string): Promise<ServiceResponse<User>> {
  // 1. 验证参数
  // 2. 检查用户名是否存在
  // 3. 创建新用户(包含默认设置、统计、初始关卡)
  // 4. 保存用户
  // 5. 返回用户信息
}

3. 连续登录天数计算

private async updateConsecutiveDays(user: User): Promise<void> {
  const today = new Date().toDateString();
  const lastLogin = new Date(user.lastLoginDate).toDateString();
  
  const oneDay = 24 * 60 * 60 * 1000;
  const daysDiff = Math.round((new Date(today).getTime() - new Date(lastLogin).getTime()) / oneDay);
  
  if (daysDiff === 1) {
    user.consecutiveDays++;  // 连续登录
  } else if (daysDiff > 1) {
    user.consecutiveDays = 1;  // 中断登录
  }
}

原理:

  • 计算上次登录与今天的天数差
  • 差为1:连续登录,天数+1
  • 差>1:中断登录,重置为1
  • 差=0:同一天,不变

步骤3: 用户数据持久化

功能说明

使用 StorageService 实现用户数据的本地存储和读取。

完整代码
// services/StorageService.ts

import { Singleton } from './base/BaseService';
import { BaseService } from './base/BaseService';
import { User } from '../models/User';

/**
 * 存储服务
 */
@Singleton
export class StorageService extends BaseService {
  private readonly USER_DATA_KEY = 'quiz_user_data';
  private readonly USERS_KEY = 'quiz_users';

  /**
   * 获取用户数据
   */
  async getUserData(): Promise<User | null> {
    try {
      const data = localStorage.getItem(this.USER_DATA_KEY);
      return data ? JSON.parse(data) : null;
    } catch {
      return null;
    }
  }

  /**
   * 保存用户数据
   */
  async saveUserData(user: User): Promise<void> {
    try {
      localStorage.setItem(this.USER_DATA_KEY, JSON.stringify(user));
    } catch (error) {
      console.error('Failed to save user data:', error);
    }
  }

  /**
   * 清除用户数据
   */
  async clearUserData(): Promise<void> {
    try {
      localStorage.removeItem(this.USER_DATA_KEY);
    } catch (error) {
      console.error('Failed to clear user data:', error);
    }
  }

  /**
   * 获取所有用户
   */
  async getUsers(): Promise<User[]> {
    try {
      const data = localStorage.getItem(this.USERS_KEY);
      return data ? JSON.parse(data) : [];
    } catch {
      return [];
    }
  }

  /**
   * 保存所有用户
   */
  async saveUsers(users: User[]): Promise<void> {
    try {
      localStorage.setItem(this.USERS_KEY, JSON.stringify(users));
    } catch (error) {
      console.error('Failed to save users:', error);
    }
  }

  /**
   * 保存单个用户
   */
  async saveUser(user: User): Promise<void> {
    const users = await this.getUsers();
    const index = users.findIndex(u => u.id === user.id);
    
    if (index === -1) {
      users.push(user);
    } else {
      users[index] = user;
    }
    
    await this.saveUsers(users);
  }

  /**
   * 删除用户
   */
  async deleteUser(userId: string): Promise<void> {
    const users = await this.getUsers();
    const filtered = users.filter(u => u.id !== userId);
    await this.saveUsers(filtered);
  }
}
代码解析

1. 存储键设计

private readonly USER_DATA_KEY = 'quiz_user_data';  // 当前用户会话
private readonly USERS_KEY = 'quiz_users';          // 所有用户列表

2. 数据存储流程

// 登录时保存会话
await this.storageService.saveUserData(user);

// 获取当前用户
const user = await this.storageService.getUserData();

// 注销时清除会话
await this.storageService.clearUserData();

⚠️ 常见问题与解决方案

问题1: 密码明文存储

现象:
密码以明文形式存储,存在安全风险。

错误代码:

// ❌ 错误:明文存储密码
newUser.password = password;

正确代码:

// ✅ 正确:加密存储密码
import * as bcrypt from 'bcrypt';

async function hashPassword(password: string): Promise<string> {
  const saltRounds = 10;
  return bcrypt.hash(password, saltRounds);
}

async function verifyPassword(password: string, hash: string): Promise<boolean> {
  return bcrypt.compare(password, hash);
}

// 注册时加密
newUser.password = await hashPassword(password);

// 登录时验证
const isValid = await verifyPassword(password, user.password);

规则/建议:

  • 使用 bcrypt 或类似算法加密密码
  • 永远不要明文存储密码
  • 登录时验证哈希值而非明文比较

问题2: 用户会话管理缺失

现象:
应用重启后用户需要重新登录。

错误代码:

// ❌ 错误:没有会话持久化
this.currentUser = null;

正确代码:

// ✅ 正确:持久化会话
async initialize(): Promise<void> {
  await this.restoreSession();
}

private async restoreSession(): Promise<void> {
  const storedUser = await this.storageService.getUserData();
  if (storedUser) {
    this.currentUser = storedUser;
  }
}

规则/建议:

  • 在服务初始化时恢复会话
  • 使用 localStorage 或 secure storage 存储会话
  • 设置合理的会话过期时间

问题3: 并发更新导致数据不一致

现象:
多个操作同时更新用户数据,导致数据覆盖丢失。

错误代码:

// ❌ 错误:没有并发控制
async addScore(userId: string, amount: number) {
  const user = await this.getUserById(userId);
  user.totalScore += amount;  // 可能被其他操作覆盖
  await this.saveUser(user);
}

正确代码:

// ✅ 正确:使用乐观锁或事务
async addScore(userId: string, amount: number) {
  const users = await this.storageService.getUsers();  // 获取最新数据
  const index = users.findIndex(u => u.id === userId);
  
  if (index === -1) {
    return createErrorResponse<User>(ServiceError.NOT_FOUND, '用户不存在');
  }
  
  users[index].totalScore += amount;
  await this.storageService.saveUsers(users);  // 原子操作
}

规则/建议:

  • 使用原子操作更新数据
  • 每次更新前获取最新数据
  • 考虑使用版本号实现乐观锁

问题4: 用户数据验证缺失

现象:
没有验证用户输入的数据,可能导致数据异常。

错误代码:

// ❌ 错误:没有数据验证
async updateNickname(userId: string, nickname: string) {
  await this.updateUser(userId, { nickname });
}

正确代码:

// ✅ 正确:添加数据验证
async updateNickname(userId: string, nickname: string): Promise<ServiceResponse<User>> {
  // 验证昵称长度
  if (!nickname || nickname.length < 2 || nickname.length > 20) {
    return createErrorResponse<User>(ServiceError.INVALID_PARAMS, '昵称长度必须在2-20之间');
  }
  
  // 验证昵称格式(只允许中文、英文、数字和下划线)
  const regex = /^[\u4e00-\u9fa5a-zA-Z0-9_]+$/;
  if (!regex.test(nickname)) {
    return createErrorResponse<User>(ServiceError.INVALID_PARAMS, '昵称只能包含中文、英文、数字和下划线');
  }
  
  return this.updateUser(userId, { nickname });
}

规则/建议:

  • 对用户输入进行验证
  • 使用正则表达式验证格式
  • 检查数据长度和类型

问题5: 连续登录天数计算错误

现象:
连续登录天数计算不准确,导致成就无法正确解锁。

错误代码:

// ❌ 错误:没有考虑时区和日期边界
user.consecutiveDays++;  // 简单递增

正确代码:

// ✅ 正确:考虑日期边界
private async updateConsecutiveDays(user: User): Promise<void> {
  const today = new Date().toDateString();
  const lastLogin = new Date(user.lastLoginDate).toDateString();
  
  const oneDay = 24 * 60 * 60 * 1000;
  const daysDiff = Math.round((new Date(today).getTime() - new Date(lastLogin).getTime()) / oneDay);
  
  if (daysDiff === 1) {
    user.consecutiveDays++;
  } else if (daysDiff > 1) {
    user.consecutiveDays = 1;
  }
}

规则/建议:

  • 使用日期字符串比较,避免时区问题
  • 计算实际天数差而非简单递增
  • 同一天登录不增加天数

📝 本章小结

核心知识点

本文详细讲解了 UserService 用户服务的实现,主要包括:

1. 用户认证

  • 登录、注册、注销流程
  • 密码验证和会话管理

2. 用户数据管理

  • CRUD操作
  • 数据持久化

3. 用户统计更新

  • 积分和经验值管理
  • 等级升级逻辑

4. 辅助功能

  • 连续登录天数计算
  • 头像和昵称更新

最佳实践总结

密码安全

// 使用 bcrypt 加密
const hash = await bcrypt.hash(password, 10);
const isValid = await bcrypt.compare(password, hash);

会话管理

// 初始化时恢复会话
async initialize(): Promise<void> {
  const stored = await this.storageService.getUserData();
  if (stored) this.currentUser = stored;
}

并发安全

// 使用原子操作
const users = await this.storageService.getUsers();
users[index].totalScore += amount;
await this.storageService.saveUsers(users);

数据验证

// 验证输入数据
if (!nickname || nickname.length < 2 || nickname.length > 20) {
  return createErrorResponse(ServiceError.INVALID_PARAMS, '无效昵称');
}

下一步预告

在下一篇文章中,我们将:

  • 🎨 讲解 QuizService 测验服务实现详解
  • 📚 介绍测验流程管理和答题逻辑
  • 🏷️ 探索测验结果计算和统计更新

🔗 相关链接


💡 提示: 建议结合项目源码阅读,动手实践效果更好!

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐