You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

283 lines
10 KiB

const request = require('superagent');
const moment = require('moment');
const productExcluded = require('../product-excluded');
const Holiday = require('../holiday');
module.exports = {
entry(app, router, opts) {
const ZENTAO_DOMAIN = opts.zentaoDomain;
async function auth() {
const session = await request.get(`${ZENTAO_DOMAIN}/api-getSessionID.json`);
const sessionData = JSON.parse(session.text).data;
const { sessionName, sessionID } = JSON.parse(sessionData);
const loginInfo = await request
.post(`${ZENTAO_DOMAIN}/user-login.json`)
.set({ 'Content-Type': 'application/x-www-form-urlencoded' })
.query({ [`${sessionName}`]: sessionID })
.send({ account: 'admin', password: 'Fashion123' });
app.fs = app.fs || {};
app.fs.session = { sessionName, sessionID };
}
(async function () {
await auth();
setInterval(async () => {
await auth();
}, 1200000); // 每 20分钟 刷新一次
}());
function isWorkday(date) {
let workday = false;
const dateString = moment(date).format('YYYY-MM-DD');
const dateInHoliday = Holiday.days.filter((d) => d.date === dateString);
if (dateInHoliday.length) {
if (!dateInHoliday[0].isOffDay) workday = true; // 补班
} else {
const day = moment(date).day();
if (day !== 0 && day !== 6) { // 工作日
workday = true;
}
}
return workday;
}
function extractBuildTime(inputString) {
const pattern = /[(\(]构建时间[::](\d{4}-\d{1,2}-\d{1,2} \d{1,2}:\d{1,2})[\))]/;
const match = inputString.match(pattern);
const buildDate = match ? moment(new Date(match[1])).format('YYYY-MM-DD HH:mm') : null;
return buildDate;
}
async function getExecutionDetail(executionId) {
const { sessionName, sessionID } = app.fs.session;
const executionDetailRes = await request
.get(`${ZENTAO_DOMAIN}/execution-view-${executionId}.json`)
.set('Cookie', `${sessionName}=${sessionID}`);
const executionDetailText = JSON.parse(executionDetailRes.text);
const executionDetailData = JSON.parse(executionDetailText.data);
const executionTeamRDs = [];
const executionTeamMembers = executionDetailData.teamMembers;
for (const member of Object.values(executionTeamMembers)) {
if (member.role === '研发') executionTeamRDs.push({ name_people: member.realname });
}
const executionProducts = Object.values(executionDetailData.products).map((product) => product.name);
return { executionTeamRDs, executionProducts };
}
async function getExecutionProgress(executionId) {
const { sessionName, sessionID } = app.fs.session;
const testTaskRes = await request
.get(`${ZENTAO_DOMAIN}/execution-testtask-${executionId}.json`)
.set('Cookie', `${sessionName}=${sessionID}`);
const testTaskText = JSON.parse(testTaskRes.text);
const progress = Object.keys(JSON.parse(testTaskText.data).tasks).length ? '测试中' : '研发中';
return progress;
}
async function getExecutionStats(period, baseURL, recPerPage) {
const { sessionName, sessionID } = app.fs.session;
const executionRes = await request
.get(baseURL)
.set('Cookie', `${sessionName}=${sessionID}; pagerExecutionAll=${recPerPage || 10}`);
const executionText = JSON.parse(executionRes.text);
const executionData = JSON.parse(executionText.data);
let executionAll = executionData.executionStats;
const pg = executionData.pager;
let pid = pg.pageID;
while (pid < pg.pageTotal) {
const res = await request
.get(baseURL.replace('.json', `-${pg.recTotal}-${pg.recPerPage}-${pid + 1}.json`))
.set('Cookie', `${sessionName}=${sessionID}`);
const resText = JSON.parse(res.text);
const resData = JSON.parse(resText.data);
executionAll = executionAll.concat(resData.executionStats);
pid++;
}
let executionStats = [];
if (period === 'month') {
const currentMonth = moment().format('YYYY-MM');
executionStats = executionAll.filter((item) => item.type === 'sprint' && (
moment(item.begin).format('YYYY-MM') === currentMonth
|| moment(item.end).format('YYYY-MM') === currentMonth
));
} else { // week
const currentDate = moment();
// 获取当前周的第一天(周一)
const firstDayOfWeek = currentDate.clone().startOf('isoWeek');
// 获取当前周的最后一天(周日)
const lastDayOfWeek = currentDate.clone().endOf('isoWeek');
executionStats = executionAll.filter((item) => {
const buildTime = extractBuildTime(item.name) || item.end;
const begin = moment.max(moment(item.begin), moment(firstDayOfWeek));
const end = moment.min(moment(buildTime), moment(lastDayOfWeek));
return item.type === 'sprint' && begin.isSameOrBefore(end);
});
}
return executionStats;
}
async function getOngoingProjects(ctx, next) {
try {
const executions = [];
// 执行(未开始)
const waitExecutionBaseURL = `${ZENTAO_DOMAIN}/execution-all-wait-0-order_asc-0.json`;
const waitExecutionStats = await getExecutionStats('month', waitExecutionBaseURL, 200);
for (const item of waitExecutionStats) {
const { id, name, end } = item;
const { executionTeamRDs, executionProducts } = await getExecutionDetail(id);
if (executionProducts.some((p) => productExcluded.includes(p))) continue;
executions.push({
execution_id: id,
name_project: name,
build_time: extractBuildTime(name),
publish_time: end,
part_people: executionTeamRDs,
progress: '需求评审中',
});
}
// 执行(进行中)
const doingExecutionBaseURL = `${ZENTAO_DOMAIN}/execution-all-doing-0-order_asc-0.json`;
const doingExecutionStats = await getExecutionStats('month', doingExecutionBaseURL, 200);
for (const item of doingExecutionStats) {
const { id, name, end } = item;
const { executionTeamRDs, executionProducts } = await getExecutionDetail(id);
if (executionProducts.some((p) => productExcluded.includes(p))) continue;
const executionProgress = await getExecutionProgress(id);
executions.push({
execution_id: id,
name_project: name.replace('&amp;', '&'),
build_time: extractBuildTime(name),
publish_time: end,
part_people: executionTeamRDs,
progress: executionProgress,
});
}
ctx.status = 200;
ctx.body = {
total: executions.length,
projects: executions,
};
} catch (e) {
ctx.status = 400;
ctx.body = {
name: 'FindError',
message: `版本计划获取失败${e}`,
};
}
}
async function getOngoingPersons(ctx, next) {
try {
const currentDate = moment();
// 获取当前周的第一天(周一)
const firstDayOfWeek = currentDate.clone().startOf('isoWeek');
// 获取当前周的最后一天(周日)
const lastDayOfWeek = currentDate.clone().endOf('isoWeek');
// 执行(未开始)
const waitExecutionBaseURL = `${ZENTAO_DOMAIN}/execution-all-wait-0-order_asc-0.json`;
const waitExecutionStats = await getExecutionStats('week', waitExecutionBaseURL, 200);
// 执行(进行中)
const doingExecutionBaseURL = `${ZENTAO_DOMAIN}/execution-all-doing-0-order_asc-0.json`;
const doingExecutionStats = await getExecutionStats('week', doingExecutionBaseURL, 200);
const executionAll = waitExecutionStats.concat(doingExecutionStats);
const executions = [];
for (const item of executionAll) {
const {
id, name, begin, end,
} = item;
const { executionTeamRDs, executionProducts } = await getExecutionDetail(id);
if (executionProducts.some((p) => productExcluded.includes(p))) continue;
executions.push({
name_project: name.replace('&amp;', '&'),
begin,
build_time: extractBuildTime(name) || end,
end,
part_people: executionTeamRDs,
});
}
const memberExecutions = executions.reduce((p, c) => {
const { part_people, ...rest } = c;
part_people.forEach((member) => {
if (p[member.name_people]) {
p[member.name_people].push(rest);
} else {
p[member.name_people] = [rest];
}
});
return p;
}, {});
const members = Object.entries(memberExecutions).map(([k, v]) => {
const memberTasks = {};
v.forEach((item) => {
const begin = moment.max(moment(item.begin), moment(firstDayOfWeek));
const end = moment.min(moment(item.build_time), moment(lastDayOfWeek));
const weekday = begin;
while (weekday.isSameOrBefore(end)) {
if (isWorkday(weekday)) {
const weekdayEN = weekday.format('dddd').toLowerCase();
if (memberTasks[`${weekdayEN}`]) {
memberTasks[`${weekdayEN}`] = `${memberTasks[`${weekdayEN}`]}${item.name_project}`;
} else {
memberTasks[`${weekdayEN}`] = item.name_project;
}
}
weekday.add(1, 'days');
}
});
const initData = {
name_people: k,
post_people: '研发',
monday: 'undefined',
tuesday: 'undefined',
wednesday: 'undefined',
thursday: 'undefined',
friday: 'undefined',
saturday: 'undefined',
sunday: 'undefined',
};
return { ...initData, ...memberTasks };
});
ctx.status = 200;
ctx.body = {
total: members.length,
projects: members,
};
} catch (e) {
ctx.status = 400;
ctx.body = {
name: 'FindError',
message: `人员计划获取失败${e}`,
};
}
}
router.get('/ongoing/projects', getOngoingProjects);
router.get('/ongoing/persons', getOngoingPersons);
},
};