@liuhange/dsh-registration-precheck 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -0
- package/lib/__tests__/precheckExecutor.test.js +58 -0
- package/lib/index.js +44 -0
- package/lib/precheckExecutor.js +114 -0
- package/lib/types/__tests__/precheckExecutor.test.d.ts +2 -0
- package/lib/types/index.d.ts +5 -0
- package/lib/types/precheckExecutor.d.ts +15 -0
- package/package.json +28 -0
package/README.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# @liuhange/dsh-registration-precheck
|
|
2
|
+
|
|
3
|
+
登记预检插件:在数据资产体检完成后,自动检查数据是否满足国家数据产权登记条件。
|
|
4
|
+
|
|
5
|
+
## 功能说明
|
|
6
|
+
|
|
7
|
+
依据《数据产权登记工作指引(试行)》(国数综政策〔2026〕35号)规定的不予登记情形,执行四项检查:
|
|
8
|
+
1. 国家安全检查:数据是否涉及敏感领域
|
|
9
|
+
2. 来源合规检查:复用脱敏模块来源合法性声明
|
|
10
|
+
3. 权属纠纷检查:读取企业权属确认结果
|
|
11
|
+
4. 材料真实性承诺:提示企业如实填报
|
|
12
|
+
|
|
13
|
+
输出《登记预检报告》,明确告知"可以申请登记"或"暂不满足登记条件"。
|
|
14
|
+
|
|
15
|
+
## 依赖
|
|
16
|
+
|
|
17
|
+
- `@liuhange/dsh-data-asset-shared: ^3.0.0`
|
|
18
|
+
- `@deepseek-ai/cordis: ^4.0.0`
|
|
19
|
+
- `@deepseek-ai/dsh-tools: 0.0.1-rc.1`
|
|
20
|
+
|
|
21
|
+
## 安装
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install @liuhange/dsh-registration-precheck
|
|
25
|
+
```
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { PrecheckExecutor } from '../precheckExecutor.js';
|
|
3
|
+
const validResult = {
|
|
4
|
+
maskingReport: '脱敏报告,来源合法,合规声明',
|
|
5
|
+
cleaningReport: '清洗报告',
|
|
6
|
+
inventoryReport: '盘点报告',
|
|
7
|
+
packagingManual: '包装报告',
|
|
8
|
+
completedStages: 4,
|
|
9
|
+
};
|
|
10
|
+
const confirmedOwnership = { hasDispute: false, confirmedAt: '2026-09-12T10:00:00Z' };
|
|
11
|
+
const disputedOwnership = { hasDispute: true, confirmedAt: '2026-09-12T10:00:00Z' };
|
|
12
|
+
const unconfirmedOwnership = { hasDispute: false, confirmedAt: '' };
|
|
13
|
+
describe('PrecheckExecutor', () => {
|
|
14
|
+
const executor = new PrecheckExecutor();
|
|
15
|
+
it('四项全通过 → 结论 CAN_REGISTER', () => {
|
|
16
|
+
const report = executor.execute(validResult, confirmedOwnership);
|
|
17
|
+
expect(report.conclusion).toBe('CAN_REGISTER');
|
|
18
|
+
expect(report.failedItems).toHaveLength(0);
|
|
19
|
+
expect(report.checks).toHaveLength(4);
|
|
20
|
+
});
|
|
21
|
+
it('权属有纠纷 → 结论 CANNOT_REGISTER', () => {
|
|
22
|
+
const report = executor.execute(validResult, disputedOwnership);
|
|
23
|
+
expect(report.conclusion).toBe('CANNOT_REGISTER');
|
|
24
|
+
expect(report.failedItems).toContain('权属纠纷检查');
|
|
25
|
+
});
|
|
26
|
+
it('权属未确认 → 结论 PENDING_CONFIRMATION', () => {
|
|
27
|
+
const report = executor.execute(validResult, unconfirmedOwnership);
|
|
28
|
+
expect(report.conclusion).toBe('PENDING_CONFIRMATION');
|
|
29
|
+
});
|
|
30
|
+
it('产物不完整 → 返回缺失项', () => {
|
|
31
|
+
const incomplete = {
|
|
32
|
+
maskingReport: '',
|
|
33
|
+
cleaningReport: '',
|
|
34
|
+
inventoryReport: '',
|
|
35
|
+
packagingManual: '',
|
|
36
|
+
completedStages: 0,
|
|
37
|
+
};
|
|
38
|
+
const report = executor.execute(incomplete, confirmedOwnership);
|
|
39
|
+
expect(report.conclusion).toBe('CANNOT_REGISTER');
|
|
40
|
+
expect(report.failedItems[0]).toContain('体检产物不完整');
|
|
41
|
+
});
|
|
42
|
+
it('来源声明缺失 → UNDETERMINED', () => {
|
|
43
|
+
const noSource = {
|
|
44
|
+
...validResult,
|
|
45
|
+
maskingReport: '脱敏报告无来源信息',
|
|
46
|
+
};
|
|
47
|
+
const report = executor.execute(noSource, confirmedOwnership);
|
|
48
|
+
const sourceCheck = report.checks.find(c => c.name === 'SOURCE_COMPLIANCE');
|
|
49
|
+
expect(sourceCheck?.result).toBe('UNDETERMINED');
|
|
50
|
+
});
|
|
51
|
+
it('相同输入相同输出(可重复性)', () => {
|
|
52
|
+
const r1 = executor.execute(validResult, confirmedOwnership);
|
|
53
|
+
const r2 = executor.execute(validResult, confirmedOwnership);
|
|
54
|
+
expect(r1.conclusion).toBe(r2.conclusion);
|
|
55
|
+
expect(r1.checks.length).toBe(r2.checks.length);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
//# sourceMappingURL=precheckExecutor.test.js.map
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
2
|
+
import { AuditLogger, } from '@liuhange/dsh-data-asset-shared';
|
|
3
|
+
import { PrecheckExecutor } from './precheckExecutor.js';
|
|
4
|
+
export const name = 'registration-precheck';
|
|
5
|
+
export const inject = ['tools'];
|
|
6
|
+
export function apply(ctx) {
|
|
7
|
+
const auditLogger = new AuditLogger();
|
|
8
|
+
const precheckExecutor = new PrecheckExecutor();
|
|
9
|
+
ctx.tools.register(defineTool({
|
|
10
|
+
name: 'registration_precheck',
|
|
11
|
+
description: '登记预检:检查数据是否满足国家数据产权登记条件,输出《登记预检报告》',
|
|
12
|
+
parameters: {
|
|
13
|
+
orchestrationResult: {
|
|
14
|
+
type: 'string',
|
|
15
|
+
required: true,
|
|
16
|
+
description: '体检全流程产物JSON(含 maskingReport/cleaningReport/inventoryReport/packagingManual)',
|
|
17
|
+
},
|
|
18
|
+
ownershipConfirmation: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
required: true,
|
|
21
|
+
description: '权属确认信息JSON(含 hasDispute/confirmedAt)',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
output: {
|
|
25
|
+
schema: { type: 'string' },
|
|
26
|
+
render: (_args, value) => [{ type: 'text', text: value }],
|
|
27
|
+
},
|
|
28
|
+
async execute(args) {
|
|
29
|
+
const orchestrationResult = JSON.parse(args.orchestrationResult);
|
|
30
|
+
const ownershipConfirmation = JSON.parse(args.ownershipConfirmation);
|
|
31
|
+
const report = precheckExecutor.execute(orchestrationResult, ownershipConfirmation);
|
|
32
|
+
auditLogger.log({
|
|
33
|
+
pluginName: 'registration-precheck',
|
|
34
|
+
operation: 'registration_precheck',
|
|
35
|
+
inputPath: '-',
|
|
36
|
+
outputPath: '-',
|
|
37
|
+
result: report.conclusion === 'CAN_REGISTER' ? 'SUCCESS' : 'FAILED',
|
|
38
|
+
});
|
|
39
|
+
return JSON.stringify(report, null, 2);
|
|
40
|
+
},
|
|
41
|
+
}));
|
|
42
|
+
console.log('[registration-precheck] 登记预检插件已加载');
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { PolicyReferenceResolver } from '@liuhange/dsh-data-asset-shared';
|
|
2
|
+
const SENSITIVE_KEYWORDS = ['军事', '国防', '机密', '秘密', '核能', '武器'];
|
|
3
|
+
export class PrecheckExecutor {
|
|
4
|
+
execute(orchestrationResult, ownershipConfirmation) {
|
|
5
|
+
const missingInputs = this.validateInputs(orchestrationResult);
|
|
6
|
+
if (missingInputs.length > 0) {
|
|
7
|
+
return {
|
|
8
|
+
conclusion: 'CANNOT_REGISTER',
|
|
9
|
+
checks: [],
|
|
10
|
+
failedItems: [`体检产物不完整,缺失: ${missingInputs.join(', ')}`],
|
|
11
|
+
policyReferences: this.getPolicyRefs(),
|
|
12
|
+
timestamp: new Date().toISOString(),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
const checks = [];
|
|
16
|
+
const failedItems = [];
|
|
17
|
+
const nationalSecurityCheck = this.checkNationalSecurity(orchestrationResult);
|
|
18
|
+
checks.push(nationalSecurityCheck);
|
|
19
|
+
if (nationalSecurityCheck.result === 'FAIL')
|
|
20
|
+
failedItems.push(nationalSecurityCheck.label);
|
|
21
|
+
const sourceComplianceCheck = this.checkSourceCompliance(orchestrationResult);
|
|
22
|
+
checks.push(sourceComplianceCheck);
|
|
23
|
+
if (sourceComplianceCheck.result === 'FAIL')
|
|
24
|
+
failedItems.push(sourceComplianceCheck.label);
|
|
25
|
+
const ownershipCheck = this.checkOwnershipDispute(ownershipConfirmation);
|
|
26
|
+
checks.push(ownershipCheck);
|
|
27
|
+
if (ownershipCheck.result === 'FAIL')
|
|
28
|
+
failedItems.push(ownershipCheck.label);
|
|
29
|
+
const materialCheck = this.checkMaterialAuthenticity();
|
|
30
|
+
checks.push(materialCheck);
|
|
31
|
+
if (materialCheck.result === 'FAIL')
|
|
32
|
+
failedItems.push(materialCheck.label);
|
|
33
|
+
const conclusion = this.determineConclusion(checks, ownershipConfirmation);
|
|
34
|
+
return {
|
|
35
|
+
conclusion,
|
|
36
|
+
checks,
|
|
37
|
+
failedItems,
|
|
38
|
+
policyReferences: this.getPolicyRefs(),
|
|
39
|
+
timestamp: new Date().toISOString(),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
validateInputs(result) {
|
|
43
|
+
const missing = [];
|
|
44
|
+
if (!result.maskingReport)
|
|
45
|
+
missing.push('maskingReport');
|
|
46
|
+
if (!result.cleaningReport)
|
|
47
|
+
missing.push('cleaningReport');
|
|
48
|
+
if (!result.inventoryReport)
|
|
49
|
+
missing.push('inventoryReport');
|
|
50
|
+
if (!result.packagingManual)
|
|
51
|
+
missing.push('packagingManual');
|
|
52
|
+
return missing;
|
|
53
|
+
}
|
|
54
|
+
checkNationalSecurity(result) {
|
|
55
|
+
const allText = `${result.maskingReport} ${result.cleaningReport} ${result.inventoryReport} ${result.packagingManual}`;
|
|
56
|
+
const found = SENSITIVE_KEYWORDS.some(kw => allText.includes(kw));
|
|
57
|
+
return {
|
|
58
|
+
name: 'NATIONAL_SECURITY',
|
|
59
|
+
label: '国家安全检查',
|
|
60
|
+
result: found ? 'FAIL' : 'PASS',
|
|
61
|
+
detail: found
|
|
62
|
+
? '数据涉及敏感领域,可能危害国家安全或公共利益'
|
|
63
|
+
: '数据未涉及敏感领域,不危害国家安全或公共利益',
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
checkSourceCompliance(result) {
|
|
67
|
+
const hasSourceStatement = result.maskingReport.includes('来源') || result.maskingReport.includes('合规');
|
|
68
|
+
return {
|
|
69
|
+
name: 'SOURCE_COMPLIANCE',
|
|
70
|
+
label: '来源合规检查',
|
|
71
|
+
result: hasSourceStatement ? 'PASS' : 'UNDETERMINED',
|
|
72
|
+
detail: hasSourceStatement
|
|
73
|
+
? '脱敏报告含来源合法性声明,来源合规'
|
|
74
|
+
: '无法判定:脱敏报告中未找到来源合法性声明',
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
checkOwnershipDispute(confirmation) {
|
|
78
|
+
if (!confirmation.confirmedAt) {
|
|
79
|
+
return {
|
|
80
|
+
name: 'OWNERSHIP_DISPUTE',
|
|
81
|
+
label: '权属纠纷检查',
|
|
82
|
+
result: 'PENDING',
|
|
83
|
+
detail: '企业尚未确认权属状态,待确认',
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
name: 'OWNERSHIP_DISPUTE',
|
|
88
|
+
label: '权属纠纷检查',
|
|
89
|
+
result: confirmation.hasDispute ? 'FAIL' : 'PASS',
|
|
90
|
+
detail: confirmation.hasDispute
|
|
91
|
+
? '存在尚未解决的数据权属纠纷'
|
|
92
|
+
: '企业确认不存在权属纠纷',
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
checkMaterialAuthenticity() {
|
|
96
|
+
return {
|
|
97
|
+
name: 'MATERIAL_AUTHENTICITY',
|
|
98
|
+
label: '材料真实性承诺',
|
|
99
|
+
result: 'PASS',
|
|
100
|
+
detail: '请企业确保如实填报登记申请材料,隐瞒真实情况或提供虚假证明将不予登记',
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
determineConclusion(checks, ownershipConfirmation) {
|
|
104
|
+
if (!ownershipConfirmation.confirmedAt) {
|
|
105
|
+
return 'PENDING_CONFIRMATION';
|
|
106
|
+
}
|
|
107
|
+
const hasFail = checks.some(c => c.result === 'FAIL');
|
|
108
|
+
return hasFail ? 'CANNOT_REGISTER' : 'CAN_REGISTER';
|
|
109
|
+
}
|
|
110
|
+
getPolicyRefs() {
|
|
111
|
+
return PolicyReferenceResolver.getInstance().resolve('PRECHECK');
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=precheckExecutor.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { OrchestrationResult, PrecheckReport } from '@liuhange/dsh-data-asset-shared';
|
|
2
|
+
export declare class PrecheckExecutor {
|
|
3
|
+
execute(orchestrationResult: OrchestrationResult, ownershipConfirmation: {
|
|
4
|
+
hasDispute: boolean;
|
|
5
|
+
confirmedAt: string;
|
|
6
|
+
}): PrecheckReport;
|
|
7
|
+
private validateInputs;
|
|
8
|
+
private checkNationalSecurity;
|
|
9
|
+
private checkSourceCompliance;
|
|
10
|
+
private checkOwnershipDispute;
|
|
11
|
+
private checkMaterialAuthenticity;
|
|
12
|
+
private determineConclusion;
|
|
13
|
+
private getPolicyRefs;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=precheckExecutor.d.ts.map
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@liuhange/dsh-registration-precheck",
|
|
3
|
+
"description": "Registration precheck plugin: verify data eligibility for national data property registration",
|
|
4
|
+
"version": "3.0.0",
|
|
5
|
+
"publishConfig": { "access": "public" },
|
|
6
|
+
"repository": { "type": "git", "url": "git+https://github.com/liuhange789/data-asset-inspector.git", "directory": "packages/data-asset/registration-precheck" },
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "lib/index.js",
|
|
9
|
+
"types": "lib/types/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": { "types": "./lib/types/index.d.ts", "default": "./lib/index.js" },
|
|
12
|
+
"./src/*": "./src/*",
|
|
13
|
+
"./package.json": "./package.json"
|
|
14
|
+
},
|
|
15
|
+
"files": ["lib/**/*.js", "lib/types/**/*.d.ts"],
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"scripts": {
|
|
18
|
+
"typecheck": "tsc --noEmit",
|
|
19
|
+
"test": "vitest run",
|
|
20
|
+
"build": "tsc"
|
|
21
|
+
},
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"@deepseek-ai/cordis": "^4.0.0",
|
|
24
|
+
"@deepseek-ai/dsh-tools": "0.0.1-rc.1",
|
|
25
|
+
"@liuhange/dsh-data-asset-shared": "^3.0.0"
|
|
26
|
+
},
|
|
27
|
+
"dsh": { "bundle": { "patch": true } }
|
|
28
|
+
}
|