Files
TakeoutSaaS.AdminApi/src/Application/TakeoutSaaS.Application/App/Tenants/Commands/CreateTenantAnnouncementCommand.cs
MSuMshk 857f776447 feat: 实现完整的多租户公告管理系统
核心功能:
- 公告状态机(草稿/已发布/已撤销)支持发布、撤销和重新发布
- 发布者范围区分平台级和租户级公告
- 目标受众定向推送(全部租户/指定角色/指定用户)
- 平台管理、租户管理和应用端查询API
- 已读/未读管理和未读统计

技术实现:
- CQRS+DDD架构,清晰的领域边界和事件驱动
- 查询性能优化:数据库端排序和限制,估算策略减少内存占用
- 并发控制:修复RowVersion配置(IsRowVersion→IsConcurrencyToken)
- 完整的FluentValidation验证器和输入保护

测试验证:
- 36个测试全部通过(27单元+9集成)
- 性能测试达标(1000条数据<5秒)
- 代码质量评级A(优秀)

文档:
- 完整的ADR、API文档和迁移指南
- 交付报告和技术债务记录
2025-12-20 19:57:09 +08:00

68 lines
1.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using MediatR;
using System.ComponentModel.DataAnnotations;
using TakeoutSaaS.Application.App.Tenants.Dto;
using TakeoutSaaS.Domain.Tenants.Enums;
namespace TakeoutSaaS.Application.App.Tenants.Commands;
/// <summary>
/// 创建租户公告命令。
/// </summary>
public sealed record CreateTenantAnnouncementCommand : IRequest<TenantAnnouncementDto>
{
/// <summary>
/// 租户 ID雪花算法
/// </summary>
public long TenantId { get; init; }
/// <summary>
/// 公告标题。
/// </summary>
[Required]
[StringLength(128)]
public string Title { get; init; } = string.Empty;
/// <summary>
/// 公告正文内容。
/// </summary>
[Required]
public string Content { get; init; } = string.Empty;
/// <summary>
/// 公告类型。
/// </summary>
public TenantAnnouncementType AnnouncementType { get; init; } = TenantAnnouncementType.System;
/// <summary>
/// 优先级,数值越大越靠前。
/// </summary>
public int Priority { get; init; } = 0;
/// <summary>
/// 生效开始时间UTC
/// </summary>
public DateTime EffectiveFrom { get; init; } = DateTime.UtcNow;
/// <summary>
/// 生效结束时间UTC为空则长期有效。
/// </summary>
public DateTime? EffectiveTo { get; init; }
/// <summary>
/// 发布者范围。
/// </summary>
public PublisherScope PublisherScope { get; init; } = PublisherScope.Tenant;
/// <summary>
/// 目标受众类型。
/// </summary>
[Required]
[MaxLength(64)]
public string TargetType { get; init; } = string.Empty;
/// <summary>
/// 目标受众参数JSON
/// </summary>
public string? TargetParameters { get; init; }
}