committed by
GitHub
57 changed files with 8873 additions and 195 deletions
@ -0,0 +1,52 @@ |
|||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Authorization.Permissions; |
||||
|
using Volo.Abp.Data; |
||||
|
using Volo.Abp.MultiTenancy; |
||||
|
using Volo.Abp.PermissionManagement; |
||||
|
|
||||
|
namespace LY.MicroService.Applications.Single.EntityFrameworkCore.DataSeeder; |
||||
|
|
||||
|
public class RolePermissionDataSeedContributor : IDataSeedContributor |
||||
|
{ |
||||
|
public ILogger<RolePermissionDataSeedContributor> Logger { protected get; set; } |
||||
|
|
||||
|
protected ICurrentTenant CurrentTenant { get; } |
||||
|
protected IPermissionDataSeeder PermissionDataSeeder { get; } |
||||
|
protected IPermissionDefinitionManager PermissionDefinitionManager { get; } |
||||
|
|
||||
|
public RolePermissionDataSeedContributor( |
||||
|
ICurrentTenant currentTenant, |
||||
|
IPermissionDataSeeder permissionDataSeeder, |
||||
|
IPermissionDefinitionManager permissionDefinitionManager) |
||||
|
{ |
||||
|
CurrentTenant = currentTenant; |
||||
|
PermissionDataSeeder = permissionDataSeeder; |
||||
|
PermissionDefinitionManager = permissionDefinitionManager; |
||||
|
|
||||
|
Logger = NullLogger<RolePermissionDataSeedContributor>.Instance; |
||||
|
} |
||||
|
|
||||
|
public async virtual Task SeedAsync(DataSeedContext context) |
||||
|
{ |
||||
|
using (CurrentTenant.Change(context.TenantId)) |
||||
|
{ |
||||
|
Logger.LogInformation("Seeding the new tenant admin role permissions..."); |
||||
|
|
||||
|
var definitionPermissions = await PermissionDefinitionManager.GetPermissionsAsync(); |
||||
|
await PermissionDataSeeder.SeedAsync( |
||||
|
RolePermissionValueProvider.ProviderName, |
||||
|
"admin", |
||||
|
definitionPermissions.Select(x => x.Name), |
||||
|
context.TenantId); |
||||
|
|
||||
|
await PermissionDataSeeder.SeedAsync( |
||||
|
RolePermissionValueProvider.ProviderName, |
||||
|
"users", |
||||
|
new string[] { "Platform.Feedback.Create" }, |
||||
|
context.TenantId); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
File diff suppressed because it is too large
@ -0,0 +1,122 @@ |
|||||
|
using System; |
||||
|
using Microsoft.EntityFrameworkCore.Migrations; |
||||
|
|
||||
|
#nullable disable |
||||
|
|
||||
|
namespace LY.MicroService.Applications.Single.EntityFrameworkCore.Migrations |
||||
|
{ |
||||
|
/// <inheritdoc />
|
||||
|
public partial class AddFeedback : Migration |
||||
|
{ |
||||
|
/// <inheritdoc />
|
||||
|
protected override void Up(MigrationBuilder migrationBuilder) |
||||
|
{ |
||||
|
migrationBuilder.CreateTable( |
||||
|
name: "AppPlatformFeedbacks", |
||||
|
columns: table => new |
||||
|
{ |
||||
|
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"), |
||||
|
TenantId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"), |
||||
|
Content = table.Column<string>(type: "varchar(255)", maxLength: 255, nullable: false) |
||||
|
.Annotation("MySql:CharSet", "utf8mb4"), |
||||
|
Category = table.Column<string>(type: "varchar(64)", maxLength: 64, nullable: false) |
||||
|
.Annotation("MySql:CharSet", "utf8mb4"), |
||||
|
Status = table.Column<int>(type: "int", nullable: false), |
||||
|
ExtraProperties = table.Column<string>(type: "longtext", nullable: false) |
||||
|
.Annotation("MySql:CharSet", "utf8mb4"), |
||||
|
ConcurrencyStamp = table.Column<string>(type: "varchar(40)", maxLength: 40, nullable: false) |
||||
|
.Annotation("MySql:CharSet", "utf8mb4"), |
||||
|
CreationTime = table.Column<DateTime>(type: "datetime(6)", nullable: false), |
||||
|
CreatorId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"), |
||||
|
LastModificationTime = table.Column<DateTime>(type: "datetime(6)", nullable: true), |
||||
|
LastModifierId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"), |
||||
|
IsDeleted = table.Column<bool>(type: "tinyint(1)", nullable: false, defaultValue: false), |
||||
|
DeleterId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"), |
||||
|
DeletionTime = table.Column<DateTime>(type: "datetime(6)", nullable: true) |
||||
|
}, |
||||
|
constraints: table => |
||||
|
{ |
||||
|
table.PrimaryKey("PK_AppPlatformFeedbacks", x => x.Id); |
||||
|
}) |
||||
|
.Annotation("MySql:CharSet", "utf8mb4"); |
||||
|
|
||||
|
migrationBuilder.CreateTable( |
||||
|
name: "AppPlatformFeedbackAttachments", |
||||
|
columns: table => new |
||||
|
{ |
||||
|
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"), |
||||
|
TenantId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"), |
||||
|
Name = table.Column<string>(type: "varchar(64)", maxLength: 64, nullable: false) |
||||
|
.Annotation("MySql:CharSet", "utf8mb4"), |
||||
|
Url = table.Column<string>(type: "varchar(255)", maxLength: 255, nullable: false) |
||||
|
.Annotation("MySql:CharSet", "utf8mb4"), |
||||
|
Size = table.Column<long>(type: "bigint", nullable: false), |
||||
|
FeedbackId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"), |
||||
|
CreationTime = table.Column<DateTime>(type: "datetime(6)", nullable: false), |
||||
|
CreatorId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci") |
||||
|
}, |
||||
|
constraints: table => |
||||
|
{ |
||||
|
table.PrimaryKey("PK_AppPlatformFeedbackAttachments", x => x.Id); |
||||
|
table.ForeignKey( |
||||
|
name: "FK_AppPlatformFeedbackAttachments_AppPlatformFeedbacks_Feedback~", |
||||
|
column: x => x.FeedbackId, |
||||
|
principalTable: "AppPlatformFeedbacks", |
||||
|
principalColumn: "Id", |
||||
|
onDelete: ReferentialAction.Cascade); |
||||
|
}) |
||||
|
.Annotation("MySql:CharSet", "utf8mb4"); |
||||
|
|
||||
|
migrationBuilder.CreateTable( |
||||
|
name: "AppPlatformFeedbackComments", |
||||
|
columns: table => new |
||||
|
{ |
||||
|
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"), |
||||
|
TenantId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"), |
||||
|
Capacity = table.Column<string>(type: "varchar(64)", maxLength: 64, nullable: false) |
||||
|
.Annotation("MySql:CharSet", "utf8mb4"), |
||||
|
Content = table.Column<string>(type: "varchar(255)", maxLength: 255, nullable: false) |
||||
|
.Annotation("MySql:CharSet", "utf8mb4"), |
||||
|
FeedbackId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"), |
||||
|
CreationTime = table.Column<DateTime>(type: "datetime(6)", nullable: false), |
||||
|
CreatorId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"), |
||||
|
LastModificationTime = table.Column<DateTime>(type: "datetime(6)", nullable: true), |
||||
|
LastModifierId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci") |
||||
|
}, |
||||
|
constraints: table => |
||||
|
{ |
||||
|
table.PrimaryKey("PK_AppPlatformFeedbackComments", x => x.Id); |
||||
|
table.ForeignKey( |
||||
|
name: "FK_AppPlatformFeedbackComments_AppPlatformFeedbacks_FeedbackId", |
||||
|
column: x => x.FeedbackId, |
||||
|
principalTable: "AppPlatformFeedbacks", |
||||
|
principalColumn: "Id", |
||||
|
onDelete: ReferentialAction.Cascade); |
||||
|
}) |
||||
|
.Annotation("MySql:CharSet", "utf8mb4"); |
||||
|
|
||||
|
migrationBuilder.CreateIndex( |
||||
|
name: "IX_AppPlatformFeedbackAttachments_FeedbackId", |
||||
|
table: "AppPlatformFeedbackAttachments", |
||||
|
column: "FeedbackId"); |
||||
|
|
||||
|
migrationBuilder.CreateIndex( |
||||
|
name: "IX_AppPlatformFeedbackComments_FeedbackId", |
||||
|
table: "AppPlatformFeedbackComments", |
||||
|
column: "FeedbackId"); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override void Down(MigrationBuilder migrationBuilder) |
||||
|
{ |
||||
|
migrationBuilder.DropTable( |
||||
|
name: "AppPlatformFeedbackAttachments"); |
||||
|
|
||||
|
migrationBuilder.DropTable( |
||||
|
name: "AppPlatformFeedbackComments"); |
||||
|
|
||||
|
migrationBuilder.DropTable( |
||||
|
name: "AppPlatformFeedbacks"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
File diff suppressed because it is too large
@ -0,0 +1,122 @@ |
|||||
|
using System; |
||||
|
using Microsoft.EntityFrameworkCore.Migrations; |
||||
|
|
||||
|
#nullable disable |
||||
|
|
||||
|
namespace LY.MicroService.Platform.EntityFrameworkCore.Migrations |
||||
|
{ |
||||
|
/// <inheritdoc />
|
||||
|
public partial class AddFeedback : Migration |
||||
|
{ |
||||
|
/// <inheritdoc />
|
||||
|
protected override void Up(MigrationBuilder migrationBuilder) |
||||
|
{ |
||||
|
migrationBuilder.CreateTable( |
||||
|
name: "AppPlatformFeedbacks", |
||||
|
columns: table => new |
||||
|
{ |
||||
|
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"), |
||||
|
TenantId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"), |
||||
|
Content = table.Column<string>(type: "varchar(255)", maxLength: 255, nullable: false) |
||||
|
.Annotation("MySql:CharSet", "utf8mb4"), |
||||
|
Category = table.Column<string>(type: "varchar(64)", maxLength: 64, nullable: false) |
||||
|
.Annotation("MySql:CharSet", "utf8mb4"), |
||||
|
Status = table.Column<int>(type: "int", nullable: false), |
||||
|
ExtraProperties = table.Column<string>(type: "longtext", nullable: false) |
||||
|
.Annotation("MySql:CharSet", "utf8mb4"), |
||||
|
ConcurrencyStamp = table.Column<string>(type: "varchar(40)", maxLength: 40, nullable: false) |
||||
|
.Annotation("MySql:CharSet", "utf8mb4"), |
||||
|
CreationTime = table.Column<DateTime>(type: "datetime(6)", nullable: false), |
||||
|
CreatorId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"), |
||||
|
LastModificationTime = table.Column<DateTime>(type: "datetime(6)", nullable: true), |
||||
|
LastModifierId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"), |
||||
|
IsDeleted = table.Column<bool>(type: "tinyint(1)", nullable: false, defaultValue: false), |
||||
|
DeleterId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"), |
||||
|
DeletionTime = table.Column<DateTime>(type: "datetime(6)", nullable: true) |
||||
|
}, |
||||
|
constraints: table => |
||||
|
{ |
||||
|
table.PrimaryKey("PK_AppPlatformFeedbacks", x => x.Id); |
||||
|
}) |
||||
|
.Annotation("MySql:CharSet", "utf8mb4"); |
||||
|
|
||||
|
migrationBuilder.CreateTable( |
||||
|
name: "AppPlatformFeedbackAttachments", |
||||
|
columns: table => new |
||||
|
{ |
||||
|
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"), |
||||
|
TenantId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"), |
||||
|
Name = table.Column<string>(type: "varchar(64)", maxLength: 64, nullable: false) |
||||
|
.Annotation("MySql:CharSet", "utf8mb4"), |
||||
|
Url = table.Column<string>(type: "varchar(255)", maxLength: 255, nullable: false) |
||||
|
.Annotation("MySql:CharSet", "utf8mb4"), |
||||
|
Size = table.Column<long>(type: "bigint", nullable: false), |
||||
|
FeedbackId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"), |
||||
|
CreationTime = table.Column<DateTime>(type: "datetime(6)", nullable: false), |
||||
|
CreatorId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci") |
||||
|
}, |
||||
|
constraints: table => |
||||
|
{ |
||||
|
table.PrimaryKey("PK_AppPlatformFeedbackAttachments", x => x.Id); |
||||
|
table.ForeignKey( |
||||
|
name: "FK_AppPlatformFeedbackAttachments_AppPlatformFeedbacks_Feedback~", |
||||
|
column: x => x.FeedbackId, |
||||
|
principalTable: "AppPlatformFeedbacks", |
||||
|
principalColumn: "Id", |
||||
|
onDelete: ReferentialAction.Cascade); |
||||
|
}) |
||||
|
.Annotation("MySql:CharSet", "utf8mb4"); |
||||
|
|
||||
|
migrationBuilder.CreateTable( |
||||
|
name: "AppPlatformFeedbackComments", |
||||
|
columns: table => new |
||||
|
{ |
||||
|
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"), |
||||
|
TenantId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"), |
||||
|
Capacity = table.Column<string>(type: "varchar(64)", maxLength: 64, nullable: false) |
||||
|
.Annotation("MySql:CharSet", "utf8mb4"), |
||||
|
Content = table.Column<string>(type: "varchar(255)", maxLength: 255, nullable: false) |
||||
|
.Annotation("MySql:CharSet", "utf8mb4"), |
||||
|
FeedbackId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"), |
||||
|
CreationTime = table.Column<DateTime>(type: "datetime(6)", nullable: false), |
||||
|
CreatorId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"), |
||||
|
LastModificationTime = table.Column<DateTime>(type: "datetime(6)", nullable: true), |
||||
|
LastModifierId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci") |
||||
|
}, |
||||
|
constraints: table => |
||||
|
{ |
||||
|
table.PrimaryKey("PK_AppPlatformFeedbackComments", x => x.Id); |
||||
|
table.ForeignKey( |
||||
|
name: "FK_AppPlatformFeedbackComments_AppPlatformFeedbacks_FeedbackId", |
||||
|
column: x => x.FeedbackId, |
||||
|
principalTable: "AppPlatformFeedbacks", |
||||
|
principalColumn: "Id", |
||||
|
onDelete: ReferentialAction.Cascade); |
||||
|
}) |
||||
|
.Annotation("MySql:CharSet", "utf8mb4"); |
||||
|
|
||||
|
migrationBuilder.CreateIndex( |
||||
|
name: "IX_AppPlatformFeedbackAttachments_FeedbackId", |
||||
|
table: "AppPlatformFeedbackAttachments", |
||||
|
column: "FeedbackId"); |
||||
|
|
||||
|
migrationBuilder.CreateIndex( |
||||
|
name: "IX_AppPlatformFeedbackComments_FeedbackId", |
||||
|
table: "AppPlatformFeedbackComments", |
||||
|
column: "FeedbackId"); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override void Down(MigrationBuilder migrationBuilder) |
||||
|
{ |
||||
|
migrationBuilder.DropTable( |
||||
|
name: "AppPlatformFeedbackAttachments"); |
||||
|
|
||||
|
migrationBuilder.DropTable( |
||||
|
name: "AppPlatformFeedbackComments"); |
||||
|
|
||||
|
migrationBuilder.DropTable( |
||||
|
name: "AppPlatformFeedbacks"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
using System; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
public class FeedbackAttachmentDto : CreationAuditedEntityDto<Guid> |
||||
|
{ |
||||
|
public string Name { get; set; } |
||||
|
public string Url { get; set; } |
||||
|
public long Size { get; set; } |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
using System; |
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using Volo.Abp.Validation; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
public class FeedbackAttachmentGetInput |
||||
|
{ |
||||
|
[Required] |
||||
|
public Guid FeedbackId { get; set; } |
||||
|
|
||||
|
[Required] |
||||
|
[DynamicStringLength(typeof(FeedbackAttachmentConsts), nameof(FeedbackAttachmentConsts.MaxNameLength))] |
||||
|
public string Name { get; set; } |
||||
|
} |
||||
@ -0,0 +1,6 @@ |
|||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
public class FeedbackAttachmentTempFileCreateDto |
||||
|
{ |
||||
|
public string Path { get; set; } |
||||
|
public string Id { get; set; } |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
public class FeedbackAttachmentTempFileDto |
||||
|
{ |
||||
|
public string Path { get; set; } |
||||
|
public string Id { get; set; } |
||||
|
public long Size { get; set; } |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
using Volo.Abp.Auditing; |
||||
|
using Volo.Abp.Content; |
||||
|
using Volo.Abp.Validation; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
|
||||
|
public class FeedbackAttachmentUploadInput |
||||
|
{ |
||||
|
[DisableAuditing] |
||||
|
[DisableValidation] |
||||
|
public IRemoteStreamContent File { get; set; } |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using Volo.Abp.Validation; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
public class FeedbackCommentCreateDto : FeedbackCommentCreateOrUpdateDto |
||||
|
{ |
||||
|
[Required] |
||||
|
[DynamicStringLength(typeof(FeedbackCommentConsts), nameof(FeedbackCommentConsts.MaxCapacityLength))] |
||||
|
public string Capacity { get; set; } |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using Volo.Abp.Validation; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
public abstract class FeedbackCommentCreateOrUpdateDto |
||||
|
{ |
||||
|
[Required] |
||||
|
[DynamicStringLength(typeof(FeedbackCommentConsts), nameof(FeedbackCommentConsts.MaxContentLength))] |
||||
|
public string Content { get; set; } |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
using System; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
public class FeedbackCommentDto : AuditedEntityDto<Guid> |
||||
|
{ |
||||
|
public string Content { get; set; } |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
using Volo.Abp.Domain.Entities; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
public class FeedbackCommentUpdateDto : FeedbackCommentCreateOrUpdateDto, IHasConcurrencyStamp |
||||
|
{ |
||||
|
public string ConcurrencyStamp { get; set; } |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using Volo.Abp.Validation; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
public class FeedbackCreateDto |
||||
|
{ |
||||
|
[Required] |
||||
|
[DynamicStringLength(typeof(FeedbackConsts), nameof(FeedbackConsts.MaxContentLength))] |
||||
|
public string Content { get; set; } |
||||
|
|
||||
|
[Required] |
||||
|
[DynamicStringLength(typeof(FeedbackConsts), nameof(FeedbackConsts.MaxCategoryLength))] |
||||
|
public string Category { get; set; } |
||||
|
|
||||
|
public List<FeedbackAttachmentTempFileCreateDto> Attachments { get; set; } |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
public class FeedbackDto : ExtensibleAuditedEntityDto<Guid> |
||||
|
{ |
||||
|
public string Content { get; set; } |
||||
|
public string Category { get; set; } |
||||
|
public FeedbackStatus Status { get; set; } |
||||
|
public List<FeedbackCommentDto> Comments { get; set; } |
||||
|
public List<FeedbackAttachmentDto> Attachments { get; set; } |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using Volo.Abp.Application.Dtos; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
public class FeedbackGetListInput : PagedAndSortedResultRequestDto |
||||
|
{ |
||||
|
public string Filter { get; set; } |
||||
|
public string Category { get; set; } |
||||
|
public FeedbackStatus? Status { get; set; } |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
using Volo.Abp.Application.Services; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
public interface IFeedbackAppService : IApplicationService |
||||
|
{ |
||||
|
Task<FeedbackDto> GetAsync(Guid id); |
||||
|
|
||||
|
Task<FeedbackDto> CreateAsync(FeedbackCreateDto input); |
||||
|
|
||||
|
Task DeleteAsync(Guid id); |
||||
|
|
||||
|
Task<PagedResultDto<FeedbackDto>> GetListAsync(FeedbackGetListInput input); |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Application.Services; |
||||
|
using Volo.Abp.Content; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
public interface IFeedbackAttachmentAppService : IApplicationService |
||||
|
{ |
||||
|
Task<FeedbackAttachmentTempFileDto> UploadAsync(FeedbackAttachmentUploadInput input); |
||||
|
|
||||
|
Task<IRemoteStreamContent> GetAsync(FeedbackAttachmentGetInput input); |
||||
|
|
||||
|
Task DeleteAsync(FeedbackAttachmentGetInput input); |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Application.Services; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
public interface IFeedbackCommentAppService : IApplicationService |
||||
|
{ |
||||
|
Task<FeedbackCommentDto> ProgressAsync(FeedbackCommentCreateDto input); |
||||
|
|
||||
|
Task<FeedbackCommentDto> CloseAsync(FeedbackCommentCreateDto input); |
||||
|
|
||||
|
Task<FeedbackCommentDto> ResolveAsync(FeedbackCommentCreateDto input); |
||||
|
|
||||
|
Task<FeedbackCommentDto> UpdateAsync(Guid id, FeedbackCommentUpdateDto input); |
||||
|
|
||||
|
Task<FeedbackCommentDto> DeleteAsync(Guid id); |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
using Volo.Abp.Application.Services; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
public interface IMyFeedbackAppService : IApplicationService |
||||
|
{ |
||||
|
Task<PagedResultDto<FeedbackDto>> GetMyFeedbacksAsync(FeedbackGetListInput input); |
||||
|
} |
||||
@ -0,0 +1,107 @@ |
|||||
|
using LINGYUN.Platform.Permissions; |
||||
|
using Microsoft.AspNetCore.Authorization; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq.Expressions; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
|
||||
|
[Authorize(PlatformPermissions.Feedback.Default)] |
||||
|
public class FeedbackAppService : PlatformApplicationServiceBase, IFeedbackAppService |
||||
|
{ |
||||
|
private readonly FeedbackAttachmentManager _attachmentManager; |
||||
|
private readonly IFeedbackRepository _feedbackRepository; |
||||
|
|
||||
|
public FeedbackAppService( |
||||
|
FeedbackAttachmentManager attachmentManager, |
||||
|
IFeedbackRepository feedbackRepository) |
||||
|
{ |
||||
|
_attachmentManager = attachmentManager; |
||||
|
_feedbackRepository = feedbackRepository; |
||||
|
} |
||||
|
|
||||
|
[Authorize(PlatformPermissions.Feedback.Create)] |
||||
|
public async virtual Task<FeedbackDto> CreateAsync(FeedbackCreateDto input) |
||||
|
{ |
||||
|
var feedback = new Feedback( |
||||
|
GuidGenerator.Create(), |
||||
|
input.Category, |
||||
|
input.Content, |
||||
|
FeedbackStatus.Created, |
||||
|
CurrentTenant.Id); |
||||
|
|
||||
|
if (input.Attachments != null) |
||||
|
{ |
||||
|
foreach (var attachment in input.Attachments) |
||||
|
{ |
||||
|
var attachmentFile = await _attachmentManager.CopyFromTempAsync( |
||||
|
feedback, |
||||
|
attachment.Path, |
||||
|
attachment.Id); |
||||
|
|
||||
|
feedback.AddAttachment( |
||||
|
GuidGenerator, |
||||
|
attachmentFile.Name, |
||||
|
$"/api/platform/feedbacks/{feedback.Id}/attachments/{attachmentFile.Name}", |
||||
|
attachmentFile.Size); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
feedback = await _feedbackRepository.InsertAsync(feedback); |
||||
|
|
||||
|
await CurrentUnitOfWork.SaveChangesAsync(); |
||||
|
|
||||
|
return ObjectMapper.Map<Feedback, FeedbackDto>(feedback); |
||||
|
} |
||||
|
|
||||
|
[Authorize(PlatformPermissions.Feedback.Delete)] |
||||
|
public async Task DeleteAsync(Guid id) |
||||
|
{ |
||||
|
var feedback = await _feedbackRepository.GetAsync(id); |
||||
|
|
||||
|
await _feedbackRepository.DeleteAsync(feedback); |
||||
|
|
||||
|
await CurrentUnitOfWork.SaveChangesAsync(); |
||||
|
} |
||||
|
|
||||
|
[Authorize(PlatformPermissions.Feedback.Default)] |
||||
|
public async virtual Task<FeedbackDto> GetAsync(Guid id) |
||||
|
{ |
||||
|
var feedback = await _feedbackRepository.GetAsync(id); |
||||
|
|
||||
|
return ObjectMapper.Map<Feedback, FeedbackDto>(feedback); |
||||
|
} |
||||
|
|
||||
|
public async virtual Task<PagedResultDto<FeedbackDto>> GetListAsync(FeedbackGetListInput input) |
||||
|
{ |
||||
|
var specification = new FeedbackGetListSpecification(input); |
||||
|
|
||||
|
var totalCount = await _feedbackRepository.GetCountAsync(specification); |
||||
|
var feedbacks = await _feedbackRepository.GetListAsync(specification, |
||||
|
input.Sorting, input.MaxResultCount, input.SkipCount); |
||||
|
|
||||
|
return new PagedResultDto<FeedbackDto>(totalCount, |
||||
|
ObjectMapper.Map<List<Feedback>, List<FeedbackDto>>(feedbacks)); |
||||
|
} |
||||
|
|
||||
|
internal class FeedbackGetListSpecification : Volo.Abp.Specifications.Specification<Feedback> |
||||
|
{ |
||||
|
protected FeedbackGetListInput Input { get; } |
||||
|
public FeedbackGetListSpecification(FeedbackGetListInput input) |
||||
|
{ |
||||
|
Input = input; |
||||
|
} |
||||
|
public override Expression<Func<Feedback, bool>> ToExpression() |
||||
|
{ |
||||
|
Expression<Func<Feedback, bool>> expression = _ => true; |
||||
|
|
||||
|
return expression |
||||
|
.AndIf(Input.Status.HasValue, x => x.Status == Input.Status) |
||||
|
.AndIf(!Input.Category.IsNullOrWhiteSpace(), x => x.Category == Input.Category) |
||||
|
.AndIf(!Input.Filter.IsNullOrWhiteSpace(), x => x.Category.Contains(Input.Filter) || |
||||
|
x.Content.Contains(Input.Filter)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,69 @@ |
|||||
|
using LINGYUN.Platform.Permissions; |
||||
|
using Microsoft.AspNetCore.Authorization; |
||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Content; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
|
||||
|
[Authorize(PlatformPermissions.Feedback.Default)] |
||||
|
public class FeedbackAttachmentAppService : PlatformApplicationServiceBase, IFeedbackAttachmentAppService |
||||
|
{ |
||||
|
private readonly IFeedbackRepository _feedbackRepository; |
||||
|
private readonly FeedbackAttachmentManager _feedbackAttachmentManager; |
||||
|
|
||||
|
public FeedbackAttachmentAppService( |
||||
|
IFeedbackRepository feedbackRepository, |
||||
|
FeedbackAttachmentManager feedbackAttachmentManager) |
||||
|
{ |
||||
|
_feedbackRepository = feedbackRepository; |
||||
|
_feedbackAttachmentManager = feedbackAttachmentManager; |
||||
|
} |
||||
|
|
||||
|
public async virtual Task<FeedbackAttachmentTempFileDto> UploadAsync(FeedbackAttachmentUploadInput input) |
||||
|
{ |
||||
|
var stream = input.File.GetStream(); |
||||
|
|
||||
|
var tmpFile = await _feedbackAttachmentManager.SaveToTempAsync(stream); |
||||
|
|
||||
|
return new FeedbackAttachmentTempFileDto |
||||
|
{ |
||||
|
Size = tmpFile.Size, |
||||
|
Path = tmpFile.Path, |
||||
|
Id = tmpFile.Id, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
public async virtual Task<IRemoteStreamContent> GetAsync(FeedbackAttachmentGetInput input) |
||||
|
{ |
||||
|
var attachment = await GetFeedbackAttachmentAsync(input); |
||||
|
|
||||
|
var stream = await _feedbackAttachmentManager.DownloadAsync(attachment); |
||||
|
|
||||
|
return new RemoteStreamContent(stream, attachment.Name); |
||||
|
} |
||||
|
|
||||
|
public async virtual Task DeleteAsync(FeedbackAttachmentGetInput input) |
||||
|
{ |
||||
|
var feedback = await _feedbackRepository.GetAsync(input.FeedbackId); |
||||
|
if (feedback.CreatorId != CurrentUser.Id) |
||||
|
{ |
||||
|
await AuthorizationService.CheckAsync(PlatformPermissions.Feedback.ManageAttachments); |
||||
|
} |
||||
|
|
||||
|
var attachment = feedback.FindAttachment(input.Name); |
||||
|
|
||||
|
feedback.RemoveAttachment(attachment.Name); |
||||
|
|
||||
|
await CurrentUnitOfWork.SaveChangesAsync(); |
||||
|
|
||||
|
await _feedbackAttachmentManager.DeleteAsync(attachment); |
||||
|
} |
||||
|
|
||||
|
protected async virtual Task<FeedbackAttachment> GetFeedbackAttachmentAsync(FeedbackAttachmentGetInput input) |
||||
|
{ |
||||
|
var feedback = await _feedbackRepository.GetAsync(input.FeedbackId); |
||||
|
var attachment = feedback.FindAttachment(input.Name); |
||||
|
return attachment ?? throw new FeedbackAttachmentNotFoundException(input.Name); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
public class FeedbackCommentAppService |
||||
|
{ |
||||
|
} |
||||
@ -0,0 +1,52 @@ |
|||||
|
using Microsoft.AspNetCore.Authorization; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Linq.Expressions; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
using Volo.Abp.Users; |
||||
|
using static LINGYUN.Platform.Feedbacks.FeedbackAppService; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
|
||||
|
[Authorize] |
||||
|
public class MyFeedbackAppService : PlatformApplicationServiceBase, IMyFeedbackAppService |
||||
|
{ |
||||
|
private readonly IFeedbackRepository _feedbackRepository; |
||||
|
public MyFeedbackAppService(IFeedbackRepository feedbackRepository) |
||||
|
{ |
||||
|
_feedbackRepository = feedbackRepository; |
||||
|
} |
||||
|
|
||||
|
public async virtual Task<PagedResultDto<FeedbackDto>> GetMyFeedbacksAsync(FeedbackGetListInput input) |
||||
|
{ |
||||
|
var specification = new FeedbackGetListByUserSpecification(CurrentUser.GetId(), input); |
||||
|
|
||||
|
var totalCount = await _feedbackRepository.GetCountAsync(specification); |
||||
|
var feedbacks = await _feedbackRepository.GetListAsync(specification, |
||||
|
input.Sorting, input.MaxResultCount, input.SkipCount); |
||||
|
|
||||
|
return new PagedResultDto<FeedbackDto>(totalCount, |
||||
|
ObjectMapper.Map<List<Feedback>, List<FeedbackDto>>(feedbacks)); |
||||
|
} |
||||
|
|
||||
|
private class FeedbackGetListByUserSpecification : FeedbackGetListSpecification |
||||
|
{ |
||||
|
protected Guid UserId { get; } |
||||
|
public FeedbackGetListByUserSpecification( |
||||
|
Guid userId, |
||||
|
FeedbackGetListInput input) |
||||
|
: base(input) |
||||
|
{ |
||||
|
UserId = userId; |
||||
|
} |
||||
|
|
||||
|
public override Expression<Func<Feedback, bool>> ToExpression() |
||||
|
{ |
||||
|
var expression = base.ToExpression(); |
||||
|
|
||||
|
return expression.And(x => x.CreatorId == UserId); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
using Volo.Abp.Specifications; |
||||
|
|
||||
|
namespace System.Linq.Expressions; |
||||
|
|
||||
|
internal static class ExpressionFuncExtensions |
||||
|
{ |
||||
|
public static Expression<Func<T, bool>> AndIf<T>( |
||||
|
this Expression<Func<T, bool>> first, |
||||
|
bool condition, |
||||
|
Expression<Func<T, bool>> second) |
||||
|
{ |
||||
|
if (condition) |
||||
|
{ |
||||
|
return ExpressionFuncExtender.And(first, second); |
||||
|
} |
||||
|
|
||||
|
return first; |
||||
|
} |
||||
|
|
||||
|
public static Expression<Func<T, bool>> OrIf<T>( |
||||
|
this Expression<Func<T, bool>> first, |
||||
|
bool condition, |
||||
|
Expression<Func<T, bool>> second) |
||||
|
{ |
||||
|
if (condition) |
||||
|
{ |
||||
|
return ExpressionFuncExtender.Or(first, second); |
||||
|
} |
||||
|
|
||||
|
return first; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,6 @@ |
|||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
public static class FeedbackAttachmentConsts |
||||
|
{ |
||||
|
public static int MaxNameLength { get; set; } = 64; |
||||
|
public static int MaxUrlLength { get; set; } = 255; |
||||
|
} |
||||
@ -0,0 +1,6 @@ |
|||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
public class FeedbackAttachmentFile(string name, long size) |
||||
|
{ |
||||
|
public string Name { get; } = name; |
||||
|
public long Size { get; } = size; |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
public class FeedbackAttachmentTempFile |
||||
|
{ |
||||
|
public string Path { get; set; } |
||||
|
public string Id { get; set; } |
||||
|
public long Size { get; set; } |
||||
|
} |
||||
@ -0,0 +1,6 @@ |
|||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
public static class FeedbackCommentConsts |
||||
|
{ |
||||
|
public static int MaxContentLength { get; set; } = 255; |
||||
|
public static int MaxCapacityLength { get; set; } = 64; |
||||
|
} |
||||
@ -0,0 +1,6 @@ |
|||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
public static class FeedbackConsts |
||||
|
{ |
||||
|
public static int MaxCategoryLength { get; set; } = 64; |
||||
|
public static int MaxContentLength { get; set; } = 255; |
||||
|
} |
||||
@ -0,0 +1,26 @@ |
|||||
|
using System.ComponentModel; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
public enum FeedbackStatus |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 新增
|
||||
|
/// </summary>
|
||||
|
[Description("FeedbackStatus:Created")] |
||||
|
Created = 1, |
||||
|
/// <summary>
|
||||
|
/// 处理中
|
||||
|
/// </summary>
|
||||
|
[Description("FeedbackStatus:InProgress")] |
||||
|
InProgress = 2, |
||||
|
/// <summary>
|
||||
|
/// 已关闭
|
||||
|
/// </summary>
|
||||
|
[Description("FeedbackStatus:Closed")] |
||||
|
Closed = 3, |
||||
|
/// <summary>
|
||||
|
/// 已解决
|
||||
|
/// </summary>
|
||||
|
[Description("FeedbackStatus:Resolved")] |
||||
|
Resolved = 4 |
||||
|
} |
||||
@ -0,0 +1,161 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Collections.ObjectModel; |
||||
|
using System.Linq; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.Data; |
||||
|
using Volo.Abp.Domain.Entities.Auditing; |
||||
|
using Volo.Abp.Guids; |
||||
|
using Volo.Abp.MultiTenancy; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
public class Feedback : FullAuditedAggregateRoot<Guid>, IMultiTenant |
||||
|
{ |
||||
|
public virtual Guid? TenantId { get; protected set; } |
||||
|
public virtual string Content { get; set; } |
||||
|
public virtual string Category { get; protected set; } |
||||
|
public virtual FeedbackStatus Status { get; protected set; } |
||||
|
public virtual ICollection<FeedbackComment> Comments { get; protected set; } |
||||
|
public virtual ICollection<FeedbackAttachment> Attachments { get; protected set; } |
||||
|
protected Feedback() |
||||
|
{ |
||||
|
ExtraProperties = new ExtraPropertyDictionary(); |
||||
|
this.SetDefaultsForExtraProperties(); |
||||
|
|
||||
|
Comments = new Collection<FeedbackComment>(); |
||||
|
Attachments = new Collection<FeedbackAttachment>(); |
||||
|
} |
||||
|
|
||||
|
public Feedback( |
||||
|
Guid id, |
||||
|
string category, |
||||
|
string content, |
||||
|
FeedbackStatus status, |
||||
|
Guid? tenantId = null) |
||||
|
: base(id) |
||||
|
{ |
||||
|
Category = Check.NotNullOrWhiteSpace(category, nameof(category), FeedbackConsts.MaxCategoryLength); |
||||
|
Content = Check.NotNullOrWhiteSpace(content, nameof(content), FeedbackConsts.MaxContentLength); |
||||
|
Status = status; |
||||
|
TenantId = tenantId; |
||||
|
|
||||
|
ExtraProperties = new ExtraPropertyDictionary(); |
||||
|
this.SetDefaultsForExtraProperties(); |
||||
|
|
||||
|
Comments = new Collection<FeedbackComment>(); |
||||
|
Attachments = new Collection<FeedbackAttachment>(); |
||||
|
} |
||||
|
|
||||
|
public FeedbackAttachment AddAttachment( |
||||
|
IGuidGenerator guidGenerator, |
||||
|
string name, |
||||
|
string url, |
||||
|
long size) |
||||
|
{ |
||||
|
if (FindAttachment(name) != null) |
||||
|
{ |
||||
|
throw new BusinessException(PlatformErrorCodes.DuplicateFeedbackAttachment) |
||||
|
.WithData("Name", name); |
||||
|
} |
||||
|
|
||||
|
var attachment = new FeedbackAttachment( |
||||
|
guidGenerator.Create(), |
||||
|
Id, |
||||
|
name, |
||||
|
url, |
||||
|
size, |
||||
|
TenantId); |
||||
|
|
||||
|
Attachments.Add(attachment); |
||||
|
|
||||
|
return attachment; |
||||
|
} |
||||
|
|
||||
|
public FeedbackAttachment FindAttachment(string name) |
||||
|
{ |
||||
|
return Attachments.FirstOrDefault(x => x.Name == name); |
||||
|
} |
||||
|
|
||||
|
public Feedback RemoveAttachment(string name) |
||||
|
{ |
||||
|
Attachments.RemoveAll(x => x.Name == name); |
||||
|
|
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
public FeedbackComment Progress( |
||||
|
IGuidGenerator generator, |
||||
|
string capacity, |
||||
|
string content) |
||||
|
{ |
||||
|
ValidateStatus(); |
||||
|
|
||||
|
var comment = new FeedbackComment( |
||||
|
generator.Create(), |
||||
|
Id, |
||||
|
capacity, |
||||
|
content, |
||||
|
TenantId); |
||||
|
|
||||
|
Comments.Add(comment); |
||||
|
|
||||
|
Status = FeedbackStatus.InProgress; |
||||
|
|
||||
|
return comment; |
||||
|
} |
||||
|
|
||||
|
public FeedbackComment Close( |
||||
|
IGuidGenerator generator, |
||||
|
string capacity, |
||||
|
string content) |
||||
|
{ |
||||
|
ValidateStatus(); |
||||
|
|
||||
|
var comment = new FeedbackComment( |
||||
|
generator.Create(), |
||||
|
Id, |
||||
|
capacity, |
||||
|
content, |
||||
|
TenantId); |
||||
|
|
||||
|
Comments.Add(comment); |
||||
|
|
||||
|
Status = FeedbackStatus.Closed; |
||||
|
|
||||
|
return comment; |
||||
|
} |
||||
|
|
||||
|
public FeedbackComment Resolve( |
||||
|
IGuidGenerator generator, |
||||
|
string capacity, |
||||
|
string content) |
||||
|
{ |
||||
|
ValidateStatus(); |
||||
|
|
||||
|
var comment = new FeedbackComment( |
||||
|
generator.Create(), |
||||
|
Id, |
||||
|
capacity, |
||||
|
content, |
||||
|
TenantId); |
||||
|
|
||||
|
Comments.Add(comment); |
||||
|
|
||||
|
Status = FeedbackStatus.Resolved; |
||||
|
|
||||
|
return comment; |
||||
|
} |
||||
|
|
||||
|
public Feedback ValidateStatus() |
||||
|
{ |
||||
|
if (Status == FeedbackStatus.Closed) |
||||
|
{ |
||||
|
throw new FeedbackStatusException(PlatformErrorCodes.UnableFeedbackCommentInStatus, FeedbackStatus.Closed); |
||||
|
} |
||||
|
if (Status == FeedbackStatus.Resolved) |
||||
|
{ |
||||
|
throw new FeedbackStatusException(PlatformErrorCodes.UnableFeedbackCommentInStatus, FeedbackStatus.Resolved); |
||||
|
} |
||||
|
return this; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
using System; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.Domain.Entities.Auditing; |
||||
|
using Volo.Abp.MultiTenancy; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
public class FeedbackAttachment : CreationAuditedEntity<Guid>, IMultiTenant |
||||
|
{ |
||||
|
public virtual Guid? TenantId { get; protected set; } |
||||
|
public virtual string Name { get; protected set; } |
||||
|
public virtual string Url { get; protected set; } |
||||
|
public virtual long Size { get; protected set; } |
||||
|
public virtual Guid FeedbackId { get; protected set; } |
||||
|
protected FeedbackAttachment() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
internal FeedbackAttachment( |
||||
|
Guid id, |
||||
|
Guid feedbackId, |
||||
|
string name, |
||||
|
string url, |
||||
|
long size, |
||||
|
Guid? tenantId) |
||||
|
:base(id) |
||||
|
{ |
||||
|
Name = Check.NotNullOrWhiteSpace(name, nameof(name), FeedbackAttachmentConsts.MaxNameLength); |
||||
|
Url = Check.NotNullOrWhiteSpace(url, nameof(url), FeedbackAttachmentConsts.MaxUrlLength); |
||||
|
Size = size; |
||||
|
FeedbackId = feedbackId; |
||||
|
TenantId = tenantId; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,114 @@ |
|||||
|
using System.IO; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.BlobStoring; |
||||
|
using Volo.Abp.Domain.Services; |
||||
|
using Volo.Abp.IO; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
public class FeedbackAttachmentManager : DomainService |
||||
|
{ |
||||
|
private const string DefaultSaveToApplication = "abp-application"; |
||||
|
private const string DefaultSaveToTempPath = "feedback-upload-tmp"; |
||||
|
|
||||
|
private readonly IBlobContainer<FeedbackContainer> _blobContainer; |
||||
|
private readonly IApplicationInfoAccessor _applicationInfoAccessor; |
||||
|
|
||||
|
public FeedbackAttachmentManager( |
||||
|
IBlobContainer<FeedbackContainer> blobContainer, |
||||
|
IApplicationInfoAccessor applicationInfoAccessor) |
||||
|
{ |
||||
|
_blobContainer = blobContainer; |
||||
|
_applicationInfoAccessor = applicationInfoAccessor; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 将文件流写入临时存储
|
||||
|
/// </summary>
|
||||
|
/// <param name="stream">文件流</param>
|
||||
|
/// <returns>返回临时存储的文件标识</returns>
|
||||
|
public async Task<FeedbackAttachmentTempFile> SaveToTempAsync(Stream stream) |
||||
|
{ |
||||
|
var blobSize = stream.Length; |
||||
|
var blobId = GuidGenerator.Create().ToString("N"); |
||||
|
var timeStampPath = Clock.Now.ToString("yyyy-MM-dd"); |
||||
|
|
||||
|
var tempFilePath = Path.Combine( |
||||
|
Path.GetTempPath(), |
||||
|
_applicationInfoAccessor.ApplicationName ?? DefaultSaveToApplication, |
||||
|
DefaultSaveToTempPath, |
||||
|
timeStampPath); |
||||
|
var tempSavedFile = Path.Combine(tempFilePath, $"{blobId}.png"); |
||||
|
|
||||
|
DirectoryHelper.CreateIfNotExists(tempFilePath); |
||||
|
|
||||
|
using (var fs = new FileStream(tempSavedFile, FileMode.Create, FileAccess.Write)) |
||||
|
{ |
||||
|
await stream.CopyToAsync(fs); |
||||
|
} |
||||
|
|
||||
|
return new FeedbackAttachmentTempFile |
||||
|
{ |
||||
|
Path = timeStampPath, |
||||
|
Size = blobSize, |
||||
|
Id = blobId, |
||||
|
}; |
||||
|
} |
||||
|
/// <summary>
|
||||
|
/// 从临时存储附件拷贝到blob存储
|
||||
|
/// </summary>
|
||||
|
/// <param name="feedback">用户反馈实体</param>
|
||||
|
/// <param name="file">附件临时存储文件</param>
|
||||
|
/// <returns>已保存的附件</returns>
|
||||
|
public async Task<FeedbackAttachmentFile> CopyFromTempAsync(Feedback feedback, string filePtah, string fileId) |
||||
|
{ |
||||
|
var fileName = $"{filePtah}/{fileId}.png"; |
||||
|
var tempFilePath = Path.Combine( |
||||
|
Path.GetTempPath(), |
||||
|
_applicationInfoAccessor.ApplicationName ?? DefaultSaveToApplication, |
||||
|
DefaultSaveToTempPath); |
||||
|
var tempFromFile = Path.Combine(tempFilePath, fileName); |
||||
|
var blobFile = $"{fileId}.png"; |
||||
|
var saveToBlobFile = $"{feedback.Id}/{blobFile}"; |
||||
|
|
||||
|
if (!File.Exists(tempFromFile)) |
||||
|
{ |
||||
|
throw new BusinessException(PlatformErrorCodes.FeedackAttachmentLoseEffectiveness) |
||||
|
.WithData("Name", fileId); |
||||
|
} |
||||
|
FeedbackAttachmentFile attachmentFile; |
||||
|
|
||||
|
using (var fs = new FileStream(tempFromFile, FileMode.Open, FileAccess.Read)) |
||||
|
{ |
||||
|
await _blobContainer.SaveAsync(saveToBlobFile, fs, true); |
||||
|
|
||||
|
attachmentFile = new FeedbackAttachmentFile(blobFile, fs.Length); |
||||
|
} |
||||
|
|
||||
|
FileHelper.DeleteIfExists(tempFromFile); |
||||
|
|
||||
|
return attachmentFile; |
||||
|
} |
||||
|
/// <summary>
|
||||
|
/// 下载附件
|
||||
|
/// </summary>
|
||||
|
/// <param name="attachment">附件实体</param>
|
||||
|
/// <returns>附件文件流</returns>
|
||||
|
public async Task<Stream> DownloadAsync(FeedbackAttachment attachment) |
||||
|
{ |
||||
|
var blobFile = $"{attachment.FeedbackId}/{attachment.Name}"; |
||||
|
|
||||
|
return await _blobContainer.GetAsync(blobFile); |
||||
|
} |
||||
|
/// <summary>
|
||||
|
/// 删除附件
|
||||
|
/// </summary>
|
||||
|
/// <param name="attachment">附件实体</param>
|
||||
|
/// <returns>附件文件流</returns>
|
||||
|
public async Task DeleteAsync(FeedbackAttachment attachment) |
||||
|
{ |
||||
|
var blobFile = $"{attachment.FeedbackId}/{attachment.Name}"; |
||||
|
|
||||
|
await _blobContainer.DeleteAsync(blobFile); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
using Volo.Abp; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
public class FeedbackAttachmentNotFoundException : BusinessException |
||||
|
{ |
||||
|
public string Name { get; protected set; } |
||||
|
public FeedbackAttachmentNotFoundException(string name) |
||||
|
: base( |
||||
|
PlatformErrorCodes.FeedackAttachmentNotFound, |
||||
|
$"User feedback: Attachment named {name} not found!") |
||||
|
{ |
||||
|
Name = name; |
||||
|
|
||||
|
WithData("Name", name); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
using System; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.Domain.Entities.Auditing; |
||||
|
using Volo.Abp.MultiTenancy; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
/// <summary>
|
||||
|
/// 评论
|
||||
|
/// </summary>
|
||||
|
public class FeedbackComment : AuditedEntity<Guid>, IMultiTenant |
||||
|
{ |
||||
|
public virtual Guid? TenantId { get; protected set; } |
||||
|
public virtual string Capacity { get; protected set; } |
||||
|
public virtual string Content { get; set; } |
||||
|
public virtual Guid FeedbackId { get; protected set; } |
||||
|
protected FeedbackComment() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
internal FeedbackComment( |
||||
|
Guid id, |
||||
|
Guid feedbackId, |
||||
|
string capacity, |
||||
|
string content, |
||||
|
Guid? tenantId = null) |
||||
|
: base(id) |
||||
|
{ |
||||
|
Capacity = Check.NotNullOrWhiteSpace(capacity, nameof(capacity), FeedbackCommentConsts.MaxCapacityLength); |
||||
|
Content = Check.NotNullOrWhiteSpace(content, nameof(content), FeedbackCommentConsts.MaxContentLength); |
||||
|
FeedbackId = feedbackId; |
||||
|
TenantId = tenantId; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
using Volo.Abp.BlobStoring; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
|
||||
|
[BlobContainerName("feedbacks")] |
||||
|
public class FeedbackContainer |
||||
|
{ |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
using LINGYUN.Platform.Localization; |
||||
|
using Microsoft.Extensions.Localization; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.ExceptionHandling; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
public class FeedbackStatusException : BusinessException, ILocalizeErrorMessage |
||||
|
{ |
||||
|
public FeedbackStatus Status { get; protected set; } |
||||
|
public FeedbackStatusException(string code, FeedbackStatus status) |
||||
|
: base(code, $"Unable to comment on issues in {status} status") |
||||
|
{ |
||||
|
Status = status; |
||||
|
|
||||
|
WithData("Status", status.ToString()); |
||||
|
} |
||||
|
|
||||
|
public string LocalizeMessage(LocalizationContext context) |
||||
|
{ |
||||
|
var localizer = context.LocalizerFactory.Create<PlatformResource>(); |
||||
|
|
||||
|
return localizer[PlatformErrorCodes.UnableFeedbackCommentInStatus, localizer[$"FeedbackStatus:{Status}"]]; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Domain.Repositories; |
||||
|
using Volo.Abp.Specifications; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
public interface IFeedbackRepository : IBasicRepository<Feedback, Guid> |
||||
|
{ |
||||
|
Task<int> GetCountAsync( |
||||
|
ISpecification<Feedback> specification, |
||||
|
CancellationToken cancellationToken = default); |
||||
|
|
||||
|
Task<List<Feedback>> GetListAsync( |
||||
|
ISpecification<Feedback> specification, |
||||
|
string sorting = $"{nameof(Feedback.CreationTime)} DESC", |
||||
|
int maxResultCount = 25, |
||||
|
int skipCount = 0, |
||||
|
CancellationToken cancellationToken = default); |
||||
|
} |
||||
@ -0,0 +1,51 @@ |
|||||
|
using LINGYUN.Platform.EntityFrameworkCore; |
||||
|
using Microsoft.EntityFrameworkCore; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Linq.Dynamic.Core; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; |
||||
|
using Volo.Abp.EntityFrameworkCore; |
||||
|
using Volo.Abp.Specifications; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
public class EfCoreFeedbackRepository : EfCoreRepository<IPlatformDbContext, Feedback, Guid>, IFeedbackRepository |
||||
|
{ |
||||
|
public EfCoreFeedbackRepository( |
||||
|
IDbContextProvider<IPlatformDbContext> dbContextProvider) |
||||
|
: base(dbContextProvider) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public async virtual Task<int> GetCountAsync( |
||||
|
ISpecification<Feedback> specification, |
||||
|
CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
return await (await GetQueryableAsync()) |
||||
|
.Where(specification.ToExpression()) |
||||
|
.CountAsync(GetCancellationToken(cancellationToken)); |
||||
|
} |
||||
|
|
||||
|
public async virtual Task<List<Feedback>> GetListAsync( |
||||
|
ISpecification<Feedback> specification, |
||||
|
string sorting = $"{nameof(Feedback.CreationTime)} DESC", |
||||
|
int maxResultCount = 25, |
||||
|
int skipCount = 0, |
||||
|
CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
return await (await GetQueryableAsync()) |
||||
|
.Where(specification.ToExpression()) |
||||
|
.OrderBy(sorting.IsNullOrWhiteSpace() ? $"{nameof(Feedback.CreationTime)} DESC" : sorting) |
||||
|
.PageBy(skipCount, maxResultCount) |
||||
|
.ToListAsync(GetCancellationToken(cancellationToken)); |
||||
|
} |
||||
|
|
||||
|
public async override Task<IQueryable<Feedback>> WithDetailsAsync() |
||||
|
{ |
||||
|
return (await base.WithDetailsAsync()) |
||||
|
.Include(x => x.Comments) |
||||
|
.Include(x => x.Attachments); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,43 @@ |
|||||
|
using LINGYUN.Platform.Permissions; |
||||
|
using Microsoft.AspNetCore.Authorization; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.AspNetCore.Mvc; |
||||
|
using Volo.Abp.Content; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
|
||||
|
[Area(PlatformRemoteServiceConsts.ModuleName)] |
||||
|
[RemoteService(Name = PlatformRemoteServiceConsts.RemoteServiceName)] |
||||
|
[Route($"api/{PlatformRemoteServiceConsts.ModuleName}/feedbacks")] |
||||
|
[Authorize(PlatformPermissions.Feedback.Default)] |
||||
|
public class FeedbackAttachmentController : AbpControllerBase, IFeedbackAttachmentAppService |
||||
|
{ |
||||
|
private readonly IFeedbackAttachmentAppService _service; |
||||
|
public FeedbackAttachmentController(IFeedbackAttachmentAppService service) |
||||
|
{ |
||||
|
_service = service; |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
[Route("{FeedbackId}/attachments/{Name}")] |
||||
|
public virtual Task<IRemoteStreamContent> GetAsync(FeedbackAttachmentGetInput input) |
||||
|
{ |
||||
|
return _service.GetAsync(input); |
||||
|
} |
||||
|
|
||||
|
[HttpPost] |
||||
|
[Route("attachments/upload")] |
||||
|
public virtual Task<FeedbackAttachmentTempFileDto> UploadAsync([FromForm] FeedbackAttachmentUploadInput input) |
||||
|
{ |
||||
|
return _service.UploadAsync(input); |
||||
|
} |
||||
|
|
||||
|
[HttpDelete] |
||||
|
[Route("{FeedbackId}/attachments/{Name}")] |
||||
|
public virtual Task DeleteAsync(FeedbackAttachmentGetInput input) |
||||
|
{ |
||||
|
return _service.DeleteAsync(input); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,51 @@ |
|||||
|
using LINGYUN.Platform.Permissions; |
||||
|
using Microsoft.AspNetCore.Authorization; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
using Volo.Abp.AspNetCore.Mvc; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
|
||||
|
[Area(PlatformRemoteServiceConsts.ModuleName)] |
||||
|
[RemoteService(Name = PlatformRemoteServiceConsts.RemoteServiceName)] |
||||
|
[Route($"api/{PlatformRemoteServiceConsts.ModuleName}/feedbacks")] |
||||
|
[Authorize(PlatformPermissions.Feedback.Default)] |
||||
|
public class FeedbackController : AbpControllerBase, IFeedbackAppService |
||||
|
{ |
||||
|
private readonly IFeedbackAppService _service; |
||||
|
public FeedbackController(IFeedbackAppService service) |
||||
|
{ |
||||
|
_service = service; |
||||
|
} |
||||
|
|
||||
|
[HttpPost] |
||||
|
[Authorize(PlatformPermissions.Feedback.Create)] |
||||
|
public virtual Task<FeedbackDto> CreateAsync(FeedbackCreateDto input) |
||||
|
{ |
||||
|
return _service.CreateAsync(input); |
||||
|
} |
||||
|
|
||||
|
[HttpDelete] |
||||
|
[Route("{id}")] |
||||
|
[Authorize(PlatformPermissions.Feedback.Delete)] |
||||
|
public virtual Task DeleteAsync(Guid id) |
||||
|
{ |
||||
|
return _service.DeleteAsync(id); |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
[Route("{id}")] |
||||
|
public virtual Task<FeedbackDto> GetAsync(Guid id) |
||||
|
{ |
||||
|
return _service.GetAsync(id); |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
public virtual Task<PagedResultDto<FeedbackDto>> GetListAsync(FeedbackGetListInput input) |
||||
|
{ |
||||
|
return _service.GetListAsync(input); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,27 @@ |
|||||
|
using Microsoft.AspNetCore.Authorization; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
using Volo.Abp.AspNetCore.Mvc; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Feedbacks; |
||||
|
|
||||
|
[Authorize] |
||||
|
[Area(PlatformRemoteServiceConsts.ModuleName)] |
||||
|
[RemoteService(Name = PlatformRemoteServiceConsts.RemoteServiceName)] |
||||
|
[Route($"api/{PlatformRemoteServiceConsts.ModuleName}/my-feedbacks")] |
||||
|
public class MyFeedbackController : AbpControllerBase, IMyFeedbackAppService |
||||
|
{ |
||||
|
private readonly IMyFeedbackAppService _service; |
||||
|
public MyFeedbackController(IMyFeedbackAppService service) |
||||
|
{ |
||||
|
_service = service; |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
public virtual Task<PagedResultDto<FeedbackDto>> GetMyFeedbacksAsync(FeedbackGetListInput input) |
||||
|
{ |
||||
|
return _service.GetMyFeedbacksAsync(input); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue