2025-12-18 19:07:21 +08:00
|
|
|
|
import { v4 as uuidv4 } from 'uuid';
|
|
|
|
|
|
import { query, run, get } from '../database';
|
|
|
|
|
|
|
|
|
|
|
|
export interface User {
|
|
|
|
|
|
id: string;
|
|
|
|
|
|
name: string;
|
|
|
|
|
|
phone: string;
|
|
|
|
|
|
password?: string;
|
|
|
|
|
|
createdAt: string;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export interface CreateUserData {
|
|
|
|
|
|
name: string;
|
|
|
|
|
|
phone: string;
|
|
|
|
|
|
password?: string;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export class UserModel {
|
|
|
|
|
|
static async create(data: CreateUserData): Promise<User> {
|
|
|
|
|
|
const id = uuidv4();
|
|
|
|
|
|
const sql = `
|
|
|
|
|
|
INSERT INTO users (id, name, phone, password)
|
|
|
|
|
|
VALUES (?, ?, ?, ?)
|
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
await run(sql, [id, data.name, data.phone, data.password || '']);
|
|
|
|
|
|
return this.findById(id) as Promise<User>;
|
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
|
if (error.code === 'SQLITE_CONSTRAINT_UNIQUE') {
|
|
|
|
|
|
throw new Error('手机号已存在');
|
|
|
|
|
|
}
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static async updatePasswordById(id: string, password: string): Promise<void> {
|
|
|
|
|
|
const sql = `UPDATE users SET password = ? WHERE id = ?`;
|
|
|
|
|
|
await run(sql, [password, id]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static async delete(id: string): Promise<void> {
|
|
|
|
|
|
await run(`DELETE FROM users WHERE id = ?`, [id]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-19 00:58:58 +08:00
|
|
|
|
// 更新用户信息
|
|
|
|
|
|
static async update(id: string, data: Partial<CreateUserData>): Promise<User> {
|
|
|
|
|
|
// 验证数据
|
|
|
|
|
|
const errors = this.validateUserData(data as CreateUserData);
|
|
|
|
|
|
if (errors.length > 0) {
|
|
|
|
|
|
throw new Error(errors.join(', '));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查手机号唯一性
|
|
|
|
|
|
if (data.phone !== undefined) {
|
|
|
|
|
|
const existingUser = await this.findByPhone(data.phone);
|
|
|
|
|
|
if (existingUser && existingUser.id !== id) {
|
|
|
|
|
|
throw new Error('手机号已存在');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 构建更新字段
|
|
|
|
|
|
const fields: string[] = [];
|
|
|
|
|
|
const values: any[] = [];
|
|
|
|
|
|
|
|
|
|
|
|
if (data.name !== undefined) {
|
|
|
|
|
|
fields.push('name = ?');
|
|
|
|
|
|
values.push(data.name);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (data.phone !== undefined) {
|
|
|
|
|
|
fields.push('phone = ?');
|
|
|
|
|
|
values.push(data.phone);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (data.password !== undefined) {
|
|
|
|
|
|
fields.push('password = ?');
|
|
|
|
|
|
values.push(data.password);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (fields.length === 0) {
|
|
|
|
|
|
return this.findById(id) as Promise<User>;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
values.push(id);
|
|
|
|
|
|
const sql = `UPDATE users SET ${fields.join(', ')} WHERE id = ?`;
|
|
|
|
|
|
|
|
|
|
|
|
await run(sql, values);
|
|
|
|
|
|
const updatedUser = await this.findById(id);
|
|
|
|
|
|
|
|
|
|
|
|
if (!updatedUser) {
|
|
|
|
|
|
throw new Error('用户不存在');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return updatedUser;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-18 19:07:21 +08:00
|
|
|
|
static async findById(id: string): Promise<User | null> {
|
|
|
|
|
|
const sql = `SELECT id, name, phone, password, created_at as createdAt FROM users WHERE id = ?`;
|
|
|
|
|
|
const user = await get(sql, [id]);
|
|
|
|
|
|
return user || null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static async findByPhone(phone: string): Promise<User | null> {
|
|
|
|
|
|
const sql = `SELECT id, name, phone, password, created_at as createdAt FROM users WHERE phone = ?`;
|
|
|
|
|
|
const user = await get(sql, [phone]);
|
|
|
|
|
|
return user || null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-19 00:58:58 +08:00
|
|
|
|
static async findByName(name: string): Promise<User[]> {
|
|
|
|
|
|
const sql = `SELECT id, name, phone, password, created_at as createdAt FROM users WHERE name = ?`;
|
|
|
|
|
|
const users = await query(sql, [name]);
|
|
|
|
|
|
return users || [];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-18 19:07:21 +08:00
|
|
|
|
static async findAll(limit = 10, offset = 0): Promise<{ users: User[]; total: number }> {
|
|
|
|
|
|
const usersSql = `
|
|
|
|
|
|
SELECT id, name, phone, password, created_at as createdAt
|
|
|
|
|
|
FROM users
|
|
|
|
|
|
ORDER BY created_at DESC
|
|
|
|
|
|
LIMIT ? OFFSET ?
|
|
|
|
|
|
`;
|
|
|
|
|
|
const countSql = `SELECT COUNT(*) as total FROM users`;
|
|
|
|
|
|
|
|
|
|
|
|
const [users, countResult] = await Promise.all([
|
|
|
|
|
|
query(usersSql, [limit, offset]),
|
|
|
|
|
|
get(countSql)
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
users,
|
|
|
|
|
|
total: countResult.total
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-19 00:58:58 +08:00
|
|
|
|
// 验证用户数据,支持部分数据验证
|
|
|
|
|
|
static validateUserData(data: Partial<CreateUserData>): string[] {
|
2025-12-18 19:07:21 +08:00
|
|
|
|
const errors: string[] = [];
|
|
|
|
|
|
|
2025-12-19 00:58:58 +08:00
|
|
|
|
// 只验证提供了的数据
|
|
|
|
|
|
if (data.name !== undefined) {
|
|
|
|
|
|
if (data.name.length < 2 || data.name.length > 20) {
|
|
|
|
|
|
errors.push('姓名长度必须在2-20个字符之间');
|
|
|
|
|
|
}
|
2025-12-18 19:07:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-19 00:58:58 +08:00
|
|
|
|
if (data.phone !== undefined) {
|
|
|
|
|
|
if (!/^1[3-9]\d{9}$/.test(data.phone)) {
|
|
|
|
|
|
errors.push('手机号格式不正确,请输入11位中国手机号');
|
|
|
|
|
|
}
|
2025-12-18 19:07:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return errors;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|