ASSET1 统一电子资产与固定资产台账
这个提交包含在:
+108
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env node
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
const DEFAULT_INDEX =
|
||||
"/home/shunli/Projects/faculty-daily-assistant/公司/00_保密索引/内部资料保密索引_含真实路径_不得外发.csv";
|
||||
|
||||
function argValue(name, fallback = null) {
|
||||
const prefix = `${name}=`;
|
||||
const found = process.argv.slice(2).find((item) => item === name || item.startsWith(prefix));
|
||||
if (!found) return fallback;
|
||||
if (found === name) return "1";
|
||||
return found.slice(prefix.length);
|
||||
}
|
||||
|
||||
function parseCsvLine(line) {
|
||||
const cells = [];
|
||||
let current = "";
|
||||
let quoted = false;
|
||||
for (let index = 0; index < line.length; index += 1) {
|
||||
const char = line[index];
|
||||
const next = line[index + 1];
|
||||
if (quoted && char === '"' && next === '"') {
|
||||
current += '"';
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
if (char === '"') {
|
||||
quoted = !quoted;
|
||||
continue;
|
||||
}
|
||||
if (!quoted && char === ",") {
|
||||
cells.push(current);
|
||||
current = "";
|
||||
continue;
|
||||
}
|
||||
current += char;
|
||||
}
|
||||
cells.push(current);
|
||||
return cells;
|
||||
}
|
||||
|
||||
function parseCsv(content) {
|
||||
const lines = content.replace(/^\uFEFF/, "").split(/\r?\n/).filter(Boolean);
|
||||
if (!lines.length) return [];
|
||||
const headers = parseCsvLine(lines[0]).map((item) => item.trim());
|
||||
return lines.slice(1).map((line) => {
|
||||
const values = parseCsvLine(line);
|
||||
return Object.fromEntries(headers.map((header, index) => [header, values[index] ?? ""]));
|
||||
});
|
||||
}
|
||||
|
||||
function mapSensitivity(level) {
|
||||
if (level === "S4") return "strictly_confidential";
|
||||
if (level === "S3") return "confidential";
|
||||
if (level === "S1") return "public_candidate";
|
||||
return "internal";
|
||||
}
|
||||
|
||||
const input = argValue("--input", DEFAULT_INDEX);
|
||||
const limit = Number(argValue("--limit", "3"));
|
||||
const reportDir =
|
||||
argValue("--report-dir") ??
|
||||
path.resolve(process.cwd(), `artifacts/qa-flow/asset-redacted-index-${new Date().toISOString().replace(/[-:.TZ]/g, "").slice(0, 14)}`);
|
||||
|
||||
fs.mkdirSync(reportDir, { recursive: true });
|
||||
|
||||
const rows = parseCsv(fs.readFileSync(input, "utf8"));
|
||||
const selected = rows.slice(0, Number.isFinite(limit) && limit > 0 ? limit : 3);
|
||||
const items = selected.map((row, index) => ({
|
||||
index,
|
||||
code: row.code || null,
|
||||
name: row.doc_type ? `${row.doc_type}-${row.code || index + 1}` : `电子资料-${row.code || index + 1}`,
|
||||
extension: row.extension || null,
|
||||
docType: row.doc_type || null,
|
||||
sourceHash: row.sha256_12 || null,
|
||||
sensitivityLevel: mapSensitivity(row.level),
|
||||
detectedCategories: row.detected_categories || null,
|
||||
requiredRedaction: row.required_redaction || null,
|
||||
rawFileImported: false,
|
||||
}));
|
||||
|
||||
const bySensitivity = items.reduce((acc, item) => {
|
||||
acc[item.sensitivityLevel] = (acc[item.sensitivityLevel] ?? 0) + 1;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const summary = {
|
||||
issueId: "ASSET1",
|
||||
status: "PASS_REDACTED_INDEX_DRY_RUN_READY",
|
||||
mode: "asset-redacted-index-dry-run",
|
||||
input: "local_confidential_index",
|
||||
checkedAt: new Date().toISOString(),
|
||||
totalRows: rows.length,
|
||||
selectedRows: items.length,
|
||||
bySensitivity,
|
||||
items,
|
||||
boundaries: {
|
||||
dryRunOnly: true,
|
||||
noFileUpload: true,
|
||||
noRawContent: true,
|
||||
noFullPathOutput: true,
|
||||
noSecretOutput: true,
|
||||
},
|
||||
};
|
||||
|
||||
fs.writeFileSync(path.join(reportDir, "summary.json"), `${JSON.stringify(summary, null, 2)}\n`, "utf8");
|
||||
console.log(JSON.stringify(summary, null, 2));
|
||||
在新工单中引用
屏蔽一个用户