import { Controller } from './base' import { RequestMapping } from '../router' import { Scenariofield, DbCollection } from '../types' import DbClient from '../bll/client/db' import i18n from '../service/i18n' import TaskFactory from '../bll/factory/import/task' import TestcaseFactory from '../bll/factory/import/testcase' import TesthubClient from '../bll/client/testhub' import TaskExportFactory from '../bll/factory/export/task' import ProjectClusterExportFactory from '../bll/factory/export/project' import TeambitionClient from '../bll/client/teambition' import { ilog } from 'ilog' import * as moment from 'moment' import * as config from 'config' export class Export extends Controller{ /** * * @api {get} /export/template 导出项目模板 * @apiName GetTemplate * @apiGroup Export * * * @apiParam {String} _scenariofieldconfigId 任务类型 * * @description: 历史原因,导出任务的路由不清晰 */ @RequestMapping.get('/export/template', { type: 'object', properties: { _scenariofieldconfigId: { type: 'string', format: 'objectid'} }, required: ['_scenariofieldconfigId'] }) async exportTemplate () { const { _scenariofieldconfigId } = this.ctx.state const dbClient = new DbClient(this.app, this.ctx) let scenariofieldconfig:any try { scenariofieldconfig = await dbClient.getScenariofieldconfigById(_scenariofieldconfigId) } catch (error) { if (error && error.statusCode === 404) { this.ctx.throw(400, 'invalidScenariofieldconfigId') } else { this.ctx.throw(400, 'pull scenariofieldconfig error') } } const scenariofields: Scenariofield[] = scenariofieldconfig.scenariofields const project = await dbClient.getProjectById(scenariofieldconfig._boundToObjectId) const factory = new TaskFactory(this.app, this.ctx) await factory.buildHeaders(scenariofields, project.proTemplateType || project.normalType) const workbook = await factory.buildWorkbook('task') workbook.xlsx.createInputStream() // // 项目名称_任务类型名称_模板 this.ctx.status = 200 this.ctx.response.attachment(`${project.name}_${scenariofieldconfig.name}_${i18n.__('template')}.xlsx`) await workbook.xlsx.write(this.ctx.res) this.ctx.res.end() } /** * * @api {get} /export/template/testcase 导出测试用例模板 * @apiName exportTestcaseTpl * @apiGroup Export * @apiVersion 0.0.1 * @apiParam {String} [basic=false] 是否为基础用例模板 * @apiParam {String} [_projectId] 项目 ID * @apiParam {String} [_testhubId] 用例库 ID */ @RequestMapping.get('/export/template/testcase', { type: 'object', properties: { basic: { type: 'boolean' }, _projectId: { type: 'string', format: 'objectid' }, _testhubId: { type: 'string', format: 'objectid' } }, required: [] }) async exportTestcaseTpl () { const { basic = false, _projectId, _testhubId } = this.ctx.state let testhub: DbCollection.Testhub let project: DbCollection.Project if (basic && _testhubId) { const testhubClient = new TesthubClient(this.app, this.ctx) testhub = await testhubClient.getTesthub(_testhubId) } else if (!basic && _projectId) { const dbClient = new DbClient(this.app, this.ctx) project = await dbClient.getProjectById(_projectId) } if (!project && !testhub) { this.ctx.throw(400, 'RequiredError: _projectId / _testhubId') } const factory = new TestcaseFactory(this.app, this.ctx, basic) const workbook = await factory.buildHeaders().buildExample().buildWorkbook(basic ? 'basictestcase' : 'testcase') workbook.xlsx.createInputStream() const fileName = basic ? `${testhub.name}_${i18n.__('basic.testcase')}_${i18n.__('template')}.xlsx` : `${project.name}_${i18n.__('testcase')}_${i18n.__('template')}.xlsx` // 项目名称_任务类型名称_模板 this.ctx.status = 200 this.ctx.response.attachment(fileName) await workbook.xlsx.write(this.ctx.res) this.ctx.res.end() } /** * @api {get} /export/resources 导出资源 * @apiName exportResources * @apiGroup Export * @apiVersion 0.0.1 * @apiParam {String} [_tasklistId] 分组 ID, 导出 csv 文件需指定对应分组ID * @apiParam {String} _projectId 目标项目 ID * @apiParam {String} resourceType 资源类型, enum: ['task'] * @apiParam {String} fileType 生成文件类型, enum: ['xlsx', 'xml', 'csv'] */ @RequestMapping.get('/export/resources', { type: 'object', properties: { _projectId: { type: 'string', format: 'objectid' }, _tasklistId: { type: 'string', format: 'objectid' }, resourceType: { type: 'string', enum: ['task'] }, fileType: { type: 'string', enum: ['xlsx', 'xml', 'csv'] } }, required: ['resourceType', 'fileType', '_projectId'] }) async exportResources () { const { fileType, resource, _projectId, _tasklistId } = this.ctx.state const factory = new TaskExportFactory(this.app, this.ctx) factory.resource = resource factory.fileType = fileType factory.projectId = _projectId factory.tasklistId = _tasklistId await factory.build() } /** * * @api {get} /export/project-cluster 导出项目集群 * @apiGroup Export * @apiParam {String} selectBy 列表类型, enum: ['owned', 'joined', 'public', 'suspended', 'archived', 'ungrouped', 'projecttag', 'starred', 'history'] * @apiParam {String} _projecttagId 项目分组 ID * @apiParam {String} _organizationId 企业ID * @apiParam {String} headers 表头, enum: ['basic:project.name', 'basic:project.status.degree', * 'basic:project.member.owners', 'basic:project.report.task.unallocated', * 'basic:project.task.done.degree', 'basic:project.task.overdue.degree', 'basic:project.create.time', 'basic:project.duration.time', 'customfield:objectId'] * */ @RequestMapping.get('/export/project-cluster', { type: 'object', properties: { selectBy: { type: 'string', enum: ['owned', 'joined', 'public', 'suspended', 'archived', 'ungrouped', 'projecttag', 'starred', 'history'] }, _projecttagId: { type: 'string', format: 'objectid' }, _organizationId: { type: 'string', format: 'objectid' }, headers: { type: 'string' } }, required: ['selectBy', '_organizationId', 'headers'] }) async exportProjectCluster () { const { _organizationId, _projecttagId, selectBy, lang } = this.ctx.state let headers = [] i18n.setLocale(lang) try { headers = JSON.parse(this.ctx.state.headers) } catch (error) { ilog.error(error) this.ctx.throw(400, 'invalid params headers') } if (headers.length > config.EXPORT_LIMIT.PROJECT_HEADER_LIMIT) { this.ctx.throw(400, `ExceedLimit: ${config.EXPORT_LIMIT.PROJECT_HEADER_LIMIT}`) } const tbClient = new TeambitionClient(this.app, this.ctx) let projectInfo: any let customfieldInfo: any let projecttagInfo: any const customfieldIds: string [] = [] headers.forEach(function (ele) { const items = ele.split(':') const type = items[0] const key = items[1] if (type === 'customfield') { customfieldIds.push(key) } }) try { projectInfo = await tbClient.getProjectCluster(_organizationId, selectBy, _projecttagId, customfieldIds) } catch (error) { ilog.error(error) this.ctx.throw(400, 'pull projectInfo error') } try { customfieldInfo = await tbClient.getCustomfields(_organizationId, customfieldIds) } catch (error) { ilog.error(error) this.ctx.throw(400, 'pull customfieldInfo error') } let projecttagName try { if (selectBy === 'projecttag') { projecttagInfo = _projecttagId && await tbClient.getProjecttag(_projecttagId) projecttagName = projecttagInfo.data.ProjectTag.name } else { projecttagName = i18n.__(`${selectBy}.project`) } } catch (error) { ilog.error(error) this.ctx.throw(400, 'pull projecttagInfo error') } const projects = projectInfo.data.OrganizationProjects.result const customfields = customfieldInfo.data.Customfields.result const projectCount = projectInfo.data.OrganizationProjects.totalSize const factory = new ProjectClusterExportFactory(this.app, this.ctx) factory.lang = lang factory.title = `【${projecttagName}】${i18n.__('project.info.table')} ${i18n.__('export.date')}:${moment().format('YYYYMMDD')}` factory.sheetName = `【${projecttagName}】${i18n.__('project.info.table')}` factory.fileName = `【${projecttagName}】${i18n.__('project.info.table')}_${moment().format('YYYYMMDD')}` factory.projects = projects factory.customfields = customfields factory.headers = headers factory.projectCount = projectCount await factory.build() } }