/** * 纪要用例(编码规范 §10.2)。 * * 「决议可经**登记触发**转为工作流的启动 —— 登记人即来源。」(OS 2.1 / 3.1) * 这是纪要与责任之间**唯一**的通路:纪要本身不产生责任。 */ import type { MinuteService } from '../../contract.ts'; import { MinuteNotFoundError, ResolutionAlreadyRegisteredError } from '../../contract.ts'; import type { MinuteDeps } from '../../port.ts'; import { addResolution, markRegistered, newMinute, registeredWorkflowOf } from '../domain/minute.ts'; export function createMinuteService(deps: MinuteDeps): MinuteService { async function require(minuteId: string) { const minute = await deps.store.get(minuteId); if (minute === undefined) throw new MinuteNotFoundError(minuteId); return minute; } return { list: (limit = 50) => deps.store.list(limit), get: (minuteId) => require(minuteId), async create(input) { const minute = newMinute({ id: deps.ids.next(), title: input.title, body: input.body, authorId: input.authorId, participants: input.participants ?? [], occurredOn: input.occurredOn, resolutions: (input.resolutions ?? []).map((r) => ({ id: deps.ids.next(), text: r.text, ownerId: r.ownerId, })), at: deps.clock.now().toISOString(), }); await deps.store.save(minute); return minute; }, async appendResolution(minuteId, text, ownerId) { const minute = await require(minuteId); const next = addResolution( minute, { id: deps.ids.next(), text, ownerId }, deps.clock.now().toISOString(), ); await deps.store.save(next); return next; }, async register(input) { const all = await deps.store.list(200); const minute = all.find((m) => m.resolutions.some((r) => r.id === input.resolutionId)); if (minute === undefined) throw new MinuteNotFoundError(input.resolutionId); // 同一决议不重复起单:起单一刻就已经有一条带来源的工作流在跑了 const already = registeredWorkflowOf(minute, input.resolutionId); if (already !== undefined) throw new ResolutionAlreadyRegisteredError(input.resolutionId, already); const resolution = minute.resolutions.find((r) => r.id === input.resolutionId)!; const started = await deps.registration.start({ processPackageId: input.processPackageId, subject: resolution.text, data: input.data, byId: input.byId, }); const next = markRegistered(minute, input.resolutionId, { workflowId: started.workflowId, byId: input.byId, at: deps.clock.now().toISOString(), })!; await deps.store.save(next); return { workflowId: started.workflowId, resolutionId: input.resolutionId }; }, }; }