puning-real-estate/scripts/generateXlsx.js
2026-01-22 11:30:00 +08:00

163 lines
5.6 KiB
JavaScript

import ExcelJS from 'exceljs';
import fs from 'fs/promises';
import path from 'path';
/**
* Flattens the nested JSON data into separate arrays for projects, licenses, and buildings.
* @param {Array<Object>} data - The nested data from merged_data.json.
* @returns {{projects: Array<Object>, licenses: Array<Object>, buildings: Array<Object>}}
*/
function flattenData(data) {
const projects = [];
const licenses = [];
const buildings = [];
data.forEach(project => {
// Create a shallow copy to avoid modifying the original object in memory
const projectCopy = { ...project };
const licensesData = projectCopy['预售许可证'];
// The nested array will not be included in the flattened project data
delete projectCopy['预售许可证'];
projects.push(projectCopy);
if (licensesData && Array.isArray(licensesData)) {
licensesData.forEach(license => {
const licenseCopy = { ...license };
const buildingsData = licenseCopy['楼幢'];
// The nested array will not be included in the flattened license data
delete licenseCopy['楼幢'];
// Add a key to link back to the parent project
licenseCopy['项目名称_key'] = project['项目名称'];
licenses.push(licenseCopy);
if (buildingsData && Array.isArray(buildingsData)) {
buildingsData.forEach(building => {
// Add keys to link back to the parent license and project
const buildingCopy = {
...building,
'许可证号_key': license['许可证号'],
'项目名称_key': project['项目名称'],
};
buildings.push(buildingCopy);
});
}
});
}
});
return { projects, licenses, buildings };
}
/**
* Adds a worksheet to the workbook with the given data, headers, and styling.
* @param {ExcelJS.Workbook} workbook - The workbook instance.
* @param {string} sheetName - The name for the new worksheet.
* @param {Array<Object>} data - The array of data for the sheet.
*/
function addSheet(workbook, sheetName, data) {
if (data.length === 0) {
console.log(`- 注意: 没有数据可用于工作表 '${sheetName}'`);
return;
}
const worksheet = workbook.addWorksheet(sheetName);
// Get all unique keys from all objects to form a complete header
const allKeys = data.reduce((keys, item) => {
if (item) {
Object.keys(item).forEach(key => {
if (!keys.includes(key)) {
keys.push(key);
}
});
}
return keys;
}, []);
worksheet.columns = allKeys.map(key => ({
header: key,
key: key,
width: key.includes('地址') || key.includes('链接') || key.includes('简介') ? 50 : 25
}));
worksheet.addRows(data);
// Style header row
worksheet.getRow(1).eachCell(cell => {
cell.font = { bold: true };
cell.fill = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'FFD3D3D3' }
};
cell.alignment = { vertical: 'middle', horizontal: 'center', wrapText: true };
});
// Add autofilter to the header row
worksheet.autoFilter = {
from: 'A1',
to: {
row: 1,
column: allKeys.length
}
};
}
/**
* Generates a multi-sheet XLSX file from the nested real estate data.
* @param {Array<Object>} data - The nested data from merged_data.json.
* @param {string} filePath - The path to save the XLSX file.
*/
async function generateXlsx(data, filePath) {
if (!data || data.length === 0) {
console.log(`没有数据可生成 XLSX 文件 (${filePath})。`);
return;
}
console.log('正在将数据处理成多个工作表...');
const { projects, licenses, buildings } = flattenData(data);
console.log(`- 项目: ${projects.length}`);
console.log(`- 许可证: ${licenses.length}`);
console.log(`- 楼幢: ${buildings.length}`);
const workbook = new ExcelJS.Workbook();
workbook.creator = 'Gemini Assistant';
workbook.created = new Date();
workbook.modified = new Date();
console.log('正在创建 Excel 工作表...');
addSheet(workbook, '项目', projects);
addSheet(workbook, '预售许可证', licenses);
addSheet(workbook, '楼幢', buildings);
await workbook.xlsx.writeFile(filePath);
console.log(`✅ 成功生成多工作表 Excel 文件: ${filePath}`);
}
/**
* Main function to read data and trigger XLSX generation.
*/
async function run() {
console.log('🚀 开始生成 Excel 分析文件...');
const dataPath = path.join(process.cwd(), 'data', 'merged_data.json');
const outputPath = path.join(process.cwd(), '普宁房产数据分析.xlsx');
try {
await fs.access(dataPath);
console.log(`读取数据源: ${dataPath}`);
const jsonData = JSON.parse(await fs.readFile(dataPath, 'utf-8'));
await generateXlsx(jsonData, outputPath);
} catch (error) {
if (error.code === 'ENOENT') {
console.error(`❌ 错误: 未找到数据文件 '${path.basename(dataPath)}'。`);
console.error('请先运行 `npm start` 来生成完整的数据文件。');
} else {
console.error('❌ 生成 XLSX 文件时发生错误:', error);
}
process.exit(1);
}
}
run();