11
This commit is contained in:
178
api/controller_front/job_postings.js
Normal file
178
api/controller_front/job_postings.js
Normal file
@@ -0,0 +1,178 @@
|
||||
/**
|
||||
* 岗位表(job_postings)客户端接口:仅查本表字段,不连表
|
||||
*/
|
||||
|
||||
const Framework = require('../../framework/node-core-framework.js');
|
||||
|
||||
/**
|
||||
* 从 resume_info.job_listings 解析 Boss Tab 文案列表(与 get_job_listings 一致)
|
||||
* @param {unknown} job_listings
|
||||
* @returns {string[]}
|
||||
*/
|
||||
function parse_resume_tab_labels(job_listings) {
|
||||
if (job_listings == null) return [];
|
||||
let arr = job_listings;
|
||||
if (typeof arr === 'string') {
|
||||
try {
|
||||
arr = JSON.parse(arr);
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
if (!Array.isArray(arr)) return [];
|
||||
const out = [];
|
||||
arr.forEach((item) => {
|
||||
if (item == null) return;
|
||||
if (typeof item === 'string') {
|
||||
const s = item.trim();
|
||||
if (s) out.push(s);
|
||||
return;
|
||||
}
|
||||
if (typeof item === 'object' && item.text != null) {
|
||||
const s = String(item.text).trim();
|
||||
if (s) out.push(s);
|
||||
}
|
||||
});
|
||||
return [...new Set(out)];
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* 按设备 SN 拉取 jobId -> 投递结果字段(is_delivered、deliver_failed_reason、applyStatus)
|
||||
* 用于 Electron 职位列表页合并展示,不查 apply_records
|
||||
*/
|
||||
'POST /job_postings/deliver_status_map': async (ctx) => {
|
||||
const models = Framework.getModels();
|
||||
const { job_postings, op } = models;
|
||||
const body = ctx.getBody() || {};
|
||||
const sn_code = body.sn_code || ctx.query?.sn_code;
|
||||
const platform = body.platform || 'boss';
|
||||
const startTime = body.startTime;
|
||||
const endTime = body.endTime;
|
||||
|
||||
if (!sn_code) {
|
||||
return ctx.fail('请提供设备SN码');
|
||||
}
|
||||
|
||||
const where = { sn_code, platform };
|
||||
if (startTime || endTime) {
|
||||
where.last_modify_time = {};
|
||||
if (startTime) {
|
||||
where.last_modify_time[op.gte] = new Date(startTime);
|
||||
}
|
||||
if (endTime) {
|
||||
const end = new Date(endTime);
|
||||
end.setHours(23, 59, 59, 999);
|
||||
where.last_modify_time[op.lte] = end;
|
||||
}
|
||||
}
|
||||
|
||||
const rows = await job_postings.findAll({
|
||||
where,
|
||||
attributes: ['jobId', 'is_delivered', 'deliver_failed_reason', 'applyStatus', 'last_modify_time'],
|
||||
order: [['last_modify_time', 'DESC']],
|
||||
limit: Math.min(Number(body.limit) || 2000, 5000)
|
||||
});
|
||||
|
||||
const list = rows.map((r) => {
|
||||
const j = r.toJSON ? r.toJSON() : r;
|
||||
return {
|
||||
jobId: j.jobId,
|
||||
is_delivered: !!j.is_delivered,
|
||||
deliver_failed_reason: j.deliver_failed_reason || '',
|
||||
applyStatus: j.applyStatus || 'pending',
|
||||
last_modify_time: j.last_modify_time
|
||||
};
|
||||
});
|
||||
|
||||
return ctx.success({ list });
|
||||
},
|
||||
|
||||
/**
|
||||
* 按设备 SN 分页查询 job_postings(仅本表字段,用于 Electron 职位列表只读展示)
|
||||
*/
|
||||
'POST /job_postings/page_list': async (ctx) => {
|
||||
const models = Framework.getModels();
|
||||
const { job_postings, resume_info, op } = models;
|
||||
const body = ctx.getBody() || {};
|
||||
const sn_code = body.sn_code || ctx.query?.sn_code;
|
||||
const platform = body.platform || 'boss';
|
||||
|
||||
if (!sn_code) {
|
||||
return ctx.fail('请提供设备SN码');
|
||||
}
|
||||
|
||||
const page = Math.max(1, Number(body.page) || 1);
|
||||
const raw_page_size = body.page_size ?? body.pageSize ?? 20;
|
||||
const page_size = Math.min(100, Math.max(1, Number(raw_page_size) || 20));
|
||||
const offset = (page - 1) * page_size;
|
||||
|
||||
const base_where = { sn_code, platform };
|
||||
const startTime = body.startTime;
|
||||
const endTime = body.endTime;
|
||||
if (startTime || endTime) {
|
||||
base_where.last_modify_time = {};
|
||||
if (startTime) {
|
||||
base_where.last_modify_time[op.gte] = new Date(startTime);
|
||||
}
|
||||
if (endTime) {
|
||||
const end = new Date(endTime);
|
||||
end.setHours(23, 59, 59, 999);
|
||||
base_where.last_modify_time[op.lte] = end;
|
||||
}
|
||||
}
|
||||
|
||||
let tab_labels = [];
|
||||
if (resume_info) {
|
||||
const resume = await resume_info.findOne({
|
||||
where: { sn_code, platform, isActive: true },
|
||||
order: [['last_modify_time', 'DESC']]
|
||||
});
|
||||
if (resume) {
|
||||
const rj = resume.toJSON ? resume.toJSON() : resume;
|
||||
tab_labels = parse_resume_tab_labels(rj.job_listings);
|
||||
}
|
||||
}
|
||||
|
||||
/** 不按「当前投递标签」过滤;列表为设备+平台下库中全部职位(各标签写入的 keyword 均会出现在列表中)。tab_labels 供前端展示「账户已同步的全部 Tab」 */
|
||||
const where = base_where;
|
||||
|
||||
const { rows, count } = await job_postings.findAndCountAll({
|
||||
where,
|
||||
attributes: [
|
||||
'id',
|
||||
'jobId',
|
||||
'jobTitle',
|
||||
'companyName',
|
||||
'salary',
|
||||
'location',
|
||||
'education',
|
||||
'experience',
|
||||
'last_modify_time',
|
||||
'create_time',
|
||||
'is_delivered',
|
||||
'deliver_failed_reason',
|
||||
'applyStatus',
|
||||
'platform',
|
||||
'keyword',
|
||||
'aiMatchScore'
|
||||
],
|
||||
limit: page_size,
|
||||
offset,
|
||||
order: [['last_modify_time', 'DESC']]
|
||||
});
|
||||
|
||||
const list = rows.map((r) => {
|
||||
const j = r.toJSON ? r.toJSON() : r;
|
||||
return j;
|
||||
});
|
||||
|
||||
return ctx.success({
|
||||
list,
|
||||
total: count,
|
||||
page,
|
||||
page_size,
|
||||
tab_labels
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -308,6 +308,98 @@ module.exports = {
|
||||
console.error('[任务管理] 获取任务统计失败:', error);
|
||||
return ctx.fail('获取任务统计失败: ' + (error.message || '未知错误'));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 按设备 SN 分页查询任务指令列表(展示请求参数与响应结果)
|
||||
*/
|
||||
'POST /task/command/page_list': async (ctx) => {
|
||||
try {
|
||||
const body = ctx.getBody() || {};
|
||||
const sn_code = body.sn_code || ctx.query?.sn_code;
|
||||
const page = Math.max(1, Number(body.page) || 1);
|
||||
const raw_page_size = body.page_size ?? body.pageSize ?? 20;
|
||||
const page_size = Math.min(100, Math.max(1, Number(raw_page_size) || 20));
|
||||
const offset = (page - 1) * page_size;
|
||||
|
||||
if (!sn_code) {
|
||||
return ctx.fail('请提供设备SN码');
|
||||
}
|
||||
|
||||
const { task_status, task_commands, op } = await Framework.getModels();
|
||||
|
||||
// 先查当前账号对应任务,再按 task_id 关联查指令
|
||||
const task_rows = await task_status.findAll({
|
||||
where: { sn_code },
|
||||
attributes: ['id'],
|
||||
order: [['id', 'DESC']],
|
||||
limit: 2000
|
||||
});
|
||||
const task_ids = task_rows.map((row) => row.id).filter((id) => id != null);
|
||||
if (!task_ids.length) {
|
||||
return ctx.success({
|
||||
list: [],
|
||||
total: 0,
|
||||
page,
|
||||
page_size
|
||||
});
|
||||
}
|
||||
|
||||
const where = {
|
||||
task_id: { [op.in]: task_ids }
|
||||
};
|
||||
if (body.command_type) {
|
||||
where.command_type = String(body.command_type).trim();
|
||||
}
|
||||
if (body.status) {
|
||||
where.status = String(body.status).trim();
|
||||
}
|
||||
|
||||
const { rows, count } = await task_commands.findAndCountAll({
|
||||
where,
|
||||
attributes: [
|
||||
'id',
|
||||
'task_id',
|
||||
'command_type',
|
||||
'command_name',
|
||||
'command_params',
|
||||
'status',
|
||||
'priority',
|
||||
'sequence',
|
||||
'retry_count',
|
||||
'max_retries',
|
||||
'start_time',
|
||||
'end_time',
|
||||
'duration',
|
||||
'result',
|
||||
'error_message',
|
||||
'progress'
|
||||
],
|
||||
order: [['id', 'DESC']],
|
||||
limit: page_size,
|
||||
offset
|
||||
});
|
||||
|
||||
const list = rows.map((r) => {
|
||||
const j = r.toJSON ? r.toJSON() : r;
|
||||
return {
|
||||
...j,
|
||||
// 前端直接展示,避免超长字段压垮 UI
|
||||
command_params_preview: j.command_params ? String(j.command_params).slice(0, 1200) : '',
|
||||
result_preview: j.result ? String(j.result).slice(0, 1200) : ''
|
||||
};
|
||||
});
|
||||
|
||||
return ctx.success({
|
||||
list,
|
||||
total: count,
|
||||
page,
|
||||
page_size
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('[任务管理] 获取指令分页失败:', error);
|
||||
return ctx.fail('获取指令分页失败: ' + (error.message || '未知错误'));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user