This commit is contained in:
张成
2026-04-16 17:39:25 +08:00
parent df0aacc782
commit a45418883c
6 changed files with 212 additions and 63 deletions

View File

@@ -323,7 +323,7 @@ export default {
sort_order: row.sort_order || 0 sort_order: row.sort_order || 0
} }
this.$refs.editModal.editShow(editRow,(newRow)=>{ this.$refs.editModal.editShow(editRow,(newRow)=>{
debugger
this.handleSaveSuccess(newRow) this.handleSaveSuccess(newRow)
}) })

View File

@@ -326,62 +326,76 @@ module.exports = {
return ctx.fail('请提供设备SN码'); return ctx.fail('请提供设备SN码');
} }
const { task_status, task_commands, op } = await Framework.getModels(); const { task_commands } = await Framework.getModels();
const sequelize = task_commands.sequelize;
const Sequelize = require('sequelize');
// 先查当前账号对应任务,再按 task_id 关联查指令 const conditions = ['ts.sn_code = :sn_code'];
const task_rows = await task_status.findAll({ const replacements = {
where: { sn_code }, 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, limit: page_size,
offset offset
}); };
const command_type = body.command_type != null ? String(body.command_type).trim() : '';
const status = body.status != null ? String(body.status).trim() : '';
if (command_type) {
conditions.push('tc.command_type = :command_type');
replacements.command_type = command_type;
}
if (status) {
conditions.push('tc.status = :status');
replacements.status = status;
}
// 仅保留真实下发到客户端的指令,过滤服务端汇总类记录
conditions.push("tc.command_type <> 'job_filter_summary'");
const where_sql = conditions.join(' AND ');
// 用 JOIN + 覆盖索引,避免大 IN 列表导致慢查询
const count_sql = `
SELECT COUNT(*) AS count
FROM task_commands tc
INNER JOIN task_status ts ON ts.id = tc.task_id
WHERE ${where_sql}
`;
const data_sql = `
SELECT
tc.id,
tc.task_id,
tc.command_type,
tc.command_name,
tc.command_params,
tc.status,
tc.priority,
tc.sequence,
tc.retry_count,
tc.max_retries,
tc.start_time,
tc.end_time,
tc.duration,
tc.result,
tc.error_message,
tc.progress
FROM task_commands tc
INNER JOIN task_status ts ON ts.id = tc.task_id
WHERE ${where_sql}
ORDER BY tc.id DESC
LIMIT :limit OFFSET :offset
`;
const [count_result, rows] = await Promise.all([
sequelize.query(count_sql, {
replacements,
type: Sequelize.QueryTypes.SELECT
}),
sequelize.query(data_sql, {
replacements,
type: Sequelize.QueryTypes.SELECT
})
]);
const count = Number(count_result && count_result[0] ? count_result[0].count : 0) || 0;
const list = rows.map((r) => { const list = rows.map((r) => {
const j = r.toJSON ? r.toJSON() : r; const j = r && typeof r.toJSON === 'function' ? r.toJSON() : r;
return { return {
...j, ...j,
// 前端直接展示,避免超长字段压垮 UI // 前端直接展示,避免超长字段压垮 UI
@@ -400,6 +414,41 @@ module.exports = {
console.error('[任务管理] 获取指令分页失败:', error); console.error('[任务管理] 获取指令分页失败:', error);
return ctx.fail('获取指令分页失败: ' + (error.message || '未知错误')); return ctx.fail('获取指令分页失败: ' + (error.message || '未知错误'));
} }
},
/**
* 按设备 SN 重试单条指令
*/
'POST /task/command/retry': async (ctx) => {
try {
const body = ctx.getBody() || {};
const query = typeof ctx.getQuery === 'function' ? (ctx.getQuery() || {}) : {};
const sn_code = body.sn_code || query.sn_code;
const command_id = Number(body.command_id || body.commandId || 0);
if (!sn_code) {
return ctx.fail('请提供设备SN码');
}
if (!command_id) {
return ctx.fail('请提供指令ID');
}
const { task_commands, task_status } = await Framework.getModels();
const command_row = await task_commands.findByPk(command_id);
if (!command_row) {
return ctx.fail('指令不存在');
}
const task_row = await task_status.findByPk(command_row.task_id);
if (!task_row || String(task_row.sn_code || '') !== String(sn_code)) {
return ctx.fail('无权重试该指令');
}
const plaAccountService = require('../services/pla_account_service.js');
const result = await plaAccountService.retryCommand({ commandId: command_id });
return ctx.success(result);
} catch (error) {
console.error('[任务管理] 重试指令失败:', error);
return ctx.fail('重试指令失败: ' + (error.message || '未知错误'));
}
} }
}; };

View File

@@ -142,6 +142,12 @@ class DeliverHandler extends BaseHandler {
return { return {
deliveredCount: deliverCommands.length, deliveredCount: deliverCommands.length,
filterSummary: {
rawCount: pendingJobs.length,
passedCount: filteredJobs.length,
deliverCount: jobsToDeliver.length,
filteredCount: Math.max(0, pendingJobs.length - filteredJobs.length)
},
...result ...result
}; };
} }

View File

@@ -94,14 +94,16 @@ class JobFilterEngine {
*/ */
async get_single_job_filter_fail_reason(job, config) { async get_single_job_filter_fail_reason(job, config) {
const j = job; const j = job;
const title_text = String(j.jobTitle || '').trim() || '未知职位';
const after_salary = this.filterBySalary([j], config); const after_salary = this.filterBySalary([j], config);
if (after_salary.length === 0) { if (after_salary.length === 0) {
return `薪资不在设定范围(${config.min_salary ?? 0}-${config.max_salary ?? 0}K)`; const salary_text = String(j.salary || j.salaryDesc || '-');
return `职位「${title_text}」薪资不在设定范围(${config.min_salary ?? 0}-${config.max_salary ?? 0}K),当前薪资: ${salary_text}`;
} }
const after_title = this.filterByTitleIncludeKeywords(after_salary, config); const after_title = this.filterByTitleIncludeKeywords(after_salary, config);
if (after_title.length === 0) { if (after_title.length === 0) {
const kws = (config.title_include_keywords || []).join('、') || '无'; const kws = (config.title_include_keywords || []).join('、') || '无';
return `职位标题须包含以下关键词之一${kws}`; return `职位${title_text}」标题未命中必含关键词:${kws}`;
} }
const after_kw = this.filterByKeywords(after_title, config); const after_kw = this.filterByKeywords(after_title, config);
if (after_kw.length === 0) { if (after_kw.length === 0) {
@@ -114,30 +116,30 @@ class JobFilterEngine {
if (match_result && match_result.details && match_result.details.exclude && match_result.details.exclude.matched) { if (match_result && match_result.details && match_result.details.exclude && match_result.details.exclude.matched) {
const hit_keywords = match_result.details.exclude.keywords || []; const hit_keywords = match_result.details.exclude.keywords || [];
if (hit_keywords.length) { if (hit_keywords.length) {
return `命中排除关键词: ${hit_keywords.join('、')}`; return `职位「${title_text}命中排除关键词: ${hit_keywords.join('、')}`;
} }
} }
if (match_result && match_result.details && match_result.details.filter && !match_result.details.filter.matched) { if (match_result && match_result.details && match_result.details.filter && !match_result.details.filter.matched) {
const needed = Array.isArray(config.filter_keywords) ? config.filter_keywords : []; const needed = Array.isArray(config.filter_keywords) ? config.filter_keywords : [];
if (needed.length) { if (needed.length) {
return `未命中包含关键词: ${needed.join('、')}`; return `职位「${title_text}未命中包含关键词: ${needed.join('、')}`;
} }
} }
return '命中排除关键词或未满足包含词规则'; return `职位「${title_text}命中排除关键词或未满足包含词规则`;
} }
if (config.filter_inactive_companies) { if (config.filter_inactive_companies) {
const after_act = await this.filterByCompanyActivity(after_kw, config.company_active_days || 7); const after_act = await this.filterByCompanyActivity(after_kw, config.company_active_days || 7);
if (after_act.length === 0) { if (after_act.length === 0) {
return '公司活跃度不满足配置'; return `职位「${title_text}公司活跃度不满足配置`;
} }
} }
if (config.deduplicate) { if (config.deduplicate) {
const after_dedup = this.deduplicateJobs(after_kw); const after_dedup = this.deduplicateJobs(after_kw);
if (after_dedup.length === 0) { if (after_dedup.length === 0) {
return '与列表内职位重复(公司+岗位名)'; return `职位「${title_text}与列表内职位重复(公司+岗位名)`;
} }
} }
return '未通过职位过滤'; return `职位「${title_text}未通过职位过滤`;
} }
/** /**

View File

@@ -485,8 +485,9 @@ class PlaAccountService {
finalParams.keyword = account.keyword; finalParams.keyword = account.keyword;
} }
// get_job_listcommand 只约定 pageCount + tabLabel与前端一致入库 keyword 由 jobManager 用 tabLabel 推导 // get_job_list优先使用调用方显式传入的 tabLabel仅在未传时回退到 resume_info.deliver_tab_label
if (commandTypeSnake === 'get_job_list') { if (commandTypeSnake === 'get_job_list') {
const passedTabLabel = finalParams.tabLabel != null ? String(finalParams.tabLabel).trim() : '';
try { try {
const resume_info = db.getModel('resume_info'); const resume_info = db.getModel('resume_info');
const resume = await resume_info.findOne({ const resume = await resume_info.findOne({
@@ -494,7 +495,7 @@ class PlaAccountService {
order: [['last_modify_time', 'DESC']], order: [['last_modify_time', 'DESC']],
attributes: ['deliver_tab_label'] attributes: ['deliver_tab_label']
}); });
if (resume && resume.deliver_tab_label) { if (!passedTabLabel && resume && resume.deliver_tab_label) {
finalParams.tabLabel = String(resume.deliver_tab_label).trim(); finalParams.tabLabel = String(resume.deliver_tab_label).trim();
} }
} catch (e) { } catch (e) {
@@ -639,6 +640,35 @@ class PlaAccountService {
command_params: JSON.stringify(commandParams) command_params: JSON.stringify(commandParams)
}; };
// 防止 result 超长导致 "Data too long for column 'result'"
const serialize_retry_result_for_db = (value) => {
const max_length = 60000;
try {
let text = JSON.stringify(value || {});
if (text.length <= max_length) {
return text;
}
const summary = {
success: !!(value && value.success),
message: (value && value.message) ? String(value.message) : '重试结果过长,已摘要存储',
truncated: true,
raw_length: text.length,
preview: text.substring(0, 1000)
};
text = JSON.stringify(summary);
if (text.length > max_length) {
return text.substring(0, max_length - 12) + '...[已截断]';
}
return text;
} catch (error) {
return JSON.stringify({
success: false,
message: '结果序列化失败',
error: error.message || 'unknown'
});
}
};
// 执行指令并同步更新当前指令记录状态 // 执行指令并同步更新当前指令记录状态
const start_time = new Date(); const start_time = new Date();
try { try {
@@ -650,7 +680,7 @@ class PlaAccountService {
start_time: start_time, start_time: start_time,
end_time: end_time, end_time: end_time,
duration: end_time.getTime() - start_time.getTime(), duration: end_time.getTime() - start_time.getTime(),
result: JSON.stringify(result || {}) result: serialize_retry_result_for_db(result)
}); });
return { return {

File diff suppressed because one or more lines are too long