Commit e7a7bf9d authored by drigle's avatar drigle

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

parent d0b00c39
This diff is collapsed.
......@@ -23,6 +23,7 @@ import ProcessCascade from '@/components/ProcessCascade';
import XinyuanAutocomplete from '@/components/XinyuanAutocomplete';
import XinyuanModal from '@/components/XinyuanModal';
import { attachGraphAncestors } from '@/services/regulationGraphData';
import { isRegulationHistory, isSameRegulation } from '@/services/regulationHistory';
import { createGraphXmindArchive } from '@/services/regulationGraphXmind';
import GraphNode from './GraphNode';
import { getGraphRecordId, getNextGraphRecordNode, getSelectedGraphRecordId } from './graphSelection';
......@@ -144,8 +145,10 @@ function enrichGraph(nextGraph, regulations, revisions) {
const revisionType = metadataOf(revision).type;
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 ? regulationById.get(String(relationId)) : titleMatch?.record;
const relatedRecord = relationId
? 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 { nodes: attachGraphAncestors(nodes), edges: graph.edges };
......
......@@ -25,6 +25,10 @@
发文时:新增制度拒绝重复名称;修订制度先生成失效历史版本,再更新现行版本;废止制度更新现行制度为“已失效”。日期统一转换为 ISO 8601,附件仅保存引用 ID。
- 修订发文先保存原制度快照,快照为“已失效”,`origin_regulation` 仅保存原制度 ID;现行制度从修订计划接收制度 Subject 定义中的业务字段,不复制计划的 `origin_regulation/type/status`、OA 匹配信息或发起日期。新增、废止发文使用同一字段白名单,避免计划关联写入制度。
- 更新现行制度时,若已有 `origin_regulation` 指向自身(兼容 ID、name 或展开的引用对象),显式写入 `null` 清空;指向其他制度的既有关联保留。此清理在该制度下一次修订或废止发文时完成,不自动批量修改存量记录。
- 制度目录的历史集合、详情“历史沿革”和拓扑节点的历史版本统一按原制度关联筛选,并按 ID/name 排除现行记录自身;详情查询也增加排除自身的服务端条件,因此存量自引用数据在清理前不会展示为自己的历史版本。历史读取或发文请求失败时沿用现有错误提示。
## 归档与拓扑
- 制度拓扑图与制度目录是两个平级入口;进入拓扑图后不呈现制度目录层级,也不提供返回制度目录操作,用户可通过左侧一级菜单在两个模块之间切换。
......
import dayjs from 'dayjs';
import { serializeGraph } from './regulationGraphData';
import { isRegulationHistory, isSameRegulation } from './regulationHistory';
import {
createDocument,
createRecord,
......@@ -18,6 +19,14 @@ export const GRAPH_SUBJECT = 'internal_regulation_graph';
const REGULATION_LEVELS = ['1级', '2级', '3级', '4级'];
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) {
if (Array.isArray(response?.records)) return response.records;
......@@ -58,6 +67,12 @@ function metadataOf(record) {
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) {
if (!value) return value;
const date = dayjs(value);
......@@ -139,7 +154,7 @@ export async function loadRegulations({ page = 0, pageSize = 20, keyword = '', f
const metadata = metadataOf(record);
const relatedRevisions = revisions.filter((revision) => referenceId(metadataOf(revision).origin_regulation) === record._id);
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 || '')));
return {
...record,
......@@ -156,7 +171,11 @@ export async function loadRegulations({ page = 0, pageSize = 20, keyword = '', f
export async function loadRegulationHistory(record) {
const id = record?._id || record?.name;
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 = '' } = {}) {
......@@ -235,6 +254,7 @@ export async function setRevisionStatus(name, status) {
export async function publishRevision(revision, values = {}) {
const metadata = metadataOf(revision);
const regulationMetadata = publicationMetadata(metadata);
const publicDate = isoDate(values.public_date || new Date().toISOString());
const attachmentIds = (values.attachments || []).map((item) => referenceId(item)).filter(Boolean);
const content = values.content ?? revision.content ?? '';
......@@ -246,17 +266,22 @@ export async function publishRevision(revision, values = {}) {
subject: REGULATION_SUBJECT,
title: metadata.regulation_name,
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) {
const originId = referenceId(metadata.origin_regulation);
const origin = await loadRecord(originId);
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 === '修订') {
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 {
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 }) });
......
......@@ -129,6 +129,8 @@
请求体使用完整 Record payload。当前用于更新模型、批次、隐藏状态和审批数据;一次性同步的模型结果行不调用更新接口。
普通 `normal` Subject 的 PUT 更新会将请求中的 `metadata` 与已有字段合并;省略字段不会清空旧值。内部制度修订/废止发文发现现行制度自引用时,通过 `metadata.origin_regulation: null` 显式清空该可选 Reference,不传入引用对象。
### 3.6 删除 Record
`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