This commit is contained in:
Ken
2026-01-19 09:33:35 +07:00
parent 374dc12b2d
commit 70838a4bc1
103 changed files with 16929 additions and 2 deletions

View File

@@ -0,0 +1,267 @@
/**
* Script: Dump và Sync All Teachers
* Mục đích: Kiểm tra và tạo user_profile cho TẤT CẢ teachers trong hệ thống
*
* Usage:
* node src/scripts/dump-and-sync-teachers.js
* node src/scripts/dump-and-sync-teachers.js --auto-fix
*/
require('dotenv').config();
const { sequelize, TeacherDetail, UserProfile, UsersAuth } = require('../models');
const teacherProfileService = require('../services/teacherProfileService');
async function dumpAllTeachers() {
console.log('📊 Dumping All Teachers Information...\n');
console.log('='.repeat(80));
try {
// Get all teachers (without join to avoid association issues)
const teachers = await TeacherDetail.findAll({
order: [['created_at', 'ASC']],
});
console.log(`\n📈 Total Teachers: ${teachers.length}\n`);
const withProfile = [];
const withoutProfile = [];
const invalidUserId = [];
for (const teacher of teachers) {
const teacherInfo = {
id: teacher.id,
user_id: teacher.user_id,
teacher_code: teacher.teacher_code,
teacher_type: teacher.teacher_type,
status: teacher.status,
created_at: teacher.created_at,
};
// Check if user_id exists in users_auth
const userAuth = await UsersAuth.findByPk(teacher.user_id, {
attributes: ['id', 'username', 'email', 'is_active'],
});
if (!userAuth) {
invalidUserId.push({
...teacherInfo,
issue: 'user_id not found in users_auth',
});
continue;
}
teacherInfo.username = userAuth.username;
teacherInfo.email = userAuth.email;
teacherInfo.is_active = userAuth.is_active;
// Check if profile exists
const userProfile = await UserProfile.findOne({
where: { user_id: teacher.user_id },
});
if (userProfile) {
teacherInfo.profile = {
id: userProfile.id,
full_name: userProfile.full_name,
phone: userProfile.phone,
needs_update: userProfile.etc?.needs_profile_update,
};
withProfile.push(teacherInfo);
} else {
withoutProfile.push(teacherInfo);
}
}
// Display results
console.log('✅ Teachers WITH Profile:', withProfile.length);
if (withProfile.length > 0) {
console.log('\nSample (first 5):');
withProfile.slice(0, 5).forEach((t, i) => {
console.log(` ${i + 1}. ${t.teacher_code} (${t.teacher_type}) - ${t.username}`);
console.log(` Profile: ${t.profile.full_name} | Phone: ${t.profile.phone || 'N/A'}`);
});
if (withProfile.length > 5) {
console.log(` ... and ${withProfile.length - 5} more\n`);
}
}
console.log('\n❌ Teachers WITHOUT Profile:', withoutProfile.length);
if (withoutProfile.length > 0) {
console.log('\nList:');
withoutProfile.forEach((t, i) => {
console.log(` ${i + 1}. ${t.teacher_code} (${t.teacher_type}) - ${t.username} - user_id: ${t.user_id}`);
});
}
console.log('\n⚠ Teachers with Invalid user_id:', invalidUserId.length);
if (invalidUserId.length > 0) {
console.log('\nList:');
invalidUserId.forEach((t, i) => {
console.log(` ${i + 1}. ${t.teacher_code} - ${t.issue} - user_id: ${t.user_id}`);
});
}
console.log('\n' + '='.repeat(80));
return {
total: teachers.length,
withProfile: withProfile.length,
withoutProfile: withoutProfile.length,
invalidUserId: invalidUserId.length,
teachers: {
withProfile,
withoutProfile,
invalidUserId,
}
};
} catch (error) {
console.error('❌ Dump failed:', error.message);
throw error;
}
}
async function autoFixProfiles(teachersData) {
console.log('\n🔧 Starting Auto-Fix for Teachers without Profile...\n');
const { withoutProfile } = teachersData.teachers;
if (withoutProfile.length === 0) {
console.log('✅ No teachers need fixing. All have profiles!\n');
return { success: 0, failed: 0 };
}
console.log(`📋 Processing ${withoutProfile.length} teachers...\n`);
const results = {
success: [],
failed: [],
};
for (const teacher of withoutProfile) {
try {
console.log(`Processing: ${teacher.teacher_code} (${teacher.username})...`);
// Create profile using service
const userProfile = await UserProfile.create({
user_id: teacher.user_id,
full_name: teacherProfileService._generateFullNameFromCode(teacher.teacher_code),
first_name: '',
last_name: '',
phone: '',
date_of_birth: null,
gender: null,
address: '',
school_id: null,
city: '',
district: '',
avatar_url: null,
etc: {
teacher_code: teacher.teacher_code,
teacher_type: teacher.teacher_type,
username: teacher.username,
synced_from: 'dump-and-sync-script',
needs_profile_update: true,
synced_at: new Date().toISOString(),
}
});
results.success.push({
teacher_code: teacher.teacher_code,
username: teacher.username,
user_id: teacher.user_id,
profile_id: userProfile.id,
full_name: userProfile.full_name,
});
console.log(` ✅ Created profile: ${userProfile.full_name}\n`);
} catch (error) {
results.failed.push({
teacher_code: teacher.teacher_code,
username: teacher.username,
error: error.message,
});
console.log(` ❌ Failed: ${error.message}\n`);
}
}
// Summary
console.log('\n' + '='.repeat(80));
console.log('📊 AUTO-FIX SUMMARY');
console.log('='.repeat(80));
console.log(`✅ Success: ${results.success.length}`);
console.log(`❌ Failed: ${results.failed.length}`);
if (results.success.length > 0) {
console.log('\n✅ Successfully Created Profiles:');
results.success.forEach((item, i) => {
console.log(` ${i + 1}. ${item.teacher_code}${item.full_name}`);
console.log(` user_id: ${item.user_id} | profile_id: ${item.profile_id}`);
});
}
if (results.failed.length > 0) {
console.log('\n❌ Failed:');
results.failed.forEach((item, i) => {
console.log(` ${i + 1}. ${item.teacher_code} - ${item.error}`);
});
}
console.log('\n' + '='.repeat(80));
return {
success: results.success.length,
failed: results.failed.length,
details: results,
};
}
async function main() {
console.log('\n🚀 Teacher Profile Dump & Sync Tool\n');
try {
// Parse arguments
const args = process.argv.slice(2);
const autoFix = args.includes('--auto-fix');
// Connect to database
await sequelize.authenticate();
console.log('✅ Database connected\n');
// Step 1: Dump all teachers
const dumpResults = await dumpAllTeachers();
// Step 2: Ask for auto-fix if not specified
if (!autoFix && dumpResults.withoutProfile > 0) {
console.log('\n⚠ Found teachers without profile!');
console.log('💡 Run with --auto-fix to create profiles automatically:');
console.log(' node src/scripts/dump-and-sync-teachers.js --auto-fix\n');
process.exit(0);
}
// Step 3: Auto-fix if requested
if (autoFix && dumpResults.withoutProfile > 0) {
const fixResults = await autoFixProfiles(dumpResults);
console.log('\n🎉 COMPLETED!');
console.log(` Total: ${dumpResults.total} teachers`);
console.log(` Fixed: ${fixResults.success} profiles`);
console.log(` Already had: ${dumpResults.withProfile} profiles`);
console.log(` Failed: ${fixResults.failed} profiles\n`);
} else if (autoFix) {
console.log('\n✅ All teachers already have profiles! Nothing to fix.\n');
}
process.exit(0);
} catch (error) {
console.error('\n❌ Error:', error.message);
console.error(error.stack);
process.exit(1);
}
}
// Run
main();