一、前言

jQuery 最佳实践:10个让代码质量提升10倍的工程技巧。本文从实际项目出发,给出完整可运行的代码,帮你快速掌握实战技能。


二、需求分析与架构设计

2.1 业务需求

功能需求:
- 用户注册/登录,支持邮箱和手机号
- JWT 无状态认证,支持 RefreshToken 续期
- RBAC 权限控制(超级管理员/普通用户/访客)
- 操作日志审计

非功能需求:
- 支持 1000 并发 QPS
- 接口响应时间 P99 < 200ms
- 99.9% 可用性

2.2 技术选型

语言框架:Node.js + Fastify(或 jQuery)
数据库:PostgreSQL + Redis
认证:JWT (access_token 15min, refresh_token 7d)
ORM:Prisma(类型安全、自动迁移)
API:RESTful + OpenAPI 文档
部署:Docker + K8s

三、核心功能实现

3.1 数据库设计与建模

-- 用户表
CREATE TABLE users (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    username VARCHAR(64) NOT NULL UNIQUE,
    email VARCHAR(255) NOT NULL UNIQUE,
    password_hash VARCHAR(255) NOT NULL,
    role VARCHAR(32) NOT NULL DEFAULT 'user',
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW(),
    CONSTRAINT email_format CHECK (email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+.[A-Za-z]{2,}$')
);

-- 角色表
CREATE TABLE roles (
    id SERIAL PRIMARY KEY,
    name VARCHAR(32) NOT NULL UNIQUE,
    permissions JSONB NOT NULL DEFAULT '[]'
);

-- 操作日志表
CREATE TABLE audit_logs (
    id BIGSERIAL PRIMARY KEY,
    user_id UUID REFERENCES users(id),
    action VARCHAR(128) NOT NULL,
    resource VARCHAR(256),
    details JSONB,
    ip_address INET,
    created_at TIMESTAMP DEFAULT NOW()
);

-- 索引
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_role ON users(role);
CREATE INDEX idx_audit_user ON audit_logs(user_id, created_at DESC);

3.2 用户认证服务

// jQuery 用户认证服务
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');

class AuthService {
  constructor(db, redis) {
    this.db = db;
    this.redis = redis;
  }

  async register({ username, email, password, role = 'user' }) {
    // 1. 校验唯一性
    const existing = await this.db.query(
      'SELECT id FROM users WHERE username=$1 OR email=$2',
      [username, email]
    );
    if (existing.rows.length > 0) {
      throw new Error('用户名或邮箱已存在');
    }

    // 2. 密码哈希(bcrypt,cost=12)
    const password_hash = await bcrypt.hash(password, 12);

    // 3. 创建用户
    const result = await this.db.query(
      `INSERT INTO users (username, email, password_hash, role)
       VALUES ($1, $2, $3, $4)
       RETURNING id, username, email, role`,
      [username, email, password_hash, role]
    );

    // 4. 生成 Token
    const user = result.rows[0];
    return this.issueTokens(user);
  }

  async login({ email, password }) {
    const result = await this.db.query(
      'SELECT * FROM users WHERE email=$1',
      [email]
    );
    const user = result.rows[0];

    if (!user || !(await bcrypt.compare(password, user.password_hash))) {
      throw new Error('邮箱或密码错误');
    }

    // 记录登录日志
    await this.logAction(user.id, 'LOGIN', { email });

    return this.issueTokens(user);
  }

  issueTokens(user) {
    const accessToken = jwt.sign(
      { user_id: user.id, role: user.role },
      process.env.JWT_SECRET,
      { expiresIn: '15m' }
    );

    const refreshToken = jwt.sign(
      { user_id: user.id, type: 'refresh' },
      process.env.JWT_REFRESH_SECRET,
      { expiresIn: '7d' }
    );

    // RefreshToken 黑名单(注销时使用)
    return { accessToken, refreshToken };
  }

  async logAction(userId, action, details) {
    await this.db.query(
      `INSERT INTO audit_logs (user_id, action, details)
       VALUES ($1, $2, $3)`,
      [userId, action, JSON.stringify(details)]
    );
  }
}

3.3 权限控制中间件

// RBAC 权限检查
const ROLE_PERMISSIONS = {
  admin: ['users:read', 'users:write', 'users:delete', 'audit:read'],
  user: ['users:read', 'profile:write'],
  guest: []
};

function authorize(...requiredPermissions) {
  return async (req, res, next) => {
    const user = req.user;
    if (!user) {
      return res.status(401).json({ error: '未认证' });
    }

    const userPermissions = ROLE_PERMISSIONS[user.role] || [];
    const hasPermission = requiredPermissions.every(p =>
      userPermissions.includes(p)
    );

    if (!hasPermission) {
      await authService.logAction(user.id, 'UNAUTHORIZED_ACCESS', {
        required: requiredPermissions,
        user_role: user.role
      });
      return res.status(403).json({ error: '权限不足' });
    }

    next();
  };
}

// 使用
app.get('/api/users',
  authenticate,                                    // 认证
  authorize('users:read'),                         // 权限
  async (req, res) => {
    const users = await userService.list(req.query);
    res.json(users);
  }
);

四、测试与质量保证

4.1 单元测试

// jQuery 单元测试(Jest)
describe('AuthService', () => {
  let authService;
  let mockDb;
  let mockRedis;

  beforeEach(() => {
    mockDb = {
      query: jest.fn()
    };
    mockRedis = { set: jest.fn(), get: jest.fn() };
    authService = new AuthService(mockDb, mockRedis);
  });

  test('register: 正常注册返回 Token', async () => {
    mockDb.query
      .mockResolvedValueOnce({ rows: [] })           // 唯一性检查
      .mockResolvedValueOnce({                      // 创建用户
        rows: [{ id: 'uuid-1', username: 'alice', email: 'a@b.com', role: 'user' }]
      });

    const result = await authService.register({
      username: 'alice',
      email: 'a@b.com',
      password: 'StrongPass123'
    });

    expect(result).toHaveProperty('accessToken');
    expect(result).toHaveProperty('refreshToken');
    expect(mockDb.query).toHaveBeenCalledTimes(2);
  });

  test('register: 重复邮箱抛异常', async () => {
    mockDb.query.mockResolvedValueOnce({
      rows: [{ id: 'existing-uuid' }]
    });

    await expect(
      authService.register({ username: 'alice', email: 'a@b.com', password: 'pass' })
    ).rejects.toThrow('用户名或邮箱已存在');
  });

  test('login: 错误密码抛异常', async () => {
    mockDb.query.mockResolvedValueOnce({
      rows: [{
        id: 'uuid-1',
        password_hash: await bcrypt.hash('correct-password', 12)
      }]
    });

    await expect(
      authService.login({ email: 'a@b.com', password: 'wrong-password' })
    ).rejects.toThrow('邮箱或密码错误');
  });
});

五、部署与运维

5.1 Docker Compose 本地开发

version: '3.8'

services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      DATABASE_URL: postgresql://appuser:secret@db:5432/appdb
      REDIS_URL: redis://redis:6379/0
      JWT_SECRET: ${JWT_SECRET}
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: appdb
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: secret
    volumes:
      - pgdata:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U appuser -d appdb"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes
    volumes:
      - redisdata:/data

volumes:
  pgdata:
  redisdata:

5.2 GitHub Actions CI/CD

name: CI/CD

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_DB: testdb
          POSTGRES_USER: test
          POSTGRES_PASSWORD: test
        options: >-
          --health-cmd pg_isready
          --health-interval 10s

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'

      - run: npm ci
      - run: npm test -- --coverage
      - uses: codecov/codecov-action@v3

  deploy:
    needs: test
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: docker build -t app .
      - run: docker push ghcr.io/${{ github.repository }}:latest

六、总结

  1. 先设计再写代码:数据库建模 + API 接口设计在前
  2. 测试驱动开发:每个功能有测试,提交前跑全量 suite
  3. 日志要完整:操作日志是排查问题的救命稻草
  4. 环境隔离:dev/staging/prod 配置要分开

💬 收藏本文!关注我,后续更新更多实战项目系列。


💬 觉得有用的话,点个赞+收藏,关注我,持续更新优质技术内容!

标签:jQuery | 最佳实践 | 代码质量 | 工程 | 实战

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐