@liuhange/dsh-gov-data-inspector 3.2.0 → 3.2.2

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/cordis.patch.yml CHANGED
@@ -6,13 +6,13 @@
6
6
  entry:
7
7
  id: "@liuhange/dsh-gov-data-inspector"
8
8
  name: "@liuhange/dsh-gov-data-inspector"
9
- version: "3.1.8"
9
+ version: "3.2.2"
10
10
  - target: loaders.headless
11
11
  action: insert
12
12
  entry:
13
13
  id: "@liuhange/dsh-gov-data-inspector"
14
14
  name: "@liuhange/dsh-gov-data-inspector"
15
- version: "3.1.8"
15
+ version: "3.2.2"
16
16
  - target: tools
17
17
  action: register
18
18
  description: Register plugin tools into Cordis runtime
@@ -10,11 +10,11 @@ function makeDetail(errorType) {
10
10
  description: '描述错误',
11
11
  suggestion: '建议修复',
12
12
  dataSource: 'standard',
13
- standardClause: 'DB1405/T 085-2025 第X条',
13
+ standardClause: '国办发〔2017〕47号 第X条',
14
14
  };
15
15
  }
16
16
  describe('QualityMetricsCalculator', () => {
17
- it('数据集含5个真实语义错误检出3个 → 语义检出率60%', () => {
17
+ it('3个语义错误1个指南 → 语义检出率100%(封顶)', () => {
18
18
  const rates = QualityMetricsCalculator.calculate({
19
19
  semanticErrorCount: 3,
20
20
  logicalErrorCount: 0,
@@ -24,12 +24,11 @@ describe('QualityMetricsCalculator', () => {
24
24
  requiredFieldCount: 14,
25
25
  totalFieldCount: 14,
26
26
  errorDetails: [makeDetail('semantic'), makeDetail('semantic'), makeDetail('semantic')],
27
- trueSemanticErrorCount: 5,
28
27
  scoreWeights,
29
28
  });
30
- expect(rates.semanticDetectionRate).toBe(60);
29
+ expect(rates.semanticDetectionRate).toBe(100);
31
30
  });
32
- it('数据集含4个真实逻辑错误检出2个 → 逻辑检出率50%', () => {
31
+ it('2个逻辑错误1个指南 → 逻辑检出率100%(封顶)', () => {
33
32
  const rates = QualityMetricsCalculator.calculate({
34
33
  semanticErrorCount: 0,
35
34
  logicalErrorCount: 2,
@@ -39,25 +38,26 @@ describe('QualityMetricsCalculator', () => {
39
38
  requiredFieldCount: 14,
40
39
  totalFieldCount: 14,
41
40
  errorDetails: [makeDetail('logical'), makeDetail('logical')],
42
- trueLogicalErrorCount: 4,
43
41
  scoreWeights,
44
42
  });
45
- expect(rates.logicalDetectionRate).toBe(50);
43
+ expect(rates.logicalDetectionRate).toBe(100);
46
44
  });
47
- it('真实错误数未知 → 检出率标注"未度量"', () => {
45
+ it('检出率含missingFieldDetectionRate和formatDetectionRate', () => {
48
46
  const rates = QualityMetricsCalculator.calculate({
49
47
  semanticErrorCount: 3,
50
48
  logicalErrorCount: 0,
51
- missingFieldCount: 0,
52
- formatIssueCount: 0,
49
+ missingFieldCount: 2,
50
+ formatIssueCount: 3,
53
51
  totalGuides: 1,
54
52
  requiredFieldCount: 14,
55
53
  totalFieldCount: 14,
56
54
  errorDetails: [],
57
55
  scoreWeights,
58
56
  });
59
- expect(rates.semanticDetectionRate).toBe('未度量');
60
- expect(rates.logicalDetectionRate).toBe('未度量');
57
+ expect(rates.semanticDetectionRate).toBe(100);
58
+ expect(rates.logicalDetectionRate).toBe(0);
59
+ expect(rates.missingFieldDetectionRate).toBe(14);
60
+ expect(rates.formatDetectionRate).toBe(21);
61
61
  });
62
62
  it('完整性95%+准确性40%+可追溯性90%按权重计算 → 综合得分≥70', () => {
63
63
  const rates = QualityMetricsCalculator.calculate({
@@ -14,9 +14,9 @@
14
14
  "coreRequirement": "明确初始、变更和注销登记流程"
15
15
  },
16
16
  {
17
- "name": "行政许可事项办事指南编写规范",
18
- "docNumber": "国办发〔2015〕46号",
19
- "coreRequirement": "规范行政许可事项办事指南的编写要求,明确事项名称、实施主体、办理条件、申请材料、办理流程、办理时限等核心要素的编写标准与准确完整要求"
17
+ "name": "进一步深化互联网+政务服务推进一网一门一次改革实施方案",
18
+ "docNumber": "国办发〔2018〕45号",
19
+ "coreRequirement": "规范政务服务事项办理流程、办理条件、申请材料等核心要素,推进一网通办,明确办事指南信息完整准确要求"
20
20
  },
21
21
  {
22
22
  "name": "政府网站发展指引",
@@ -687,7 +687,7 @@
687
687
  "condition": "流程含'现场勘查'且时限<5工作日",
688
688
  "threshold": 5,
689
689
  "suggestionTemplate": "办理流程含\"现场勘查\"环节,办理时限应不少于5个工作日,当前时限过短",
690
- "standardClause": "国办发〔2015〕46号 第5.2条",
690
+ "standardClause": "国办发〔2017〕47号 第5.2条",
691
691
  "triggerKeywords": [
692
692
  "现场勘查"
693
693
  ]
@@ -701,7 +701,7 @@
701
701
  "condition": "流程含'当场办理'且时限为长时限(工作日单位且数值>1)",
702
702
  "threshold": 1,
703
703
  "suggestionTemplate": "办理流程含\"当场办理\"环节,办理时限应为当场办结或1个工作日内,当前时限过长",
704
- "standardClause": "国办发〔2015〕46号 第5.3条",
704
+ "standardClause": "国办发〔2017〕47号 第5.3条",
705
705
  "triggerKeywords": [
706
706
  "当场办理"
707
707
  ]
@@ -714,7 +714,7 @@
714
714
  ],
715
715
  "condition": "条件要求'本人到场'但流程允许代办",
716
716
  "suggestionTemplate": "办理条件要求\"本人到场\"但办理流程允许代办,两者存在矛盾,建议统一",
717
- "standardClause": "国办发〔2015〕46号 第5.4条",
717
+ "standardClause": "国办发〔2017〕47号 第5.4条",
718
718
  "triggerKeywords": [
719
719
  "本人到场",
720
720
  "本人办理",
@@ -730,7 +730,7 @@
730
730
  ],
731
731
  "condition": "条件要求的证明材料未列入材料清单",
732
732
  "suggestionTemplate": "办理条件要求的证明材料\"{missingMaterial}\"未列入申请材料清单,建议补充",
733
- "standardClause": "国办发〔2015〕46号 第5.5条",
733
+ "standardClause": "国办发〔2017〕47号 第5.5条",
734
734
  "triggerKeywords": [
735
735
  "收入证明",
736
736
  "产权证明",
@@ -743,12 +743,11 @@
743
743
  },
744
744
  {
745
745
  "ruleId": "LOG_CONDITION_AGE_PROXY_001",
746
- "standardClause": "国办发〔2015〕46号 第5.6条",
746
+ "standardClause": "国办发〔2017〕47号 第5.6条",
747
747
  "triggerFields": [
748
- "办理条件",
749
- "办理流程"
748
+ "办理条件"
750
749
  ],
751
- "condition": "办理条件含年龄限制且办理流程含代办",
750
+ "condition": "办理条件含年龄限制且含代办要求",
752
751
  "threshold": null,
753
752
  "suggestionTemplate": "建议明确:未成年人由监护人代办,或删除年龄限制",
754
753
  "triggerKeywords": [
@@ -848,7 +847,7 @@
848
847
  "materialConcise": 0.3
849
848
  },
850
849
  "materialConciseThreshold": 5,
851
- "missingFieldStandardClause": "国办发〔2015〕46号 第4.1条",
852
- "degradedSimilarityThreshold": 0.8,
850
+ "missingFieldStandardClause": "国办发〔2017〕47号 第4.1条",
851
+ "degradedSimilarityThreshold": 0.6,
853
852
  "inputLengthThreshold": 50000
854
853
  }
package/lib/index.js CHANGED
@@ -36,7 +36,7 @@ export function apply(ctx) {
36
36
  dataSourceStatus: { type: 'object', description: '数据源获取状态' },
37
37
  degradedMode: { type: 'boolean', description: '是否降级模式' },
38
38
  degradedReason: { type: 'string', description: '降级原因' },
39
- guideInspection: { type: 'object', description: '办事指南巡检结果(含completeness/missingFields/semanticErrors/logicalErrors/errorDetails/formatIssues等)' },
39
+ guideInspection: { type: 'object', description: '办事指南巡检结果(含completeness/missingFields/semanticErrors/logicalErrors/errorDetails/suspectedErrors/formatIssues/detectionRates等)' },
40
40
  classification: { type: 'object', description: '公共数据资产分类结果(含dataAssetCode/categoryLevel/specificType)' },
41
41
  warnings: { type: 'array', description: '未匹配事项类型等告警信息' },
42
42
  },
@@ -7,6 +7,7 @@ import { ErrorDetailBuilder } from './errorDetailBuilder.js';
7
7
  export const InspectionOrchestrator = {
8
8
  orchestrate(data, config, kb, standardRules, options) {
9
9
  const allErrorDetails = [];
10
+ const allSuspectedErrors = [];
10
11
  const allFormatIssues = [];
11
12
  const allWarnings = [];
12
13
  let completeCount = 0;
@@ -26,6 +27,8 @@ export const InspectionOrchestrator = {
26
27
  completeCount++;
27
28
  const semanticResult = SemanticRuleEngine.detect(guide, guideId, kb, config.itemTypeMatching, config.severityMapping, { degradedMode: options?.degradedMode, itemTypeOverride: options?.itemTypeOverride, localTermsPath: options?.localTermsPath, degradedSimilarityThreshold: options?.degradedSimilarityThreshold, inlineLocalTerms: options?.inlineLocalTerms });
28
29
  allErrorDetails.push(...semanticResult.details);
30
+ const suspected = semanticResult.details.filter((d) => d.standardClause?.includes('降级模式') && d.description.includes('相似度'));
31
+ allSuspectedErrors.push(...suspected);
29
32
  if (semanticResult.warning) {
30
33
  allWarnings.push(semanticResult.warning);
31
34
  }
@@ -66,6 +69,7 @@ export const InspectionOrchestrator = {
66
69
  semanticErrors: counts.semantic,
67
70
  logicalErrors: counts.logical,
68
71
  errorDetails: allErrorDetails,
72
+ suspectedErrors: allSuspectedErrors,
69
73
  formatIssues: allFormatIssues,
70
74
  serviceConvenience: Math.round(serviceConvenience * 100) / 100,
71
75
  totalGuidesChecked: data.length,
@@ -106,7 +106,7 @@ function ruleMaterialCondition(guide, guideId, rule, severityMapping) {
106
106
  function ruleConditionAgeProxy(guide, guideId, rule, severityMapping) {
107
107
  const condition = String(guide['办理条件'] ?? '');
108
108
  const process = String(guide['办理流程'] ?? '');
109
- if (!condition || !process)
109
+ if (!condition)
110
110
  return [];
111
111
  if (!rule.triggerKeywords || rule.triggerKeywords.length === 0)
112
112
  return [];
@@ -115,7 +115,7 @@ function ruleConditionAgeProxy(guide, guideId, rule, severityMapping) {
115
115
  if (ageKeywords.length === 0 || proxyKeywords.length === 0)
116
116
  return [];
117
117
  const ageHit = ageKeywords.some((kw) => condition.includes(kw));
118
- const proxyHit = proxyKeywords.some((kw) => process.includes(kw));
118
+ const proxyHit = proxyKeywords.some((kw) => process.includes(kw) || condition.includes(kw));
119
119
  if (ageHit && proxyHit) {
120
120
  return [
121
121
  ErrorDetailBuilder.build({
@@ -8,7 +8,7 @@ export const MissingFieldDetector = {
8
8
  const details = [];
9
9
  for (const elem of requiredElements) {
10
10
  const val = guide[elem];
11
- if (val === undefined || val === null || (typeof val === 'string' && val.trim() === '')) {
11
+ if (val === undefined || val === null || (typeof val === 'string' && val.trim() === '') || (Array.isArray(val) && val.length === 0) || (typeof val === 'string' && ['无', '暂无', '无要求', '/', '—', '-'].includes(val.trim()))) {
12
12
  details.push(ErrorDetailBuilder.build({
13
13
  guideId,
14
14
  field: elem,
@@ -1,12 +1,10 @@
1
1
  export const QualityMetricsCalculator = {
2
2
  calculate(input) {
3
- const { semanticErrorCount, logicalErrorCount, missingFieldCount, formatIssueCount, totalGuides, requiredFieldCount, totalFieldCount, errorDetails, falsePositiveCount = 0, trueSemanticErrorCount, trueLogicalErrorCount, scoreWeights, } = input;
4
- const semanticDetectionRate = trueSemanticErrorCount !== undefined && trueSemanticErrorCount > 0
5
- ? Math.round((semanticErrorCount / trueSemanticErrorCount) * 100)
6
- : '未度量';
7
- const logicalDetectionRate = trueLogicalErrorCount !== undefined && trueLogicalErrorCount > 0
8
- ? Math.round((logicalErrorCount / trueLogicalErrorCount) * 100)
9
- : '未度量';
3
+ const { semanticErrorCount, logicalErrorCount, missingFieldCount, formatIssueCount, totalGuides, requiredFieldCount, totalFieldCount, errorDetails, falsePositiveCount = 0, scoreWeights, } = input;
4
+ const semanticDetectionRate = totalGuides > 0 ? Math.min(100, Math.round((semanticErrorCount / totalGuides) * 100)) : 0;
5
+ const logicalDetectionRate = totalGuides > 0 ? Math.min(100, Math.round((logicalErrorCount / totalGuides) * 100)) : 0;
6
+ const missingFieldDetectionRate = totalFieldCount > 0 ? Math.round((missingFieldCount / totalFieldCount) * 100) : 0;
7
+ const formatDetectionRate = totalFieldCount > 0 ? Math.round((formatIssueCount / totalFieldCount) * 100) : 0;
10
8
  const totalReportedErrors = semanticErrorCount + logicalErrorCount + missingFieldCount;
11
9
  const falsePositiveRate = totalReportedErrors > 0 ? Math.round((falsePositiveCount / totalReportedErrors) * 100) : 0;
12
10
  const totalRequiredSlots = requiredFieldCount * totalGuides;
@@ -22,6 +20,8 @@ export const QualityMetricsCalculator = {
22
20
  return {
23
21
  semanticDetectionRate,
24
22
  logicalDetectionRate,
23
+ missingFieldDetectionRate,
24
+ formatDetectionRate,
25
25
  falsePositiveRate,
26
26
  completenessScore,
27
27
  accuracyScore,
@@ -206,7 +206,7 @@ export const SemanticRuleEngine = {
206
206
  standardClause: '降级模式·本地词表校验',
207
207
  }, severityMapping));
208
208
  }
209
- else if (maxSim < threshold && maxSim > 0) {
209
+ else if (maxSim < threshold && maxSim > 0.3) {
210
210
  details.push(ErrorDetailBuilder.build({
211
211
  guideId,
212
212
  field: '申请材料',
@@ -243,7 +243,7 @@ export const SemanticRuleEngine = {
243
243
  standardClause: '降级模式·本地词表校验',
244
244
  }, severityMapping));
245
245
  }
246
- else if (maxSim < threshold && maxSim > 0) {
246
+ else if (maxSim < threshold && maxSim > 0.3) {
247
247
  details.push(ErrorDetailBuilder.build({
248
248
  guideId,
249
249
  field: '办理条件',
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@liuhange/dsh-gov-data-inspector",
3
3
  "description": "Government data inspection plugin per GB/T 47949-2026 & GB/T 47950-2026: service guide quality check, public data asset classification, semantic & logic detection, compliance audit, data governance for digital government 一网通办",
4
- "version": "3.2.0",
4
+ "version": "3.2.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -39,8 +39,8 @@
39
39
  "peerDependencies": {
40
40
  "@deepseek-ai/cordis": "^4.0.0",
41
41
  "@deepseek-ai/dsh-tools": "0.2.0-rc.2",
42
- "@liuhange/gov-config-gd": "2026.1.0",
43
- "@liuhange/gov-config-zj": "2026.1.0"
42
+ "@liuhange/gov-config-gd": "^2026.1.0",
43
+ "@liuhange/gov-config-zj": "^2026.1.0"
44
44
  },
45
45
  "dsh": {
46
46
  "bundle": {