using Rmsys.Domain;
using Rmsys.Domain.Entities;
namespace Rmsys.Api.Auth;
///
/// 会议状态守卫与状态流转规则。
///
/// 背景:此前只有「删除会议」检查了状态(已上会不许删),
/// 上传材料、改上会结果、写批注、改数据包设置全都没有判断,
/// 导致已归档的会议还能被追加材料、篡改表决结果,且审计里看不出是会后补的。
///
public static class MeetingGuard
{
/// 允许修改内容的状态(结果登记后即冻结,归档后彻底冻结)
private static readonly string[] Writable =
{
MeetingStatus.Draft, MeetingStatus.Reviewing,
MeetingStatus.Delivered, MeetingStatus.Ongoing
};
/// 返回 null 表示可写;否则返回拒绝原因
public static string? CheckWritable(Meeting m)
{
if (Writable.Contains(m.Status)) return null;
return m.Status switch
{
MeetingStatus.Registered =>
"会议结果已登记,材料与表决不允许再修改(如需修改请先将状态撤回为「会议召开中」)",
MeetingStatus.Archived =>
"会议已归档,不允许修改(如需修改请先「撤回归档」)",
_ => $"会议当前状态({MeetingStatus.Label(m.Status)})不允许修改"
};
}
///
/// 状态流转表。只允许相邻推进/回退,不能从「已归档」直接跳回「筹备中」。
/// 「撤回归档」是唯一合法的逆向出口。
///
private static readonly Dictionary Transitions = new()
{
[MeetingStatus.Draft] = new[] { MeetingStatus.Reviewing },
[MeetingStatus.Reviewing] = new[] { MeetingStatus.Draft, MeetingStatus.Delivered },
[MeetingStatus.Delivered] = new[] { MeetingStatus.Reviewing, MeetingStatus.Ongoing },
[MeetingStatus.Ongoing] = new[] { MeetingStatus.Delivered, MeetingStatus.Registered },
[MeetingStatus.Registered] = new[] { MeetingStatus.Ongoing, MeetingStatus.Archived },
[MeetingStatus.Archived] = new[] { MeetingStatus.Registered }
};
public static bool CanTransition(string from, string to) =>
!string.Equals(from, to, StringComparison.Ordinal)
&& Transitions.TryGetValue(from, out var allowed)
&& allowed.Contains(to);
/// 允许的目标状态(给前端下拉/提示用)
public static string[] AllowedNext(string from) =>
Transitions.TryGetValue(from, out var allowed) ? allowed : Array.Empty();
public static string TransitionDenied(string from, string to) =>
$"不能从「{MeetingStatus.Label(from)}」直接变更为「{MeetingStatus.Label(to)}」" +
$"(允许的下一步:{string.Join("、", AllowedNext(from).Select(MeetingStatus.Label))})";
}