刘光辉
5 小时以前 cf11e63b659afcc9792ab3e395f1b45d844431a8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import type { SharedFillExecuteCommand, SharedFillPreview } from '#/api/x/eln/records';
 
export class ElnSharedFillParamError extends Error {}
 
export function resolveTestWorkItemIds(value: unknown): string[] {
  if (!Array.isArray(value)) throw new ElnSharedFillParamError('请选择至少两个检验项');
  const ids = value.map((item) => {
    if (typeof item !== 'string' || !item.trim()) throw new ElnSharedFillParamError('检验项选择无效');
    return item.trim();
  });
  const unique = [...new Set(ids)];
  if (unique.length < 2) throw new ElnSharedFillParamError('请选择至少两个检验项');
  return unique;
}
 
interface BuildOptions {
  fieldValues: Record<string, string | undefined>;
  operationCode: string;
  overwriteReasons: Record<string, string | undefined>;
  selectedFieldIds: string[];
  selectedTargetIds: string[];
}
 
function normalizedDecimal(value: string | undefined) {
  const text = value?.trim();
  if (!text) return undefined;
  const match = /^([+-]?)(?:(\d+)(?:\.(\d*))?|\.(\d+))(?:e([+-]?\d+))?$/i.exec(text);
  if (!match) return text;
  const integer = match[2] || '';
  const fraction = match[3] ?? match[4] ?? '';
  let digits = `${integer}${fraction}`.replace(/^0+/, '');
  if (!digits) return '0';
  let scale = BigInt(fraction.length) - BigInt(match[5] || '0');
  while (digits.endsWith('0')) {
    digits = digits.slice(0, -1);
    scale -= 1n;
  }
  return `${match[1] === '-' ? '-' : ''}${digits}e${scale}`;
}
 
export function requiresTargetOverwrite(
  preview: SharedFillPreview,
  testWorkItemId: string,
  fieldVersionIds: string[],
  fieldValues: Record<string, string | undefined>,
) {
  const target = preview.targets.find((item) => item.testWorkItemId === testWorkItemId);
  if (!target) return false;
  return preview.fields.some(
    (field) =>
      fieldVersionIds.includes(field.fieldVersionId) &&
      field.defaultExcludedTestWorkItemIds.includes(testWorkItemId) &&
      normalizedDecimal(target.currentValues[field.fieldVersionId]) !== normalizedDecimal(fieldValues[field.fieldVersionId]),
  );
}
 
export function buildSharedFillCommand(preview: SharedFillPreview, options: BuildOptions): SharedFillExecuteCommand {
  if (!options.selectedFieldIds.length) throw new ElnSharedFillParamError('请选择至少一个共享字段');
  if (!options.selectedTargetIds.length) throw new ElnSharedFillParamError('请选择至少一个填写目标');
  const fields = options.selectedFieldIds.map((fieldVersionId) => {
    const field = preview.fields.find((item) => item.fieldVersionId === fieldVersionId);
    if (!field) throw new ElnSharedFillParamError('共享字段预览已失效');
    const value = options.fieldValues[fieldVersionId]?.trim();
    if (!value) throw new ElnSharedFillParamError(`请填写${field.fieldName || field.fieldCode}`);
    return { field, value };
  });
  const targets = options.selectedTargetIds.map((testWorkItemId) => {
    const target = preview.targets.find((item) => item.testWorkItemId === testWorkItemId);
    if (!target?.eligible || !target.recordId || target.recordVersion === undefined) {
      throw new ElnSharedFillParamError(`${testWorkItemId} 当前不可批量填写`);
    }
    if (fields.some(({ field }) => !field.targetTestWorkItemIds.includes(testWorkItemId))) {
      throw new ElnSharedFillParamError(`${testWorkItemId} 不包含全部选中字段`);
    }
    const requiresOverwrite = requiresTargetOverwrite(preview, testWorkItemId, options.selectedFieldIds, options.fieldValues);
    const overwriteReason = options.overwriteReasons[testWorkItemId]?.trim();
    if (requiresOverwrite && !overwriteReason) throw new ElnSharedFillParamError(`请填写 ${testWorkItemId} 的覆盖原因`);
    return {
      expectedRecordVersion: target.recordVersion,
      overwrite: requiresOverwrite,
      ...(overwriteReason ? { overwriteReason } : {}),
      recordId: target.recordId,
      testWorkItemId,
    };
  });
  return {
    operationCode: options.operationCode,
    previewToken: preview.previewToken,
    targets,
    testWorkItemIds: preview.targets.map((target) => target.testWorkItemId),
    values: fields.map(({ field, value }) => ({ fieldVersionId: field.fieldVersionId, value })),
  };
}
 
export function defaultTargetIds(preview: SharedFillPreview, selectedFieldIds: string[]) {
  if (!selectedFieldIds.length) return [];
  const fields = preview.fields.filter((field) => selectedFieldIds.includes(field.fieldVersionId));
  return preview.targets
    .filter(
      (target) =>
        target.eligible &&
        fields.every(
          (field) => field.targetTestWorkItemIds.includes(target.testWorkItemId) && !field.defaultExcludedTestWorkItemIds.includes(target.testWorkItemId),
        ),
    )
    .map((target) => target.testWorkItemId);
}