导读:本专栏主要分享同学们在XIAOJUSURVEY&北大开源实践课程的学习成果。
专栏背景:【XIAOJUSURVEY&北大】2024滴滴开源XIAOJUSURVEY北大软微开源实践课
文内项目Github:XIAOJUSURVEY
作者:Oseast
背景
本文主要是实现下载中心的源码分析,为了实现数据的导出有以下几个基本功能:
-
整合问卷回收内容
-
获取已经整合导出的问卷列表
-
下载问卷到本地
-
后台的文件删除
这四个基本功能就对应了四个api请求,这四个请求实现如下分析。
一、整合问卷回收内容
要定位问卷回收内容可通过前端返回指定的问卷元数据id实现,因此前端向后端发送请求必须要包括surveyId
这个参数。这个参数可以在问卷分析页通过动态路由轻松获得,即:surveyId: this.$route.params.id
。
后端收到这个数据之后,控制器SurveyDownloadController
通过mongodb查询问卷的基本题目信息交由生产者进行加工。为了防止问卷内容过多整合时间过长导致前端一直等待返回,这里实现了一个简单的消息队列进行解耦。控制器在把任务发送到消息队列上后直接返回,后续过程由后端自动完成。isMask
为是否脱敏保存的标志messageService是实现的简单的消息队列responseSchema是问卷的基本配置。
const { surveyId, isMask } = value;
const responseSchema = await this.responseSchemaService.getResponseSchemaByPageId(surveyId);
const id= await this.surveyDownloadService.createDownload({
surveyId,
responseSchema
});
this.messageService.addMessage({
responseSchema,
surveyId,
isMask,
id,
});
return {
code: 200,
data: { message: '正在生成下载文件,请稍后查看'
},
};
在消息队列中每加入一条任务就会唤醒一位消费者,这个消费者会将问卷信息发送给surveyDownloadService进行加工:
private processMessages = async (): Promise<void> => {
if (this.processing >= this.concurrency || this.queue.length === 0) {
return;
}
const messagesToProcess = Math.min(this.queue.length, this.concurrency - this.processing);
const messages = this.queue.splice(0, messagesToProcess);
this.processing += messagesToProcess;
await Promise.all(messages.map(async (message) => {
console.log(`开始计算: ${message}`);
await this.handleMessage(message);
this.emit('messageProcessed', message);
}));
this.processing -= messagesToProcess;
if (this.queue.length > 0) {
setImmediate(() => this.processMessages());
}
};
async handleMessage(message: QueueItem) {
const { surveyId, responseSchema, isMask,id } = message;
await this.surveyDownloadService.getDownloadPath({
responseSchema,
surveyId,
isMask,
id
});
}
surveyDownloadService
先通过responseSchema
获取题目的基本信息listHead
,然后通过数据库收集对应问卷的回收数据,然后把回收表中的回答和题目对应起来并格式化,组成listBody
,再把这两个列表保存成文件存放在后端相应位置,同时数据库中创建一个对应的信息表:
async getDownloadPath({
surveyId,
responseSchema,
isMask,
id,
}: {
surveyId: string;
responseSchema: ResponseSchema;
isMask: boolean;
id: object;
}) {
const dataList = responseSchema?.code?.dataConf?.dataList || [];
const Head = getListHeadByDataList(dataList);
const listHead=this.formatHead(Head);
const dataListMap = keyBy(dataList, 'field');
const where = {
pageId: surveyId,
'curStatus.status': {
$ne: 'removed',
},
};
const [surveyResponseList, total] =
await this.surveyResponseRepository.findAndCount({
where,
order: {
createDate: -1,
},
});
const [surveyMeta] = await this.SurveyDmetaRepository.find({
where: {
surveyPath: responseSchema.surveyPath,
},
});
const listBody = sur