appointment_system/backend/src/services/adminUserService.js

312 lines
7.0 KiB
JavaScript

const User = require('../models/User');
const Appointment = require('../models/Appointment');
const Invitation = require('../models/Invitation');
const Withdrawal = require('../models/Withdrawal');
const LoginHistory = require('../models/LoginHistory');
const { Op } = require('sequelize');
const { Parser } = require('json2csv');
/**
* Admin User Service
* Handles user management operations for admin panel
*/
/**
* Get paginated user list with search and filters
* @param {Object} options - Query options
* @returns {Promise<Object>} User list with pagination
*/
const getUserList = async (options = {}) => {
const {
page = 1,
limit = 20,
search = '',
status = '',
language = '',
sortBy = 'createdAt',
sortOrder = 'DESC',
} = options;
const offset = (page - 1) * limit;
// Build where clause
const where = {};
// Search by nickname, real name, phone, or wechat ID
if (search) {
where[Op.or] = [
{ nickname: { [Op.like]: `%${search}%` } },
{ realName: { [Op.like]: `%${search}%` } },
{ phone: { [Op.like]: `%${search}%` } },
{ wechatId: { [Op.like]: `%${search}%` } },
];
}
// Filter by status
if (status) {
where.status = status;
}
// Filter by language
if (language) {
where.language = language;
}
// Query users
const { count, rows: users } = await User.findAndCountAll({
where,
limit: parseInt(limit),
offset,
order: [[sortBy, sortOrder]],
attributes: [
'id',
'uid',
'nickname',
'avatar',
'realName',
'phone',
'whatsapp',
'wechatId',
'language',
'balance',
'invitationCode',
'status',
'createdAt',
'updatedAt',
],
});
return {
users,
pagination: {
total: count,
page: parseInt(page),
limit: parseInt(limit),
totalPages: Math.ceil(count / limit),
},
};
};
/**
* Get detailed user information
* @param {String} userId - User ID
* @returns {Promise<Object>} User details with related data
*/
const getUserDetails = async (userId) => {
// Get user
const user = await User.findByPk(userId, {
attributes: [
'id',
'uid',
'wechatOpenId',
'nickname',
'avatar',
'realName',
'phone',
'whatsapp',
'wechatId',
'language',
'balance',
'invitationCode',
'invitedBy',
'status',
'createdAt',
'updatedAt',
],
});
if (!user) {
throw new Error('User not found');
}
// Get inviter information if exists
let inviter = null;
if (user.invitedBy) {
inviter = await User.findByPk(user.invitedBy, {
attributes: ['id', 'nickname', 'realName'],
});
}
// Get appointment statistics
const appointmentStats = await Appointment.findAll({
where: { userId },
attributes: [
'status',
[require('sequelize').fn('COUNT', require('sequelize').col('id')), 'count'],
],
group: ['status'],
raw: true,
});
const appointmentCounts = {
total: 0,
pending: 0,
confirmed: 0,
'in-progress': 0,
completed: 0,
cancelled: 0,
};
appointmentStats.forEach((stat) => {
appointmentCounts[stat.status] = parseInt(stat.count);
appointmentCounts.total += parseInt(stat.count);
});
// Get invitation statistics
const invitationStats = await Invitation.findAll({
where: { inviterId: userId },
attributes: [
[require('sequelize').fn('COUNT', require('sequelize').col('id')), 'totalInvites'],
[require('sequelize').fn('SUM', require('sequelize').col('reward_amount')), 'totalRewards'],
],
raw: true,
});
const invitationCounts = {
totalInvites: parseInt(invitationStats[0]?.totalInvites || 0),
totalRewards: parseFloat(invitationStats[0]?.totalRewards || 0),
};
// Get withdrawal statistics
const withdrawalStats = await Withdrawal.findAll({
where: { userId },
attributes: [
'status',
[require('sequelize').fn('COUNT', require('sequelize').col('id')), 'count'],
[require('sequelize').fn('SUM', require('sequelize').col('amount')), 'total'],
],
group: ['status'],
raw: true,
});
const withdrawalCounts = {
total: 0,
totalAmount: 0,
waiting: 0,
processing: 0,
completed: 0,
rejected: 0,
};
withdrawalStats.forEach((stat) => {
withdrawalCounts[stat.status] = parseInt(stat.count);
withdrawalCounts.total += parseInt(stat.count);
withdrawalCounts.totalAmount += parseFloat(stat.total || 0);
});
// Get recent login history
const loginHistory = await LoginHistory.findAll({
where: { userId, userType: 'user' },
limit: 10,
order: [['loginAt', 'DESC']],
attributes: ['id', 'ipAddress', 'userAgent', 'deviceInfo', 'loginAt'],
});
return {
user: user.toJSON(),
inviter,
appointments: appointmentCounts,
invitations: invitationCounts,
withdrawals: withdrawalCounts,
loginHistory,
};
};
/**
* Update user status
* @param {String} userId - User ID
* @param {String} status - New status (active/suspended)
* @returns {Promise<Object>} Updated user
*/
const updateUserStatus = async (userId, status) => {
const user = await User.findByPk(userId);
if (!user) {
throw new Error('User not found');
}
if (!['active', 'suspended'].includes(status)) {
throw new Error('Invalid status value');
}
user.status = status;
await user.save();
return user;
};
/**
* Export user data to CSV
* @param {Object} options - Query options (same as getUserList)
* @returns {Promise<String>} CSV string
*/
const exportUsersToCSV = async (options = {}) => {
// Get all users matching criteria (no pagination)
const { search = '', status = '', language = '' } = options;
const where = {};
if (search) {
where[Op.or] = [
{ nickname: { [Op.like]: `%${search}%` } },
{ realName: { [Op.like]: `%${search}%` } },
{ phone: { [Op.like]: `%${search}%` } },
{ wechatId: { [Op.like]: `%${search}%` } },
];
}
if (status) {
where.status = status;
}
if (language) {
where.language = language;
}
const users = await User.findAll({
where,
order: [['createdAt', 'DESC']],
attributes: [
'id',
'nickname',
'realName',
'phone',
'whatsapp',
'wechatId',
'language',
'balance',
'invitationCode',
'status',
'createdAt',
],
});
// Convert to plain objects
const userData = users.map((user) => ({
ID: user.id,
Nickname: user.nickname || '',
'Real Name': user.realName || '',
Phone: user.phone || '',
WhatsApp: user.whatsapp || '',
'WeChat ID': user.wechatId || '',
Language: user.language,
Balance: user.balance,
'Invitation Code': user.invitationCode || '',
Status: user.status,
'Created At': user.createdAt.toISOString(),
}));
// Convert to CSV
const json2csvParser = new Parser();
const csv = json2csvParser.parse(userData);
return csv;
};
module.exports = {
getUserList,
getUserDetails,
updateUserStatus,
exportUsersToCSV,
};