Commit e7a7bf9d authored by drigle's avatar drigle

fix: 修复制度发文自引用与历史版本筛选

parent d0b00c39
import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import test from 'node:test';
import vm from 'node:vm';
import dayjs from 'dayjs';
import ts from 'typescript';
import * as historyHelpers from '../src/services/regulationHistory.js';
import * as graphHelpers from '../src/services/regulationGraphData.js';
const { isRegulationHistory } = historyHelpers;
const source = await fs.readFile(new URL('../src/services/internalRegulationService.js', import.meta.url), 'utf8');
const compiled = ts.transpileModule(source, {
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022, esModuleInterop: true },
}).outputText;
const serviceFactory = new vm.Script(`(function(require, module, exports) { ${compiled}\n})`).runInNewContext();
const clone = (value) => value === undefined ? undefined : JSON.parse(JSON.stringify(value));
const valueAt = (record, path) => path.split('.').reduce((value, key) => value?.[key], record);
function createService(records, { ignoreSelfExclusion = false } = {}) {
const database = clone(records);
const calls = [];
let sequence = 0;
const dataService = {
async loadRecord(identity) {
return clone(database.find((record) => record._id === identity || record.name === identity));
},
async createRecord(payload) {
calls.push({ action: 'create', payload: clone(payload) });
sequence += 1;
const record = { ...clone(payload), _id: `history-id-${sequence}`, name: `history-${sequence}` };
database.push(record);
return clone(record);
},
async updateRecord(identity, payload) {
calls.push({ action: 'update', identity, payload: clone(payload) });
const record = database.find((item) => item._id === identity || item.name === identity);
assert.ok(record, `Record ${identity} must exist`);
const data = clone(payload);
// Match the backend's PUT behavior: omitted metadata survives, null clears it.
Object.assign(record, { ...data, metadata: { ...record.metadata, ...data.metadata } });
return clone(record);
},
async queryRecords(query) {
calls.push({ action: 'query', query: clone(query) });
const records = database.filter((record) => record.subject === query.subject
&& Object.entries(query.filter || {}).every(([field, expected]) => {
const actual = valueAt(record, field);
if (expected && typeof expected === 'object') {
if ('$in' in expected) return expected.$in.includes(actual);
if ('$ne' in expected) return ignoreSelfExclusion || actual !== expected.$ne;
if ('$regex' in expected) return new RegExp(expected.$regex).test(actual || '');
}
return actual === expected;
}));
if (query.sort?.startsWith('-')) {
const field = query.sort.slice(1);
records.sort((left, right) => String(valueAt(right, field) || '').localeCompare(String(valueAt(left, field) || '')));
}
const pageSize = query.page_size || 1000;
const start = (query.page || 0) * pageSize;
return { records: clone(records.slice(start, start + pageSize)), total_records: records.length };
},
};
const testModule = { exports: {} };
const dependencies = {
dayjs,
'./DataService': dataService,
'./regulationGraphData': graphHelpers,
'./regulationHistory': historyHelpers,
};
serviceFactory((name) => {
assert.ok(name in dependencies, `Unexpected dependency ${name}`);
return dependencies[name];
}, testModule, testModule.exports);
return { service: testModule.exports, database, calls };
}
function currentRegulation(metadata = {}) {
return {
_id: 'current-id', name: 'current', title: '现行制度', subject: 'internal_regulation', content: '原正文',
metadata: {
regulation_name: '现行制度', effective_status: '现行有效', public_date: '2025-01-01T00:00:00.000Z',
level: '1级', tags: ['保留标签'], related_regulations: ['related-id'], category: ['category-id'],
...metadata,
},
};
}
function revisionPlan(name = 'revision', metadata = {}) {
return {
_id: `${name}-id`, name, subject: 'internal_regulation_revision', content: '修订正文',
metadata: {
regulation_name: '修订制度', type: '修订', status: '已提交', origin_regulation: 'current-id',
level: '2级', departments: ['法律合规部'], approve: ['董事会'], comment: '修订备注',
regulation_name_oa: 'OA制度名称', regulation_name_oa_submitted: '提交名称', match_status: '完全匹配',
remark: '计划批注', created_at: '2026-09-01T00:00:00.000Z',
...metadata,
},
};
}
const publication = { public_date: '2026-09-30', content: '发文正文', attachments: ['document-id'], document: { title: '发文结构' } };
for (const type of ['新增', '修订', '废止']) {
test(`${type} publication copies business fields without revision workflow fields`, async () => {
const current = currentRegulation();
const plan = revisionPlan('revision', { type, ...(type === '新增' ? { origin_regulation: undefined } : {}) });
const { service, database } = createService([current, plan]);
const published = await service.publishRevision(plan, publication);
for (const field of ['type', 'status', 'regulation_name_oa', 'regulation_name_oa_submitted', 'match_status', 'remark', 'created_at', 'origin_regulation']) {
assert.equal(Object.hasOwn(published.metadata, field), false, field);
}
assert.equal(published.metadata.regulation_name, plan.metadata.regulation_name);
assert.equal(published.metadata.level, '2级');
assert.deepEqual(published.metadata.approve, ['董事会']);
assert.equal(published.metadata.effective_status, type === '废止' ? '已失效' : '现行有效');
const finalPlan = database.find((record) => record.name === plan.name);
assert.equal(finalPlan.metadata.status, '已发文');
assert.equal(finalPlan.metadata.origin_regulation, plan.metadata.origin_regulation);
if (type === '修订') {
const history = database.find((record) => record.name.startsWith('history-'));
assert.equal(history.metadata.origin_regulation, current._id);
assert.equal(history.metadata.effective_status, '已失效');
assert.equal(history.content, current.content);
assert.deepEqual(published.metadata.tags, current.metadata.tags);
assert.deepEqual(published.metadata.related_regulations, current.metadata.related_regulations);
assert.deepEqual(published.metadata.category, current.metadata.category);
}
});
}
test('consecutive revisions keep one current record and link both snapshots to it', async () => {
const current = currentRegulation();
const firstPlan = revisionPlan();
const secondPlan = revisionPlan('second-plan', { regulation_name: '第二次修订' });
const { service, database } = createService([current, firstPlan, secondPlan]);
await service.publishRevision(firstPlan, publication);
await service.publishRevision(secondPlan, { ...publication, public_date: '2026-10-01', content: '第二版正文' });
const published = database.find((record) => record._id === current._id);
const history = await service.loadRegulationHistory(published);
assert.equal(published.metadata.origin_regulation, undefined);
assert.equal(published.content, '第二版正文');
assert.equal(history.length, 2);
assert.deepEqual(Array.from(history, (record) => record.content), ['发文正文', '原正文']);
assert.ok(history.every((record) => record.metadata.origin_regulation === current._id && record._id !== current._id));
});
for (const type of ['修订', '废止']) {
test(`${type} clears existing self references by ID, name and populated object`, async () => {
for (const reference of ['current-id', 'current', { _id: 'current-id', name: 'current', title: '现行制度' }]) {
const current = currentRegulation({ origin_regulation: reference });
const plan = revisionPlan('revision', { type });
const { service, database, calls } = createService([current, plan]);
await service.publishRevision(plan, publication);
const update = calls.find((call) => call.action === 'update' && call.identity === current.name);
assert.equal(update.payload.metadata.origin_regulation, null);
assert.equal(database.find((record) => record._id === current._id).metadata.origin_regulation, null);
if (type === '修订') {
assert.equal(database.find((record) => record.name.startsWith('history-')).metadata.origin_regulation, current._id);
}
}
});
}
test('publication preserves a non-self origin relationship and copies only its ID', async () => {
const current = currentRegulation({ origin_regulation: { _id: 'other-id', name: 'other', title: '其他制度' } });
const plan = revisionPlan();
const { service } = createService([current, plan]);
const published = await service.publishRevision(plan, publication);
assert.equal(published.metadata.origin_regulation, 'other-id');
});
test('history matching excludes self by both ID and name and accepts populated references', () => {
const current = currentRegulation({ origin_regulation: 'current-id' });
const records = [
current,
{ name: 'current', metadata: { origin_regulation: 'current-id' } },
{ _id: 'old-id', name: 'old', metadata: { origin_regulation: { _id: 'current-id', name: 'current' } } },
{ _id: 'old-name', metadata: { origin_regulation: 'current' } },
{ _id: 'unrelated', metadata: { origin_regulation: 'another-current' } },
{ _id: 'missing', metadata: {} },
];
assert.deepEqual(records.filter((record) => isRegulationHistory(record, current)).map((record) => record._id), ['old-id', 'old-name']);
assert.equal(isRegulationHistory(current, current.name), false);
assert.equal(isRegulationHistory(records[2], current.name), true);
assert.equal(isRegulationHistory(records[2], null), false);
});
test('detail history excludes legacy self references in the query and returned data', async () => {
const current = currentRegulation({ origin_regulation: 'current-id' });
const historical = { ...current, _id: 'old-id', name: 'old', metadata: { ...current.metadata, effective_status: '已失效', origin_regulation: current._id } };
for (const ignoreSelfExclusion of [false, true]) {
const { service, calls } = createService([current, historical], { ignoreSelfExclusion });
const history = await service.loadRegulationHistory(current);
assert.deepEqual(Array.from(history, (record) => record._id), ['old-id']);
assert.deepEqual(calls[0].query.filter._id, { $ne: current._id });
}
});
test('name-only detail history also excludes the current record', async () => {
const current = { name: 'named-current', subject: 'internal_regulation', metadata: { origin_regulation: 'named-current' } };
const historical = { name: 'named-history', subject: 'internal_regulation', metadata: { origin_regulation: 'named-current' } };
const { service, calls } = createService([current, historical]);
const history = await service.loadRegulationHistory(current);
assert.deepEqual(Array.from(history, (record) => record.name), ['named-history']);
assert.deepEqual(calls[0].query.filter.name, { $ne: current.name });
});
test('directory histories exclude legacy self references before the next publication', async () => {
const current = currentRegulation({ origin_regulation: 'current-id' });
const historical = { ...current, _id: 'old-id', name: 'old', metadata: { ...current.metadata, effective_status: '已失效' } };
const { service } = createService([current, historical]);
const result = await service.loadRegulations();
const row = result.records.find((record) => record._id === current._id);
assert.deepEqual(Array.from(row.revisions, (record) => record._id), ['old-id']);
});
test('topology histories exclude self for nodes linked by record ID or name', async () => {
const graphSource = await fs.readFile(new URL('../src/pages/internal_regulation_manager/RegulationGraph.jsx', import.meta.url), 'utf8');
const start = graphSource.indexOf('function enrichGraph(');
const end = graphSource.indexOf('function parseXmindJson(', start);
assert.ok(start >= 0 && end > start);
const enrichGraph = new vm.Script(`${graphSource.slice(start, end)}\nenrichGraph`).runInNewContext({
normalizeGraph: (graph) => graph,
normalizeNode: (node) => node,
metadataOf: (record) => record?.metadata || {},
referenceId: (value) => value && typeof value === 'object' ? value._id || value.name : value,
...graphHelpers,
...historyHelpers,
});
const current = currentRegulation({ origin_regulation: 'current-id' });
const historical = { ...current, _id: 'old-id', name: 'old', metadata: { ...current.metadata, effective_status: '已失效' } };
const unrelated = currentRegulation({ regulation_name: '其他制度', origin_regulation: 'unrelated-id' });
unrelated._id = 'unrelated-id';
unrelated.name = 'unrelated';
for (const reference of [current._id, current.name]) {
const graph = { nodes: [{ id: 'node', title: current.title, relatedRegulation: reference, historyRegulation: [current, historical] }], edges: [] };
const enriched = enrichGraph(graph, [current, historical, unrelated], []);
assert.deepEqual(Array.from(enriched.nodes[0].historyRegulation, (record) => record._id), [historical._id]);
assert.equal(enriched.nodes[0].relatedRegulation, reference);
}
});
...@@ -23,6 +23,7 @@ import ProcessCascade from '@/components/ProcessCascade'; ...@@ -23,6 +23,7 @@ import ProcessCascade from '@/components/ProcessCascade';
import XinyuanAutocomplete from '@/components/XinyuanAutocomplete'; import XinyuanAutocomplete from '@/components/XinyuanAutocomplete';
import XinyuanModal from '@/components/XinyuanModal'; import XinyuanModal from '@/components/XinyuanModal';
import { attachGraphAncestors } from '@/services/regulationGraphData'; import { attachGraphAncestors } from '@/services/regulationGraphData';
import { isRegulationHistory, isSameRegulation } from '@/services/regulationHistory';
import { createGraphXmindArchive } from '@/services/regulationGraphXmind'; import { createGraphXmindArchive } from '@/services/regulationGraphXmind';
import GraphNode from './GraphNode'; import GraphNode from './GraphNode';
import { getGraphRecordId, getNextGraphRecordNode, getSelectedGraphRecordId } from './graphSelection'; import { getGraphRecordId, getNextGraphRecordNode, getSelectedGraphRecordId } from './graphSelection';
...@@ -144,8 +145,10 @@ function enrichGraph(nextGraph, regulations, revisions) { ...@@ -144,8 +145,10 @@ function enrichGraph(nextGraph, regulations, revisions) {
const revisionType = metadataOf(revision).type; const revisionType = metadataOf(revision).type;
status = revisionType === '新增' ? 'addition' : revisionType === '废止' ? 'forbidden' : 'revise'; status = revisionType === '新增' ? 'addition' : revisionType === '废止' ? 'forbidden' : 'revise';
} }
const history = relationId ? regulations.filter((record) => String(referenceId(metadataOf(record).origin_regulation)) === String(relationId)) : node.historyRegulation; const relatedRecord = relationId
const relatedRecord = relationId ? regulationById.get(String(relationId)) : titleMatch?.record; ? regulationById.get(String(relationId)) || regulations.find((record) => isSameRegulation(record, relationId))
: titleMatch?.record;
const history = relationId ? regulations.filter((record) => isRegulationHistory(record, relatedRecord || relationId)) : node.historyRegulation;
return normalizeNode({ ...node, relatedRegulation: relationId || node.relatedRegulation, relatedRegulationType: status || 'active', historyRegulation: history, title: node.title || metadataOf(relatedRecord).regulation_name || relatedRecord?.title }); return normalizeNode({ ...node, relatedRegulation: relationId || node.relatedRegulation, relatedRegulationType: status || 'active', historyRegulation: history, title: node.title || metadataOf(relatedRecord).regulation_name || relatedRecord?.title });
}); });
return { nodes: attachGraphAncestors(nodes), edges: graph.edges }; return { nodes: attachGraphAncestors(nodes), edges: graph.edges };
......
...@@ -25,6 +25,10 @@ ...@@ -25,6 +25,10 @@
发文时:新增制度拒绝重复名称;修订制度先生成失效历史版本,再更新现行版本;废止制度更新现行制度为“已失效”。日期统一转换为 ISO 8601,附件仅保存引用 ID。 发文时:新增制度拒绝重复名称;修订制度先生成失效历史版本,再更新现行版本;废止制度更新现行制度为“已失效”。日期统一转换为 ISO 8601,附件仅保存引用 ID。
- 修订发文先保存原制度快照,快照为“已失效”,`origin_regulation` 仅保存原制度 ID;现行制度从修订计划接收制度 Subject 定义中的业务字段,不复制计划的 `origin_regulation/type/status`、OA 匹配信息或发起日期。新增、废止发文使用同一字段白名单,避免计划关联写入制度。
- 更新现行制度时,若已有 `origin_regulation` 指向自身(兼容 ID、name 或展开的引用对象),显式写入 `null` 清空;指向其他制度的既有关联保留。此清理在该制度下一次修订或废止发文时完成,不自动批量修改存量记录。
- 制度目录的历史集合、详情“历史沿革”和拓扑节点的历史版本统一按原制度关联筛选,并按 ID/name 排除现行记录自身;详情查询也增加排除自身的服务端条件,因此存量自引用数据在清理前不会展示为自己的历史版本。历史读取或发文请求失败时沿用现有错误提示。
## 归档与拓扑 ## 归档与拓扑
- 制度拓扑图与制度目录是两个平级入口;进入拓扑图后不呈现制度目录层级,也不提供返回制度目录操作,用户可通过左侧一级菜单在两个模块之间切换。 - 制度拓扑图与制度目录是两个平级入口;进入拓扑图后不呈现制度目录层级,也不提供返回制度目录操作,用户可通过左侧一级菜单在两个模块之间切换。
......
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { serializeGraph } from './regulationGraphData'; import { serializeGraph } from './regulationGraphData';
import { isRegulationHistory, isSameRegulation } from './regulationHistory';
import { import {
createDocument, createDocument,
createRecord, createRecord,
...@@ -18,6 +19,14 @@ export const GRAPH_SUBJECT = 'internal_regulation_graph'; ...@@ -18,6 +19,14 @@ export const GRAPH_SUBJECT = 'internal_regulation_graph';
const REGULATION_LEVELS = ['1级', '2级', '3级', '4级']; const REGULATION_LEVELS = ['1级', '2级', '3级', '4级'];
const REVISION_TYPES = ['新增', '修订', '废止']; const REVISION_TYPES = ['新增', '修订', '废止'];
// Only internal_regulation fields may be copied from a revision plan.
// origin_regulation belongs to the plan/history relationship, not this update.
const REGULATION_PUBLICATION_FIELDS = [
'regulation_name', 'effective_status', 'version', 'level', 'effective_date',
'departments', 'tags', 'attachments', 'related_regulations', 'reference_regulation',
'document_file', 'document', 'public_date', 'approve', 'comment', 'attachment_pdf',
'processes', 'category', 'citing_regulations',
];
function recordsOf(response) { function recordsOf(response) {
if (Array.isArray(response?.records)) return response.records; if (Array.isArray(response?.records)) return response.records;
...@@ -58,6 +67,12 @@ function metadataOf(record) { ...@@ -58,6 +67,12 @@ function metadataOf(record) {
return record?.metadata || {}; return record?.metadata || {};
} }
function publicationMetadata(metadata) {
return Object.fromEntries(REGULATION_PUBLICATION_FIELDS
.filter((field) => Object.hasOwn(metadata, field))
.map((field) => [field, metadata[field]]));
}
function isoDate(value) { function isoDate(value) {
if (!value) return value; if (!value) return value;
const date = dayjs(value); const date = dayjs(value);
...@@ -139,7 +154,7 @@ export async function loadRegulations({ page = 0, pageSize = 20, keyword = '', f ...@@ -139,7 +154,7 @@ export async function loadRegulations({ page = 0, pageSize = 20, keyword = '', f
const metadata = metadataOf(record); const metadata = metadataOf(record);
const relatedRevisions = revisions.filter((revision) => referenceId(metadataOf(revision).origin_regulation) === record._id); const relatedRevisions = revisions.filter((revision) => referenceId(metadataOf(revision).origin_regulation) === record._id);
const histories = history const histories = history
.filter((item) => referenceId(metadataOf(item).origin_regulation) === record._id) .filter((item) => isRegulationHistory(item, record))
.sort((left, right) => String(metadataOf(right).public_date || '').localeCompare(String(metadataOf(left).public_date || ''))); .sort((left, right) => String(metadataOf(right).public_date || '').localeCompare(String(metadataOf(left).public_date || '')));
return { return {
...record, ...record,
...@@ -156,7 +171,11 @@ export async function loadRegulations({ page = 0, pageSize = 20, keyword = '', f ...@@ -156,7 +171,11 @@ export async function loadRegulations({ page = 0, pageSize = 20, keyword = '', f
export async function loadRegulationHistory(record) { export async function loadRegulationHistory(record) {
const id = record?._id || record?.name; const id = record?._id || record?.name;
if (!id) return []; if (!id) return [];
return queryAll(REGULATION_SUBJECT, { filter: { 'metadata.origin_regulation': id }, sort: '-metadata.public_date' }); const filter = { 'metadata.origin_regulation': id };
if (record._id) filter._id = { $ne: record._id };
else if (record.name) filter.name = { $ne: record.name };
const history = await queryAll(REGULATION_SUBJECT, { filter, sort: '-metadata.public_date' });
return history.filter((item) => isRegulationHistory(item, record));
} }
export async function loadRevisions({ page = 0, pageSize = 20, status = '起草中', keyword = '' } = {}) { export async function loadRevisions({ page = 0, pageSize = 20, status = '起草中', keyword = '' } = {}) {
...@@ -235,6 +254,7 @@ export async function setRevisionStatus(name, status) { ...@@ -235,6 +254,7 @@ export async function setRevisionStatus(name, status) {
export async function publishRevision(revision, values = {}) { export async function publishRevision(revision, values = {}) {
const metadata = metadataOf(revision); const metadata = metadataOf(revision);
const regulationMetadata = publicationMetadata(metadata);
const publicDate = isoDate(values.public_date || new Date().toISOString()); const publicDate = isoDate(values.public_date || new Date().toISOString());
const attachmentIds = (values.attachments || []).map((item) => referenceId(item)).filter(Boolean); const attachmentIds = (values.attachments || []).map((item) => referenceId(item)).filter(Boolean);
const content = values.content ?? revision.content ?? ''; const content = values.content ?? revision.content ?? '';
...@@ -246,17 +266,22 @@ export async function publishRevision(revision, values = {}) { ...@@ -246,17 +266,22 @@ export async function publishRevision(revision, values = {}) {
subject: REGULATION_SUBJECT, subject: REGULATION_SUBJECT,
title: metadata.regulation_name, title: metadata.regulation_name,
content, content,
metadata: normalizeMetadata({ ...metadata, effective_status: '现行有效', public_date: publicDate, effective_date: values.effective_date || publicDate, attachments: attachmentIds, document: values.document, status: undefined }), metadata: normalizeMetadata({ ...regulationMetadata, effective_status: '现行有效', public_date: publicDate, effective_date: values.effective_date || publicDate, attachments: attachmentIds, document: values.document }),
}); });
} else if (metadata.origin_regulation) { } else if (metadata.origin_regulation) {
const originId = referenceId(metadata.origin_regulation); const originId = referenceId(metadata.origin_regulation);
const origin = await loadRecord(originId); const origin = await loadRecord(originId);
if (!origin) throw new Error('原制度不存在,无法发文'); if (!origin) throw new Error('原制度不存在,无法发文');
const originMetadata = { ...metadataOf(origin) };
if (isSameRegulation(originMetadata.origin_regulation, origin)) {
// PUT merges metadata on the server, so omission cannot clear a legacy value.
originMetadata.origin_regulation = null;
}
if (metadata.type === '修订') { if (metadata.type === '修订') {
await createRecord({ subject: REGULATION_SUBJECT, title: `${origin.title || metadata.regulation_name} ${dayjs(metadataOf(origin).public_date || metadataOf(origin).effective_date).format('YYYY-MM-DD')}`, content: origin.content || '', metadata: normalizeMetadata({ ...metadataOf(origin), origin_regulation: origin._id, effective_status: '已失效' }) }); await createRecord({ subject: REGULATION_SUBJECT, title: `${origin.title || metadata.regulation_name} ${dayjs(metadataOf(origin).public_date || metadataOf(origin).effective_date).format('YYYY-MM-DD')}`, content: origin.content || '', metadata: normalizeMetadata({ ...metadataOf(origin), origin_regulation: origin._id, effective_status: '已失效' }) });
regulation = await updateRecord(origin.name || origin._id, { name: origin.name, content, metadata: normalizeMetadata({ ...metadataOf(origin), ...metadata, effective_status: '现行有效', public_date: publicDate, attachments: attachmentIds, document: values.document }) }); regulation = await updateRecord(origin.name || origin._id, { name: origin.name, content, metadata: normalizeMetadata({ ...originMetadata, ...regulationMetadata, effective_status: '现行有效', public_date: publicDate, attachments: attachmentIds, document: values.document }) });
} else { } else {
regulation = await updateRecord(origin.name || origin._id, { name: origin.name, metadata: normalizeMetadata({ ...metadataOf(origin), ...metadata, effective_status: '已失效', public_date: publicDate }) }); regulation = await updateRecord(origin.name || origin._id, { name: origin.name, metadata: normalizeMetadata({ ...originMetadata, ...regulationMetadata, effective_status: '已失效', public_date: publicDate }) });
} }
} }
await updateRecord(revision.name, { name: revision.name, content, metadata: normalizeMetadata({ ...metadata, status: '已发文', public_date: publicDate, attachments: attachmentIds, document: values.document }) }); await updateRecord(revision.name, { name: revision.name, content, metadata: normalizeMetadata({ ...metadata, status: '已发文', public_date: publicDate, attachments: attachmentIds, document: values.document }) });
......
...@@ -129,6 +129,8 @@ ...@@ -129,6 +129,8 @@
请求体使用完整 Record payload。当前用于更新模型、批次、隐藏状态和审批数据;一次性同步的模型结果行不调用更新接口。 请求体使用完整 Record payload。当前用于更新模型、批次、隐藏状态和审批数据;一次性同步的模型结果行不调用更新接口。
普通 `normal` Subject 的 PUT 更新会将请求中的 `metadata` 与已有字段合并;省略字段不会清空旧值。内部制度修订/废止发文发现现行制度自引用时,通过 `metadata.origin_regulation: null` 显式清空该可选 Reference,不传入引用对象。
### 3.6 删除 Record ### 3.6 删除 Record
`DELETE /record/:name` `DELETE /record/:name`
......
function recordIdentities(value) {
const ids = value && typeof value === 'object'
? [value._id, value.id, value.name]
: [value];
return ids.filter((id) => id !== null && id !== undefined && id !== '').map(String);
}
export function isSameRegulation(record, reference) {
const referenceIds = new Set(recordIdentities(reference));
return recordIdentities(record).some((id) => referenceIds.has(id));
}
export function isRegulationHistory(record, currentRegulation) {
return !isSameRegulation(record, currentRegulation)
&& isSameRegulation(record?.metadata?.origin_regulation, currentRegulation);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment