企业微信通知增加原始输入
This commit is contained in:
1 parent
7ea7c211cc
commit
be7d765018
5 files changed
+242
-11
No files matched your search
@@ -98,6 +98,17 @@ function jsonObject(value: unknown): Record<string, unknown> {
|
||||
: {};
|
||||
}
|
||||
|
||||
export function readLeaderSummaryOriginalText(config: AppConfig, ciphertext: unknown): string | null {
|
||||
if (typeof ciphertext !== 'string' || !ciphertext) return null;
|
||||
try {
|
||||
return decryptText(config, ciphertext);
|
||||
} catch {
|
||||
// One unreadable historical input must not stop this batch's notifications.
|
||||
// Neither ciphertext nor decrypted instructions belong in diagnostics.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export class LeaderNotificationService {
|
||||
private projectorTimer: NodeJS.Timeout | null = null;
|
||||
private projectorInFlight: Promise<void> | null = null;
|
||||
@@ -433,6 +444,7 @@ export class LeaderNotificationService {
|
||||
task.business_route_id,
|
||||
task.success_receipt,
|
||||
task.created_at,
|
||||
task.original_text_ciphertext,
|
||||
assignee.username AS assignee_username,
|
||||
source_event.id AS source_outbox_event_id,
|
||||
COALESCE(delivery_state.has_exposed_needs_review, false) AS has_exposed_needs_review
|
||||
@@ -545,6 +557,7 @@ export class LeaderNotificationService {
|
||||
businessRouteId: text(row.business_route_id) || null,
|
||||
assigneeUsername: text(row.assignee_username),
|
||||
createdAt: String(row.created_at),
|
||||
originalText: readLeaderSummaryOriginalText(this.config, row.original_text_ciphertext),
|
||||
successReceipt: jsonObject(row.success_receipt),
|
||||
hadNeedsReview: hadExposedNeedsReview
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@ export interface LeaderTaskSummaryInput {
|
||||
businessRouteId: string | null;
|
||||
assigneeUsername: string;
|
||||
createdAt: string | Date;
|
||||
originalText?: string | null;
|
||||
successReceipt?: Record<string, unknown> | null;
|
||||
hadNeedsReview?: boolean;
|
||||
}
|
||||
@@ -38,6 +39,29 @@ const NEEDS_REVIEW_STATUSES = new Set([
|
||||
'uncertain'
|
||||
]);
|
||||
|
||||
// A local display budget; only the original input may be abbreviated. Reserve
|
||||
// space for the outcome so a long instruction cannot hide the actual result.
|
||||
export const LEADER_SUMMARY_MAX_MESSAGE_BYTES = 4_000;
|
||||
const INPUT_TRUNCATION_NOTICE = '\n…(原文过长,剩余内容请在平台查看)';
|
||||
|
||||
function originalInputText(value: string | null | undefined, byteBudget: number): string {
|
||||
const original = typeof value === 'string'
|
||||
? value.replace(/\r\n?/gu, '\n').replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/gu, '').trim()
|
||||
: '';
|
||||
if (!original) return '原始输入暂不可用,请在平台查看。';
|
||||
if (Buffer.byteLength(original, 'utf8') <= byteBudget) return original;
|
||||
const prefixBudget = Math.max(0, byteBudget - Buffer.byteLength(INPUT_TRUNCATION_NOTICE, 'utf8'));
|
||||
let prefix = '';
|
||||
let bytes = 0;
|
||||
for (const character of original) {
|
||||
const nextBytes = Buffer.byteLength(character, 'utf8');
|
||||
if (bytes + nextBytes > prefixBudget) break;
|
||||
prefix += character;
|
||||
bytes += nextBytes;
|
||||
}
|
||||
return prefix + INPUT_TRUNCATION_NOTICE;
|
||||
}
|
||||
|
||||
function singleLine(value: unknown, maxLength: number): string {
|
||||
return String(value ?? '')
|
||||
.normalize('NFKC')
|
||||
@@ -131,23 +155,26 @@ export function buildLeaderTaskSummary(
|
||||
const route = businessRouteById(input.businessRouteId);
|
||||
const businessLabel = singleLine(route?.directive || '其他任务', 120) || '其他任务';
|
||||
const assignee = singleLine(input.assigneeUsername, 160) || '未知员工';
|
||||
const taskId = singleLine(input.taskId, 200) || '任务编号未记录';
|
||||
const header = milestone === 'resolved' ? '【员工任务摘要·结果更新】' : '【员工任务摘要】';
|
||||
const prefix = [
|
||||
header,
|
||||
`员工:${assignee}`,
|
||||
`业务:${businessLabel}`,
|
||||
`状态:${statusLabel}`,
|
||||
`提交:${formatShanghaiTimestamp(input.createdAt)}`,
|
||||
'',
|
||||
'原始输入:',
|
||||
''
|
||||
].join('\n');
|
||||
const suffix = `\n\n结果:${resultText.slice(0, 300)}`;
|
||||
const inputBudget = LEADER_SUMMARY_MAX_MESSAGE_BYTES - Buffer.byteLength(prefix + suffix, 'utf8');
|
||||
return {
|
||||
milestone,
|
||||
deliveryStatus,
|
||||
statusLabel,
|
||||
businessLabel,
|
||||
resultText: resultText.slice(0, 300),
|
||||
messageText: [
|
||||
header,
|
||||
`员工:${assignee}`,
|
||||
`业务:${businessLabel}`,
|
||||
`状态:${statusLabel}`,
|
||||
`任务:${taskId}`,
|
||||
`提交:${formatShanghaiTimestamp(input.createdAt)}`,
|
||||
`结果:${resultText.slice(0, 300)}`
|
||||
].join('\n')
|
||||
messageText: prefix + originalInputText(input.originalText, inputBudget) + suffix
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user