jQuery 最佳实践:10个让代码质量提升10倍的工程技巧
本文介绍10个提升jQuery代码质量的工程实践技巧,涵盖用户认证系统开发全流程。主要内容包括:需求分析(支持JWT认证、RBAC权限控制)、技术选型(Node.js+PostgreSQL+Redis)、数据库设计(用户表、角色表、操作日志表)、核心功能实现(注册登录、权限控制中间件)、单元测试(Jest框架)以及Docker部署方案。文章强调测试驱动开发、日志完整性和环境隔离的重要性,提供可直接
·
一、前言
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
六、总结
- 先设计再写代码:数据库建模 + API 接口设计在前
- 测试驱动开发:每个功能有测试,提交前跑全量 suite
- 日志要完整:操作日志是排查问题的救命稻草
- 环境隔离:dev/staging/prod 配置要分开
💬 收藏本文!关注我,后续更新更多实战项目系列。
💬 觉得有用的话,点个赞+收藏,关注我,持续更新优质技术内容!
标签:jQuery | 最佳实践 | 代码质量 | 工程 | 实战
更多推荐
所有评论(0)