const { query } = require('../config/database') const crypto = require('crypto') const { paginate } = require('../utils/pagination') const { DEFAULT_PASSWORD } = require('../config/constants') function md5(str) { return crypto.createHash('md5').update(str).digest('hex') } function generateToken() { return crypto.randomBytes(32).toString('hex') } // 用户登录(支持双重身份) async function login(ctx) { const { phone, password, loginType } = ctx.request.body if (!phone || !password) { ctx.body = { code: 400, message: '请输入手机号和密码' } return } const users = await query('SELECT * FROM users WHERE phone = ?', [phone]) if (users.length === 0) { ctx.body = { code: 401, message: '用户不存在' } return } const user = users[0] if (user.status === 0) { ctx.body = { code: 401, message: '账号已被禁用' } return } if (user.password !== md5(password)) { ctx.body = { code: 401, message: '密码错误' } return } // 店员登录需要验证角色 if (loginType === 'staff' && user.role !== 1) { ctx.body = { code: 401, message: '该账号不是店员账号' } return } const token = generateToken() await query('UPDATE users SET token = ? WHERE id = ?', [token, user.id]) const userInfo = { id: user.id, phone: user.phone, name: user.name, avatar: user.avatar, points: user.points, role: user.role, token } ctx.body = { code: 200, data: userInfo } } // 用户注册(普通用户) async function register(ctx) { const { phone, password, name } = ctx.request.body if (!phone || !password || !name) { ctx.body = { code: 400, message: '请填写完整信息' } return } const existing = await query('SELECT * FROM users WHERE phone = ?', [phone]) if (existing.length > 0) { ctx.body = { code: 400, message: '该手机号已注册' } return } const result = await query( 'INSERT INTO users (phone, password, name, avatar, points, role) VALUES (?, ?, ?, ?, ?, ?)', [phone, md5(password), name, '', 0, 0] ) ctx.body = { code: 200, message: '注册成功', data: { id: result.insertId, phone, name, avatar: '', points: 0, role: 0 } } } async function requireStaffAuth(ctx) { const authHeader = ctx.headers.authorization || '' const token = authHeader.replace('Bearer ', '') if (!token) { ctx.body = { code: 401, message: '未登录,请先登录店员账号' } return null } const operators = await query('SELECT * FROM users WHERE token = ? AND role = 1 AND status = 1', [token]) if (operators.length === 0) { ctx.body = { code: 401, message: '权限不足,仅店员可操作' } return null } return operators[0] } async function createUser(phone, name, role) { const existing = await query('SELECT * FROM users WHERE phone = ?', [phone]) if (existing.length > 0) return { conflict: true } const result = await query( 'INSERT INTO users (phone, password, name, avatar, points, role) VALUES (?, ?, ?, ?, ?, ?)', [phone, md5(DEFAULT_PASSWORD), name, '', 0, role] ) return { conflict: false, data: { id: result.insertId, phone, name, avatar: '', points: 0, role } } } // 店员注册(需要店员权限) async function registerStaff(ctx) { const { phone, name } = ctx.request.body if (!phone || !name) { ctx.body = { code: 400, message: '请填写手机号和姓名' } return } const operator = await requireStaffAuth(ctx) if (!operator) return const result = await createUser(phone, name, 1) if (result.conflict) { ctx.body = { code: 400, message: '该手机号已注册' } return } ctx.body = { code: 200, message: `店员注册成功,默认密码为${DEFAULT_PASSWORD}`, data: result.data } } // 店员帮助用户注册(需要店员权限) async function registerByStaff(ctx) { const { phone, name } = ctx.request.body if (!phone || !name) { ctx.body = { code: 400, message: '请填写手机号和姓名' } return } const operator = await requireStaffAuth(ctx) if (!operator) return const result = await createUser(phone, name, 0) if (result.conflict) { ctx.body = { code: 400, message: '该手机号已注册' } return } ctx.body = { code: 200, message: `用户注册成功,默认密码为${DEFAULT_PASSWORD}`, data: result.data } } // 获取用户信息 async function getUserInfo(ctx) { const userId = parseInt(ctx.query.id) if (!userId) { ctx.body = { code: 400, message: '缺少用户ID' } return } const users = await query('SELECT * FROM users WHERE id = ? AND status = 1', [userId]) if (users.length > 0) { const user = users[0] ctx.body = { code: 200, data: { id: user.id, phone: user.phone, name: user.name, avatar: user.avatar, points: user.points, role: user.role } } } else { ctx.body = { code: 404, message: '用户不存在' } } } // 获取用户列表 async function getUsers(ctx) { let sql = 'SELECT id, phone, name, points, role, status, created_at FROM users WHERE status = 1' const params = [] if (ctx.query.role !== undefined) { sql += ' AND role = ?' params.push(parseInt(ctx.query.role)) } if (ctx.query.keyword) { sql += ' AND (phone LIKE ? OR name LIKE ?)' params.push(`%${ctx.query.keyword}%`, `%${ctx.query.keyword}%`) } sql += ' ORDER BY created_at DESC' const result = await paginate(query, sql, params, ctx.query.page, ctx.query.pageSize) ctx.body = { code: 200, ...result } } // 更新用户信息 async function updateUser(ctx) { const userId = parseInt(ctx.params.id) const { name, avatar, points, status } = ctx.request.body const updateFields = [] const updateParams = [] if (name !== undefined) { updateFields.push('name = ?') updateParams.push(name) } if (avatar !== undefined) { updateFields.push('avatar = ?') updateParams.push(avatar) } if (points !== undefined) { updateFields.push('points = ?') updateParams.push(parseInt(points)) } if (status !== undefined) { updateFields.push('status = ?') updateParams.push(parseInt(status)) } if (updateFields.length === 0) { ctx.body = { code: 400, message: '没有需要更新的字段' } return } updateParams.push(userId) const result = await query(`UPDATE users SET ${updateFields.join(', ')} WHERE id = ?`, updateParams) if (result.affectedRows > 0) { ctx.body = { code: 200, message: '更新成功' } } else { ctx.body = { code: 404, message: '用户不存在' } } } // 删除用户 async function deleteUser(ctx) { const userId = parseInt(ctx.params.id) const result = await query('UPDATE users SET status = 0 WHERE id = ?', [userId]) if (result.affectedRows > 0) { ctx.body = { code: 200, message: '禁用成功' } } else { ctx.body = { code: 404, message: '用户不存在' } } } // 修改密码 async function changePassword(ctx) { const { phone, oldPassword, newPassword } = ctx.request.body if (!phone || !oldPassword || !newPassword) { ctx.body = { code: 400, message: '请填写完整信息' } return } const users = await query('SELECT * FROM users WHERE phone = ? AND status = 1', [phone]) if (users.length === 0) { ctx.body = { code: 404, message: '用户不存在' } return } const user = users[0] if (user.password !== md5(oldPassword)) { ctx.body = { code: 400, message: '原密码错误' } return } await query('UPDATE users SET password = ? WHERE id = ?', [md5(newPassword), user.id]) ctx.body = { code: 200, message: '密码修改成功' } } // 重置密码(店员操作) async function resetPassword(ctx) { const { userId } = ctx.request.body if (!userId) { ctx.body = { code: 400, message: '请指定用户ID' } return } const defaultPassword = DEFAULT_PASSWORD const result = await query('UPDATE users SET password = ? WHERE id = ?', [md5(defaultPassword), userId]) if (result.affectedRows > 0) { ctx.body = { code: 200, message: `密码已重置为${DEFAULT_PASSWORD}` } } else { ctx.body = { code: 404, message: '用户不存在' } } } // 调整积分 async function addPoints(ctx) { const { userId, points, description } = ctx.request.body if (!userId || points === undefined) { ctx.body = { code: 400, message: '请指定用户ID和积分变动值' } return } const pointsInt = parseInt(points) if (isNaN(pointsInt)) { ctx.body = { code: 400, message: '积分值无效' } return } // 检查用户是否存在 const users = await query('SELECT * FROM users WHERE id = ? AND status = 1', [userId]) if (users.length === 0) { ctx.body = { code: 404, message: '用户不存在' } return } const user = users[0] const newPoints = Math.max(0, user.points + pointsInt) // 更新用户积分 await query('UPDATE users SET points = ? WHERE id = ?', [newPoints, userId]) // 记录积分变动日志 await query( 'INSERT INTO points_logs (user_id, type, amount, description) VALUES (?, ?, ?, ?)', [userId, pointsInt >= 0 ? 'earn' : 'spend', Math.abs(pointsInt), description || '管理员调整积分'] ) ctx.body = { code: 200, message: '积分调整成功', data: { userId, oldPoints: user.points, newPoints, change: pointsInt } } } // 获取积分记录 async function getPointsLogs(ctx) { const userId = parseInt(ctx.query.userId) if (!userId) { ctx.body = { code: 400, message: '请指定用户ID' } return } const logs = await query( 'SELECT * FROM points_logs WHERE user_id = ? ORDER BY created_at DESC', [userId] ) ctx.body = { code: 200, data: logs } } module.exports = { login, register, registerStaff, registerByStaff, getUserInfo, getUsers, updateUser, deleteUser, changePassword, resetPassword, addPoints, getPointsLogs }