wuqun
2 years ago
4 changed files with 422 additions and 0 deletions
@ -0,0 +1,333 @@ |
|||||
|
'use strict'; |
||||
|
const moment = require('moment') |
||||
|
const fs = require('fs'); |
||||
|
|
||||
|
async function salesList(ctx) { |
||||
|
try { |
||||
|
const { memberList, packageUserData } = ctx.app.fs.utils |
||||
|
const { |
||||
|
keywordTarget, keyword, limit, page, state, |
||||
|
hiredateStart, hiredateEnd, marital, native, workPlace, |
||||
|
orderBy, orderDirection, placeSearch |
||||
|
} = ctx.query |
||||
|
|
||||
|
const userRes = await memberList({ |
||||
|
keywordTarget, keyword, limit: '', page: '', state, |
||||
|
hiredateStart, hiredateEnd, marital, native, workPlace, |
||||
|
orderBy, orderDirection, |
||||
|
nowAttendanceTime: true |
||||
|
}) |
||||
|
|
||||
|
let { packageUser: members } = await packageUserData(userRes, { |
||||
|
state: true, |
||||
|
}) |
||||
|
|
||||
|
const { models } = ctx.fs.dc; |
||||
|
let res = await models.SalesDistribution.findAndCountAll({ |
||||
|
where: { del: false }, |
||||
|
offset: Number(page) * Number(limit), |
||||
|
limit: Number(limit), |
||||
|
}) |
||||
|
|
||||
|
let rslt = [] |
||||
|
res.rows.map(d => { |
||||
|
let valid = false; |
||||
|
let info = members.find(m => m.pepUserId == d.dataValues.pepUserId); |
||||
|
if (info) { |
||||
|
if (placeSearch) { |
||||
|
let exist1 = d.dataValues.provinces.join(',').indexOf(placeSearch) != -1 |
||||
|
let exist2 = d.dataValues.cities.join(',').indexOf(placeSearch) != -1 |
||||
|
if (exist1 || exist2) { |
||||
|
valid = true; |
||||
|
} |
||||
|
} else { |
||||
|
valid = true; |
||||
|
} |
||||
|
} |
||||
|
if (valid) { |
||||
|
let item = { |
||||
|
name: info.userName, |
||||
|
userCode: info.userCode, |
||||
|
post: info.userPost, |
||||
|
department: info.departmrnt, |
||||
|
hireDate: info.hiredate,//入职时间
|
||||
|
regularDate: info.regularDate,//转正时间
|
||||
|
...d.dataValues |
||||
|
} |
||||
|
rslt.push(item); |
||||
|
} |
||||
|
}) |
||||
|
ctx.status = 200; |
||||
|
ctx.body = { |
||||
|
count: res.count, |
||||
|
rows: rslt |
||||
|
}; |
||||
|
} catch (error) { |
||||
|
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); |
||||
|
ctx.status = 400; |
||||
|
ctx.body = { |
||||
|
message: typeof error == 'string' ? error : undefined |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async function add(ctx) { |
||||
|
try { |
||||
|
const { models } = ctx.fs.dc; |
||||
|
const { pepUserId, provinces, cities } = ctx.request.body |
||||
|
|
||||
|
const existRes = await models.SalesDistribution.findOne({ |
||||
|
where: { pepUserId } |
||||
|
}) |
||||
|
|
||||
|
if (existRes && !existRes.del) { |
||||
|
throw '当前销售人员信息已存在' |
||||
|
} |
||||
|
|
||||
|
let storageData = { pepUserId, provinces, cities, del: false } |
||||
|
if (existRes && existRes.del) { |
||||
|
await models.SalesDistribution.update(storageData, { |
||||
|
where: { pepUserId } |
||||
|
}) |
||||
|
} else { |
||||
|
await models.SalesDistribution.create(storageData) |
||||
|
} |
||||
|
ctx.status = 204; |
||||
|
} catch (error) { |
||||
|
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); |
||||
|
ctx.status = 400; |
||||
|
ctx.body = { |
||||
|
message: typeof error == 'string' ? error : undefined |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async function edit(ctx) { |
||||
|
try { |
||||
|
const { models } = ctx.fs.dc; |
||||
|
const { pepUserId, provinces, cities } = ctx.request.body |
||||
|
|
||||
|
const existRes = await models.SalesDistribution.findOne({ |
||||
|
where: { pepUserId } |
||||
|
}) |
||||
|
|
||||
|
if (!existRes) { |
||||
|
throw '当前销售人员信息不存在' |
||||
|
} |
||||
|
|
||||
|
let storageData = { pepUserId, provinces, cities, del: false } |
||||
|
|
||||
|
await models.SalesDistribution.update(storageData, { |
||||
|
where: { |
||||
|
pepUserId: pepUserId |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
ctx.status = 204; |
||||
|
} catch (error) { |
||||
|
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); |
||||
|
ctx.status = 400; |
||||
|
ctx.body = { |
||||
|
message: typeof error == 'string' ? error : undefined |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async function del(ctx) { |
||||
|
try { |
||||
|
const { models } = ctx.fs.dc; |
||||
|
const { pepUserId } = ctx.query |
||||
|
|
||||
|
await models.SalesDistribution.update({ |
||||
|
del: true, |
||||
|
}, { |
||||
|
where: { |
||||
|
pepUserId, |
||||
|
} |
||||
|
}) |
||||
|
ctx.status = 204; |
||||
|
} 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; |
||||
|
const { clickHouse, opts: { qiniu } } = ctx.app.fs |
||||
|
const { simpleExcelDown, memberList, packageUserData } = ctx.app.fs.utils |
||||
|
const { |
||||
|
keywordTarget, keyword, limit, page, state, keys = '', |
||||
|
hiredateStart, hiredateEnd, marital, native, workPlace, |
||||
|
orderBy, orderDirection, |
||||
|
} = ctx.query |
||||
|
|
||||
|
const userRes = await memberList({ |
||||
|
keywordTarget, keyword, limit, page, state, |
||||
|
hiredateStart, hiredateEnd, marital, native, workPlace, |
||||
|
orderBy, orderDirection, |
||||
|
nowAttendanceTime: true |
||||
|
}) |
||||
|
|
||||
|
const tableAttributes = models['Member'].tableAttributes |
||||
|
const optionKeys = keys.split(',') |
||||
|
|
||||
|
let { packageUser: exportD, pepUserIds } = await packageUserData(userRes) |
||||
|
|
||||
|
let preHeader = [{ |
||||
|
title: '员工编号', |
||||
|
key: 'userCode', |
||||
|
}, { |
||||
|
title: '姓名', |
||||
|
key: 'userName', |
||||
|
}] |
||||
|
let header = [].concat(preHeader) |
||||
|
for (let k in tableAttributes) { |
||||
|
const comment = tableAttributes[k].comment |
||||
|
if (k != 'id' && k != 'pepUserId' && comment) { |
||||
|
if ([].includes(k)) { |
||||
|
// 截住不想导出的字段
|
||||
|
continue |
||||
|
} |
||||
|
header.push({ |
||||
|
title: comment || '-', |
||||
|
key: k, |
||||
|
// index: tableAttributes[k].index,
|
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (optionKeys.includes('overtimeStatistic')) { |
||||
|
header = header.concat([{ |
||||
|
title: '累计加班次数', |
||||
|
key: 'overTimeCount', |
||||
|
}, { |
||||
|
title: '累计加班总时长 / h', |
||||
|
key: 'overTimeDuration', |
||||
|
},]) |
||||
|
} |
||||
|
if (optionKeys.includes('vacateStatistic')) { |
||||
|
header = header.concat([{ |
||||
|
title: '累计请假次数', |
||||
|
key: 'vacateCount', |
||||
|
}, { |
||||
|
title: '累计请假总时长 / h', |
||||
|
key: 'vacateDuration', |
||||
|
},]) |
||||
|
} |
||||
|
|
||||
|
// 查询累计加班次数及总时长
|
||||
|
const statisticOvertimeRes = await clickHouse.hr.query(` |
||||
|
SELECT |
||||
|
pep_user_id AS pepUserId, |
||||
|
count(id) AS count, |
||||
|
sum(duration) AS duration |
||||
|
FROM |
||||
|
overtime |
||||
|
WHERE pep_user_id IN (${pepUserIds.join(',')}) |
||||
|
GROUP BY pep_user_id |
||||
|
`).toPromise()
|
||||
|
|
||||
|
const statisticVacateRes = await clickHouse.hr.query(` |
||||
|
SELECT |
||||
|
pep_user_id AS pepUserId, |
||||
|
count(id) AS count, |
||||
|
sum(duration) AS duration |
||||
|
FROM |
||||
|
vacate |
||||
|
WHERE pep_user_id IN (${pepUserIds.join(',')}) |
||||
|
GROUP BY pep_user_id |
||||
|
`).toPromise()
|
||||
|
|
||||
|
exportD.forEach(d => { |
||||
|
d.departmrnt = d.departmrnt.map(dep => dep.name).join('、') |
||||
|
d.role = d.role.map(r => r.name).join('、') |
||||
|
|
||||
|
d.idPhoto ? d.idPhoto = qiniu.domain + '/' + d.idPhoto : '' |
||||
|
d.vitae ? d.vitae = qiniu.domain + '/' + d.vitae : '' |
||||
|
|
||||
|
const corOverTime = statisticOvertimeRes.find(so => so.pepUserId == d.pepUserId) |
||||
|
d.overTimeCount = corOverTime ? corOverTime.count : 0 |
||||
|
d.overTimeDuration = corOverTime ? (corOverTime.duration / 3600).toFixed(1) : 0 |
||||
|
const corVacate = statisticVacateRes.find(so => so.pepUserId == d.pepUserId) |
||||
|
d.vacateCount = corVacate ? corVacate.count : 0 |
||||
|
d.vacateDuration = corVacate ? (corVacate.duration / 3600).toFixed(1) : 0 |
||||
|
}) |
||||
|
|
||||
|
const fileName = `人员信息_${moment().format('YYYYMMDDHHmmss')}` + '.csv' |
||||
|
const filePath = await simpleExcelDown({ data: exportD, header, fileName: fileName }) |
||||
|
const fileData = fs.readFileSync(filePath); |
||||
|
|
||||
|
ctx.status = 200; |
||||
|
ctx.set('Content-Type', 'application/x-xls'); |
||||
|
ctx.set('Content-disposition', 'attachment; filename=' + encodeURI(fileName)); |
||||
|
ctx.body = fileData; |
||||
|
} catch (error) { |
||||
|
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); |
||||
|
ctx.status = 400; |
||||
|
ctx.body = { |
||||
|
message: typeof error == 'string' ? error : undefined |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async function addSalesMemberBulk(ctx) { |
||||
|
let errorMsg = { message: '导入销售人员信息失败' }; |
||||
|
const transaction = await ctx.fs.dc.orm.transaction(); |
||||
|
try { |
||||
|
const models = ctx.fs.dc.models; |
||||
|
const data = ctx.request.body; |
||||
|
let addArr = []; |
||||
|
let editArr = []; |
||||
|
let list = await models.SalesDistribution.findAll({ |
||||
|
attributes: ['pepUserId'] |
||||
|
}); |
||||
|
data.map(d => { |
||||
|
let exist = list.find(m => m.pepUserId == d.pepUserId);//项企的人员编号字段还没有
|
||||
|
if (exist) { |
||||
|
editArr.push(d); |
||||
|
} else { |
||||
|
addArr.push(d); |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
//处理新增的
|
||||
|
if (addArr.length) { |
||||
|
await models.SalesDistribution.bulkCreate(addArr); |
||||
|
} |
||||
|
|
||||
|
//处理编辑的
|
||||
|
if (editArr.length) { |
||||
|
for (let i in editArr) { |
||||
|
let { pepUserId, provinces, cities, del = false } = editArr[i]; |
||||
|
|
||||
|
let dataToUpdate = { |
||||
|
provinces, |
||||
|
cities, |
||||
|
del |
||||
|
} |
||||
|
await models.SalesDistribution.update(dataToUpdate, { where: { pepUserId: pepUserId } }); |
||||
|
} |
||||
|
} |
||||
|
await transaction.commit(); |
||||
|
ctx.status = 204; |
||||
|
} catch (error) { |
||||
|
await transaction.rollback(); |
||||
|
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); |
||||
|
ctx.status = 400; |
||||
|
ctx.body = errorMsg; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
module.exports = { |
||||
|
salesList, |
||||
|
add, |
||||
|
edit, |
||||
|
del, |
||||
|
exportData, |
||||
|
addSalesMemberBulk, |
||||
|
} |
@ -0,0 +1,62 @@ |
|||||
|
/* eslint-disable*/ |
||||
|
|
||||
|
'use strict'; |
||||
|
|
||||
|
module.exports = dc => { |
||||
|
const DataTypes = dc.ORM; |
||||
|
const sequelize = dc.orm; |
||||
|
const SalesDistribution = sequelize.define("salesDistribution", { |
||||
|
id: { |
||||
|
type: DataTypes.INTEGER, |
||||
|
allowNull: false, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: true, |
||||
|
field: "id", |
||||
|
autoIncrement: true, |
||||
|
unique: "sales_distribution_id_uindex" |
||||
|
}, |
||||
|
pepUserId: { |
||||
|
type: DataTypes.INTEGER, |
||||
|
allowNull: false, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "pep_user_id", |
||||
|
autoIncrement: false |
||||
|
}, |
||||
|
provinces: { |
||||
|
type: DataTypes.ARRAY(DataTypes.STRING), |
||||
|
allowNull: false, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "provinces", |
||||
|
autoIncrement: false |
||||
|
}, |
||||
|
cities: { |
||||
|
type: DataTypes.ARRAY(DataTypes.STRING), |
||||
|
allowNull: true, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "cities", |
||||
|
autoIncrement: false |
||||
|
}, |
||||
|
del: { |
||||
|
type: DataTypes.BOOLEAN, |
||||
|
allowNull: true, |
||||
|
defaultValue: null, |
||||
|
comment: null, |
||||
|
primaryKey: false, |
||||
|
field: "del", |
||||
|
autoIncrement: false |
||||
|
} |
||||
|
}, { |
||||
|
tableName: "sales_distribution", |
||||
|
comment: "", |
||||
|
indexes: [] |
||||
|
}); |
||||
|
dc.models.SalesDistribution = SalesDistribution; |
||||
|
return SalesDistribution; |
||||
|
}; |
@ -0,0 +1,24 @@ |
|||||
|
'use strict'; |
||||
|
|
||||
|
const salesDistribution = require('../../controllers/salesDistribution'); |
||||
|
|
||||
|
module.exports = function (app, router, opts) { |
||||
|
|
||||
|
app.fs.api.logAttr['GET/sales/member/list'] = { content: '查询销售人员列表', visible: true }; |
||||
|
router.get('/sales/member/list', salesDistribution.salesList); |
||||
|
|
||||
|
app.fs.api.logAttr['POST/sales/member/add'] = { content: '添加销售人员信息', visible: true }; |
||||
|
router.post('/sales/member/add', salesDistribution.add); |
||||
|
|
||||
|
app.fs.api.logAttr['PUT/sales/member/modify'] = { content: '编辑销售人员信息', visible: true }; |
||||
|
router.put('/sales/member/modify', salesDistribution.edit); |
||||
|
|
||||
|
app.fs.api.logAttr['DEL/sales/member/del'] = { content: '删除销售人员信息', visible: true }; |
||||
|
router.del('/sales/member/del', salesDistribution.del); |
||||
|
|
||||
|
app.fs.api.logAttr['POST/add/sales/members/bulk'] = { content: '导入销售人员信息', visible: true }; |
||||
|
router.post('/add/sales/members/bulk', salesDistribution.addSalesMemberBulk); |
||||
|
|
||||
|
app.fs.api.logAttr['GET/sales/members/export'] = { content: '导出销售人员信息', visible: true }; |
||||
|
router.get('/sales/members/export', salesDistribution.exportData); |
||||
|
}; |
Loading…
Reference in new issue