forked from wangziqi/gongxue-base
fix student import and profile labels
This commit is contained in:
@@ -35,6 +35,122 @@ interface AuthenticatedRequest {
|
||||
user: AuthenticatedUser;
|
||||
}
|
||||
|
||||
|
||||
interface StudentImportRow {
|
||||
name: string;
|
||||
studentNo?: string;
|
||||
phone?: string;
|
||||
idNumber?: string;
|
||||
gender?: string;
|
||||
ethnicity?: string;
|
||||
emergencyContact?: string;
|
||||
emergencyPhone?: string;
|
||||
organization?: string;
|
||||
supervisor?: string;
|
||||
organizationId?: number;
|
||||
}
|
||||
|
||||
const STUDENT_IMPORT_COLUMNS = [
|
||||
{ header: '姓名', key: 'name', width: 15 },
|
||||
{ header: '学号', key: 'studentNo', width: 15 },
|
||||
{ header: '性别', key: 'gender', width: 8 },
|
||||
{ header: '电话', key: 'phone', width: 18 },
|
||||
{ header: '身份证号', key: 'idNumber', width: 22 },
|
||||
{ header: '民族', key: 'ethnicity', width: 10 },
|
||||
{ header: '紧急联系人', key: 'emergencyContact', width: 15 },
|
||||
{ header: '紧急联系人电话', key: 'emergencyPhone', width: 18 },
|
||||
{ header: '所属机构名称', key: 'organization', width: 18 },
|
||||
{ header: '负责人/班主任', key: 'supervisor', width: 15 },
|
||||
];
|
||||
|
||||
const STUDENT_EXPORT_COLUMNS = [
|
||||
...STUDENT_IMPORT_COLUMNS.map((column) => ({
|
||||
...column,
|
||||
header: column.key === 'organization' ? '所属机构' : column.header,
|
||||
})),
|
||||
{ header: '状态', key: 'status', width: 10 },
|
||||
];
|
||||
|
||||
const STUDENT_IMPORT_HEADER_MAP: Record<string, keyof StudentImportRow> = {
|
||||
姓名: 'name',
|
||||
学号: 'studentNo',
|
||||
电话: 'phone',
|
||||
手机号: 'phone',
|
||||
'学号/身份证': 'idNumber',
|
||||
身份证: 'idNumber',
|
||||
身份证号: 'idNumber',
|
||||
性别: 'gender',
|
||||
民族: 'ethnicity',
|
||||
紧急联系人: 'emergencyContact',
|
||||
紧急联系人电话: 'emergencyPhone',
|
||||
所属机构: 'organization',
|
||||
所属机构名称: 'organization',
|
||||
负责人: 'supervisor',
|
||||
'负责人/班主任': 'supervisor',
|
||||
};
|
||||
|
||||
function getExcelCellText(cell: ExcelJS.Cell): string {
|
||||
const value = cell.value;
|
||||
if (value === null || value === undefined) return '';
|
||||
if (typeof value === 'object') {
|
||||
if ('text' in value) return String(value.text || '');
|
||||
if ('richText' in value && Array.isArray(value.richText)) {
|
||||
return value.richText.map((part) => part.text).join('');
|
||||
}
|
||||
if ('result' in value) return String(value.result || '');
|
||||
}
|
||||
return String(value);
|
||||
}
|
||||
|
||||
function parseStudentImportRows(ws: ExcelJS.Worksheet): StudentImportRow[] {
|
||||
const headerIndex = new Map<number, keyof StudentImportRow>();
|
||||
ws.getRow(1).eachCell((cell, colNumber) => {
|
||||
const header = getExcelCellText(cell).trim();
|
||||
const field = STUDENT_IMPORT_HEADER_MAP[header];
|
||||
if (field) headerIndex.set(colNumber, field);
|
||||
});
|
||||
|
||||
const rows: StudentImportRow[] = [];
|
||||
ws.eachRow((row, idx) => {
|
||||
if (idx === 1) return;
|
||||
|
||||
const parsed: Partial<StudentImportRow> = {};
|
||||
if (headerIndex.size > 0) {
|
||||
headerIndex.forEach((field, colNumber) => {
|
||||
const value = getExcelCellText(row.getCell(colNumber)).trim();
|
||||
if (value) {
|
||||
Object.assign(parsed, { [field]: value });
|
||||
}
|
||||
});
|
||||
} else {
|
||||
parsed.name = getExcelCellText(row.getCell(1)).trim();
|
||||
parsed.studentNo = getExcelCellText(row.getCell(2)).trim() || undefined;
|
||||
parsed.gender = getExcelCellText(row.getCell(3)).trim() || undefined;
|
||||
parsed.phone = getExcelCellText(row.getCell(4)).trim() || undefined;
|
||||
parsed.idNumber = getExcelCellText(row.getCell(5)).trim() || undefined;
|
||||
parsed.ethnicity = getExcelCellText(row.getCell(6)).trim() || undefined;
|
||||
parsed.emergencyContact = getExcelCellText(row.getCell(7)).trim() || undefined;
|
||||
parsed.emergencyPhone = getExcelCellText(row.getCell(8)).trim() || undefined;
|
||||
parsed.organization = getExcelCellText(row.getCell(9)).trim() || undefined;
|
||||
parsed.supervisor = getExcelCellText(row.getCell(10)).trim() || undefined;
|
||||
}
|
||||
|
||||
rows.push({
|
||||
name: parsed.name || '',
|
||||
studentNo: parsed.studentNo,
|
||||
phone: parsed.phone,
|
||||
idNumber: parsed.idNumber,
|
||||
gender: parsed.gender,
|
||||
ethnicity: parsed.ethnicity,
|
||||
emergencyContact: parsed.emergencyContact,
|
||||
emergencyPhone: parsed.emergencyPhone,
|
||||
organization: parsed.organization,
|
||||
supervisor: parsed.supervisor,
|
||||
});
|
||||
});
|
||||
return rows;
|
||||
}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Controller('students')
|
||||
export class StudentsController {
|
||||
@@ -92,18 +208,7 @@ export class StudentsController {
|
||||
);
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
const ws = workbook.addWorksheet('学生名单');
|
||||
ws.columns = [
|
||||
{ header: '姓名', key: 'name', width: 12 },
|
||||
{ header: '性别', key: 'gender', width: 8 },
|
||||
{ header: '电话', key: 'phone', width: 18 },
|
||||
{ header: '学号/身份证', key: 'idNumber', width: 22 },
|
||||
{ header: '民族', key: 'ethnicity', width: 10 },
|
||||
{ header: '紧急联系人', key: 'emergencyContact', width: 15 },
|
||||
{ header: '紧急联系人电话', key: 'emergencyPhone', width: 18 },
|
||||
{ header: '所属机构', key: 'organization', width: 18 },
|
||||
{ header: '负责人/班主任', key: 'supervisor', width: 15 },
|
||||
{ header: '状态', key: 'status', width: 10 },
|
||||
];
|
||||
ws.columns = STUDENT_EXPORT_COLUMNS;
|
||||
ws.getRow(1).font = { bold: true };
|
||||
ws.getRow(1).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFE0E0E0' } };
|
||||
const statusMap: Record<string, string> = {
|
||||
@@ -115,6 +220,7 @@ export class StudentsController {
|
||||
for (const s of students) {
|
||||
ws.addRow({
|
||||
name: s.name,
|
||||
studentNo: s.studentNo || '',
|
||||
gender: s.gender || '',
|
||||
phone: s.phone || '',
|
||||
idNumber: s.idNumber || '',
|
||||
@@ -150,24 +256,15 @@ export class StudentsController {
|
||||
async downloadTemplate(@Res() res: Response) {
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
const ws = workbook.addWorksheet('学生导入模板');
|
||||
ws.columns = [
|
||||
{ header: '姓名', key: 'name', width: 15 },
|
||||
{ header: '电话', key: 'phone', width: 18 },
|
||||
{ header: '学号/身份证', key: 'idNumber', width: 22 },
|
||||
{ header: '性别', key: 'gender', width: 8 },
|
||||
{ header: '民族', key: 'ethnicity', width: 10 },
|
||||
{ header: '紧急联系人', key: 'emergencyContact', width: 15 },
|
||||
{ header: '紧急联系人电话', key: 'emergencyPhone', width: 18 },
|
||||
{ header: '所属机构名称', key: 'organization', width: 18 },
|
||||
{ header: '负责人/班主任', key: 'supervisor', width: 15 },
|
||||
];
|
||||
ws.columns = STUDENT_IMPORT_COLUMNS;
|
||||
ws.getRow(1).font = { bold: true };
|
||||
ws.getRow(1).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFE0E0E0' } };
|
||||
ws.addRow({
|
||||
name: '张三',
|
||||
phone: '13800138000',
|
||||
idNumber: '2024001',
|
||||
studentNo: '2024001',
|
||||
gender: '男',
|
||||
phone: '13800138000',
|
||||
idNumber: '11010120060101001X',
|
||||
ethnicity: '汉族',
|
||||
emergencyContact: '张父',
|
||||
emergencyPhone: '13900000000',
|
||||
@@ -288,32 +385,7 @@ export class StudentsController {
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
await workbook.xlsx.load(file.buffer as any);
|
||||
const ws = workbook.worksheets[0];
|
||||
const rows: {
|
||||
name: string;
|
||||
phone?: string;
|
||||
idNumber?: string;
|
||||
gender?: string;
|
||||
ethnicity?: string;
|
||||
emergencyContact?: string;
|
||||
emergencyPhone?: string;
|
||||
organization?: string;
|
||||
supervisor?: string;
|
||||
organizationId?: number;
|
||||
}[] = [];
|
||||
ws.eachRow((row, idx) => {
|
||||
if (idx === 1) return;
|
||||
rows.push({
|
||||
name: String(row.getCell(1).value || ''),
|
||||
phone: String(row.getCell(2).value || ''),
|
||||
idNumber: String(row.getCell(3).value || ''),
|
||||
gender: String(row.getCell(4).value || '').trim() || undefined,
|
||||
ethnicity: String(row.getCell(5).value || '').trim() || undefined,
|
||||
emergencyContact: String(row.getCell(6).value || '').trim() || undefined,
|
||||
emergencyPhone: String(row.getCell(7).value || '').trim() || undefined,
|
||||
organization: String(row.getCell(8).value || '').trim() || undefined,
|
||||
supervisor: String(row.getCell(9).value || '').trim() || undefined,
|
||||
});
|
||||
});
|
||||
const rows = parseStudentImportRows(ws);
|
||||
// Resolve organization names to IDs
|
||||
for (const row of rows) {
|
||||
if (row.organization) {
|
||||
@@ -346,32 +418,7 @@ export class StudentsController {
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
await workbook.xlsx.load(file.buffer as unknown as ArrayBuffer);
|
||||
const ws = workbook.worksheets[0];
|
||||
const rows: {
|
||||
name: string;
|
||||
phone?: string;
|
||||
idNumber?: string;
|
||||
gender?: string;
|
||||
ethnicity?: string;
|
||||
emergencyContact?: string;
|
||||
emergencyPhone?: string;
|
||||
organization?: string;
|
||||
supervisor?: string;
|
||||
organizationId?: number;
|
||||
}[] = [];
|
||||
ws.eachRow((row, idx) => {
|
||||
if (idx === 1) return;
|
||||
rows.push({
|
||||
name: String(row.getCell(1).value || ''),
|
||||
phone: String(row.getCell(2).value || ''),
|
||||
idNumber: String(row.getCell(3).value || ''),
|
||||
gender: String(row.getCell(4).value || '').trim() || undefined,
|
||||
ethnicity: String(row.getCell(5).value || '').trim() || undefined,
|
||||
emergencyContact: String(row.getCell(6).value || '').trim() || undefined,
|
||||
emergencyPhone: String(row.getCell(7).value || '').trim() || undefined,
|
||||
organization: String(row.getCell(8).value || '').trim() || undefined,
|
||||
supervisor: String(row.getCell(9).value || '').trim() || undefined,
|
||||
});
|
||||
});
|
||||
const rows = parseStudentImportRows(ws);
|
||||
// Resolve organization names to IDs
|
||||
for (const row of rows) {
|
||||
if (row.organization) {
|
||||
@@ -386,7 +433,7 @@ export class StudentsController {
|
||||
userId: req.user?.id,
|
||||
username: req.user?.username,
|
||||
module: '学生管理',
|
||||
action: '匹配导入学生',
|
||||
action: '更新已有学生资料',
|
||||
detail: result.message,
|
||||
ipAddress,
|
||||
userAgent,
|
||||
|
||||
Reference in New Issue
Block a user