From 5d566b70c64f732197638e8aa3437e9d1f7d8b3a Mon Sep 17 00:00:00 2001 From: Julin Date: Thu, 17 Aug 2023 09:59:39 +0800 Subject: [PATCH] =?UTF-8?q?=E2=80=9C=E6=9C=AC=E6=9C=88=E5=9C=A8=E7=A0=94?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E2=80=9D=E6=95=B0=E6=8D=AE=E6=BA=90=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E2=80=9C=E7=A6=85=E9=81=93=E2=80=9D=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/client/src/layout/index.js | 61 ++- .../src/sections/homePage/containers/index.js | 392 +++++++++--------- web/config-product-excluded.js | 83 ++++ web/package.json | 3 +- web/routes/missionboard/index.js | 225 ++++++++++ 5 files changed, 536 insertions(+), 228 deletions(-) create mode 100644 web/config-product-excluded.js create mode 100644 web/routes/missionboard/index.js diff --git a/web/client/src/layout/index.js b/web/client/src/layout/index.js index 1222a62..8c41166 100644 --- a/web/client/src/layout/index.js +++ b/web/client/src/layout/index.js @@ -1,18 +1,18 @@ -import React, { useEffect, useState } from "react"; -import moment from "moment"; -import { Provider } from "react-redux"; -import { createBrowserHistory } from "history"; -import { ConnectedRouter } from "connected-react-router"; -import { Switch, Route } from "react-router-dom"; -import { ConfigProvider } from "antd"; -import zhCN from "antd/lib/locale/zh_CN"; -import { basicReducer } from "@peace/utils"; -import * as layoutActions from "./actions/global"; -import { Layout, NoMatch } from "./containers"; -import configStore from "./store"; -import "moment/locale/zh-cn"; +import React, { useEffect, useState } from 'react'; +import moment from 'moment'; +import { Provider } from 'react-redux'; +import { createBrowserHistory } from 'history'; +import { ConnectedRouter } from 'connected-react-router'; +import { Switch, Route } from 'react-router-dom'; +import { ConfigProvider } from 'antd'; +import zhCN from 'antd/lib/locale/zh_CN'; +import { basicReducer } from '@peace/utils'; +import * as layoutActions from './actions/global'; +import { Layout, NoMatch } from './containers'; +import configStore from './store'; +import 'moment/locale/zh-cn'; -moment.locale("zh-cn"); +moment.locale('zh-cn'); const { initLayout, initApiRoot } = layoutActions; @@ -33,22 +33,22 @@ function Root(props) { path: route.path, breadcrumb: route.breadcrumb, component: route.component || null, - authCode: route.authCode || "", + authCode: route.authCode || '', key: route.key, }; - if (!route.path.startsWith("/")) { + if (!route.path.startsWith('/')) { console.error(`路由配置需以 "/" 开始:${route.path}`); } - if (route.path.length > 1 && route.path[route.path.length] == "/") { + if (route.path.length > 1 && route.path[route.path.length] == '/') { console.error(`除根路由路由配置不可以以 "/" 结束:${route.path}`); } - if (parentRoute && parentRoute != "/") { + if (parentRoute && parentRoute != '/') { obj.path = parentRoute + route.path; } if (route.exact === false) { obj.exact = false; } - if (route.hasOwnProperty("childRoutes")) { + if (route.hasOwnProperty('childRoutes')) { combineRoutes.push(obj); flat(route.childRoutes, obj.path); } else { @@ -90,12 +90,11 @@ function Root(props) { }; for (const s of sections) { - if (!s.key) - console.warn(`请给你的section添加一个key值,section name:${s.name}`); + if (!s.key) { console.warn(`请给你的section添加一个key值,section name:${s.name}`); } for (const r of s.routes) { - if (r.type == "inner" || r.type == "home") { + if (r.type == 'inner' || r.type == 'home') { inners.push(r.route); - } else if (r.type == "outer") { + } else if (r.type == 'outer') { outers.push(r.route); } } @@ -104,14 +103,14 @@ function Root(props) { } if (s.actions) { actions = { ...actions, [s.key]: s.actions }; - if (s.key != "auth") { + if (s.key != 'auth') { for (const ak in s.actions) { const actions = s.actions[ak]; - if (actions && typeof actions === "object") { + if (actions && typeof actions === 'object') { for (const actionName in actions) { initReducer(reducers, actionName, actions[actionName]); } - } else if (typeof actions === "function") { + } else if (typeof actions === 'function') { initReducer(reducers, ak, actions); } } @@ -123,7 +122,7 @@ function Root(props) { const store = configStore(reducers, history); store.dispatch(initLayout(title, copyright, sections, actions)); store.dispatch(actions.auth.initAuth()); - store.dispatch(initApiRoot()); + // store.dispatch(initApiRoot()); const newCombineRoutes = flatRoutes(inners); @@ -138,21 +137,21 @@ function Root(props) { path={route.path} component={route.component} /> - )) + )), ); setCombineRoutes( newCombineRoutes.map((route) => ( - )) + )), ); }, []); @@ -173,7 +172,7 @@ function Root(props) { ) : ( - "" + '' ); } diff --git a/web/client/src/sections/homePage/containers/index.js b/web/client/src/sections/homePage/containers/index.js index 135bc24..62a002d 100644 --- a/web/client/src/sections/homePage/containers/index.js +++ b/web/client/src/sections/homePage/containers/index.js @@ -1,57 +1,82 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React, { useState, useEffect,useRef } from "react"; -import { connect } from "react-redux"; -import { Spin, Tabs, Table } from "antd"; -import classnames from "classnames/bind"; -import styles from "./index.less"; -import { push } from "react-router-redux"; -import moment from "moment"; -import { getProject, getPeople, getWait,postKeySearch } from "../actions/profile"; -import { Scroller } from "$components"; -import ProTable from "@ant-design/pro-table"; +import React, { useState, useEffect, useRef } from 'react'; +import { connect } from 'react-redux'; +import { Spin, Table } from 'antd'; +import classnames from 'classnames/bind'; +import { push } from 'react-router-redux'; +import moment from 'moment'; +import request from 'superagent'; +import ProTable from '@ant-design/pro-table'; +import { Scroller } from '$components'; +import { + getPeople, getWait, postKeySearch, +} from '../actions/profile'; +import styles from './index.less'; + +const schedule = require('node-schedule'); + const CX = classnames.bind(styles); -const topImg = "/assets/images/top.png"; -const pointImg = "/assets/images/point.png"; +const topImg = '/assets/images/top.png'; +const pointImg = '/assets/images/point.png'; function Management(props) { - const { dispatch, user } = props - const height = document.body.clientHeight - const width=document.body.clientWidth - const [dataSource, setDataSource] = useState([]) - const [name, setName] = useState()//名字查询 - const [ajob, setAjob] = useState()//岗位查询 - const [projectData, setProjectData] = useState([]) - const [peopleData, setPeopleData] = useState([]) - const [waitData, setWaitData] = useState([]) - const [counts,setCounts]=useState() + const { dispatch, user } = props; + const [name, setName] = useState();// 名字查询 + const [ajob, setAjob] = useState();// 岗位查询 + const [projectData, setProjectData] = useState([]); + const [peopleData, setPeopleData] = useState([]); + const [waitData, setWaitData] = useState([]); const ref = useRef(); + + function getOngoingProjects() { + request.get('/ongoing/projects').then((res) => { + setProjectData(res?.body?.projects); + }); + } + useEffect(() => { - dispatch(getProject()).then((res) => { - setProjectData(res?.payload.data?.projects) - }) + getOngoingProjects(); + + schedule.scheduleJob('0 0 7,18 * * ?', () => { // 每天 7时、18时 定时更新 + getOngoingProjects(); + }); + dispatch(getPeople()).then((res) => { - setPeopleData(res.payload.data.projects) - }) + setPeopleData(res?.payload.data?.projects); + }); dispatch(getWait()).then((res) => { - setWaitData(res.payload.data.projects) - }) - },[]) + setWaitData(res?.payload.data?.projects); + }); + }, []); const projectCol = [ + { + title: '执行ID', + dataIndex: 'execution_id', + key: 'execution_id', + align: 'center', + }, { title: '项目名称', dataIndex: 'name_project', key: 'name_project', - align: 'center' + align: 'center', }, { title: '投入人力', dataIndex: 'part_people', key: 'part_people', align: 'center', - render: (dom, record) => { - return
{record.part_people.map((i, index) => record.part_people.length == 1 ? {i.name_people} : {i.name_people}{index < record.part_people.length-1 ? '、':'' }) }
- }, + render: (dom, record) => ( +
+ {record.part_people.map((i, index) => (record.part_people.length == 1 ? {i.name_people} : ( + + {i.name_people} + {index < record.part_people.length - 1 ? '、' : ''} + + )))} +
+ ), }, { title: '构建时间', @@ -62,45 +87,39 @@ function Management(props) { // sortOrder:'descend' , sorter: { compare: (a, b) => { - const one = moment(a.build_time).format('YYYYMMDD') - const two=moment(b.build_time).format('YYYYMMDD') - return one - two + const one = moment(a.build_time).format('YYYYMMDD'); + const two = moment(b.build_time).format('YYYYMMDD'); + return one - two; }, }, - render: (dom, record) => { - return <>{moment(record.build_time).format('YYYY-MM-DD')=='1999-01-01'?'待定':moment(record.build_time).format('YYYY-MM-DD')} - }, + render: (dom, record) => <>{record.build_time ? moment(record.build_time).format('YYYY-MM-DD') : '待定'}, }, { title: '发布时间', dataIndex: 'publish_time', key: 'publish_time', align: 'center', - render: (dom, record) => { - return moment(record.publish_time).format('YYYY-MM-DD') - }, + render: (dom, record) => moment(record.publish_time).format('YYYY-MM-DD'), }, { title: '进度', dataIndex: 'progress', key: 'progress', - align: 'center' + align: 'center', }, { title: '状态', dataIndex: 'deferred_payment', key: 'deferred_payment', align: 'center', - render: (dom, record) => { - const current = new Date() - return <> + render: (dom, record) => ( + <> { - ((record.progress == '研发中' && Number(moment(current).format('YYYYMMDD')) > Number(moment(record.build_time).format('YYYYMMDD'))) && moment(record.build_time).format('YYYY-MM-DD') !== '1999-01-01') - || ((record.progress == '测试中' && Number(moment(current).format('YYYYMMDD')) > Number(moment(record.publish_time).format('YYYYMMDD'))) && moment(record.build_time).format('YYYY-MM-DD') !== '1999-01-01') - || ((record.progress == '需求评审中' && Number(moment(current).format('YYYYMMDD')) > Number(moment(record.build_time).format('YYYYMMDD'))) && moment(record.build_time).format('YYYY-MM-DD') !== '1999-01-01') ? -
已延期
:
未延期
} + record.publish_time && moment().isAfter(moment(record.publish_time)) + ?
已延期
:
未延期
+ } - }, + ), }, ]; @@ -112,126 +131,102 @@ function Management(props) { ellipsis: true, hideInSearch: true, width: 100, - render: (dom, record) => { - return record.name_people; - }, - - }, { + render: (dom, record) => record.name_people, + + }, { title: '岗位', key: 'post_people', dataIndex: 'post_people', - valueType: "select", + valueType: 'select', initialValue: null, fieldProps: { onChange: (value, cs) => { - setAjob(value); + setAjob(value); + }, + options: [ + { + label: '全部岗位', + value: null, }, - options: [ - { - label: "全部岗位", - value: null, - }, - { - label: "研发", - value: '研发', - }, - { - label: "测试", - value: '测试', - }, - { - label: "UI", - value: 'UI', + { + label: '研发', + value: '研发', }, - ], - }, - render: (dom, record) => { - return record.post_people; + { + label: '测试', + value: '测试', + }, + { + label: 'UI', + value: 'UI', + }, + ], }, - }, + render: (dom, record) => record.post_people, + }, { - title: "周一", - key: "md", - dataIndex: "md", + title: '周一', + key: 'md', + dataIndex: 'md', hideInSearch: true, - - render: (dom, record) => { - return
{record.md=='undefined'?'--' :record.md}
; - }, + render: (dom, record) =>
{record.md == 'undefined' ? '--' : record.md}
, }, { - title: "周二", - key: "td", - dataIndex: "td", + title: '周二', + key: 'td', + dataIndex: 'td', hideInSearch: true, - - render: (dom, record) => { - return
{record.td=='undefined'?'--' :record.td}
; - }, + render: (dom, record) =>
{record.td == 'undefined' ? '--' : record.td}
, }, { - title: "周三", - key: "wd", - dataIndex: "wd", + title: '周三', + key: 'wd', + dataIndex: 'wd', hideInSearch: true, - - render: (dom, record) => { - return
{record.wd=='undefined'?'--' :record.wd}
; - }, + render: (dom, record) =>
{record.wd == 'undefined' ? '--' : record.wd}
, }, { - title: "周四", - key: "thd ", - dataIndex: "thd ", + title: '周四', + key: 'thd ', + dataIndex: 'thd ', hideInSearch: true, - - render: (dom, record) => { - return
{record.thd=='undefined'?'--' :record.thd}
; - }, + render: (dom, record) =>
{record.thd == 'undefined' ? '--' : record.thd}
, }, { - title: "周五", - key: "fd", - dataIndex: "fd", + title: '周五', + key: 'fd', + dataIndex: 'fd', hideInSearch: true, - - render: (dom, record) => { - return
{record.fd=='undefined'?'--' :record.fd}
; - }, + render: (dom, record) =>
{record.fd == 'undefined' ? '--' : record.fd}
, }, { - title: "周六", - key: "sd", - dataIndex: "sd", + title: '周六', + key: 'sd', + dataIndex: 'sd', hideInSearch: true, - render: (dom, record) => { - return
{record.sd=='undefined'?'--' :record.sd}
; - }, + render: (dom, record) =>
{record.sd == 'undefined' ? '--' : record.sd}
, }, { - title: "周日", - key: "ssd", - dataIndex: "ssd", + title: '周日', + key: 'ssd', + dataIndex: 'ssd', hideInSearch: true, - render: (dom, record) => { - return
{record.ssd=='undefined'?'--' :record.ssd}
; - }, + render: (dom, record) =>
{record.ssd == 'undefined' ? '--' : record.ssd}
, }, - {title: "关键字搜索", - key: "direction", + { + title: '关键字搜索', + key: 'direction', hideInTable: true, - dataIndex: "direction", + dataIndex: 'direction', order: 6, fieldProps: { onChange: (value, cs) => { setName(value.target.value); - }, - placeholder: '请输入关键字进行搜索', - getPopupContainer: (triggerNode) => triggerNode.parentNode, + }, + placeholder: '请输入关键字进行搜索', + getPopupContainer: (triggerNode) => triggerNode.parentNode, }, - - - } + }, ]; const waitCol = [ @@ -239,115 +234,120 @@ function Management(props) { title: '项目', dataIndex: 'name_project', key: 'name_project', - align: 'center' + align: 'center', }, { title: '需求来源', dataIndex: 'from_project', key: 'from_project', - align: 'center' + align: 'center', }, { title: '对接人', dataIndex: 'contacts', key: 'contacts', - align: 'center' - },{ + align: 'center', + }, { title: '评估工时', dataIndex: 'consum_time', key: 'consum_time', - align: 'center' + align: 'center', }, { title: '处理进度', dataIndex: 'progress', key: 'progress', - align: 'center' + align: 'center', }, - + ]; return ( <> - -
- -
-
- -
本周在研项目
-
/Research project this week
+
+ +
+
+ +
本月在研项目
+
/Research project this week
- +
- -
- -
待研发项目
-
/Project to be developed
-
+ +
+ +
待研发项目
+
/Project to be developed
+
-
- -
人员情况
-
/Information on Personnel
- {/* */} - + +
人员情况
+
/Information on Personnel
+ {/* */} + { - if (name || ajob) { const query = { - value1:name, - value2:ajob, - } - const res = await dispatch(postKeySearch(query)); - setPeopleData(res?.payload.data.projects); - return { - ...res, - // total: res.payload.data.data ? res.payload.data.total : 0, + value1: name, + value2: ajob, }; - } else { - const res = await dispatch(getPeople()); - console.log(res) + const res = await dispatch(postKeySearch(query)); setPeopleData(res?.payload.data.projects); return { - ...res, - // total: res.payload.data.data ? res.payload.data.total : 0, + ...res, + // total: res.payload.data.data ? res.payload.data.total : 0, }; } - + const res = await dispatch(getPeople()); + setPeopleData(res?.payload.data.projects); + return { + ...res, + // total: res.payload.data.data ? res.payload.data.total : 0, + }; }} onReset={(v) => { - setName(null) - setAjob(null) + setName(null); + setAjob(null); }} - /> - {/* */} - - -
+ /> + {/* */} + { if ( - user && - user.authorized && - JSON.parse(sessionStorage.getItem("user")) + user + && user.authorized + && JSON.parse(sessionStorage.getItem('user')) ) { - dispatch(push("/article")); + dispatch(push('/article')); } else { - dispatch(push("/login")); + dispatch(push('/login')); } }} > diff --git a/web/config-product-excluded.js b/web/config-product-excluded.js new file mode 100644 index 0000000..40df502 --- /dev/null +++ b/web/config-product-excluded.js @@ -0,0 +1,83 @@ +module.exports = [ + // 硬件产品化系列 + '分布式数据采集节点', + '称重采集仪', + '振动采集仪', + '云振动采集仪', + '地灾产品', + '模块化动态采集模块', + '光电扰度仪', + '移动测斜仪', + '光纤光栅解调仪', + '塔吊监测', + '水质在线监测', + '扬尘在线监测', + '分布式数据采集系统网关\\/节点', + '静力水准仪', + '振弦采集仪', + '分布式GPRS节点', + '一体化振动\\/测斜', + '磁通量产品', + '振弦类传感器', + '盒式固定测斜仪', + '导轮式固定测斜仪', + '外购传感器', + '多功能静态数据采集仪', + '激光测距仪', + 'DTU-通讯设备', + '分布式振弦数据采集模块', + '加速度计', + '聚醚管&聚安酯管', + '硬件机械辅材设计', + '地质灾害监测预警监测设备-一体式倾角/加速度/裂缝计(FS-YTZD-QJ/JS/LF)', + '微型应变计', + 'FS-RTU-P4', + '水利RTU FS-SL-RTU', + '国产化MCU替代', + '智能空气净化器(FS-KJ02)', + '模块化-多功能静态数据采集仪(FS-M24)', + '微型振弦采集仪(FS-iVW)', + '分布式节点', + '无线倾角传感器', + '高支模监测系统V3.00', + '振弦式温度计', + '高精度渗压计', + '高精度渗压计开发项目', + '智芯系列振弦产品开发项目', + '一体化水位传感器', + '分布式采集节点(FS-LPWAN02-LORA/4G/NB)', + + // 第三方检定 + 'UI设计', + '第三方检定', + '第三方检定支撑', + + // 测试部 + '测试部', + + // 软件研发部项目集 + '软件研发部日常工作', + + // 外场故障 + '外场故障', + + // ci-cd + 'Jenkins集成', + '禅道', + '研发看板系统', + + // 硬件研发--嵌入式开发 + '固件库开发', + '单片机开发', + 'linux开发', + '上位机开发', + + // 测试部 + '测试部门', + + // 硬件研发部项目型任务 + '硬件研发部任务', + + // 软件共性技术研究 + '共性技术', +]; diff --git a/web/package.json b/web/package.json index 9c690db..c9d2522 100644 --- a/web/package.json +++ b/web/package.json @@ -82,7 +82,7 @@ "ahooks": "^3.1.14", "antd": "^4.14.1", "antd-theme-generator": "^1.2.8", - "args": "^5.0.1", + "args": "^3.0.7", "array-move": "^3.0.1", "braft-editor": "^2.3.9", "classnames": "^2.3.1", @@ -107,6 +107,7 @@ "leva": "^0.9.23", "mini-dynamic-antd-theme": "^0.5.3", "moment": "^2.22.0", + "node-schedule": "^2.1.1", "npm": "^7.20.6", "perfect-scrollbar": "^1.5.5", "postprocessing": "^6.24.1", diff --git a/web/routes/missionboard/index.js b/web/routes/missionboard/index.js new file mode 100644 index 0000000..44c55ee --- /dev/null +++ b/web/routes/missionboard/index.js @@ -0,0 +1,225 @@ +const request = require('superagent'); +const moment = require('moment'); +const productExcluded = require('../../config-product-excluded'); + +module.exports = { + entry(app, router, opts) { + async function auth() { + const session = await request.get('https://pms.anxinyun.cn/api-getSessionID.json'); + const sessionData = JSON.parse(session.text).data; + const { sessionName, sessionID } = JSON.parse(sessionData); + + const loginInfo = await request + .post('https://pms.anxinyun.cn/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 extractBuildTime(inputString) { + const pattern = /[(\(]构建时间:(.*?)[\))]/; + const match = inputString.match(pattern); + const buildDate = match ? match[1] : null; + return buildDate; + } + + async function getExecutionDetail(executionId) { + const { sessionName, sessionID } = app.fs.session; + + const executionDetailRes = await request + .get(`https://pms.anxinyun.cn/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(`https://pms.anxinyun.cn/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(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++; + } + + const currentMonth = moment().format('YYYY-MM'); + + const executionStats = executionAll.filter((item) => item.type === 'sprint' && ( + moment(item.begin).format('YYYY-MM') === currentMonth + || moment(item.end).format('YYYY-MM') === currentMonth + )); + + return executionStats; + } + + async function getOngoingProjects(ctx, next) { + try { + const { sessionName, sessionID } = app.fs.session; + + const executions = []; + const currentMonth = moment().format('YYYY-MM'); + + // 执行(未开始) + const waitExecutionBaseURL = 'https://pms.anxinyun.cn/execution-all-wait-0-order_asc-0.json'; + const waitExecutionStats = await getExecutionStats(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 = 'https://pms.anxinyun.cn/execution-all-doing-0-order_asc-0.json'; + const doingExecutionStats = await getExecutionStats(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('&', '&'), + 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 { sessionName, sessionID } = app.fs.session; + + const executions = []; + + const currentDate = moment(); + // 获取当前周的第一天(周一) + const firstDayOfWeek = currentDate.clone().startOf('isoWeek'); + // 获取当前周的最后一天(周日) + const lastDayOfWeek = currentDate.clone().endOf('isoWeek'); + + // 执行(未开始) + const waitExecutionRes = await request + .get('https://pms.anxinyun.cn/execution-all-wait-0-order_asc-0.json') + .set('Cookie', `${sessionName}=${sessionID}`); + const waitExecutionText = JSON.parse(waitExecutionRes.text); + const waitExecutionStats = JSON.parse(waitExecutionText.data).executionStats + .filter((item) => item.type === 'sprint' && ( + moment(item.begin).isBetween(firstDayOfWeek, lastDayOfWeek, undefined, '[]') + || moment(item.end).isBetween(firstDayOfWeek, lastDayOfWeek, undefined, '[]') + )); + + for (const item of waitExecutionStats) { + 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, + begin, + build_time: extractBuildTime(name), + 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; + }, {}); + + ctx.status = 200; + ctx.body = { + total: Object.keys(memberExecutions).length, + projects: memberExecutions, + }; + } catch (e) { + ctx.status = 400; + ctx.body = { + name: 'FindError', + message: `人员计划获取失败${e}`, + }; + } + } + + router.get('/ongoing/projects', getOngoingProjects); + router.get('/ongoing/persons', getOngoingPersons); + }, +};