Browse Source

个人加班统计

master
巴林闲侠 3 years ago
parent
commit
51d0cc6dd3
  1. 148
      api/app/lib/controllers/member/index.js
  2. 9
      api/app/lib/models/overtime.js
  3. 9
      api/app/lib/models/vacate.js
  4. 6
      api/app/lib/routes/member/index.js
  5. 41
      api/app/lib/schedule/attendance.js
  6. 2
      api/sequelize-automate.config.js

148
api/app/lib/controllers/member/index.js

@ -31,7 +31,7 @@ async function add (ctx) {
} }
}) })
} else { } else {
await models.create(storageData) await models.Member.create(storageData)
} }
ctx.status = 204; ctx.status = 204;
@ -384,10 +384,156 @@ async function list (ctx) {
} }
} }
async function overTimeStatistics (ctx) {
try {
const { models } = ctx.fs.dc;
const { clickHouse } = ctx.app.fs
const { database: pepEmis } = clickHouse.pepEmis.opts.config
const { startDate, endDate, pepUserId } = ctx.query
const timeOption = []
if (startDate && endDate) {
timeOption.push(
`wpStory.create_at <= '${moment(endDate).format('YYYY-MM-DD HH:mm:ss')}'
AND wpStory.create_at > '${moment(startDate).format('YYYY-MM-DD HH:mm:ss')}'`
)
}
const dataRes = await clickHouse.hr.query(`
SELECT
compensate,
create_at AS createTime,
start_time AS startTime,
end_time AS endTime,
pay_dayoff AS payDayoff,
pay_festivals AS payFestivals,
pay_workday AS payWorkday,
reason,
take_rest_dayoff AS takeRestDayoff,
take_rest_festivals AS takeRestFestivals,
take_rest_workday AS takeRestWorkday,
wpStory.id AS storyId
FROM
overtime
INNER JOIN ${pepEmis}.workflow_process_history AS wpStory
ON wpStory.id = overtime.pep_process_story_id
${timeOption.length ? `AND ${timeOption.join(' AND ')}` : ''}
WHERE overtime.pep_user_id = ${pepUserId}
`).toPromise()
const statisticRes = await clickHouse.hr.query(`
SELECT
count(overtime.id) AS overTimeCount,
sum(pay_dayoff) AS sumPayDayoff,
sum(pay_festivals) AS sumPayFestivals,
sum(pay_workday) AS sumPayWorkday,
sum(take_rest_dayoff) AS sumTakeRestDayoff,
sum(take_rest_festivals) AS sumTakeRestFestivals,
sum(take_rest_workday) AS sumTakeRestWorkday
FROM
overtime
INNER JOIN ${pepEmis}.workflow_process_history AS wpStory
ON wpStory.id = overtime.pep_process_story_id
${timeOption.length ? `AND ${timeOption.join(' AND ')}` : ''}
WHERE overtime.pep_user_id = ${pepUserId}
GROUP BY overtime.pep_user_id
`).toPromise()
let returnD = {
...(statisticRes.length ? statisticRes[0] : {}),
data: dataRes
}
ctx.status = 200;
ctx.body = returnD
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: error`);
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
async function vacateStatistics (ctx) {
try {
const { models } = ctx.fs.dc;
const { clickHouse } = ctx.app.fs
const { database: pepEmis } = clickHouse.pepEmis.opts.config
const { startDate, endDate, pepUserId } = ctx.query
const timeOption = []
if (startDate && endDate) {
timeOption.push(
`wpStory.create_at <= '${moment(endDate).format('YYYY-MM-DD HH:mm:ss')}'
AND wpStory.create_at > '${moment(startDate).format('YYYY-MM-DD HH:mm:ss')}'`
)
}
const dataRes = await clickHouse.hr.query(`
SELECT
type,
create_at AS createTime,
start_time AS startTime,
end_time AS endTime,
duration,
reason,
wpStory.id AS storyId
FROM
vacate
INNER JOIN ${pepEmis}.workflow_process_history AS wpStory
ON wpStory.id = vacate.pep_process_story_id
${timeOption.length ? `AND ${timeOption.join(' AND ')}` : ''}
WHERE vacate.pep_user_id = ${pepUserId}
`).toPromise()
const statisticRes = await clickHouse.hr.query(`
SELECT
type,
count(vacate.id) AS count,
sum(duration) AS sumDuration
FROM
vacate
INNER JOIN ${pepEmis}.workflow_process_history AS wpStory
ON wpStory.id = vacate.pep_process_story_id
${timeOption.length ? `AND ${timeOption.join(' AND ')}` : ''}
WHERE vacate.pep_user_id = ${pepUserId}
GROUP BY type
`).toPromise()
let returnD = {
statistic: statisticRes,
data: dataRes
}
ctx.status = 200;
ctx.body = returnD
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: error`);
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
async function exportData (ctx) {
try {
const { models } = ctx.fs.dc;
ctx.status = 20;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: error`);
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : undefined
}
}
}
module.exports = { module.exports = {
add, add,
edit, edit,
del, del,
searchPepMember, searchPepMember,
list, list,
overTimeStatistics,
vacateStatistics,
exportData,
}; };

9
api/app/lib/models/overtime.js

@ -131,6 +131,15 @@ module.exports = dc => {
primaryKey: false, primaryKey: false,
field: "compensate", field: "compensate",
autoIncrement: false autoIncrement: false
},
reason: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: "原因",
primaryKey: false,
field: "reason",
autoIncrement: false
} }
}, { }, {
tableName: "overtime", tableName: "overtime",

9
api/app/lib/models/vacate.js

@ -77,6 +77,15 @@ module.exports = dc => {
primaryKey: false, primaryKey: false,
field: "wf_process_state", field: "wf_process_state",
autoIncrement: false autoIncrement: false
},
reason: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: "原因",
primaryKey: false,
field: "reason",
autoIncrement: false
} }
}, { }, {
tableName: "vacate", tableName: "vacate",

6
api/app/lib/routes/member/index.js

@ -17,4 +17,10 @@ module.exports = function (app, router, opts) {
app.fs.api.logAttr['GET/member/list'] = { content: '查询人员列表', visible: true }; app.fs.api.logAttr['GET/member/list'] = { content: '查询人员列表', visible: true };
router.get('/member/list', member.list); router.get('/member/list', member.list);
app.fs.api.logAttr['GET/member/overtime'] = { content: '查询单个人员加班统计数据', visible: true };
router.get('/member/overtime', member.overTimeStatistics);
app.fs.api.logAttr['GET/member/vacate'] = { content: '查询单个人员请假统计数据', visible: true };
router.get('/member/vacate', member.vacateStatistics);
}; };

41
api/app/lib/schedule/attendance.js

@ -5,8 +5,8 @@ module.exports = function (app, opts) {
const updateAttendance = app.fs.scheduleInit( const updateAttendance = app.fs.scheduleInit(
// 妥妥流水账 (*^▽^*) // 妥妥流水账 (*^▽^*)
{ {
interval: '34 21 4 * * *', // interval: '34 21 4 * * *',
// interval: '*/3 * * * *', interval: '34 21 */1 * * *',
// immediate: true, // immediate: true,
// proRun: true, // proRun: true,
}, },
@ -22,6 +22,9 @@ module.exports = function (app, opts) {
const { } = app.fs.utils const { } = app.fs.utils
let overtimeNeedData = { let overtimeNeedData = {
reason: {
keyWord: ['加班事由']
},
begainTime: { begainTime: {
keyWord: ['加班开始时间'], keyWord: ['加班开始时间'],
require: true, require: true,
@ -44,6 +47,9 @@ module.exports = function (app, opts) {
} }
let vacateNeedData = { let vacateNeedData = {
reason: {
keyWord: ['请假事由']
},
begainTime: { begainTime: {
keyWord: ['请假开始时间', '请假起始时间'], keyWord: ['请假开始时间', '请假起始时间'],
keys: ['leaveStartTime'], keys: ['leaveStartTime'],
@ -160,7 +166,7 @@ module.exports = function (app, opts) {
) )
} else { } else {
console.warn( console.warn(
`表单数据缺失:`, `表单数据缺失:[${nd}]`,
applyDetail applyDetail
); );
} }
@ -177,6 +183,8 @@ module.exports = function (app, opts) {
} }
} }
const existOvertimeCount = await models.Overtime.count()
const existVacateCount = await models.Vacate.count()
const attendanceRes = await clickHouse.pepEmis.query( const attendanceRes = await clickHouse.pepEmis.query(
` `
@ -207,6 +215,11 @@ module.exports = function (app, opts) {
AND fgroup.name = '假勤管理' AND fgroup.name = '假勤管理'
INNER JOIN ${camWorkflow}.act_hi_procinst AS procin INNER JOIN ${camWorkflow}.act_hi_procinst AS procin
ON procin.id_ = story.procinst_id ON procin.id_ = story.procinst_id
` +
`
${existOvertimeCount || existVacateCount ?
`WHERE story.create_at > '${moment().subtract(1, 'month').format('YYYY-MM-DD HH:mm:ss')}'`
: ''}
` `
).toPromise() ).toPromise()
@ -218,7 +231,7 @@ module.exports = function (app, opts) {
if (a.processName.indexOf('请假') > -1) { if (a.processName.indexOf('请假') > -1) {
let needData = JSON.parse(JSON.stringify(vacateNeedData)) let needData = JSON.parse(JSON.stringify(vacateNeedData))
getData(a, needData) getData(a, needData)
const { begainTime, endTime, type, hrAffirmType, duration, hrAffirmDuration } = needData const { begainTime, endTime, type, hrAffirmType, duration, hrAffirmDuration, reason } = needData
if (begainTime.value && endTime.value && type.value) { if (begainTime.value && endTime.value && type.value) {
let durationSec = 0 let durationSec = 0
if (hrAffirmDuration.value) { if (hrAffirmDuration.value) {
@ -253,6 +266,7 @@ module.exports = function (app, opts) {
duration: durationSec, duration: durationSec,
type: typeStorage, type: typeStorage,
wfProcessState: a.state, wfProcessState: a.state,
reason: reason.value
} }
if (existRes) { if (existRes) {
await models.Vacate.update(storageD, { await models.Vacate.update(storageD, {
@ -277,7 +291,7 @@ module.exports = function (app, opts) {
// 获取表单里的数据并插入 needData // 获取表单里的数据并插入 needData
getData(a, needData) getData(a, needData)
const { begainTime, endTime, duration, compensate, hrAffirmDuration } = needData const { begainTime, endTime, duration, compensate, hrAffirmDuration, reason } = needData
if (begainTime.value && endTime.value && hrAffirmDuration.value && compensate.value) { if (begainTime.value && endTime.value && hrAffirmDuration.value && compensate.value) {
let durationSec = parseFloat(hrAffirmDuration.value) * 3600 let durationSec = parseFloat(hrAffirmDuration.value) * 3600
@ -372,7 +386,8 @@ module.exports = function (app, opts) {
payWorkday, payWorkday,
payDayoff, payDayoff,
payFestivals, payFestivals,
compensate: compensate.value compensate: compensate.value,
reason: reason.value
} }
if (existRes) { if (existRes) {
await models.Overtime.update(storageD, { await models.Overtime.update(storageD, {
@ -410,17 +425,17 @@ module.exports = function (app, opts) {
console.info(` console.info(`
假勤数据更新 用时 ${moment().diff(startTime, 'seconds')} s 假勤数据更新 用时 ${moment().diff(startTime, 'seconds')} s
`) `)
console.info(` console.info(`
${attendanceRes.length}; ${attendanceRes.length};
新增${insertCount}; 新增${insertCount};
更新数据${updateCount}; 更新数据${updateCount};
非完成状态流程${unCompletedCount}; 非完成状态流程${unCompletedCount};
不明流程${unknowCount} 不明流程${unknowCount}
无效(warning)${invalidCount}; 无效(warning)${invalidCount};
`); `);
} catch (error) { } catch (error) {
app.fs.logger.error(`sechedule: updateAttendance, error: ${error}`); app.fs.logger.error(`sechedule: updateAttendance, error: ${error} `);
} }
}); });
return { return {

2
api/sequelize-automate.config.js

@ -26,7 +26,7 @@ module.exports = {
dir: './app/lib/models', // 指定输出 models 文件的目录 dir: './app/lib/models', // 指定输出 models 文件的目录
typesDir: 'models', // 指定输出 TypeScript 类型定义的文件目录,只有 TypeScript / Midway 等会有类型定义 typesDir: 'models', // 指定输出 TypeScript 类型定义的文件目录,只有 TypeScript / Midway 等会有类型定义
emptyDir: false, // !!! 谨慎操作 生成 models 之前是否清空 `dir` 以及 `typesDir` emptyDir: false, // !!! 谨慎操作 生成 models 之前是否清空 `dir` 以及 `typesDir`
tables: ['member'], // 指定生成哪些表的 models,如 ['user', 'user_post'];如果为 null,则忽略改属性 tables: ['overtime','vacate'], // 指定生成哪些表的 models,如 ['user', 'user_post'];如果为 null,则忽略改属性
skipTables: [], // 指定跳过哪些表的 models,如 ['user'];如果为 null,则忽略改属性 skipTables: [], // 指定跳过哪些表的 models,如 ['user'];如果为 null,则忽略改属性
tsNoCheck: false, // 是否添加 `@ts-nocheck` 注释到 models 文件中 tsNoCheck: false, // 是否添加 `@ts-nocheck` 注释到 models 文件中
ignorePrefix: [], // 生成的模型名称忽略的前缀,因为 项目中有以下表名是以 t_ 开头的,在实际模型中不需要, 可以添加多个 [ 't_data_', 't_',] ,长度较长的 前缀放前面 ignorePrefix: [], // 生成的模型名称忽略的前缀,因为 项目中有以下表名是以 t_ 开头的,在实际模型中不需要, 可以添加多个 [ 't_data_', 't_',] ,长度较长的 前缀放前面

Loading…
Cancel
Save