优化文档结构,整合内外网访问和钉钉登录相关信息,删除冗余内容,提升可读性

This commit is contained in:
chenpei 2026-01-14 15:40:09 +08:00
parent 0368cf8f62
commit 562b7c6cdb

View File

@ -0,0 +1,240 @@
/**
* 分析外网服务器上存在的老素材源文件大小
*/
import { readFile, writeFile } from 'fs/promises'
interface ExternalCheckRow {
素材ID: string
素材类型: string
产品型号: string
作者: string
使用店铺: string
源文件路径: string
外网服务器URL: string
文件是否存在: string
源文件大小: string
创建时间: string
}
async function main() {
console.log('='.repeat(60))
console.log('分析外网服务器上存在的老素材源文件大小')
console.log('='.repeat(60))
console.log()
// 读取外网服务器检查报告
console.log('📋 读取外网服务器检查报告...')
let externalCheckData: ExternalCheckRow[] = []
try {
const externalCheckContent = await readFile('老素材外网服务器检查报告.csv', 'utf-8')
const lines = externalCheckContent.split('\n').filter(line => line.trim())
if (lines.length < 2) {
throw new Error('CSV文件格式错误')
}
// 简单的CSV解析(处理带引号的字段)
function parseCSVLine(line: string): string[] {
const result: string[] = []
let current = ''
let inQuotes = false
for (let i = 0; i < line.length; i++) {
const char = line[i]
if (char === '"') {
if (inQuotes && line[i + 1] === '"') {
current += '"'
i++ // 跳过下一个引号
} else {
inQuotes = !inQuotes
}
} else if (char === ',' && !inQuotes) {
result.push(current)
current = ''
} else {
current += char
}
}
result.push(current)
return result
}
const headers = parseCSVLine(lines[0]).map(h => h.trim())
for (let i = 1; i < lines.length; i++) {
const values = parseCSVLine(lines[i])
const row: any = {}
headers.forEach((header, index) => {
row[header] = (values[index] || '').trim()
})
externalCheckData.push(row as ExternalCheckRow)
}
console.log(` 读取到 ${externalCheckData.length} 条记录`)
} catch (error) {
console.error('❌ 无法读取外网服务器检查报告:', error)
process.exit(1)
}
// 筛选出存在的文件
const existingFiles = externalCheckData.filter(row => row['文件是否存在'] === '是')
const missingFiles = externalCheckData.filter(row => row['文件是否存在'] === '否')
console.log(` 外网服务器存在: ${existingFiles.length} 个`)
console.log(` 外网服务器缺失: ${missingFiles.length} 个`)
console.log()
// 分析文件大小
console.log('📊 分析文件大小...')
let totalSize = 0
let totalSizeGB = 0
const sizeStats: Array<{
materialId: string
materialType: string
productModel: string
authorName: string
categoryName: string
zipFilePath: string
externalUrl: string
fileSize: number
fileSizeMB: number
fileSizeGB: number
createdAt: string
}> = []
for (const row of existingFiles) {
const fileSizeStr = (row['源文件大小(字节)'] || row['源文件大小'] || '0').trim()
const fileSize = parseInt(fileSizeStr, 10)
if (!isNaN(fileSize) && fileSize > 0) {
totalSize += fileSize
const fileSizeMB = fileSize / (1024 * 1024)
const fileSizeGB = fileSize / (1024 * 1024 * 1024)
sizeStats.push({
materialId: row['素材ID'],
materialType: row['素材类型'],
productModel: row['产品型号'],
authorName: row['作者'],
categoryName: row['使用店铺'],
zipFilePath: row['源文件路径'],
externalUrl: row['外网服务器URL'],
fileSize,
fileSizeMB: Math.round(fileSizeMB * 100) / 100,
fileSizeGB: Math.round(fileSizeGB * 1000) / 1000,
createdAt: row['创建时间'],
})
}
}
totalSizeGB = totalSize / (1024 * 1024 * 1024)
console.log(` 总文件数: ${sizeStats.length} 个`)
console.log(` 总大小: ${(totalSizeGB).toFixed(2)} GB (${totalSize} 字节)`)
console.log()
// 按大小排序
sizeStats.sort((a, b) => b.fileSize - a.fileSize)
// 统计信息
const avgSize = totalSize / sizeStats.length
const avgSizeMB = avgSize / (1024 * 1024)
const maxSize = sizeStats[0]?.fileSize || 0
const minSize = sizeStats[sizeStats.length - 1]?.fileSize || 0
console.log('📈 大小统计:')
console.log(` 平均大小: ${(avgSizeMB).toFixed(2)} MB`)
console.log(` 最大文件: ${(maxSize / (1024 * 1024)).toFixed(2)} MB (${sizeStats[0]?.productModel || 'N/A'})`)
console.log(` 最小文件: ${(minSize / (1024 * 1024)).toFixed(2)} MB (${sizeStats[sizeStats.length - 1]?.productModel || 'N/A'})`)
console.log()
// 按素材类型统计
const typeStats: Record<string, { count: number; totalSize: number; avgSize: number }> = {}
for (const stat of sizeStats) {
if (!typeStats[stat.materialType]) {
typeStats[stat.materialType] = { count: 0, totalSize: 0, avgSize: 0 }
}
typeStats[stat.materialType].count++
typeStats[stat.materialType].totalSize += stat.fileSize
}
for (const type in typeStats) {
typeStats[type].avgSize = typeStats[type].totalSize / typeStats[type].count
}
console.log('📦 按素材类型统计:')
for (const [type, stats] of Object.entries(typeStats).sort((a, b) => b[1].totalSize - a[1].totalSize)) {
const totalGB = stats.totalSize / (1024 * 1024 * 1024)
const avgMB = stats.avgSize / (1024 * 1024)
console.log(` ${type}: ${stats.count} 个, 总计 ${totalGB.toFixed(2)} GB, 平均 ${avgMB.toFixed(2)} MB`)
}
console.log()
// 生成详细报告
console.log('💾 生成详细报告...')
const reportRows: string[] = []
reportRows.push('素材ID,素材类型,产品型号,作者,使用店铺,源文件路径,外网服务器URL,文件大小(字节),文件大小(MB),文件大小(GB),创建时间')
for (const stat of sizeStats) {
const row = [
stat.materialId,
stat.materialType,
stat.productModel,
stat.authorName,
stat.categoryName,
stat.zipFilePath,
stat.externalUrl,
stat.fileSize.toString(),
stat.fileSizeMB.toFixed(2),
stat.fileSizeGB.toFixed(3),
stat.createdAt,
]
reportRows.push(row.map(field => {
if (field === null || field === undefined) return ''
const str = String(field)
if (str.includes(',') || str.includes('"') || str.includes('\n')) {
return `"${str.replace(/"/g, '""')}"`
}
return str
}).join(','))
}
const csvContent = reportRows.join('\n')
const outputDir = process.env.IS_IN_CONTAINER === 'true' ? '/tmp' : '.'
const statsFile = `${outputDir}/老素材外网服务器源文件大小统计.csv`
const summaryFile = `${outputDir}/老素材外网服务器源文件大小汇总.csv`
await writeFile(statsFile, csvContent, 'utf-8')
console.log(`✅ 报告已生成: ${statsFile}`)
console.log(` 包含 ${sizeStats.length} 个存在文件的大小信息`)
console.log()
// 生成汇总报告
const summaryRows: string[] = []
summaryRows.push('统计项,数值')
summaryRows.push(`总文件数,${sizeStats.length}`)
summaryRows.push(`总大小(字节),${totalSize}`)
summaryRows.push(`总大小(MB),${(totalSize / (1024 * 1024)).toFixed(2)}`)
summaryRows.push(`总大小(GB),${totalSizeGB.toFixed(2)}`)
summaryRows.push(`平均大小(MB),${avgSizeMB.toFixed(2)}`)
summaryRows.push(`最大文件(MB),${(maxSize / (1024 * 1024)).toFixed(2)}`)
summaryRows.push(`最小文件(MB),${(minSize / (1024 * 1024)).toFixed(2)}`)
summaryRows.push('')
summaryRows.push('素材类型,文件数,总大小(GB),平均大小(MB)')
for (const [type, stats] of Object.entries(typeStats).sort((a, b) => b[1].totalSize - a[1].totalSize)) {
const totalGB = stats.totalSize / (1024 * 1024 * 1024)
const avgMB = stats.avgSize / (1024 * 1024)
summaryRows.push(`${type},${stats.count},${totalGB.toFixed(2)},${avgMB.toFixed(2)}`)
}
await writeFile(summaryFile, summaryRows.join('\n'), 'utf-8')
console.log(`✅ 汇总报告已生成: ${summaryFile}`)
console.log()
}
main().catch((error) => {
console.error('执行失败:', error)
process.exit(1)
})