重构作业结构:优化实体模型、DTO映射和前端界面
Some checks failed
TechAct / explore-gitea-actions (push) Failing after 13s
Some checks failed
TechAct / explore-gitea-actions (push) Failing after 13s
- 重构AppMainStruct、AssignmentQuestion、Question等实体模型 - 更新相关DTO以匹配新的数据结构 - 优化前端页面布局和组件 - 添加全局信息和笔记功能相关代码 - 更新数据库迁移和程序配置
This commit is contained in:
@@ -21,6 +21,7 @@ namespace TechHelper.Context
|
||||
public DbSet<Submission> Submissions { get; set; }
|
||||
public DbSet<SubmissionDetail> SubmissionDetails { get; set; }
|
||||
public DbSet<QuestionContext> QuestionContexts { get; set; }
|
||||
public DbSet<Global> Globals { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
|
@@ -2,6 +2,10 @@
|
||||
using AutoMapper.Internal.Mappers;
|
||||
using Entities.Contracts;
|
||||
using Entities.DTO;
|
||||
using Newtonsoft.Json;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace TechHelper.Context
|
||||
{
|
||||
@@ -23,13 +27,13 @@ namespace TechHelper.Context
|
||||
public AutoMapperProFile()
|
||||
{
|
||||
CreateMap<UserForRegistrationDto, User>()
|
||||
.ForMember(dest => dest.Id, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.UserName, opt => opt.MapFrom(src => src.Name))
|
||||
.ForMember(dest => dest.Id, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.UserName, opt => opt.MapFrom(src => src.Name))
|
||||
.ForMember(dest => dest.Email, opt => opt.MapFrom(src => src.Email))
|
||||
.ForMember(dest => dest.PhoneNumber, opt => opt.MapFrom(src => src.PhoneNumber))
|
||||
.ForMember(dest => dest.Address, opt => opt.MapFrom(src => src.HomeAddress))
|
||||
.ForMember(dest => dest.PasswordHash, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.EmailConfirmed, opt => opt.Ignore());
|
||||
.ForMember(dest => dest.Address, opt => opt.MapFrom(src => src.HomeAddress))
|
||||
.ForMember(dest => dest.PasswordHash, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.EmailConfirmed, opt => opt.Ignore());
|
||||
|
||||
CreateMap<ClassDto, Class>()
|
||||
.ForMember(d => d.Number, o => o.MapFrom(src => src.Class))
|
||||
@@ -44,7 +48,7 @@ namespace TechHelper.Context
|
||||
|
||||
CreateMap<QuestionDto, Question>().ReverseMap();
|
||||
|
||||
CreateMap<QuestionContext, QuestionContextDto>().ReverseMap();
|
||||
CreateMap<QuestionContext, QuestionContextDto>().ReverseMap();
|
||||
|
||||
|
||||
|
||||
@@ -52,7 +56,13 @@ namespace TechHelper.Context
|
||||
// Submission
|
||||
CreateMap<SubmissionDto, Submission>().ReverseMap();
|
||||
|
||||
CreateMap<SubmissionDetailDto, SubmissionDetail>().ReverseMap();
|
||||
CreateMap<SubmissionDetailDto, SubmissionDetail>().ReverseMap();
|
||||
|
||||
|
||||
CreateMap<SubjectTypeMetadataDto, Global>()
|
||||
.ForMember(dest => dest.Info, opt => opt.MapFrom(src => JsonConvert.SerializeObject(src.Data)));
|
||||
CreateMap<Global, SubjectTypeMetadataDto>()
|
||||
.ForMember(dest => dest.Data, opt => opt.MapFrom(src => JsonConvert.DeserializeObject<Dictionary<string, (string Color, string DisplayName)>>(src.Info)));
|
||||
|
||||
}
|
||||
}
|
||||
|
91
TechHelper.Server/Controllers/NoteController.cs
Normal file
91
TechHelper.Server/Controllers/NoteController.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using Entities.DTO;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using TechHelper.Services;
|
||||
|
||||
namespace TechHelper.Server.Controllers
|
||||
{
|
||||
[Route("api/note")]
|
||||
[ApiController]
|
||||
public class NoteController : ControllerBase
|
||||
{
|
||||
private readonly INoteService _noteService;
|
||||
|
||||
// 通过依赖注入获取 NoteService
|
||||
public NoteController(INoteService noteService)
|
||||
{
|
||||
_noteService = noteService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有全局数据。
|
||||
/// GET: api/Note
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> GetAll([FromQuery] QueryParameter query)
|
||||
{
|
||||
var response = await _noteService.GetAllAsync(query);
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据 ID 获取单个全局数据。
|
||||
/// GET: api/Note/{id}
|
||||
/// </summary>
|
||||
[HttpGet("{id}")]
|
||||
public async Task<IActionResult> Get(byte id)
|
||||
{
|
||||
var response = await _noteService.GetAsync(id);
|
||||
if (!response.Status)
|
||||
{
|
||||
return NotFound(response);
|
||||
}
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加新的全局数据。
|
||||
/// POST: api/Note
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Add([FromBody] GlobalDto model)
|
||||
{
|
||||
var response = await _noteService.AddAsync(model);
|
||||
if (!response.Status)
|
||||
{
|
||||
return BadRequest(response);
|
||||
}
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新已存在的全局数据。
|
||||
/// PUT: api/Note
|
||||
/// </summary>
|
||||
[HttpPut]
|
||||
public async Task<IActionResult> Update([FromBody] GlobalDto model)
|
||||
{
|
||||
var response = await _noteService.UpdateAsync(model);
|
||||
if (!response.Status)
|
||||
{
|
||||
return NotFound(response);
|
||||
}
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据 ID 删除全局数据。
|
||||
/// DELETE: api/Note/{id}
|
||||
/// </summary>
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> Delete(byte id)
|
||||
{
|
||||
var response = await _noteService.DeleteAsync(id);
|
||||
if (!response.Status)
|
||||
{
|
||||
return NotFound(response);
|
||||
}
|
||||
return Ok(response);
|
||||
}
|
||||
}
|
||||
}
|
1277
TechHelper.Server/Migrations/20250901072725_question_qt_update.Designer.cs
generated
Normal file
1277
TechHelper.Server/Migrations/20250901072725_question_qt_update.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,93 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional
|
||||
|
||||
namespace TechHelper.Server.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class question_qt_update : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DeleteData(
|
||||
table: "AspNetRoles",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("0775702a-5db7-4747-94d0-4376fad2b58b"));
|
||||
|
||||
migrationBuilder.DeleteData(
|
||||
table: "AspNetRoles",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("37f41430-0cb7-44e5-988b-976200bd602d"));
|
||||
|
||||
migrationBuilder.DeleteData(
|
||||
table: "AspNetRoles",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("df89b9a0-65ef-42dd-b2cb-e59997a72e70"));
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "QType",
|
||||
table: "questions",
|
||||
type: "longtext",
|
||||
nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AddColumn<byte>(
|
||||
name: "Type",
|
||||
table: "assignment_questions",
|
||||
type: "tinyint unsigned",
|
||||
nullable: false,
|
||||
defaultValue: (byte)0);
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
table: "AspNetRoles",
|
||||
columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" },
|
||||
values: new object[,]
|
||||
{
|
||||
{ new Guid("67de6514-79a5-4a9c-b54c-13cac296b0c6"), null, "Teacher", "TEACHER" },
|
||||
{ new Guid("94f0d8d9-ffba-4e28-b578-8596363d42ae"), null, "Student", "STUDENT" },
|
||||
{ new Guid("bf46ed67-2dc9-40f8-8717-37dd3572f274"), null, "Administrator", "ADMINISTRATOR" }
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DeleteData(
|
||||
table: "AspNetRoles",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("67de6514-79a5-4a9c-b54c-13cac296b0c6"));
|
||||
|
||||
migrationBuilder.DeleteData(
|
||||
table: "AspNetRoles",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("94f0d8d9-ffba-4e28-b578-8596363d42ae"));
|
||||
|
||||
migrationBuilder.DeleteData(
|
||||
table: "AspNetRoles",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("bf46ed67-2dc9-40f8-8717-37dd3572f274"));
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "QType",
|
||||
table: "questions");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Type",
|
||||
table: "assignment_questions");
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
table: "AspNetRoles",
|
||||
columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" },
|
||||
values: new object[,]
|
||||
{
|
||||
{ new Guid("0775702a-5db7-4747-94d0-4376fad2b58b"), null, "Teacher", "TEACHER" },
|
||||
{ new Guid("37f41430-0cb7-44e5-988b-976200bd602d"), null, "Administrator", "ADMINISTRATOR" },
|
||||
{ new Guid("df89b9a0-65ef-42dd-b2cb-e59997a72e70"), null, "Student", "STUDENT" }
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
1296
TechHelper.Server/Migrations/20250901080732_question_qt_update_2.Designer.cs
generated
Normal file
1296
TechHelper.Server/Migrations/20250901080732_question_qt_update_2.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional
|
||||
|
||||
namespace TechHelper.Server.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class question_qt_update_2 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DeleteData(
|
||||
table: "AspNetRoles",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("67de6514-79a5-4a9c-b54c-13cac296b0c6"));
|
||||
|
||||
migrationBuilder.DeleteData(
|
||||
table: "AspNetRoles",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("94f0d8d9-ffba-4e28-b578-8596363d42ae"));
|
||||
|
||||
migrationBuilder.DeleteData(
|
||||
table: "AspNetRoles",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("bf46ed67-2dc9-40f8-8717-37dd3572f274"));
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "global",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||
Area = table.Column<byte>(type: "tinyint unsigned", nullable: false),
|
||||
Info = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_global", x => x.id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
table: "AspNetRoles",
|
||||
columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" },
|
||||
values: new object[,]
|
||||
{
|
||||
{ new Guid("49854839-b861-4d42-bdbe-96b1a66c25ef"), null, "Teacher", "TEACHER" },
|
||||
{ new Guid("5c7a7971-2610-4bce-9e41-0caffd5a5558"), null, "Student", "STUDENT" },
|
||||
{ new Guid("83ff7de8-edc9-47f8-8de8-22f892ca6bb5"), null, "Administrator", "ADMINISTRATOR" }
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "global");
|
||||
|
||||
migrationBuilder.DeleteData(
|
||||
table: "AspNetRoles",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("49854839-b861-4d42-bdbe-96b1a66c25ef"));
|
||||
|
||||
migrationBuilder.DeleteData(
|
||||
table: "AspNetRoles",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("5c7a7971-2610-4bce-9e41-0caffd5a5558"));
|
||||
|
||||
migrationBuilder.DeleteData(
|
||||
table: "AspNetRoles",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("83ff7de8-edc9-47f8-8de8-22f892ca6bb5"));
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
table: "AspNetRoles",
|
||||
columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" },
|
||||
values: new object[,]
|
||||
{
|
||||
{ new Guid("67de6514-79a5-4a9c-b54c-13cac296b0c6"), null, "Teacher", "TEACHER" },
|
||||
{ new Guid("94f0d8d9-ffba-4e28-b578-8596363d42ae"), null, "Student", "STUDENT" },
|
||||
{ new Guid("bf46ed67-2dc9-40f8-8717-37dd3572f274"), null, "Administrator", "ADMINISTRATOR" }
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
1299
TechHelper.Server/Migrations/20250901083708_question_qt_update_3.Designer.cs
generated
Normal file
1299
TechHelper.Server/Migrations/20250901083708_question_qt_update_3.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional
|
||||
|
||||
namespace TechHelper.Server.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class question_qt_update_3 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DeleteData(
|
||||
table: "AspNetRoles",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("49854839-b861-4d42-bdbe-96b1a66c25ef"));
|
||||
|
||||
migrationBuilder.DeleteData(
|
||||
table: "AspNetRoles",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("5c7a7971-2610-4bce-9e41-0caffd5a5558"));
|
||||
|
||||
migrationBuilder.DeleteData(
|
||||
table: "AspNetRoles",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("83ff7de8-edc9-47f8-8de8-22f892ca6bb5"));
|
||||
|
||||
migrationBuilder.AddColumn<byte>(
|
||||
name: "SubjectArea",
|
||||
table: "AspNetUsers",
|
||||
type: "tinyint unsigned",
|
||||
nullable: false,
|
||||
defaultValue: (byte)0);
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
table: "AspNetRoles",
|
||||
columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" },
|
||||
values: new object[,]
|
||||
{
|
||||
{ new Guid("2670f35a-df0c-4071-8879-80eb99d138a1"), null, "Teacher", "TEACHER" },
|
||||
{ new Guid("8c6c5e8e-ef00-444c-9c7c-cba5cd6f7043"), null, "Student", "STUDENT" },
|
||||
{ new Guid("9eda9d90-0cd2-4fbe-b07e-f90bd01f32db"), null, "Administrator", "ADMINISTRATOR" }
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DeleteData(
|
||||
table: "AspNetRoles",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("2670f35a-df0c-4071-8879-80eb99d138a1"));
|
||||
|
||||
migrationBuilder.DeleteData(
|
||||
table: "AspNetRoles",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("8c6c5e8e-ef00-444c-9c7c-cba5cd6f7043"));
|
||||
|
||||
migrationBuilder.DeleteData(
|
||||
table: "AspNetRoles",
|
||||
keyColumn: "Id",
|
||||
keyValue: new Guid("9eda9d90-0cd2-4fbe-b07e-f90bd01f32db"));
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SubjectArea",
|
||||
table: "AspNetUsers");
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
table: "AspNetRoles",
|
||||
columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" },
|
||||
values: new object[,]
|
||||
{
|
||||
{ new Guid("49854839-b861-4d42-bdbe-96b1a66c25ef"), null, "Teacher", "TEACHER" },
|
||||
{ new Guid("5c7a7971-2610-4bce-9e41-0caffd5a5558"), null, "Student", "STUDENT" },
|
||||
{ new Guid("83ff7de8-edc9-47f8-8de8-22f892ca6bb5"), null, "Administrator", "ADMINISTRATOR" }
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@@ -219,6 +219,9 @@ namespace TechHelper.Server.Migrations
|
||||
.HasColumnType("varchar(1024)")
|
||||
.HasColumnName("title");
|
||||
|
||||
b.Property<byte>("Type")
|
||||
.HasColumnType("tinyint unsigned");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AssignmentId");
|
||||
@@ -332,6 +335,25 @@ namespace TechHelper.Server.Migrations
|
||||
b.ToTable("class_teachers", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Entities.Contracts.Global", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<byte>("Area")
|
||||
.HasColumnType("tinyint unsigned");
|
||||
|
||||
b.Property<string>("Info")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("global");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Entities.Contracts.KeyPoint", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
@@ -445,6 +467,10 @@ namespace TechHelper.Server.Migrations
|
||||
.HasColumnType("longtext")
|
||||
.HasColumnName("options");
|
||||
|
||||
b.Property<string>("QType")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<byte>("SubjectArea")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("tinyint unsigned")
|
||||
@@ -723,6 +749,9 @@ namespace TechHelper.Server.Migrations
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<byte>("SubjectArea")
|
||||
.HasColumnType("tinyint unsigned");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
@@ -771,19 +800,19 @@ namespace TechHelper.Server.Migrations
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = new Guid("df89b9a0-65ef-42dd-b2cb-e59997a72e70"),
|
||||
Id = new Guid("8c6c5e8e-ef00-444c-9c7c-cba5cd6f7043"),
|
||||
Name = "Student",
|
||||
NormalizedName = "STUDENT"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("0775702a-5db7-4747-94d0-4376fad2b58b"),
|
||||
Id = new Guid("2670f35a-df0c-4071-8879-80eb99d138a1"),
|
||||
Name = "Teacher",
|
||||
NormalizedName = "TEACHER"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("37f41430-0cb7-44e5-988b-976200bd602d"),
|
||||
Id = new Guid("9eda9d90-0cd2-4fbe-b07e-f90bd01f32db"),
|
||||
Name = "Administrator",
|
||||
NormalizedName = "ADMINISTRATOR"
|
||||
});
|
||||
|
@@ -17,9 +17,9 @@ using Microsoft.OpenApi.Models;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddControllers(); // <EFBFBD><EFBFBD><EFBFBD><EFBFBD> MVC <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> (<28><><EFBFBD><EFBFBD> API)
|
||||
builder.Services.AddControllers(); // 添加 MVC 控制器服务 (用于 API)
|
||||
|
||||
// 2. <EFBFBD><EFBFBD><EFBFBD>ݿ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> (DbContext)
|
||||
// 2. 数据库服务 (DbContext)
|
||||
builder.Services.AddDbContext<ApplicationContext>(options =>
|
||||
options.UseMySql(
|
||||
builder.Configuration.GetConnectionString("XSDB"),
|
||||
@@ -34,17 +34,18 @@ builder.Services.AddDbContext<ApplicationContext>(options =>
|
||||
.AddCustomRepository<ClassTeacher, ClassTeacherRepository>()
|
||||
.AddCustomRepository<Question, QuestionRepository>()
|
||||
.AddCustomRepository<QuestionContext, QuestionContextRepository>()
|
||||
.AddCustomRepository<Submission, SubmissionRepository>();
|
||||
.AddCustomRepository<Submission, SubmissionRepository>()
|
||||
.AddCustomRepository<Global, GlobalRepository>();
|
||||
|
||||
builder.Services.AddAutoMapper(typeof(AutoMapperProFile).Assembly);
|
||||
|
||||
// 3. <EFBFBD><EFBFBD><EFBFBD>÷<EFBFBD><EFBFBD><EFBFBD> (IOptions)
|
||||
// 3. 配置服务 (IOptions)
|
||||
builder.Services.Configure<ApiConfiguration>(builder.Configuration.GetSection("ApiConfiguration"));
|
||||
builder.Services.Configure<JwtConfiguration>(builder.Configuration.GetSection("JWTSettings"));
|
||||
|
||||
|
||||
// 4. <EFBFBD><EFBFBD>֤<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> (Identity, JWT, <EFBFBD>Զ<EFBFBD><EFBFBD><EFBFBD> Auth)
|
||||
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD> ASP.NET Core Identity (<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĭ<EFBFBD>ϵ<EFBFBD> Cookie <20><>֤<EFBFBD><D6A4><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD>)
|
||||
// 4. 认证和授权服务 (Identity, JWT, 自定义 Auth)
|
||||
// 添加 ASP.NET Core Identity (包含默认的 Cookie 认证和授权服务)
|
||||
builder.Services.AddIdentity<User, IdentityRole<Guid>>(opt =>
|
||||
{
|
||||
opt.User.AllowedUserNameCharacters = "";
|
||||
@@ -60,25 +61,25 @@ builder.Services.Configure<DataProtectionTokenProviderOptions>(Options =>
|
||||
});
|
||||
|
||||
|
||||
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD> JWT Bearer <EFBFBD><EFBFBD>֤<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
// 添加 JWT Bearer 认证方案
|
||||
var jwtSettings = builder.Configuration.GetSection("JWTSettings");
|
||||
builder.Services.AddAuthentication(options =>
|
||||
{
|
||||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; // <EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĭ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ JWT Bearer
|
||||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; // <EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĭ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ս<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ JWT Bearer
|
||||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; // 设置默认认证方案为 JWT Bearer
|
||||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; // 设置默认挑战方案为 JWT Bearer
|
||||
})
|
||||
.AddJwtBearer(options =>
|
||||
{
|
||||
options.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuer = true, // <EFBFBD><EFBFBD>֤ǩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
ValidateAudience = true, // <EFBFBD><EFBFBD>֤<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
ValidateLifetime = true, // <EFBFBD><EFBFBD>֤<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ч<EFBFBD><EFBFBD>
|
||||
ValidateIssuerSigningKey = true, // <EFBFBD><EFBFBD>֤ǩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Կ
|
||||
ValidateIssuer = true, // 验证签发人
|
||||
ValidateAudience = true, // 验证受众
|
||||
ValidateLifetime = true, // 验证令牌有效期
|
||||
ValidateIssuerSigningKey = true, // 验证签名密钥
|
||||
|
||||
ValidIssuer = jwtSettings["validIssuer"], // <EFBFBD>Ϸ<EFBFBD><EFBFBD><EFBFBD>ǩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
ValidAudience = jwtSettings["validAudience"], // <EFBFBD>Ϸ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings["securityKey"])) // ǩ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Կ
|
||||
ValidIssuer = jwtSettings["validIssuer"], // 合法的签发人
|
||||
ValidAudience = jwtSettings["validAudience"], // 合法的受众
|
||||
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings["securityKey"])) // 签名密钥
|
||||
};
|
||||
});
|
||||
|
||||
@@ -90,6 +91,7 @@ builder.Services.AddScoped<IExamService, ExamService>();
|
||||
builder.Services.AddScoped<IUserSerivces, UserServices>();
|
||||
builder.Services.AddScoped<ISubmissionServices, SubmissionServices>();
|
||||
builder.Services.AddScoped<IExamRepository, ExamRepository>();
|
||||
builder.Services.AddScoped<INoteService, NoteService>();
|
||||
|
||||
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
@@ -128,7 +130,7 @@ builder.Services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy("AllowSpecificOrigin",
|
||||
builder => builder
|
||||
.WithOrigins("https://localhost:7047", "http://localhost:7047")
|
||||
.WithOrigins("https://localhost:7047", "http://localhost:7047", "https://localhost:5001", "http://localhost:5001")
|
||||
.AllowAnyHeader()
|
||||
.AllowAnyMethod()
|
||||
.AllowCredentials());
|
||||
|
17
TechHelper.Server/Repository/GlobalRepository.cs
Normal file
17
TechHelper.Server/Repository/GlobalRepository.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using Entities.Contracts;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SharedDATA.Api;
|
||||
using TechHelper.Context;
|
||||
|
||||
namespace TechHelper.Repository
|
||||
{
|
||||
public class GlobalRepository : Repository<Global>, IRepository<Global>
|
||||
{
|
||||
public GlobalRepository(ApplicationContext dbContext) : base(dbContext)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
13
TechHelper.Server/Services/INoteService.cs
Normal file
13
TechHelper.Server/Services/INoteService.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using Entities.Contracts;
|
||||
using Entities.DTO;
|
||||
using System.Net;
|
||||
|
||||
namespace TechHelper.Services
|
||||
{
|
||||
|
||||
|
||||
public interface INoteService : IBaseService<GlobalDto, byte>
|
||||
{
|
||||
|
||||
}
|
||||
}
|
107
TechHelper.Server/Services/NoteService.cs
Normal file
107
TechHelper.Server/Services/NoteService.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using AutoMapper;
|
||||
using Entities.Contracts;
|
||||
using Entities.DTO;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using SharedDATA.Api;
|
||||
using System.Net;
|
||||
|
||||
namespace TechHelper.Services
|
||||
{
|
||||
public class NoteService : INoteService
|
||||
{
|
||||
private readonly IUnitOfWork _work;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
public NoteService(IUnitOfWork work)
|
||||
{
|
||||
_work = work;
|
||||
}
|
||||
|
||||
public async Task<ApiResponse> AddAsync(GlobalDto model)
|
||||
{
|
||||
try
|
||||
{
|
||||
var globalEntity = new Global
|
||||
{
|
||||
Area = model.SubjectArea,
|
||||
Info = model.Data
|
||||
};
|
||||
|
||||
await _work.GetRepository<Global>().InsertAsync(globalEntity);
|
||||
await _work.SaveChangesAsync();
|
||||
|
||||
return ApiResponse.Success("数据已成功添加。");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return ApiResponse.Error($"添加数据时发生错误: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ApiResponse> DeleteAsync(byte id)
|
||||
{
|
||||
var globalRepo = _work.GetRepository<Global>();
|
||||
var globalEntity = await globalRepo.GetFirstOrDefaultAsync(predicate: x => x.Area == (SubjectAreaEnum)id);
|
||||
|
||||
if (globalEntity == null)
|
||||
{
|
||||
return ApiResponse.Error("未找到要删除的数据。");
|
||||
}
|
||||
|
||||
globalRepo.Delete(globalEntity);
|
||||
await _work.SaveChangesAsync();
|
||||
|
||||
return ApiResponse.Success("数据已成功删除。");
|
||||
}
|
||||
|
||||
public async Task<ApiResponse> GetAllAsync(QueryParameter query)
|
||||
{
|
||||
var repository = _work.GetRepository<Global>();
|
||||
// 获取所有实体,并将其 Info 属性作为结果返回
|
||||
var entities = await repository.GetAllAsync();
|
||||
|
||||
// 直接返回字符串列表
|
||||
var resultData = entities.Select(e => e.Info).ToList();
|
||||
|
||||
return ApiResponse.Success("数据已成功检索。", resultData);
|
||||
}
|
||||
|
||||
public async Task<ApiResponse> GetAsync(byte id)
|
||||
{
|
||||
var globalEntity = await _work.GetRepository<Global>().GetFirstOrDefaultAsync(predicate: x => x.Area == (SubjectAreaEnum)id);
|
||||
|
||||
if (globalEntity == null)
|
||||
{
|
||||
return ApiResponse.Error("未找到数据。");
|
||||
}
|
||||
|
||||
// 直接返回 Info 字符串
|
||||
return ApiResponse.Success("数据已成功检索。", globalEntity.Info);
|
||||
}
|
||||
|
||||
public async Task<ApiResponse> UpdateAsync(GlobalDto model)
|
||||
{
|
||||
try
|
||||
{
|
||||
var repository = _work.GetRepository<Global>();
|
||||
var existingEntity = await repository.GetFirstOrDefaultAsync(predicate: x => x.Area == (SubjectAreaEnum)model.SubjectArea);
|
||||
|
||||
if (existingEntity == null)
|
||||
{
|
||||
return ApiResponse.Error("未找到要更新的数据。");
|
||||
}
|
||||
|
||||
// 直接将传入的字符串赋值给 Info 属性
|
||||
existingEntity.Info = model.Data;
|
||||
repository.Update(existingEntity);
|
||||
await _work.SaveChangesAsync();
|
||||
|
||||
return ApiResponse.Success("数据已成功更新。");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return ApiResponse.Error($"更新数据时发生错误: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -23,6 +23,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.3" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.1" />
|
||||
</ItemGroup>
|
||||
|
Reference in New Issue
Block a user