Commit 2ac68b34 by chenjinjing

no message

parent 49b94055
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -6,17 +6,21 @@
"dependencies": {
"@alicloud/sms-sdk": "^1.1.6",
"@types/node": "^10.12.18",
"archiver": "^7.0.1",
"compression": "^1.7.4",
"exceljs": "^4.4.0",
"express": "^4.17.1",
"express-async-handler": "^1.1.4",
"express-history-api-fallback": "^2.2.1",
"formidable": "^1.2.1",
"fs-extra": "^11.3.2",
"log4js": "^6.6.1",
"lru-cache": "^4.1.5",
"md5": "^2.2.1",
"moment": "^2.24.0",
"mongoose": "^5.4.0",
"mysql": "^2.18.1",
"node-fetch": "^2.7.0",
"node-xlsx": "^0.16.1",
"nodemailer": "^6.1.1",
"officegen": "^0.6.5",
......
<config>
<port>9098</port>
<sign>xxx90909082fsdahfjosadjfpoiwausjorip2hjklrhn1ioud0u124rx0qwejfokasjfolksaujfoas</sign>
<dbServer>http://192.168.0.71:9096</dbServer>
<dbServer>http://192.168.0.71:40012</dbServer>
<imgUrl>http://192.168.0.71:9098</imgUrl>
<imgFileUrl>http://192.168.0.71:9097</imgFileUrl>
<fileUrl>/yuyi/files/1/</fileUrl>
......
import { ERRORENUM } from "../config/enum/errorEnum";
import { BizError } from "../util/bizError";
import * as archiver from 'archiver';
import * as fs from 'fs-extra';
import * as path from 'path';
import * as ExcelJS from 'exceljs';
import { selectData } from "../data/operationalData";
import { OPERATIONALDATATYPE, TABLENAME } from "../config/enum/dbEnum";
import { getAllDwOutPut } from './zaiFu';
const moment = require("moment");
const fetch = require('node-fetch');
/**
* 下载企业相关文件、数据表格和详情Excel的zip包
*/
export async function downloadEnterpriseFilesZip(eId: string) {
if (!eId) throw new BizError(ERRORENUM.参数错误, `下载企业文件缺失eId`);
try {
const projectRoot = path.resolve(__dirname, '../..');
const tempBaseDir = path.join(projectRoot, 'temp');
await fs.ensureDir(tempBaseDir);
const tempDir = path.join(tempBaseDir, `temp_enterprise_files_${eId}_${Date.now()}`);
console.log(`创建临时目录: ${tempDir}`);
await fs.ensureDir(tempDir);
// 获取企业名称
console.log('=== 开始获取企业信息 ===');
const enterpriseInfo = await getEnterpriseInfo(eId);
const enterpriseName = enterpriseInfo?.enterpriseName || '未知企业';
console.log(`企业名称: ${enterpriseName}`);
// 步骤1: 获取企业详情数据并生成Excel
console.log('=== 开始生成企业详情Excel ===');
const excelFilePath = await generateEnterpriseDetailExcel(eId, enterpriseName, tempDir);
// 步骤2: 下载企业相关文件
console.log('=== 开始下载企业文件 ===');
await downloadEnterpriseFiles(eId, tempDir);
// 步骤3: 创建zip文件
const safeEnterpriseName = sanitizeFileName(enterpriseName);
const zipFileName = `企业完整数据_${safeEnterpriseName}_${eId}_${moment().format('YYYYMMDDHHmmss')}.zip`;
const zipFilePath = path.join(tempBaseDir, zipFileName);
console.log(`创建ZIP文件: ${zipFilePath}`);
await createZipArchive(tempDir, zipFilePath);
const zipStats = await fs.stat(zipFilePath);
console.log(`ZIP文件创建成功,大小: ${zipStats.size} bytes`);
// 清理临时目录
console.log('清理临时目录...');
await fs.remove(tempDir);
return {
success: true,
filePath: zipFilePath,
fileName: zipFileName
};
} catch (error) {
console.error('下载企业文件zip包失败:', error);
throw new BizError(ERRORENUM.系统错误, '文件打包失败: ' + error.message);
}
}
/**
* 生成企业详情Excel文件
*/
async function generateEnterpriseDetailExcel(eId: string, enterpriseName: string, tempDir: string): Promise<string> {
try {
console.log('获取企业详情数据...');
const details = await getAllDwOutPut(eId);
console.log('企业详情数据结构:', JSON.stringify(details, null, 2));
const safeEnterpriseName = sanitizeFileName(enterpriseName);
const excelFileName = `企业详情_${safeEnterpriseName}_${eId}.xlsx`;
const excelFilePath = path.join(tempDir, excelFileName);
const workbook = new ExcelJS.Workbook();
// 设置文档属性
workbook.creator = '企业管理系统';
workbook.lastModifiedBy = '企业管理系统';
workbook.created = new Date();
workbook.modified = new Date();
// 1. 基础信息工作表
await createBasicInfoSheet(workbook, details.enterprise, '基础信息');
// 2. 经营数据工作表
await createBusinessDataSheet(workbook, details.manage, '经营数据');
// 3. 融资情况工作表
await createFinancingSheet(workbook, details.financing, '融资情况');
// 4. 荣誉奖项工作表
await createHonorSheet(workbook, details.honor, '荣誉奖项');
// 保存Excel文件
await workbook.xlsx.writeFile(excelFilePath);
console.log(`Excel文件生成成功: ${excelFilePath}`);
return excelFilePath;
} catch (error) {
console.error('生成企业详情Excel失败:', error);
throw new BizError(ERRORENUM.系统错误, '生成Excel文件失败: ' + error.message);
}
}
/**
* 创建基础信息工作表 - 表格格式
*/
async function createBasicInfoSheet(workbook: ExcelJS.Workbook, enterpriseData: any, sheetName: string) {
const worksheet = workbook.addWorksheet(sheetName);
if (!enterpriseData || !enterpriseData.dataList || !Array.isArray(enterpriseData.dataList)) {
worksheet.getCell('A1').value = '暂无基础信息数据';
return;
}
const dataList = enterpriseData.dataList;
// 设置列宽
worksheet.columns = [
{ width: 15 },
{ width: 20 },
{ width: 15 },
{ width: 25 },
{ width: 12 },
{ width: 25 },
{ width: 12 },
{ width: 8 },
{ width: 8 }
];
// 添加标题
worksheet.mergeCells('A1:I1');
const titleCell = worksheet.getCell('A1');
titleCell.value = '企业基础信息';
titleCell.font = { bold: true, size: 14 };
titleCell.alignment = { horizontal: 'center', vertical: 'middle' };
titleCell.fill = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'FFDDEBF7' }
};
// 添加表头(第一行)
if (dataList.length > 0 && Array.isArray(dataList[0])) {
const headerRow = worksheet.addRow(dataList[0]);
// 设置表头样式
headerRow.font = { bold: true, color: { argb: 'FFFFFFFF' } };
headerRow.fill = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'FF2F75B5' }
};
headerRow.alignment = { horizontal: 'center', vertical: 'middle' };
headerRow.height = 25;
}
// 添加数据行(从第二行开始)
let currentRow = 3; // 标题占1行,表头占1行,数据从第3行开始
for (let i = 1; i < dataList.length; i++) {
if (Array.isArray(dataList[i])) {
const dataRow = worksheet.addRow(dataList[i]);
// 设置数据行样式
dataRow.alignment = { horizontal: 'left', vertical: 'middle' };
// 交替行颜色
if (i % 2 === 0) {
dataRow.fill = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'FFF2F2F2' }
};
}
currentRow++;
}
}
// 设置边框
const dataRange = `A2:I${currentRow}`;
for (let row = 2; row <= currentRow; row++) {
for (let col = 1; col <= 9; col++) {
const cell = worksheet.getCell(row, col);
cell.border = {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' }
};
}
}
console.log(`基础信息工作表创建完成,共 ${dataList.length - 1} 条数据`);
}
/**
* 创建经营数据工作表
*/
async function createBusinessDataSheet(workbook: ExcelJS.Workbook, manageData: any, sheetName: string) {
const worksheet = workbook.addWorksheet(sheetName);
if (!manageData || !manageData.dataList || !Array.isArray(manageData.dataList)) {
worksheet.getCell('A1').value = '暂无经营数据';
return;
}
const dataList = manageData.dataList;
// 设置列宽
worksheet.columns = [
{ width: 15 },
{ width: 20 },
{ width: 15 },
{ width: 15 },
{ width: 15 },
{ width: 15 }
];
// 添加标题
worksheet.mergeCells('A1:F1');
const titleCell = worksheet.getCell('A1');
titleCell.value = '企业经营数据';
titleCell.font = { bold: true, size: 14 };
titleCell.alignment = { horizontal: 'center', vertical: 'middle' };
titleCell.fill = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'FFE2EFDA' }
};
// 添加表头(第一行)
if (dataList.length > 0 && Array.isArray(dataList[0])) {
const headerRow = worksheet.addRow(dataList[0]);
// 设置表头样式
headerRow.font = { bold: true, color: { argb: 'FFFFFFFF' } };
headerRow.fill = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'FF70AD47' }
};
headerRow.alignment = { horizontal: 'center', vertical: 'middle' };
headerRow.height = 25;
}
// 添加数据行
let currentRow = 3;
let hasData = false;
for (let i = 1; i < dataList.length; i++) {
if (Array.isArray(dataList[i]) && dataList[i].some((cell: any) => cell && cell !== '-')) {
const dataRow = worksheet.addRow(dataList[i]);
// 设置数据行样式
dataRow.alignment = { horizontal: 'left', vertical: 'middle' };
// 交替行颜色
if (i % 2 === 0) {
dataRow.fill = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'FFF2F2F2' }
};
}
currentRow++;
hasData = true;
}
}
if (!hasData) {
worksheet.getCell('A3').value = '暂无经营数据';
worksheet.getCell('A3').alignment = { horizontal: 'center', vertical: 'middle' };
} else {
// 设置边框
const dataRange = `A2:F${currentRow}`;
for (let row = 2; row <= currentRow; row++) {
for (let col = 1; col <= 6; col++) {
const cell = worksheet.getCell(row, col);
cell.border = {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' }
};
}
}
}
console.log(`经营数据工作表创建完成,共 ${dataList.length - 1} 条数据`);
}
/**
* 创建融资情况工作表
*/
async function createFinancingSheet(workbook: ExcelJS.Workbook, financingData: any, sheetName: string) {
const worksheet = workbook.addWorksheet(sheetName);
if (!financingData || !financingData.dataList || !Array.isArray(financingData.dataList)) {
worksheet.getCell('A1').value = '暂无融资数据';
return;
}
const dataList = financingData.dataList;
// 设置列宽
worksheet.columns = [
{ width: 15 },
{ width: 20 },
{ width: 15 },
{ width: 15 },
{ width: 15 },
{ width: 20 }
];
// 添加标题
worksheet.mergeCells('A1:F1');
const titleCell = worksheet.getCell('A1');
titleCell.value = '企业融资情况';
titleCell.font = { bold: true, size: 14 };
titleCell.alignment = { horizontal: 'center', vertical: 'middle' };
titleCell.fill = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'FFFFE699' }
};
// 添加表头(第一行)
if (dataList.length > 0 && Array.isArray(dataList[0])) {
const headerRow = worksheet.addRow(dataList[0]);
// 设置表头样式
headerRow.font = { bold: true, color: { argb: 'FFFFFFFF' } };
headerRow.fill = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'FFFFC000' }
};
headerRow.alignment = { horizontal: 'center', vertical: 'middle' };
headerRow.height = 25;
}
// 添加数据行
let currentRow = 3;
let hasData = false;
for (let i = 1; i < dataList.length; i++) {
if (Array.isArray(dataList[i]) && dataList[i].some((cell: any) => cell && cell !== '-')) {
const dataRow = worksheet.addRow(dataList[i]);
// 设置数据行样式
dataRow.alignment = { horizontal: 'left', vertical: 'middle' };
// 交替行颜色
if (i % 2 === 0) {
dataRow.fill = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'FFF2F2F2' }
};
}
currentRow++;
hasData = true;
}
}
if (!hasData) {
worksheet.getCell('A3').value = '暂无融资数据';
worksheet.getCell('A3').alignment = { horizontal: 'center', vertical: 'middle' };
} else {
// 设置边框
for (let row = 2; row <= currentRow; row++) {
for (let col = 1; col <= 6; col++) {
const cell = worksheet.getCell(row, col);
cell.border = {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' }
};
}
}
}
console.log(`融资情况工作表创建完成,共 ${dataList.length - 1} 条数据`);
}
/**
* 创建荣誉奖项工作表
*/
async function createHonorSheet(workbook: ExcelJS.Workbook, honorData: any, sheetName: string) {
const worksheet = workbook.addWorksheet(sheetName);
if (!honorData || !honorData.dataList || !Array.isArray(honorData.dataList)) {
worksheet.getCell('A1').value = '暂无荣誉数据';
return;
}
const dataList = honorData.dataList;
// 设置列宽
worksheet.columns = [
{ width: 15 },
{ width: 25 },
{ width: 20 },
{ width: 15 },
{ width: 15 }
];
// 添加标题
worksheet.mergeCells('A1:E1');
const titleCell = worksheet.getCell('A1');
titleCell.value = '企业荣誉奖项';
titleCell.font = { bold: true, size: 14 };
titleCell.alignment = { horizontal: 'center', vertical: 'middle' };
titleCell.fill = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'FFE6E0EC' }
};
// 添加表头(第一行)
if (dataList.length > 0 && Array.isArray(dataList[0])) {
const headerRow = worksheet.addRow(dataList[0]);
// 设置表头样式
headerRow.font = { bold: true, color: { argb: 'FFFFFFFF' } };
headerRow.fill = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'FF8064A2' }
};
headerRow.alignment = { horizontal: 'center', vertical: 'middle' };
headerRow.height = 25;
}
// 添加数据行
let currentRow = 3;
let hasData = false;
for (let i = 1; i < dataList.length; i++) {
if (Array.isArray(dataList[i]) && dataList[i].some((cell: any) => cell && cell !== '-')) {
const dataRow = worksheet.addRow(dataList[i]);
// 设置数据行样式
dataRow.alignment = { horizontal: 'left', vertical: 'middle' };
// 交替行颜色
if (i % 2 === 0) {
dataRow.fill = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'FFF2F2F2' }
};
}
currentRow++;
hasData = true;
}
}
if (!hasData) {
worksheet.getCell('A3').value = '暂无荣誉数据';
worksheet.getCell('A3').alignment = { horizontal: 'center', vertical: 'middle' };
} else {
// 设置边框
for (let row = 2; row <= currentRow; row++) {
for (let col = 1; col <= 5; col++) {
const cell = worksheet.getCell(row, col);
cell.border = {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' }
};
}
}
}
console.log(`荣誉奖项工作表创建完成,共 ${dataList.length - 1} 条数据`);
}
/**
* 下载企业相关文件
*/
async function downloadEnterpriseFiles(eId: string, tempDir: string) {
// 先获取所有文件信息并验证
console.log('=== 开始验证文件信息 ===');
const leaseFiles = await getLeaseFiles(eId);
const leaseValidCount = await validateAndLogFiles(leaseFiles, '租赁信息');
const trademarkFiles = await getTrademarkFiles(eId);
const trademarkValidCount = await validateAndLogFiles(trademarkFiles, '商标');
const copyrightFiles = await getCopyrightFiles(eId);
const copyrightValidCount = await validateAndLogFiles(copyrightFiles, '作品著作权');
const softwareCopyrightFiles = await getSoftwareCopyrightFiles(eId);
const softwareValidCount = await validateAndLogFiles(softwareCopyrightFiles, '软件著作权');
const patentFiles = await getPatentFiles(eId);
const patentValidCount = await validateAndLogFiles(patentFiles, '专利');
console.log('=== 文件验证完成,开始下载 ===');
// 下载文件
await downloadAndSaveFiles(leaseFiles, path.join(tempDir, '租赁信息'), tempDir);
await downloadAndSaveFiles(trademarkFiles, path.join(tempDir, '商标'), tempDir);
await downloadAndSaveFiles(copyrightFiles, path.join(tempDir, '作品著作权'), tempDir);
await downloadAndSaveFiles(softwareCopyrightFiles, path.join(tempDir, '软件著作权'), tempDir);
await downloadAndSaveFiles(patentFiles, path.join(tempDir, '专利'), tempDir);
}
/**
* 获取企业信息
*/
async function getEnterpriseInfo(eId: string): Promise<any> {
try {
// 根据你的数据库结构调整查询
const enterpriseInfo = await selectData(
OPERATIONALDATATYPE.查询单个,
'enterprise', // 或者你的企业表名
{ eId },
['enterpriseName'] // 可能的名称字段
);
if (!enterpriseInfo) {
console.warn(`未找到企业信息: ${eId}`);
return null;
}
console.log(`获取到企业信息:`, enterpriseInfo);
return enterpriseInfo;
} catch (error) {
console.error(`获取企业信息失败: ${eId}`, error);
return null;
}
}
/**
* 清理文件名中的非法字符
*/
function sanitizeFileName(fileName: string): string {
if (!fileName) return '未知企业';
// 移除或替换文件名中的非法字符
return fileName
.replace(/[<>:"/\\|?*]/g, '_') // 替换Windows非法字符
.replace(/\s+/g, ' ') // 合并多个空格
.trim() // 去除首尾空格
.substring(0, 100); // 限制长度避免过长
}
/**
* 验证并记录文件信息
*/
async function validateAndLogFiles(files: {url: string, fileName: string}[], category: string) {
console.log(`\n=== ${category}文件验证 ===`);
console.log(`总数: ${files.length} 个文件`);
let existingCount = 0;
for (const file of files) {
console.log(`文件: ${file.fileName}`);
console.log(`URL: ${file.url}`);
// 检查文件是否存在
const exists = await checkFileExists(file.url);
console.log(`存在性: ${exists ? '存在' : '不存在'}`);
if (exists) {
existingCount++;
}
console.log('---');
}
console.log(`${category}文件统计: 存在 ${existingCount}/${files.length} 个文件`);
return existingCount;
}
/**
* 改进的下载函数 - 跳过不存在的文件
*/
async function downloadAndSaveFiles(files: {url: string, fileName: string}[], targetDir: string, baseTempDir: string): Promise<void> {
if (files.length === 0) {
console.log(`没有文件需要下载到目录: ${targetDir}`);
return;
}
await fs.ensureDir(targetDir);
// 先检查所有文件的存在性
const validFiles: {url: string, fileName: string}[] = [];
for (const file of files) {
const exists = await checkFileExists(file.url);
if (exists) {
validFiles.push(file);
console.log(`✅ 文件可用: ${file.fileName}`);
} else {
console.warn(`❌ 文件不存在,跳过: ${file.fileName} (${file.url})`);
}
}
console.log(`有效文件数量: ${validFiles.length}/${files.length}`);
if (validFiles.length === 0) {
console.log(`没有可下载的有效文件,跳过目录: ${targetDir}`);
return;
}
// 只下载存在的文件
const downloadPromises = validFiles.map(async (file) => {
try {
if (!file.url || typeof file.url !== 'string') {
console.warn(`文件URL无效:`, file.url);
return;
}
console.log(`开始下载文件: ${file.fileName} from ${file.url}`);
const fetchOptions: any = {
method: 'GET',
timeout: 30000,
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Accept': '*/*'
}
};
// 关键修复:添加认证 token
const SECRET_TOKEN = "Ngz86cuAKxblwXR9OiKSWbfkj7oZ8R0lMU8pTfpVYBDCkvtUb0ZwbaBvwWyfv2O9";
// 如果是内部文件服务器的URL,添加认证token
if (file.url.includes('192.168.0.71') || file.url.includes('fh.tninnopark.cn')) {
fetchOptions.headers['token'] = SECRET_TOKEN;
console.log(`添加认证token到请求头`);
}
const response = await fetch(file.url, fetchOptions);
if (!response.ok) {
console.warn(`下载文件失败: ${file.url}, 状态码: ${response.status}`);
return;
}
const buffer = await response.buffer();
if (buffer.length === 0) {
console.warn(`文件内容为空: ${file.fileName}`);
return;
}
const filePath = path.join(targetDir, file.fileName);
await fs.writeFile(filePath, buffer);
console.log(`✅ 文件下载成功: ${file.fileName}, 大小: ${buffer.length} bytes`);
} catch (error) {
console.error(`下载文件失败: ${file.fileName} (${file.url})`, error.message);
}
});
const results = await Promise.allSettled(downloadPromises);
const successful = results.filter(result => result.status === 'fulfilled').length;
const failed = results.filter(result => result.status === 'rejected').length;
console.log(`目录 ${targetDir} 下载完成: 成功 ${successful} 个, 失败 ${failed} 个, 总共 ${validFiles.length} 个有效文件`);
}
/**
* 检查文件是否存在于文件服务器
*/
async function checkFileExists(url: string): Promise<boolean> {
try {
const SECRET_TOKEN = "Ngz86cuAKxblwXR9OiKSWbfkj7oZ8R0lMU8pTfpVYBDCkvtUb0ZwbaBvwWyfv2O9";
const fetchOptions: any = {
method: 'HEAD',
timeout: 10000,
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
};
// 同样为HEAD请求添加认证token
if (url.includes('192.168.0.71') || url.includes('fh.tninnopark.cn')) {
fetchOptions.headers['token'] = SECRET_TOKEN;
}
const response = await fetch(url, fetchOptions);
return response.ok;
} catch (error) {
console.log(`文件存在性检查失败: ${url}`, error.message);
return false;
}
}
/**
* 安全的URL构建函数,避免中文编码问题
*/
function buildSafeUrl(baseUrl: string, filePath: string): string {
// 确保基础URL格式正确
const cleanBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
// 确保文件路径格式正确
const cleanFilePath = filePath.startsWith('/') ? filePath : `/${filePath}`;
// 直接拼接,避免URL对象的自动编码
return `${cleanBaseUrl}${cleanFilePath}`;
}
/**
* 统一处理文件路径,将相对路径转换为绝对URL
* 修复中文文件名编码问题
* http://192.168.0.71:9097
* https://fh.tninnopark.cn
*/
function processFilePath(filePath: any, baseUrl: string = 'http://192.168.0.71:9097'): string | null {
if (!filePath) {
console.log('文件路径为空');
return null;
}
console.log(`原始文件路径: ${filePath}, 类型: ${typeof filePath}`);
// 如果是数组,取第一个元素
if (Array.isArray(filePath)) {
filePath = filePath[0];
}
// 处理JSON字符串格式
if (typeof filePath === 'string' && filePath.startsWith('[')) {
try {
const pathArray = JSON.parse(filePath);
filePath = pathArray[0];
console.log(`解析JSON数组路径: ${filePath}`);
} catch (e) {
console.warn('解析文件路径数组失败:', filePath);
return null;
}
}
// 验证路径格式
if (!filePath || typeof filePath !== 'string') {
return null;
}
// 如果已经是完整的URL,直接返回
if (filePath.startsWith('http://') || filePath.startsWith('https://')) {
return filePath;
}
// 格式化基础URL
const formattedBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
// 关键修复:确保路径格式正确且中文不被编码
let finalPath = filePath;
if (!finalPath.startsWith('/')) {
finalPath = '/' + finalPath;
}
// 构建完整的URL(不对路径部分进行编码)
const url = new URL(formattedBaseUrl);
url.pathname = finalPath;
const fullUrl = buildSafeUrl(baseUrl, finalPath);
console.log(`安全构建的URL: ${fullUrl}`);
return fullUrl;
}
/**
* 获取租赁信息相关文件
*/
async function getLeaseFiles(eId: string): Promise<{url: string, fileName: string}[]> {
const filesList = [
"sanFangXieYi", "fuHuaXieYi", "fangWuZuLing", "fuHuaXieYiBuChong",
"chengXinChengNuoHan", "yingYeZhiZhao", "ruFuZiLiao", "ruZhuJiHua",
"yaJinZhiFu", "cardCopy"
];
const leaseInfo = await selectData(
OPERATIONALDATATYPE.查询多个,
TABLENAME.租赁信息,
{ eId },
filesList
);
const files: {url: string, fileName: string}[] = [];
leaseInfo.forEach(lease => {
filesList.forEach(field => {
if (lease[field]) {
const processedUrl = processFilePath(lease[field]);
if (processedUrl) {
const fileName = getLeaseFileName(field, lease);
files.push({
url: processedUrl,
fileName: fileName
});
}
}
});
});
return files;
}
/**
* 获取租赁文件显示名称
*/
function getLeaseFileName(field: string, lease: any): string {
const nameMap: {[key: string]: string} = {
sanFangXieYi: '三方协议',
fuHuaXieYi: '孵化协议',
fangWuZuLing: '房屋租赁合同',
fuHuaXieYiBuChong: '孵化协议补充协议书',
chengXinChengNuoHan: '诚信承诺函',
yingYeZhiZhao: '营业执照复印件',
ruFuZiLiao: '入孵资料明细',
ruZhuJiHua: '入驻计划复印件',
yaJinZhiFu: '押金支付凭证',
cardCopy: '身份证复印件'
};
const baseName = nameMap[field] || field;
// 获取实际的文件扩展名
let extension = '';
if (lease[field] && typeof lease[field] === 'string') {
const processedUrl = processFilePath(lease[field]);
if (processedUrl) {
const ext = path.extname(processedUrl);
if (ext) {
extension = ext;
} else {
// 如果没有扩展名,根据字段类型推测
if (field === 'cardCopy' || field === 'yingYeZhiZhao') {
extension = '.jpg'; // 身份证和营业执照通常是图片
} else {
extension = '.pdf'; // 其他文件默认PDF
}
}
}
}
// 确保文件名安全
const safeFileName = baseName.replace(/[<>:"/\\|?*]/g, '_');
return `${safeFileName}${extension}`;
}
/**
* 获取商标相关文件
*/
async function getTrademarkFiles(eId: string): Promise<{url: string, fileName: string}[]> {
const trademarkInfo = await selectData(
OPERATIONALDATATYPE.查询多个,
'trade_mark',
{ eId },
["tmId", "regNo", "name", "imageUrl"]
);
const files: {url: string, fileName: string}[] = [];
trademarkInfo.forEach(trademark => {
if (trademark.imageUrl) {
let processedUrl = processFilePath(trademark.imageUrl);
// 特殊处理商标图片URL:移除多余的 /img 后缀
// if (processedUrl && processedUrl.endsWith('/img')) {
// processedUrl = processedUrl.slice(0, -4); // 移除最后的 '/img'
// console.log(`修复商标图片URL: ${processedUrl}`);
// }
if (processedUrl) {
const extension = path.extname(processedUrl) || '.jpg';
// 清理文件名中的特殊字符
const safeName = (trademark.name || '').replace(/[<>:"/\\|?*]/g, '_');
const fileName = `商标_${trademark.regNo || trademark.tmId}_${safeName}${extension}`;
files.push({
url: processedUrl,
fileName: fileName
});
}
}
});
return files;
}
/**
* 获取作品著作权相关文件
*/
async function getCopyrightFiles(eId: string): Promise<{url: string, fileName: string}[]> {
const copyrightInfo = await selectData(
OPERATIONALDATATYPE.查询多个,
'copy_right',
{ eId },
["crId", "registerNo", "name", "iprUrl"]
);
const files: {url: string, fileName: string}[] = [];
copyrightInfo.forEach(copyright => {
if (copyright.iprUrl) {
const processedUrl = processFilePath(copyright.iprUrl);
if (processedUrl) {
const extension = path.extname(processedUrl) || '.pdf';
const fileName = `作品著作权_${copyright.registerNo || copyright.crId}_${copyright.name}${extension}`;
files.push({
url: processedUrl,
fileName: fileName
});
}
}
});
return files;
}
/**
* 获取软件著作权相关文件
*/
async function getSoftwareCopyrightFiles(eId: string): Promise<{url: string, fileName: string}[]> {
const softwareCopyrightInfo = await selectData(
OPERATIONALDATATYPE.查询多个,
'software_copyright',
{ eId },
["scId", "registerNo", "name", "iprUrl"]
);
const files: {url: string, fileName: string}[] = [];
softwareCopyrightInfo.forEach(software => {
if (software.iprUrl) {
const processedUrl = processFilePath(software.iprUrl);
if (processedUrl) {
const extension = path.extname(processedUrl) || '.pdf';
const fileName = `软件著作权_${software.registerNo || software.scId}_${software.name}${extension}`;
files.push({
url: processedUrl,
fileName: fileName
});
}
}
});
return files;
}
/**
* 获取专利相关文件
*/
async function getPatentFiles(eId: string): Promise<{url: string, fileName: string}[]> {
const patentInfo = await selectData(
OPERATIONALDATATYPE.查询多个,
'patent',
{ eId },
["patentId", "applicationNumber", "title", "iprUrl"]
);
const files: {url: string, fileName: string}[] = [];
patentInfo.forEach(patent => {
if (patent.iprUrl) {
const processedUrl = processFilePath(patent.iprUrl);
if (processedUrl) {
const extension = path.extname(processedUrl) || '.pdf';
const fileName = `专利_${patent.applicationNumber || patent.patentId}_${patent.title}${extension}`;
files.push({
url: processedUrl,
fileName: fileName
});
}
}
});
return files;
}
/**
* 简单的图片文件头验证
*/
function isValidImageHeader(header: string): boolean {
const validHeaders = [
'ffd8ffe0', // JPEG
'ffd8ffe1', // JPEG
'ffd8ffe2', // JPEG
'89504e47', // PNG
'47494638', // GIF
'424d' // BMP (前2字节)
];
return validHeaders.some(valid => header.startsWith(valid));
}
/**
* 创建ZIP压缩包
*/
async function createZipArchive(sourceDir: string, outPath: string): Promise<void> {
return new Promise((resolve, reject) => {
const output = fs.createWriteStream(outPath);
const archive = archiver('zip', {
zlib: { level: 9 } // 最高压缩级别
});
output.on('close', () => {
console.log(`ZIP文件创建完成: ${outPath}, 大小: ${archive.pointer()} bytes`);
resolve();
});
archive.on('error', (err) => {
reject(err);
});
archive.pipe(output);
archive.directory(sourceDir, false);
archive.finalize();
});
}
//数据同步
import moment = require("moment");
import { OPERATIONALDATATYPE, TABLENAME } from "../config/enum/dbEnum";
import { selectData, selectManyTableData } from "../data/operationalData";
export async function integration() {
let selectParam:any = {startTime:{"%between%":["2025-07-01", "2025-09-30"]}};
let enterpriseFilesList = ["eId", "enterpriseName", "uscc", "industry", "logonTime", "logonAddress","operatingAddress", "mainBusiness"];
let leaseFileList = ["area", "rent", "building", "roomNumber", "startTime", "endTime" ];
let fuhuaFileList = [ "startTime", "endTime"];
let manyTableInfo:any = {};
manyTableInfo[TABLENAME.企业孵化信息] = {column:fuhuaFileList, where:selectParam };
manyTableInfo[TABLENAME.租赁信息] = {column:leaseFileList, where:{} };
let dbList = await selectManyTableData(OPERATIONALDATATYPE.多表联查, TABLENAME.企业基础信息表, {}, enterpriseFilesList, manyTableInfo);
let dataList = [];
let distinctMap = {};
dbList.forEach(info => {
let { enterprise_fuhuas, enterprise_leases } = info;
let enterprise_fuhua = enterprise_fuhuas[0];
let enterprise_lease = enterprise_leases[0];
let leaseAddress = "";
if (enterprise_lease.building ) leaseAddress += `${enterprise_lease.building}号楼`;
if (enterprise_lease.roomNumber) leaseAddress += `${enterprise_lease.roomNumber}室`;
if (!leaseAddress) leaseAddress = JSON.parse(info.logonAddress)[JSON.parse(info.logonAddress).length - 1]
let newindustry = [];
JSON.parse(info.industry).forEach(key => {
switch(key) {
case 1: newindustry.push(6);break;
case 2: newindustry.push(3);break;
case 3: newindustry.push(8);break;
case 4: newindustry.push(8);break;
case 5: newindustry.push(8);break;
case 6: newindustry.push(8);break;
}
})
let addInfo = {
name:info.enterpriseName,
uscc:info.uscc,
industry:newindustry,
logonTime:new Date(info.logonTime).valueOf(),
// logonTime:info.logonTime,
firstIncubationTime:new Date(enterprise_fuhua.startTime).valueOf(),
isNaturalPersonHolding:true,
logonAddress:JSON.parse(info.logonAddress),
operatingAddress:info.operatingAddress ? JSON.parse(info.operatingAddress) : JSON.parse(info.logonAddress),
leasedArea:enterprise_lease.area ? parseFloat(enterprise_lease.area) : 0,
mainBusiness:info.mainBusiness || "",
jiaSu:2,
leaseAddress,
price:parseFloat(enterprise_lease.rent),
contractStartTime:new Date(enterprise_fuhua.startTime).valueOf(),
contractEndTime:new Date(enterprise_fuhua.endTime).valueOf(),
payStartTime:new Date(enterprise_fuhua.startTime).valueOf(),
payLong:moment(enterprise_fuhua.endTime).diff(moment(enterprise_fuhua.startTime), 'months'),
areaUnit:1
};
dataList.push(addInfo);
if (!distinctMap[info.uscc]) distinctMap[info.uscc] = addInfo;
});
let outList = Object.values(distinctMap);
console.log();
}
......@@ -7,14 +7,117 @@ import moment = require("moment");
import { getQcc } from "../util/request";
import { OPERATIONALDATATYPE, TABLEID, TABLENAME } from "../config/enum/dbEnum";
import { operationalData, selectData } from "../data/operationalData";
import { getMySqlMs, getPinyinInitials, randomId } from "../tools/system";
import { FINANCINGROUNDS, IPRALLTYPE } from "../config/enum/enum";
import { table } from "console";
import { changeEnumValue } from "../util/verificationEnum";
import { getMySqlMs, getPinyinInitials, getPwdMd5, randomId } from "../tools/system";
import { FINANCINGROUNDS } from "../config/enum/enum";
const xlsx = require('node-xlsx');
const path = require('path');
const fs = require('fs');
/**
* 迁移所有企业用户的密码为加密格式
*/
export async function migrateEnterpriseUserPasswords() {
try {
// 获取所有企业用户
const enterpriseUsers = await selectData(
OPERATIONALDATATYPE.查询多个,
TABLENAME.企业用户表,
{},
["uId", "uscc", "pwd", "userName"]
);
if (!enterpriseUsers || enterpriseUsers.length === 0) {
console.log("没有找到需要迁移的企业用户");
return { success: true, migrated: 0 };
}
let migratedCount = 0;
// 遍历所有用户并更新密码
for (const user of enterpriseUsers) {
// 跳过已经加密的密码(假设加密后的密码长度为32位)
if (user.pwd && user.pwd.length === 32) {
console.log(`用户 ${user.uscc} 的密码已经加密,跳过`);
continue;
}
// 使用uId作为盐值进行加密
const encryptedPwd = getPwdMd5(user.uId, user.pwd);
// 更新数据库中的密码
await operationalData(
OPERATIONALDATATYPE.修改,
TABLENAME.企业用户表,
{ pwd: encryptedPwd },
{ uId: user.uId }
);
migratedCount++;
console.log(`已迁移用户 ${user.userName} 的密码`);
}
console.log(`企业用户密码迁移完成,共迁移了 ${migratedCount} 个用户`);
return { success: true, migrated: migratedCount };
} catch (error) {
console.error("企业用户密码迁移过程中发生错误:", error);
return { success: false, error: error.message };
}
}
/**
* 迁移所有企业用户的密码为加密格式
*/
export async function migrateAdminPasswords() {
try {
// 获取所有企业用户
const enterpriseUsers = await selectData(
OPERATIONALDATATYPE.查询多个,
TABLENAME.管理后台用户,
{},
["aId", "loginId", "pwd", "name"]
);
if (!enterpriseUsers || enterpriseUsers.length === 0) {
console.log("没有找到需要迁移的企业用户");
return { success: true, migrated: 0 };
}
let migratedCount = 0;
// 遍历所有用户并更新密码
for (const user of enterpriseUsers) {
// 跳过已经加密的密码(假设加密后的密码长度为32位)
if (user.pwd && user.pwd.length === 32) {
console.log(`用户 ${user.loginId} 的密码已经加密,跳过`);
continue;
}
// 使用aId作为盐值进行加密
const encryptedPwd = getPwdMd5(user.aId, user.pwd);
// 更新数据库中的密码
await operationalData(
OPERATIONALDATATYPE.修改,
TABLENAME.管理后台用户,
{ pwd: encryptedPwd },
{ aId: user.aId }
);
migratedCount++;
console.log(`已迁移用户 ${user.name} 的密码`);
}
console.log(`企业用户密码迁移完成,共迁移了 ${migratedCount} 个用户`);
return { success: true, migrated: migratedCount };
} catch (error) {
console.error("企业用户密码迁移过程中发生错误:", error);
return { success: false, error: error.message };
}
}
/**
* 获取当个excel文件数据
* @param filePath
......
......@@ -4,7 +4,7 @@
import moment = require("moment");
import { OPERATIONALDATATYPE, TABLENAME } from "../config/enum/dbEnum";
import { BUILDING, CHANGESTATE, EMIGRATIONTYPE, FUHUASTATE, INDUSTRY, OFFLINEPROMOTION, ONLINEPROMOTION, PROMOTIONTYPE } from "../config/enum/enum";
import { BUILDING, CHANGESTATE, EMIGRATIONTYPE, FUHUASTATE, INDUSTRY, OFFLINEPROMOTION, ONLINEPROMOTION, PROMOTIONTYPE, STATE } from "../config/enum/enum";
import { operationalData, selectData, selectManyTableData } from "../data/operationalData";
import { getIntervalYear, getMySqlMs, randomId } from "../tools/system";
import { changeEnumValue } from "../util/verificationEnum";
......@@ -60,16 +60,19 @@ export async function getBaseData() {
// })
// })
// 累加在孵面积(考虑租赁时间)
zaifuResList.forEach(info => {
info.enterprise_leases.forEach(lease => {
// 确认租赁时间在有效范围内
baseData.fuhuaData["在孵面积"] += parseFloat(lease.area);
// if (nowTime >= lease.startTime && nowTime <= lease.endTime) {
// zaifuResList.forEach(info => {
// info.enterprise_leases.forEach(lease => {
// // 确认租赁时间在有效范围内
// baseData.fuhuaData["在孵面积"] += parseFloat(lease.area);
// }
});
});
// // if (nowTime >= lease.startTime && nowTime <= lease.endTime) {
// // baseData.fuhuaData["在孵面积"] += parseFloat(lease.area);
// // }
// });
// });
// baseData.fuhuaData["在孵面积占比"] = ((baseData.fuhuaData["在孵面积(㎡)"] / baseData.fuhuaData["总面积(㎡)"])*100).toFixed(2) + "%";
/**在孵企业面积 */
let {孵化器总面积, 在孵企业面积, 在孵企业面积占比, 各楼栋在孵企业面积} = await get在孵企业面积();
baseData.fuhuaData["在孵面积"] = 在孵企业面积;
/**迁出企业 */
let qianchuList = await selectData(OPERATIONALDATATYPE.查询多个, TABLENAME.企业孵化信息, {state:FUHUASTATE.迁出}, {});
......@@ -119,18 +122,49 @@ export async function getBaseData() {
*/
let entryList = await selectData(OPERATIONALDATATYPE.查询多个, TABLENAME.入驻信息表, {}, [`info_enterId`, `building`, `occupancyRate`, `enteredEnterprises`]);
let 入驻企业 = await get入驻企业列表();
let 备案孵化面积 = {
"1":4800,
"3":6200,
"4":13200
}
let 入孵率占比 = {
"1":"",
"3":"",
"4":""
}
//这栋楼的在孵企业总面积 / 这栋楼的备案面积
let 入孵率1 = (各楼栋在孵企业面积["1"] / 备案孵化面积["1"]) * 100;
let 入孵率3 = (各楼栋在孵企业面积["3"] / 备案孵化面积["3"]) * 100;
let 入孵率4 = (各楼栋在孵企业面积["4"] / 备案孵化面积["4"]) * 100;
入孵率占比["1"] = 入孵率1.toFixed(2) + "%";
入孵率占比["3"] = 入孵率3.toFixed(2) + "%";
入孵率占比["4"] = 入孵率4.toFixed(2) + "%";
if (入孵率1 > 100) 入孵率占比["1"] = "100%";
if (入孵率3 > 100) 入孵率占比["3"] = "100%";
if (入孵率4 > 100) 入孵率占比["4"] = "100%";
let entryInfo = {
"1":{"入驻率":"", "入驻企业":入驻企业[BUILDING["1号楼"]].length},
"3":{"入驻率":"", "入驻企业":入驻企业[BUILDING["3号楼"]].length},
"4":{"入驻率":"", "入驻企业":入驻企业[BUILDING["4号楼"]].length}};
"1":{"入孵率":入孵率占比["1"], "入驻企业":入驻企业[BUILDING["1号楼"]].length},
"3":{"入孵率":入孵率占比["3"], "入驻企业":入驻企业[BUILDING["3号楼"]].length},
"4":{"入孵率":入孵率占比["4"], "入驻企业":入驻企业[BUILDING["4号楼"]].length}
};
for (let key in entryInfo) {
let 入驻信息updateInfo = {occupancyRate:entryInfo[key].入孵率, enteredEnterprises:entryInfo[key].入驻企业};
let updateParam = {building:key}
await operationalData(OPERATIONALDATATYPE.修改, TABLENAME.入驻信息表, 入驻信息updateInfo, updateParam);
}
if (entryList.length) {
entryList.forEach( info => {
// let = changeEnumValue(BUILDING, info.building);
let building = info.building;
if (!entryInfo[building]) {
entryInfo[building] = { "入率": "", "入驻企业": "" };
entryInfo[building] = { "入率": "", "入驻企业": "" };
}
entryInfo[building].入驻 = info.occupancyRate;
// entryInfo[building].入孵率 = info.occupancyRate;
entryInfo[building].入驻企业 = 入驻企业[building].length;
})
}
......@@ -367,16 +401,14 @@ export async function getRiskData() {
中低风险: 0,
关注: 0
}
let riskEnterprises: any[] = [];
let riskEnterprises:any[] = [];
let fhColumn = ["enterpriseName", "industry", "eId", "shijiaoziben"];
//获取所有企业孵化信息
let manyTableInfo: any = {}
// manyTableInfo[TABLENAME.企业孵化信息] = { column: ["fId", "eId", "startTime", "endTime", "state"], where: { state: FUHUASTATE.实体孵化 } };
manyTableInfo[TABLENAME.企业孵化信息] = { column: ["fId", "eId", "moveOutTime", "moveOutType"], where: {} };
// manyTableInfo[TABLENAME.企业经营信息] = { column: ["annual"], where: {} };
let manyTableInfo:any = {};
manyTableInfo[TABLENAME.企业孵化信息] = { column: ["fId", "eId", "moveOutTime", "moveOutType", "state"], where: {} };
let fhdbList = await selectManyTableData(OPERATIONALDATATYPE.多表联查, TABLENAME.企业基础信息表, {state: CHANGESTATE.已通过}, fhColumn, manyTableInfo);
let manageList = await selectData(OPERATIONALDATATYPE.查询多个, TABLENAME.企业经营信息, {}, ["annual", "eId"]);
let manageList = await selectData(OPERATIONALDATATYPE.查询多个, TABLENAME.企业经营信息, {isSubmit:STATE.}, ["annual", "eId"]);
let map = {};
manageList.forEach(info => {
......@@ -389,14 +421,14 @@ export async function getRiskData() {
yj.高风险 += 1;
riskLevel = "高风险";
riskEnterprises.push([info.enterpriseName, riskLevel]);
} else if (!map[info.eId]) {
yj.中低风险 += 1;
riskLevel = "中低风险";
riskEnterprises.push([info.enterpriseName, riskLevel]);
} else if (info.enterprise_fuhuas[0].moveOutType == EMIGRATIONTYPE.到期退租) {
yj.关注 += 1;
riskLevel = "关注";
riskEnterprises.push([info.enterpriseName, riskLevel]);
} else if (info.enterprise_fuhuas[0].state != FUHUASTATE.迁出 && !map[info.eId]) {
yj.中低风险 += 1;
riskLevel = "中低风险";
riskEnterprises.push([info.enterpriseName, riskLevel]);
}
});
......@@ -411,6 +443,58 @@ export async function getRiskData() {
};
}
// export async function getRiskData() {
// let yj = {
// 高风险: 0,
// 中低风险: 0,
// 关注: 0
// }
// let riskEnterprises: any[] = [];
// let fhColumn = ["enterpriseName", "industry", "eId", "shijiaoziben"];
// //获取所有企业孵化信息
// let manyTableInfo: any = {}
// // manyTableInfo[TABLENAME.企业孵化信息] = { column: ["fId", "eId", "startTime", "endTime", "state"], where: { state: FUHUASTATE.实体孵化 } };
// manyTableInfo[TABLENAME.企业孵化信息] = { column: ["fId", "eId", "moveOutTime", "moveOutType"], where: {} };
// // manyTableInfo[TABLENAME.企业经营信息] = { column: ["annual"], where: {} };
// let fhdbList = await selectManyTableData(OPERATIONALDATATYPE.多表联查, TABLENAME.企业基础信息表, {state: CHANGESTATE.已通过}, fhColumn, manyTableInfo);
// let manageList = await selectData(OPERATIONALDATATYPE.查询多个, TABLENAME.企业经营信息, {}, ["annual", "eId"]);
// let map = {};
// manageList.forEach(info => {
// map[info.eId] = 1;
// });
// fhdbList.forEach(info => {
// let riskLevel = "";
// if (info.enterprise_fuhuas[0].moveOutType == EMIGRATIONTYPE.违约退租) {
// yj.高风险 += 1;
// riskLevel = "高风险";
// riskEnterprises.push([info.enterpriseName, riskLevel]);
// } else if (!map[info.eId]) {
// yj.中低风险 += 1;
// riskLevel = "中低风险";
// riskEnterprises.push([info.enterpriseName, riskLevel]);
// } else if (info.enterprise_fuhuas[0].moveOutType == EMIGRATIONTYPE.到期退租) {
// yj.关注 += 1;
// riskLevel = "关注";
// riskEnterprises.push([info.enterpriseName, riskLevel]);
// }
// });
// let yujiData = [];
// for (let key in yj) {
// yujiData.push({key, value:yj[key]});
// }
// return {
// yujiData,
// riskEnterprises
// };
// }
/**
* 价值分析
*/
......@@ -527,18 +611,18 @@ export async function getYuYiFuHua() {
let entryList = await selectData(OPERATIONALDATATYPE.查询多个, TABLENAME.入驻信息表, {}, [`info_enterId`, `building`, `occupancyRate`, `enteredEnterprises`]);
let 入驻企业 = await get入驻企业列表();
let entryInfo = {
"1":{"入率":"", "入驻企业":入驻企业[BUILDING["1号楼"]].length},
"3":{"入率":"", "入驻企业":入驻企业[BUILDING["3号楼"]].length},
"4":{"入率":"", "入驻企业":入驻企业[BUILDING["4号楼"]].length}};
"1":{"入率":"", "入驻企业":入驻企业[BUILDING["1号楼"]].length},
"3":{"入率":"", "入驻企业":入驻企业[BUILDING["3号楼"]].length},
"4":{"入率":"", "入驻企业":入驻企业[BUILDING["4号楼"]].length}};
if (entryList.length) {
entryList.forEach(info => {
// let building = changeEnumValue(BUILDING, info.building);
let building = info.building;
if (!entryInfo[building]) {
entryInfo[building] = { "入率": "", "入驻企业": "" };
entryInfo[building] = { "入率": "", "入驻企业": "" };
}
entryInfo[building]. = info.occupancyRate;
entryInfo[building]. = info.occupancyRate;
entryInfo[building].入驻企业 = 入驻企业[building].length;
});
}
......@@ -575,7 +659,7 @@ export async function getYuYiFuHua() {
/**
* 雨艺孵化器基本信息修改
* @param yId
* @param entryInfo {"1号楼":{入率:"", 入驻企业:""}, ......}
* @param entryInfo {"1号楼":{入率:"", 入驻企业:""}, ......}
* @param promotionInfo {"线上推广":{"活动宣讲":0, "三方机构合作":0}, "线下推广":{"活动宣讲":0, "三方机构合作":0}}
*/
export async function updateYuYi(yId, param) {
......@@ -599,7 +683,7 @@ export async function updateYuYi(yId, param) {
let entryInfo = param.entryInfo;
for (let key in entryInfo) {
let 入驻信息updateInfo = {occupancyRate:entryInfo[key]., enteredEnterprises:entryInfo[key].入驻企业};
let 入驻信息updateInfo = {occupancyRate:entryInfo[key]., enteredEnterprises:entryInfo[key].入驻企业};
let updateParam = {building:key}
await operationalData(OPERATIONALDATATYPE.修改, TABLENAME.入驻信息表, 入驻信息updateInfo, updateParam);
}
......@@ -636,6 +720,58 @@ export async function updateYuYi(yId, param) {
export async function get在孵企业面积() {
// 获取当前时间
// let nowTime = new Date( moment().format("YYYY-01-01") ).valueOf();
let nowTime = new Date().valueOf();
let zlColumn = ["eId", "area", "unitPrice", "isDeposit", "startTime", "endTime", "rentFreeStart", "rentFreeEnd", "roomNumber",
"rent", "notes", "building",
"sanFangXieYi", "fuHuaXieYi", "fangWuZuLing", "fuHuaXieYiBuChong", "chengXinChengNuoHan", "yingYeZhiZhao", "ruFuZiLiao", "ruZhuJiHua", "yaJinZhiFu", "cardCopy"];
// "rent", "notes", "leaseContract", "entryPlan", "businessLicense", "agreement"];
// let manyTableInfo: any = {}
// manyTableInfo[TABLENAME.企业孵化信息] = { column: ["fId", "eId", "startTime", "endTime", "state"], where: {state: { "%between%": [FUHUASTATE.实体孵化, FUHUASTATE.虚拟孵化] }} };
// manyTableInfo[TABLENAME.租赁信息] = { column: zlColumn, where: {"startTime": {"%lt%": nowTime}, "endTime": { "%gt%": nowTime }} }; //租赁开始时间小于当前时间、租赁结束时间大于当前时间
// let zldbList = await selectManyTableData(OPERATIONALDATATYPE.多表联查, TABLENAME.企业基础信息表, {}, ["industry", "eId"], manyTableInfo);
/**2025.06.05 罗总开会确认去掉租赁时间,在孵面积计算只用判断是否在孵企业 */
let manyTableInfo: any = {}
manyTableInfo[TABLENAME.企业孵化信息] = { column: ["fId", "eId", "startTime", "endTime", "state"], where: {state: { "%between%": [FUHUASTATE.实体孵化, FUHUASTATE.虚拟孵化] }} };
manyTableInfo[TABLENAME.租赁信息] = { column: zlColumn, where: {} }; //租赁开始时间小于当前时间、租赁结束时间大于当前时间
let zldbList = await selectManyTableData(OPERATIONALDATATYPE.多表联查, TABLENAME.企业基础信息表, {state: CHANGESTATE.已通过}, ["industry", "eId"], manyTableInfo);
let 在孵企业面积 = 0;
let 各楼栋在孵企业面积 = {
"1":0,
"3":0,
"4":0
}
zldbList.forEach( info => {
let {enterprise_fuhuas, enterprise_leases} = info;
// if (nowTime <= new Date(enterprise_fuhuas[0].endTime).valueOf()) {
// enterprise_leases.forEach( leases => {
// if (leases.area) 在孵企业面积 += parseFloat(leases.area);
// })
// }
enterprise_leases.forEach( leases => {
if (leases.building) {
if (leases.area) 各楼栋在孵企业面积[leases.building] += parseFloat(leases.area);
}
if (leases.area) 在孵企业面积 += parseFloat(leases.area);
})
});
let 雨艺孵化器dbList = await selectData(OPERATIONALDATATYPE.查询单个, TABLENAME.羽翼孵化器信息, {}, []);
let 孵化器总面积 = parseFloat(雨艺孵化器dbList.totalArea);
let zfqymjzb = ((在孵企业面积 / 孵化器总面积) * 100);
let 在孵企业面积占比 = "0%";
if (zfqymjzb >= 100) 在孵企业面积占比 = "100%";
else 在孵企业面积占比 = zfqymjzb.toFixed(2) + "%";
return {孵化器总面积, 在孵企业面积, 在孵企业面积占比, 各楼栋在孵企业面积};
}
......@@ -46,7 +46,8 @@ import { systemSendMail } from "./mail";
logonAddress:JSON.stringify(["", "", "", param.logonAddress]),
mail:param.mail,//邮箱地址
state:enumConfig.CHANGESTATE.未审核,
register:enumConfig.CHANGESTATE.未审核
register:enumConfig.CHANGESTATE.未审核,
// createTime:getMySqlMs(),
};
await operationalData(OPERATIONALDATATYPE.增加, TABLENAME.企业基础信息表, addEInfo, {});
......@@ -346,6 +347,29 @@ export async function settleInEnterpriseOut(eId:string, descType, desc:string) {
mailStr += `<p>我们期待未来与贵公司的合作机会,衷心祝愿贵公司业务发展顺利!</p>`;
await systemSendMail(resInfo.eId, enumConfig.MAILTYPE.驳回入孵申请, mailStr );
/**设置7天后再次提醒 */
setTimeout( async() => {
try {
//检查企业状态是否仍然是已驳回(未重新提交)
let currentInfo = await selectData(OPERATIONALDATATYPE.查询单个, TABLENAME.企业基础信息表, {eId}, ["register"]);
if (currentInfo && currentInfo.register === enumConfig.CHANGESTATE.已驳回) {
let reminderMailStr = "";
reminderMailStr += "<p>【雨艺孵化器】入孵申请提醒</p>";
reminderMailStr += "<p>尊敬的客户:</p>";
reminderMailStr += "<p>我们注意到您在7天前提交的入孵申请未通过初审,目前尚未收到您的重新提交。</p>";
reminderMailStr += `<p>驳回原因:${descStr}</p>`;
reminderMailStr += "<p>如果您仍有入驻意向,请根据审核意见完善资料后重新提交申请。</p>";
reminderMailStr += "<p>如有任何疑问,欢迎随时联系我们。</p>";
reminderMailStr += "<p>期待您的回复!</p>";
await systemSendMail(resInfo.eId, enumConfig.MAILTYPE.驳回入孵申请, reminderMailStr);
console.log(`7天后入孵申请驳回提醒邮件已发送给企业 ${resInfo.eId}`);
}
} catch (error) {
console.error(`发送7天后入孵申请驳回提醒邮件失败:`, error);
}
}, 7 * 24 * 60 * 60 * 1000); // 7天后的毫秒数
return {isSuccess:true};
}
......@@ -626,7 +650,7 @@ export async function enterpriseRegisterExamineOut(eId:string, descType, desc:st
}
await operationalData( OPERATIONALDATATYPE.增加, TABLENAME.入孵申请审批表, addInfo, {} );
/**发送邮件 */
/**发送邮件 - 首次驳回通知 */
let mailStr = "";
mailStr += "<p>感谢贵公司积极配合入孵流程。经审核,贵公司提交的入孵材料<span style='font-weight: 700;'>暂未通过审核</span>,主要原因如下:</p>";
mailStr += `<ul>`;
......@@ -637,6 +661,29 @@ export async function enterpriseRegisterExamineOut(eId:string, descType, desc:st
mailStr += `<p>感谢理解与配合!</p>`;
await systemSendMail(resInfo.eId, enumConfig.MAILTYPE.驳回入孵材料申请, mailStr );
/**设置7天后再次提醒 */
setTimeout(async () => {
try {
// 检查企业状态是否仍然是已驳回(未重新提交材料)
let currentInfo = await selectData(OPERATIONALDATATYPE.查询单个, TABLENAME.企业基础信息表, {eId}, ["state"]);
if (currentInfo && currentInfo.state === enumConfig.CHANGESTATE.已驳回) {
let reminderMailStr = "";
reminderMailStr += "<p>【雨艺孵化器】入孵材料审核提醒</p>";
reminderMailStr += "<p>尊敬的客户:</p>";
reminderMailStr += "<p>我们注意到您在7天前提交的入孵材料未通过审核,目前尚未收到您重新提交的完善材料。</p>";
reminderMailStr += `<p>驳回原因:${descStr}</p>`;
reminderMailStr += "<p>请您根据审核意见尽快完善相关材料并重新提交,以便我们继续为您处理入孵申请。</p>";
reminderMailStr += "<p>如有任何疑问,欢迎随时联系我们。</p>";
reminderMailStr += "<p>期待您的完善材料!</p>";
await systemSendMail(resInfo.eId, enumConfig.MAILTYPE.驳回入孵材料申请, reminderMailStr);
console.log(`7天后入孵材料驳回提醒邮件已发送给企业 ${resInfo.eId}`);
}
} catch (error) {
console.error(`发送7天后入孵材料驳回提醒邮件失败:`, error);
}
}, 7 * 24 * 60 * 60 * 1000); // 7天后的毫秒数
return {isSuccess:true};
}
......
......@@ -5,7 +5,7 @@
import { OPERATIONALDATATYPE, TABLENAME } from "../config/enum/dbEnum";
import { ERRORENUM } from "../config/enum/errorEnum";
import { operationalData, selectData } from "../data/operationalData";
import { getMySqlMs, getToken, md5PwdStr } from "../tools/system";
import { getMySqlMs, getPwdMd5, getToken, md5PwdStr } from "../tools/system";
import { BizError } from "../util/bizError";
......@@ -16,10 +16,12 @@ export async function adminLogin(loginId:string, pwd:string) {
let filesList = ["name", "aId", "pwd"];
let adminUserInfo = await selectData(OPERATIONALDATATYPE.查询单个, TABLENAME.管理后台用户, {loginId}, filesList);
console.log(adminUserInfo.pwd);
if (!adminUserInfo || !adminUserInfo.aId) {
throw new BizError(ERRORENUM.账号或密码错误);
}
if (adminUserInfo.pwd != pwd) {
const encryptedPwd = getPwdMd5(adminUserInfo.aId, pwd);
if (adminUserInfo.pwd != encryptedPwd) {
throw new BizError(ERRORENUM.账号或密码错误);
}
......@@ -78,3 +80,29 @@ export async function enterpriseLogout(eId:string) {
return {isSuccess:true};
}
/**
* 修改密码
* @param uId
* @param pwd 原密码
* @param newPwd 新密码
* @param confirmPwd 再次确认密码
* @returns
*/
export async function changePassword(uId:string, pwd:string, newPwd:string, confirmPwd:string) {
let adminUserInfo = await selectData(OPERATIONALDATATYPE.查询单个, TABLENAME.管理后台用户, { aId:uId }, ["loginId", "aId", "pwd"]);
if (!adminUserInfo.aId) throw new BizError(ERRORENUM.数据不存在);
/**验证当前密码 */
if (pwd != adminUserInfo.pwd) throw new BizError(ERRORENUM.原密码错误);
if (newPwd != confirmPwd) throw new BizError(ERRORENUM.密码不一致);
if (newPwd.search(/^[A-Za-z0-9]{6,18}$/) < 0) throw new BizError(ERRORENUM.密码只能由618位字符和数字组成);
await operationalData(OPERATIONALDATATYPE.修改, TABLENAME.管理后台用户, {pwd:newPwd}, { aId:uId });
return {isSuccess:true};
}
......@@ -880,19 +880,19 @@ export async function dwFinancingSituatione(eId: string) {
resInfo.forEach(info => {
let {enterpriseName, uscc, enterprise_financings} = info;
enterprise_financings.forEach(finnancing => {
let subList = [];
valueList.forEach(subInfo => {
if (subInfo == "enterpriseName") subList.push(enterpriseName);//企业名称
if (subInfo == "uscc") subList.push(uscc); //统一信用代码
if (subInfo == "financingAmount") subList.push(enterprise_financings[0].financingAmount);//融资金额
if (subInfo == "financingRounds") subList.push(changeEnumValue(enumConfig.FINANCINGROUNDS, enterprise_financings[0].financingRounds));//融资轮次
if (subInfo == "investmentDate") subList.push(moment(enterprise_financings[0].investmentDate).format("YYYY-MM-DD"));//获得投资时间
if (subInfo == "investmentInstitutionsName") subList.push(enterprise_financings[0].investmentInstitutionsName);//投资机构名称
if (subInfo == "financingAmount") subList.push(finnancing.financingAmount);//融资金额
if (subInfo == "financingRounds") subList.push(changeEnumValue(enumConfig.FINANCINGROUNDS, finnancing.financingRounds));//融资轮次
if (subInfo == "investmentDate") subList.push(moment(finnancing.investmentDate).format("YYYY-MM-DD"));//获得投资时间
if (subInfo == "investmentInstitutionsName") subList.push(finnancing.investmentInstitutionsName);//投资机构名称
});
dataList.push(subList);
})
})
return {dataList};
......
......@@ -37,7 +37,7 @@ export enum TABLENAME {
企业服务表 ='enterprise_service',
企业基础信息表 ='enterprise',
政策表 = 'policy',
管理后台用户 = 'adminUser',//以下为新表
管理后台用户 = 'adminuser',//以下为新表
变更信息表 = "info_update",
企业标签表 = "enterprise_label",
股权结构 = "ownership",
......
......@@ -81,7 +81,8 @@ export enum EMIGRATIONTYPE {
毕业迁出 = 1,
毕业未迁出, //新加状态
到期退租,
违约退租
违约退租,
到期未迁出
}
......@@ -176,7 +177,8 @@ export enum FINANCINGROUNDS {
export enum POLICYTYPE {
财政补贴 = 1,
资质申报 = 2,
政策扶持 = 3
政策扶持 = 3,
// 政策宣讲 = 3
}
......@@ -187,7 +189,8 @@ export enum CLIENTPOLICYTYPE {
全部 = 0,
财政补贴 = 1,
资质申报 = 2,
政策扶持 = 3
政策扶持 = 3,
// 政策宣讲 = 3
}
......
......@@ -41,7 +41,10 @@ export enum ERRORENUM {
该用户邮箱为空,
邮件发送失败,
该企业暂未提交入孵材料,
请选择驳回原因
请选择驳回原因,
原密码错误,
密码不一致,
系统错误
}
export enum ERRORCODEENUM {
......
// 使用示例:下载多个图片并打包成ZIP
const { downloadImagesByUrls, downloadImagesWithNames, downloadImagesAndZip } = require('./imgZip');
// 示例1:根据URL数组下载图片并打包
async function example1() {
const imageUrls = [
'https://example.com/image1.jpg',
'https://example.com/image2.png',
'https://example.com/image3.gif'
];
const result = await downloadImagesByUrls(imageUrls, 'my_images');
if (result.isSuccess) {
console.log('ZIP创建成功:', result.zipPath);
} else {
console.error('错误:', result.error);
}
}
// 示例2:指定自定义文件名
async function example2() {
const imageUrls = [
'https://example.com/image1.jpg',
'https://example.com/image2.png'
];
const filenames = [
'封面图.jpg',
'详情图.png'
];
const result = await downloadImagesWithNames(imageUrls, filenames, 'custom_names');
if (result.isSuccess) {
console.log('ZIP创建成功:', result.zipPath);
} else {
console.error('错误:', result.error);
}
}
// 示例3:使用完整的图片信息对象
async function example3() {
const images = [
{ url: 'https://example.com/image1.jpg', filename: '图片1.jpg' },
{ url: 'https://example.com/image2.png' }, // 不指定文件名,会自动生成
{ url: 'https://example.com/image3.gif', filename: '特殊图片.gif' }
];
const result = await downloadImagesAndZip(images, 'mixed_images');
if (result.isSuccess) {
console.log('ZIP创建成功:', result.zipPath);
} else {
console.error('错误:', result.error);
}
}
module.exports = {
example1,
example2,
example3
};
import { integration } from "./biz/dataAsync";
import { migrateAdminPasswords, migrateEnterpriseUserPasswords } from "./biz/dataInit";
import { updateItemQCCData } from "./biz/qccInit";
import { initConfig, systemConfig } from "./config/serverConfig";
import { initApiDataStorage } from "./data/dataInterfaceWithCache";
......@@ -9,10 +11,14 @@ async function lanuch() {
httpServer.createServer(systemConfig.port);
console.log('This indicates that the server is started successfully.');
await initApiDataStorage();
// await initApiDataStorage();
// await migrateEnterpriseUserPasswords();
// await migrateAdminPasswords();
// await integration();
}
lanuch();
......@@ -14,7 +14,7 @@ export class httpServer {
httpServer.all('*', (req, res, next) => {
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header('Access-Control-Allow-Headers', 'Content-Type,request-origin,userid,token');
res.header('Access-Control-Allow-Headers', 'Content-Type,request-origin,userid,token,Authorization');
res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
res.header('Access-Control-Allow-Credentials', true);
res.header("X-Powered-By", ' 3.2.1');
......
......@@ -18,6 +18,7 @@ export function setRouter(httpServer) {
//管理员登录登出
httpServer.post('/admin/user/login', asyncHandler(login));
httpServer.post('/admin/user/logout', checkUser, asyncHandler(logout));
httpServer.post('/admin/user/changepwd', checkUser, asyncHandler(changePassword));
// //新入孵企业
httpServer.post('/admin/enterprise/settlein/list', checkUser, asyncHandler(settleIn));
......@@ -737,6 +738,20 @@ async function logout(req, res) {
/**
* 修改密码
* @param req
* @param res
*/
async function changePassword(req, res) {
const UserInfo = req.userInfo;
let {pwd, newPwd, confirmPwd } = req.body
let result = await userBiz.changePassword(UserInfo.uId, pwd, newPwd, confirmPwd );
res.success(result);
}
/**
*
* @param req
* @param res
......
......@@ -6,7 +6,7 @@ import * as asyncHandler from 'express-async-handler';
import { checkInterior } from '../middleware/user';
import { getMySqlMs, getPinyinInitials, randomId } from '../tools/system';
import { OPERATIONALDATATYPE, TABLEID, TABLENAME } from '../config/enum/dbEnum';
import { ANSWERTYPE, CHANGESTATE, DEGREE, FUHUASTATE, IPRALLTYPE, STATE, ZZMM } from '../config/enum/enum';
import { ANSWERTYPE, CHANGESTATE, DEGREE, FOLLOWUPSTATUS, FUHUASTATE, IPRALLTYPE, NEEDCATEGORY, OUTCOME, STATE, ZZMM } from '../config/enum/enum';
import { operationalData, selectData } from '../data/operationalData';
import { initApiDataStorage } from '../data/dataInterfaceWithCache';
import { updateQCCDataTask } from '../biz/qccInit';
......@@ -30,8 +30,108 @@ export function setRouter(httpServer) {
httpServer.post('/admin/data/maintenance/datainit/loginTime', checkInterior, asyncHandler(dataUpdateLoginTime));
httpServer.post('/admin/data/maintenance/datainit/updatetime', checkInterior, asyncHandler(updateTime));
httpServer.post('/admin/data/maintenance/datainit/addenterprise', checkInterior, asyncHandler(addEnterprise));
httpServer.post('/admin/data/maintenance/datainit/addservice', checkInterior, asyncHandler(addService));
}
/**
* 导入企业服务
*/
export async function addService(req, res) {
const FIELD_MAPPING = {
/**企业服务表 */
'需求类别': 'needCategory',
'需求内容': 'needContent',
'申请时间': 'applyTime',
'跟进状态': 'followUpStatus',
'解决时间': 'resolveTime',
'反馈': 'fangKui',
'反馈时间': 'shouLiTime',
'结果': 'outcome',
'备注': 'desc'
};
//2.读取文件
let {sheetMap} = getExcel(path.join(__dirname.substring(0,__dirname.indexOf("out")), "res", '企业服务导入数据.xlsx' ));
let dataList1 = sheetMap['未受理'];
let dataList2 = sheetMap['已受理'];
let dataList3 = sheetMap['已结束'];
for (let i = 1; i < dataList1.length; i++) {
let info = dataList1[i];
let enterpriseName =info[0];
let uscc = info[1];
let enterpriseDbInfo = await selectData(OPERATIONALDATATYPE.查询单个, TABLENAME.企业基础信息表, {enterpriseName, uscc}, ["eId", "uscc", "enterpriseName"]);
if (!enterpriseDbInfo.eId) throw new BizError("未匹配到对应企业");
let excelInfo = {
esId: randomId(TABLEID.企业服务表),
eId:enterpriseDbInfo.eId,
needCategory:NEEDCATEGORY[info[2]],
needContent:info[3],
applyTime:info[4],
followUpStatus:FOLLOWUPSTATUS.未受理,
}
// 新增企业服务
await operationalData(OPERATIONALDATATYPE.增加, TABLENAME.企业服务表, excelInfo, {});
console.log(`新增未受理企业服务信息完成:${enterpriseName}`);
}
for (let i = 1; i < dataList2.length; i++) {
let info = dataList2[i];
let enterpriseName =info[0];
let uscc = info[1];
let enterpriseDbInfo = await selectData(OPERATIONALDATATYPE.查询单个, TABLENAME.企业基础信息表, {enterpriseName, uscc}, ["eId", "uscc", "enterpriseName"]);
if (!enterpriseDbInfo.eId) throw new BizError("未匹配到对应企业");
//{fangKui, followUpStatus:enumConfig.FOLLOWUPSTATUS.受理中, shouLiTime:getMySqlMs()}
let excelInfo = {
esId: randomId(TABLEID.企业服务表),
eId:enterpriseDbInfo.eId,
needCategory:NEEDCATEGORY[info[2]],
needContent:info[3],
applyTime:info[4],
fangKui:info[5],
shouLiTime:getMySqlMs(info[6]),
followUpStatus:FOLLOWUPSTATUS.受理中,
}
// 新增企业服务
await operationalData(OPERATIONALDATATYPE.增加, TABLENAME.企业服务表, excelInfo, {});
console.log(`新增受理中企业服务信息完成:${enterpriseName}`);
}
for (let i = 1; i < dataList3.length; i++) {
let info = dataList3[i];
let enterpriseName =info[0];
let uscc = info[1];
let enterpriseDbInfo = await selectData(OPERATIONALDATATYPE.查询单个, TABLENAME.企业基础信息表, {enterpriseName, uscc}, ["eId", "uscc", "enterpriseName"]);
if (!Object.keys(enterpriseDbInfo).length) throw new BizError(ERRORENUM.企业不存在);
if (!enterpriseDbInfo.eId) throw new BizError(ERRORENUM.企业不存在);
//{outcome, followUpStatus:enumConfig.FOLLOWUPSTATUS.已完成, desc, resolveTime}
let excelInfo = {
esId: randomId(TABLEID.企业服务表),
eId:enterpriseDbInfo.eId,
needCategory:NEEDCATEGORY[info[2]],
needContent:info[3],
applyTime:info[4],
fangKui:info[5],
shouLiTime:getMySqlMs(info[6]),
outcome: OUTCOME[info[7]],
desc:info[8],
followUpStatus:FOLLOWUPSTATUS.已完成,
}
// 新增企业服务
await operationalData(OPERATIONALDATATYPE.增加, TABLENAME.企业服务表, excelInfo, {});
console.log(`新增已完成企业服务信息完成:${enterpriseName}`);
}
res.success({isSuccess:true});
}
......
......@@ -8,6 +8,7 @@ import * as fuhuaRouters from './fuhua';
import * as dbInitRouters from './dbinit';
import * as userRuFuRouters from './userRuFu';
import * as answerRouters from './answer';
import * as zipRouters from './zip';
export function setRouter(httpServer){
/**下拉框等公用 路由 */
publicRouters.setRouter(httpServer);
......@@ -20,4 +21,6 @@ export function setRouter(httpServer){
userRuFuRouters.setRouter(httpServer);
answerRouters.setRouter(httpServer);
zipRouters.setRouter(httpServer);
}
\ No newline at end of file
/**
* zip文件下载
*/
import * as asyncHandler from 'express-async-handler';
import * as zipBiz from '../biz/createZip';
import { checkUser } from '../middleware/user';
import * as fs from 'fs-extra';
export function setRouter(httpServer) {
httpServer.post('/admin/zip/download', checkUser, asyncHandler(downloadZipfiles));
}
/**
* 下载企业文件ZIP包
* @param req
* @param res
*/
async function downloadZipfiles(req, res) {
try {
console.log('收到ZIP下载请求,参数:', req.body);
let { eId } = req.body;
if (!eId) {
return res.status(400).json({ error: '缺少必要参数eId' });
}
let result = await zipBiz.downloadEnterpriseFilesZip(eId);
console.log('ZIP文件创建成功:', result.fileName);
// 设置响应头
res.setHeader('Content-Type', 'application/zip');
res.setHeader('Content-Disposition', `attachment; filename=${encodeURIComponent(result.fileName)}`);
res.setHeader('Access-Control-Expose-Headers', 'Content-Disposition');
// 检查文件是否存在
if (!await fs.pathExists(result.filePath)) {
throw new Error('ZIP文件未找到: ' + result.filePath);
}
// 获取文件状态
const stats = await fs.stat(result.filePath);
res.setHeader('Content-Length', stats.size);
console.log(`准备发送文件: ${result.fileName}, 大小: ${stats.size} bytes`);
// 发送文件
res.sendFile(result.filePath, (err) => {
if (err) {
console.error('发送文件失败:', err);
if (!res.headersSent) {
res.status(500).json({ error: '文件发送失败', message: err.message });
}
} else {
console.log('文件发送成功');
}
// 可选:发送完成后删除临时文件
setTimeout(async () => {
try {
if (await fs.pathExists(result.filePath)) {
await fs.remove(result.filePath);
console.log('临时文件已清理:', result.filePath);
}
} catch (cleanupError) {
console.error('清理临时文件失败:', cleanupError);
}
}, 1000);
});
} catch (error) {
console.error('下载文件失败:', error);
if (!res.headersSent) {
res.status(500).json({
error: '下载失败',
message: error.message,
stack: process.env.NODE_ENV === 'development' ? error.stack : undefined
});
}
}
}
{
"compilerOptions": {
"module": "commonjs",
"target": "es2017",
"target": "ES2020",
"sourceMap": true,
"rootDir":"./src",
"outDir":"./out"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment