Yuyuqt commited on
Commit
87c9973
·
1 Parent(s): c596926

feat: add user management, book management, categories management, membership management, and borrowing management

Browse files
Database/Models/Book.cs CHANGED
@@ -1,4 +1,4 @@
1
- using System;
2
  using System.Collections.Generic;
3
 
4
  namespace Database.Models;
@@ -22,6 +22,12 @@ public partial class Book
22
  public string? Description { get; set; }
23
 
24
  public string? CoverUrl { get; set; }
 
 
 
 
 
 
25
 
26
  public virtual ICollection<Borrowing> Borrowings { get; set; } = new List<Borrowing>();
27
 
 
1
+ using System;
2
  using System.Collections.Generic;
3
 
4
  namespace Database.Models;
 
22
  public string? Description { get; set; }
23
 
24
  public string? CoverUrl { get; set; }
25
+
26
+ public int TotalCopies { get; set; }
27
+
28
+ public int AvailableCopies { get; set; }
29
+
30
+ public DateTime? UpdatedAt { get; set; }
31
 
32
  public virtual ICollection<Borrowing> Borrowings { get; set; } = new List<Borrowing>();
33
 
Database/Models/LibraryManagementContext.cs CHANGED
@@ -1,4 +1,4 @@
1
- using System;
2
  using System.Collections.Generic;
3
  using Microsoft.EntityFrameworkCore;
4
 
@@ -53,6 +53,9 @@ public partial class LibraryManagementContext : DbContext
53
  .HasMaxLength(20)
54
  .HasDefaultValue("Available");
55
  entity.Property(e => e.Title).HasMaxLength(200);
 
 
 
56
 
57
  entity.HasMany(d => d.Categories).WithMany(p => p.Books)
58
  .UsingEntity<Dictionary<string, object>>(
@@ -130,7 +133,7 @@ public partial class LibraryManagementContext : DbContext
130
  entity.Property(e => e.PhoneNumber).HasMaxLength(20);
131
  entity.Property(e => e.Role)
132
  .HasMaxLength(20)
133
- .HasDefaultValue("User");
134
  entity.Property(e => e.StudentId).HasMaxLength(20);
135
  entity.Property(e => e.SuspensionEndDate).HasColumnType("datetime");
136
  entity.Property(e => e.UpdatedAt).HasColumnType("datetime");
 
1
+ using System;
2
  using System.Collections.Generic;
3
  using Microsoft.EntityFrameworkCore;
4
 
 
53
  .HasMaxLength(20)
54
  .HasDefaultValue("Available");
55
  entity.Property(e => e.Title).HasMaxLength(200);
56
+ entity.Property(e => e.TotalCopies).HasDefaultValue(1);
57
+ entity.Property(e => e.AvailableCopies).HasDefaultValue(1);
58
+ entity.Property(e => e.UpdatedAt).HasColumnType("datetime");
59
 
60
  entity.HasMany(d => d.Categories).WithMany(p => p.Books)
61
  .UsingEntity<Dictionary<string, object>>(
 
133
  entity.Property(e => e.PhoneNumber).HasMaxLength(20);
134
  entity.Property(e => e.Role)
135
  .HasMaxLength(20)
136
+ .HasDefaultValue("Member");
137
  entity.Property(e => e.StudentId).HasMaxLength(20);
138
  entity.Property(e => e.SuspensionEndDate).HasColumnType("datetime");
139
  entity.Property(e => e.UpdatedAt).HasColumnType("datetime");
LibraryManagement.Backend/Features/Auth/AuthController.cs CHANGED
@@ -1,4 +1,6 @@
 
1
  using Microsoft.AspNetCore.Mvc;
 
2
 
3
  namespace LibraryManagement.Backend.Features.Auth
4
  {
@@ -40,5 +42,18 @@ namespace LibraryManagement.Backend.Features.Auth
40
  return Unauthorized(new { message = ex.Message });
41
  }
42
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  }
44
  }
 
1
+ using Microsoft.AspNetCore.Authorization;
2
  using Microsoft.AspNetCore.Mvc;
3
+ using System.Security.Claims;
4
 
5
  namespace LibraryManagement.Backend.Features.Auth
6
  {
 
42
  return Unauthorized(new { message = ex.Message });
43
  }
44
  }
45
+
46
+ [HttpGet("me")]
47
+ [Authorize]
48
+ public IActionResult GetCurrentUser()
49
+ {
50
+ var claims = User.Claims.Select(c => new { c.Type, c.Value }).ToList();
51
+ return Ok(new
52
+ {
53
+ IsAuthenticated = User.Identity?.IsAuthenticated,
54
+ Name = User.Identity?.Name,
55
+ Claims = claims
56
+ });
57
+ }
58
  }
59
  }
LibraryManagement.Backend/Features/Auth/AuthService.cs CHANGED
@@ -5,6 +5,8 @@ using System.IdentityModel.Tokens.Jwt;
5
  using System.Security.Claims;
6
  using System.Text;
7
 
 
 
8
  namespace LibraryManagement.Backend.Features.Auth
9
  {
10
  public interface IAuthService
@@ -17,11 +19,13 @@ namespace LibraryManagement.Backend.Features.Auth
17
  {
18
  private readonly LibraryManagementContext _context;
19
  private readonly IConfiguration _configuration;
 
20
 
21
- public AuthService(LibraryManagementContext context, IConfiguration configuration)
22
  {
23
  _context = context;
24
  _configuration = configuration;
 
25
  }
26
 
27
  public async Task<AuthResponse> Register(RegisterRequest request)
@@ -47,6 +51,11 @@ namespace LibraryManagement.Backend.Features.Auth
47
  _context.Users.Add(user);
48
  await _context.SaveChangesAsync();
49
 
 
 
 
 
 
50
  return await AuthenticateUser(user);
51
  }
52
 
 
5
  using System.Security.Claims;
6
  using System.Text;
7
 
8
+ using LibraryManagement.Backend.Features.Subscriptions;
9
+
10
  namespace LibraryManagement.Backend.Features.Auth
11
  {
12
  public interface IAuthService
 
19
  {
20
  private readonly LibraryManagementContext _context;
21
  private readonly IConfiguration _configuration;
22
+ private readonly ISubscriptionService _subscriptionService;
23
 
24
+ public AuthService(LibraryManagementContext context, IConfiguration configuration, ISubscriptionService subscriptionService)
25
  {
26
  _context = context;
27
  _configuration = configuration;
28
+ _subscriptionService = subscriptionService;
29
  }
30
 
31
  public async Task<AuthResponse> Register(RegisterRequest request)
 
51
  _context.Users.Add(user);
52
  await _context.SaveChangesAsync();
53
 
54
+ if (!string.IsNullOrEmpty(user.StudentId))
55
+ {
56
+ await _subscriptionService.SubscribeUserAsync(user.Id, 3); // 3 is "Basic Yearly"
57
+ }
58
+
59
  return await AuthenticateUser(user);
60
  }
61
 
LibraryManagement.Backend/Features/Books/BookController.cs ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using Microsoft.AspNetCore.Authorization;
2
+ using Microsoft.AspNetCore.Mvc;
3
+
4
+ namespace LibraryManagement.Backend.Features.Books
5
+ {
6
+ [ApiController]
7
+ [Route("api/books")]
8
+ public class BookController : ControllerBase
9
+ {
10
+ private readonly IBookService _bookService;
11
+
12
+ public BookController(IBookService bookService)
13
+ {
14
+ _bookService = bookService;
15
+ }
16
+
17
+ [HttpGet]
18
+ [Authorize]
19
+ public async Task<ActionResult<IEnumerable<BookDto>>> GetBooks()
20
+ {
21
+ var books = await _bookService.GetAllBooksAsync();
22
+ return Ok(books);
23
+ }
24
+
25
+ [HttpGet("{id}")]
26
+ [Authorize]
27
+ public async Task<ActionResult<BookDto>> GetBook(int id)
28
+ {
29
+ var book = await _bookService.GetBookByIdAsync(id);
30
+ if (book == null) return NotFound();
31
+ return Ok(book);
32
+ }
33
+
34
+ [HttpPost]
35
+ [Authorize(Roles = "Librarian")]
36
+ public async Task<ActionResult<BookDto>> CreateBook([FromBody] BookCreateRequest request)
37
+ {
38
+ try
39
+ {
40
+ var book = await _bookService.CreateBookAsync(request);
41
+ return CreatedAtAction(nameof(GetBook), new { id = book.Id }, book);
42
+ }
43
+ catch (Exception ex)
44
+ {
45
+ return BadRequest(new { message = ex.Message });
46
+ }
47
+ }
48
+
49
+ [HttpPut("{id}")]
50
+ [Authorize(Roles = "Librarian")]
51
+ public async Task<ActionResult<BookDto>> UpdateBook(int id, [FromBody] BookUpdateRequest request)
52
+ {
53
+ var updatedBook = await _bookService.UpdateBookAsync(id, request);
54
+ if (updatedBook == null) return NotFound();
55
+ return Ok(updatedBook);
56
+ }
57
+
58
+ [HttpDelete("{id}")]
59
+ [Authorize(Roles = "Librarian")]
60
+ public async Task<IActionResult> DeleteBook(int id)
61
+ {
62
+ var success = await _bookService.DeleteBookAsync(id);
63
+ if (!success) return NotFound();
64
+ return NoContent();
65
+ }
66
+ }
67
+ }
LibraryManagement.Backend/Features/Books/BookService.cs ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using Database.Models;
2
+ using Microsoft.EntityFrameworkCore;
3
+
4
+ namespace LibraryManagement.Backend.Features.Books
5
+ {
6
+ public interface IBookService
7
+ {
8
+ Task<IEnumerable<BookDto>> GetAllBooksAsync();
9
+ Task<BookDto?> GetBookByIdAsync(int id);
10
+ Task<BookDto> CreateBookAsync(BookCreateRequest request);
11
+ Task<BookDto?> UpdateBookAsync(int id, BookUpdateRequest request);
12
+ Task<bool> DeleteBookAsync(int id);
13
+ }
14
+
15
+ public class BookService : IBookService
16
+ {
17
+ private readonly LibraryManagementContext _context;
18
+
19
+ public BookService(LibraryManagementContext context)
20
+ {
21
+ _context = context;
22
+ }
23
+
24
+ public async Task<IEnumerable<BookDto>> GetAllBooksAsync()
25
+ {
26
+ return await _context.Books
27
+ .Where(b => b.IsActive)
28
+ .Select(b => MapToDto(b))
29
+ .ToListAsync();
30
+ }
31
+
32
+ public async Task<BookDto?> GetBookByIdAsync(int id)
33
+ {
34
+ var book = await _context.Books.FindAsync(id);
35
+ if (book == null || !book.IsActive) return null;
36
+ return MapToDto(book);
37
+ }
38
+
39
+ public async Task<BookDto> CreateBookAsync(BookCreateRequest request)
40
+ {
41
+ if (await _context.Books.AnyAsync(b => b.Isbn == request.Isbn))
42
+ {
43
+ throw new Exception("A book with this ISBN already exists.");
44
+ }
45
+
46
+ var book = new Book
47
+ {
48
+ Title = request.Title,
49
+ Isbn = request.Isbn,
50
+ Author = request.Author,
51
+ Description = request.Description,
52
+ CoverUrl = request.CoverUrl,
53
+ TotalCopies = request.TotalCopies,
54
+ AvailableCopies = request.TotalCopies,
55
+ Status = request.TotalCopies > 0 ? "Available" : "Out Of Stock",
56
+ IsActive = true,
57
+ CreatedAt = DateTime.UtcNow
58
+ };
59
+
60
+ _context.Books.Add(book);
61
+ await _context.SaveChangesAsync();
62
+
63
+ return MapToDto(book);
64
+ }
65
+
66
+ public async Task<BookDto?> UpdateBookAsync(int id, BookUpdateRequest request)
67
+ {
68
+ var book = await _context.Books.FindAsync(id);
69
+ if (book == null || !book.IsActive) return null;
70
+
71
+ book.Title = request.Title;
72
+ book.Author = request.Author;
73
+ book.Description = request.Description;
74
+ book.CoverUrl = request.CoverUrl;
75
+
76
+ // Basic logic to sync availability when total copies change
77
+ int difference = request.TotalCopies - book.TotalCopies;
78
+ book.TotalCopies = request.TotalCopies;
79
+ book.AvailableCopies += difference;
80
+
81
+ if (book.AvailableCopies < 0) book.AvailableCopies = 0;
82
+ book.Status = book.AvailableCopies > 0 ? "Available" : "Out Of Stock";
83
+
84
+ book.UpdatedAt = DateTime.UtcNow;
85
+
86
+ await _context.SaveChangesAsync();
87
+ return MapToDto(book);
88
+ }
89
+
90
+ public async Task<bool> DeleteBookAsync(int id)
91
+ {
92
+ var book = await _context.Books.FindAsync(id);
93
+ if (book == null) return false;
94
+
95
+ book.IsActive = false;
96
+ book.UpdatedAt = DateTime.UtcNow;
97
+
98
+ await _context.SaveChangesAsync();
99
+ return true;
100
+ }
101
+
102
+ private static BookDto MapToDto(Book book)
103
+ {
104
+ return new BookDto
105
+ {
106
+ Id = book.Id,
107
+ Title = book.Title,
108
+ Isbn = book.Isbn,
109
+ Author = book.Author,
110
+ Status = book.Status,
111
+ Description = book.Description,
112
+ CoverUrl = book.CoverUrl,
113
+ TotalCopies = book.TotalCopies,
114
+ AvailableCopies = book.AvailableCopies,
115
+ CreatedAt = book.CreatedAt
116
+ };
117
+ }
118
+ }
119
+
120
+ public class BookDto
121
+ {
122
+ public int Id { get; set; }
123
+ public string Title { get; set; } = string.Empty;
124
+ public string Isbn { get; set; } = string.Empty;
125
+ public string Author { get; set; } = string.Empty;
126
+ public string Status { get; set; } = string.Empty;
127
+ public string? Description { get; set; }
128
+ public string? CoverUrl { get; set; }
129
+ public int TotalCopies { get; set; }
130
+ public int AvailableCopies { get; set; }
131
+ public DateTime CreatedAt { get; set; }
132
+ }
133
+
134
+ public class BookCreateRequest
135
+ {
136
+ public string Title { get; set; } = string.Empty;
137
+ public string Isbn { get; set; } = string.Empty;
138
+ public string Author { get; set; } = string.Empty;
139
+ public string? Description { get; set; }
140
+ public string? CoverUrl { get; set; }
141
+ public int TotalCopies { get; set; }
142
+ }
143
+
144
+ public class BookUpdateRequest
145
+ {
146
+ public string Title { get; set; } = string.Empty;
147
+ public string Author { get; set; } = string.Empty;
148
+ public string? Description { get; set; }
149
+ public string? CoverUrl { get; set; }
150
+ public int TotalCopies { get; set; }
151
+ }
152
+ }
LibraryManagement.Backend/Features/Borrowing/BorrowingController.cs ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using Microsoft.AspNetCore.Authorization;
2
+ using Microsoft.AspNetCore.Mvc;
3
+ using System.Security.Claims;
4
+
5
+ namespace LibraryManagement.Backend.Features.Borrowings
6
+ {
7
+ [ApiController]
8
+ [Route("api/borrowings")]
9
+ public class BorrowingController : ControllerBase
10
+ {
11
+ private readonly IBorrowingService _borrowingService;
12
+
13
+ public BorrowingController(IBorrowingService borrowingService)
14
+ {
15
+ _borrowingService = borrowingService;
16
+ }
17
+
18
+ [HttpPost("borrow")]
19
+ [Authorize]
20
+ public async Task<ActionResult<BorrowingDto>> BorrowBook([FromBody] BorrowRequest request)
21
+ {
22
+ try
23
+ {
24
+ var userIdStr = User.FindFirstValue(ClaimTypes.NameIdentifier);
25
+ if (string.IsNullOrEmpty(userIdStr)) return Unauthorized();
26
+
27
+ var userId = int.Parse(userIdStr);
28
+ var borrowing = await _borrowingService.BorrowBookAsync(userId, request.BookId);
29
+ return Ok(borrowing);
30
+ }
31
+ catch (Exception ex)
32
+ {
33
+ return BadRequest(new { message = ex.Message });
34
+ }
35
+ }
36
+
37
+ [HttpPost("return/{id}")]
38
+ [Authorize(Roles = "Librarian")]
39
+ public async Task<ActionResult<BorrowingDto>> ReturnBook(int id)
40
+ {
41
+ try
42
+ {
43
+ var borrowing = await _borrowingService.ReturnBookAsync(id);
44
+ return Ok(borrowing);
45
+ }
46
+ catch (Exception ex)
47
+ {
48
+ return BadRequest(new { message = ex.Message });
49
+ }
50
+ }
51
+
52
+ [HttpGet("me")]
53
+ [Authorize]
54
+ public async Task<ActionResult<IEnumerable<BorrowingDto>>> GetMyBorrowings()
55
+ {
56
+ var userIdStr = User.FindFirstValue(ClaimTypes.NameIdentifier);
57
+ if (string.IsNullOrEmpty(userIdStr)) return Unauthorized();
58
+
59
+ var userId = int.Parse(userIdStr);
60
+ var borrowings = await _borrowingService.GetUserBorrowingsAsync(userId);
61
+ return Ok(borrowings);
62
+ }
63
+
64
+ [HttpGet]
65
+ [Authorize(Roles = "Librarian")]
66
+ public async Task<ActionResult<IEnumerable<BorrowingDto>>> GetAllBorrowings()
67
+ {
68
+ var borrowings = await _borrowingService.GetAllBorrowingsAsync();
69
+ return Ok(borrowings);
70
+ }
71
+ }
72
+ }
LibraryManagement.Backend/Features/Borrowing/BorrowingModels.cs ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+
3
+ namespace LibraryManagement.Backend.Features.Borrowings
4
+ {
5
+ public class BorrowingDto
6
+ {
7
+ public int Id { get; set; }
8
+ public int UserId { get; set; }
9
+ public string UserEmail { get; set; } = string.Empty;
10
+ public int BookId { get; set; }
11
+ public string BookTitle { get; set; } = string.Empty;
12
+ public DateTime BorrowDate { get; set; }
13
+ public DateTime DueDate { get; set; }
14
+ public DateTime? ReturnDate { get; set; }
15
+ public string Status { get; set; } = string.Empty;
16
+ public decimal FineAmount { get; set; }
17
+ public bool IsFinePaid { get; set; }
18
+ }
19
+
20
+ public class BorrowRequest
21
+ {
22
+ public int BookId { get; set; }
23
+ }
24
+ }
LibraryManagement.Backend/Features/Borrowing/BorrowingService.cs ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using Database.Models;
2
+ using Microsoft.EntityFrameworkCore;
3
+
4
+ namespace LibraryManagement.Backend.Features.Borrowings
5
+ {
6
+ public interface IBorrowingService
7
+ {
8
+ Task<BorrowingDto> BorrowBookAsync(int userId, int bookId);
9
+ Task<BorrowingDto> ReturnBookAsync(int borrowingId);
10
+ Task<IEnumerable<BorrowingDto>> GetUserBorrowingsAsync(int userId);
11
+ Task<IEnumerable<BorrowingDto>> GetAllBorrowingsAsync();
12
+ }
13
+
14
+ public class BorrowingService : IBorrowingService
15
+ {
16
+ private readonly LibraryManagementContext _context;
17
+ private const decimal FinePerDay = 500;
18
+
19
+ public BorrowingService(LibraryManagementContext context)
20
+ {
21
+ _context = context;
22
+ }
23
+
24
+ public async Task<BorrowingDto> BorrowBookAsync(int userId, int bookId)
25
+ {
26
+ // 1. Check for Active Membership
27
+ var subscription = await _context.UserSubscriptions
28
+ .Include(s => s.Membership)
29
+ .Where(s => s.UserId == userId && s.IsActive && s.ExpiryDate > DateTime.UtcNow)
30
+ .FirstOrDefaultAsync();
31
+
32
+ if (subscription == null)
33
+ {
34
+ throw new Exception("Active membership required to borrow books.");
35
+ }
36
+
37
+ // 2. Check for Overdue Books or Unpaid Fines
38
+ var hasBlockers = await _context.Borrowings
39
+ .AnyAsync(b => b.UserId == userId &&
40
+ ((b.Status == "Borrowed" && b.DueDate < DateTime.UtcNow) || (b.FineAmount > 0 && !b.IsFinePaid)));
41
+
42
+ if (hasBlockers)
43
+ {
44
+ throw new Exception("Borrowing blocked: You have overdue books or unpaid fines.");
45
+ }
46
+
47
+ // 3. Check MaxBooks limit
48
+ var activeBorrowCount = await _context.Borrowings
49
+ .CountAsync(b => b.UserId == userId && b.Status == "Borrowed");
50
+
51
+ if (activeBorrowCount >= subscription.Membership.MaxBooks)
52
+ {
53
+ throw new Exception($"Borrowing limit reached: You can only borrow {subscription.Membership.MaxBooks} books at a time.");
54
+ }
55
+
56
+ // 4. Check Book Availability
57
+ var book = await _context.Books.FindAsync(bookId);
58
+ if (book == null || !book.IsActive || book.AvailableCopies <= 0)
59
+ {
60
+ throw new Exception("Book is currently unavailable.");
61
+ }
62
+
63
+ // 5. Create Borrowing Record
64
+ var borrowing = new Borrowing
65
+ {
66
+ UserId = userId,
67
+ BookId = bookId,
68
+ BorrowDate = DateTime.UtcNow,
69
+ DueDate = DateTime.UtcNow.AddDays(subscription.Membership.BorrowingDays),
70
+ Status = "Borrowed",
71
+ FineAmount = 0,
72
+ IsFinePaid = false
73
+ };
74
+
75
+ // 6. Update Book Stock
76
+ book.AvailableCopies--;
77
+ if (book.AvailableCopies == 0)
78
+ {
79
+ book.Status = "Out Of Stock";
80
+ }
81
+
82
+ _context.Borrowings.Add(borrowing);
83
+ await _context.SaveChangesAsync();
84
+
85
+ // Load relations for mapping
86
+ await _context.Entry(borrowing).Reference(b => b.Book).LoadAsync();
87
+ await _context.Entry(borrowing).Reference(b => b.User).LoadAsync();
88
+
89
+ return MapToDto(borrowing);
90
+ }
91
+
92
+ public async Task<BorrowingDto> ReturnBookAsync(int borrowingId)
93
+ {
94
+ var borrowing = await _context.Borrowings
95
+ .Include(b => b.Book)
96
+ .Include(b => b.User)
97
+ .FirstOrDefaultAsync(b => b.Id == borrowingId);
98
+
99
+ if (borrowing == null) throw new Exception("Borrowing record not found.");
100
+ if (borrowing.Status == "Returned") throw new Exception("Book has already been returned.");
101
+
102
+ var now = DateTime.UtcNow;
103
+ borrowing.ReturnDate = now;
104
+ borrowing.Status = "Returned";
105
+
106
+ // Calculate Fine
107
+ if (now > borrowing.DueDate)
108
+ {
109
+ var overdueDays = (now.Date - borrowing.DueDate.Date).Days;
110
+ if (overdueDays > 0)
111
+ {
112
+ borrowing.FineAmount = overdueDays * FinePerDay;
113
+ }
114
+ }
115
+
116
+ // Update Book Stock
117
+ var book = borrowing.Book;
118
+ book.AvailableCopies++;
119
+ book.Status = "Available";
120
+
121
+ await _context.SaveChangesAsync();
122
+ return MapToDto(borrowing);
123
+ }
124
+
125
+ public async Task<IEnumerable<BorrowingDto>> GetUserBorrowingsAsync(int userId)
126
+ {
127
+ var borrowings = await _context.Borrowings
128
+ .Include(b => b.Book)
129
+ .Include(b => b.User)
130
+ .Where(b => b.UserId == userId)
131
+ .OrderByDescending(b => b.BorrowDate)
132
+ .ToListAsync();
133
+
134
+ return borrowings.Select(MapToDto);
135
+ }
136
+
137
+ public async Task<IEnumerable<BorrowingDto>> GetAllBorrowingsAsync()
138
+ {
139
+ var borrowings = await _context.Borrowings
140
+ .Include(b => b.Book)
141
+ .Include(b => b.User)
142
+ .OrderByDescending(b => b.BorrowDate)
143
+ .ToListAsync();
144
+
145
+ return borrowings.Select(MapToDto);
146
+ }
147
+
148
+ private static BorrowingDto MapToDto(Borrowing b)
149
+ {
150
+ return new BorrowingDto
151
+ {
152
+ Id = b.Id,
153
+ UserId = b.UserId,
154
+ UserEmail = b.User?.Email ?? "Unknown",
155
+ BookId = b.BookId,
156
+ BookTitle = b.Book?.Title ?? "Unknown",
157
+ BorrowDate = b.BorrowDate,
158
+ DueDate = b.DueDate,
159
+ ReturnDate = b.ReturnDate,
160
+ Status = b.Status,
161
+ FineAmount = b.FineAmount,
162
+ IsFinePaid = b.IsFinePaid
163
+ };
164
+ }
165
+ }
166
+ }
LibraryManagement.Backend/Features/Categories/CategoryController.cs ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using Microsoft.AspNetCore.Authorization;
2
+ using Microsoft.AspNetCore.Mvc;
3
+
4
+ namespace LibraryManagement.Backend.Features.Categories
5
+ {
6
+ [ApiController]
7
+ [Route("api/categories")]
8
+ public class CategoryController : ControllerBase
9
+ {
10
+ private readonly ICategoryService _categoryService;
11
+
12
+ public CategoryController(ICategoryService categoryService)
13
+ {
14
+ _categoryService = categoryService;
15
+ }
16
+
17
+ [HttpGet]
18
+ [Authorize] // Accessible by both Librarian and Member
19
+ public async Task<ActionResult<IEnumerable<CategoryDto>>> GetCategories()
20
+ {
21
+ var categories = await _categoryService.GetAllCategoriesAsync();
22
+ return Ok(categories);
23
+ }
24
+
25
+ [HttpPost]
26
+ [Authorize(Roles = "Librarian")]
27
+ public async Task<ActionResult<CategoryDto>> CreateCategory([FromBody] CategoryCreateRequest request)
28
+ {
29
+ try
30
+ {
31
+ var category = await _categoryService.CreateCategoryAsync(request);
32
+ return CreatedAtAction(nameof(GetCategories), category);
33
+ }
34
+ catch (Exception ex)
35
+ {
36
+ return BadRequest(new { message = ex.Message });
37
+ }
38
+ }
39
+
40
+ [HttpDelete("{id}")]
41
+ [Authorize(Roles = "Librarian")]
42
+ public async Task<IActionResult> DeleteCategory(int id)
43
+ {
44
+ var success = await _categoryService.DeleteCategoryAsync(id);
45
+ if (!success) return NotFound();
46
+ return NoContent();
47
+ }
48
+ }
49
+ }
LibraryManagement.Backend/Features/Categories/CategoryService.cs ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using Database.Models;
2
+ using Microsoft.EntityFrameworkCore;
3
+
4
+ namespace LibraryManagement.Backend.Features.Categories
5
+ {
6
+ public interface ICategoryService
7
+ {
8
+ Task<IEnumerable<CategoryDto>> GetAllCategoriesAsync();
9
+ Task<CategoryDto> CreateCategoryAsync(CategoryCreateRequest request);
10
+ Task<bool> DeleteCategoryAsync(int id);
11
+ }
12
+
13
+ public class CategoryService : ICategoryService
14
+ {
15
+ private readonly LibraryManagementContext _context;
16
+
17
+ public CategoryService(LibraryManagementContext context)
18
+ {
19
+ _context = context;
20
+ }
21
+
22
+ public async Task<IEnumerable<CategoryDto>> GetAllCategoriesAsync()
23
+ {
24
+ return await _context.Categories
25
+ .Select(c => new CategoryDto { Id = c.Id, Name = c.Name })
26
+ .ToListAsync();
27
+ }
28
+
29
+ public async Task<CategoryDto> CreateCategoryAsync(CategoryCreateRequest request)
30
+ {
31
+ if (await _context.Categories.AnyAsync(c => c.Name == request.Name))
32
+ {
33
+ throw new Exception("Category with this name already exists.");
34
+ }
35
+
36
+ var category = new Category
37
+ {
38
+ Name = request.Name
39
+ };
40
+
41
+ _context.Categories.Add(category);
42
+ await _context.SaveChangesAsync();
43
+
44
+ return new CategoryDto { Id = category.Id, Name = category.Name };
45
+ }
46
+
47
+ public async Task<bool> DeleteCategoryAsync(int id)
48
+ {
49
+ var category = await _context.Categories.FindAsync(id);
50
+ if (category == null) return false;
51
+
52
+ // Note: EF automatically handles removing associations in many-to-many join tables
53
+ _context.Categories.Remove(category);
54
+ await _context.SaveChangesAsync();
55
+ return true;
56
+ }
57
+ }
58
+
59
+ public class CategoryDto
60
+ {
61
+ public int Id { get; set; }
62
+ public string Name { get; set; } = string.Empty;
63
+ }
64
+
65
+ public class CategoryCreateRequest
66
+ {
67
+ public string Name { get; set; } = string.Empty;
68
+ }
69
+ }
LibraryManagement.Backend/Features/Subscriptions/SubscriptionController.cs ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using Microsoft.AspNetCore.Authorization;
2
+ using Microsoft.AspNetCore.Mvc;
3
+ using System.Security.Claims;
4
+
5
+ namespace LibraryManagement.Backend.Features.Subscriptions
6
+ {
7
+ [ApiController]
8
+ [Route("api")]
9
+ public class SubscriptionController : ControllerBase
10
+ {
11
+ private readonly ISubscriptionService _subscriptionService;
12
+
13
+ public SubscriptionController(ISubscriptionService subscriptionService)
14
+ {
15
+ _subscriptionService = subscriptionService;
16
+ }
17
+
18
+ [HttpGet("memberships")]
19
+ [AllowAnonymous]
20
+ public async Task<ActionResult<IEnumerable<MembershipDto>>> GetMemberships()
21
+ {
22
+ return Ok(await _subscriptionService.GetMembershipsAsync());
23
+ }
24
+
25
+ [HttpGet("subscriptions/me")]
26
+ [Authorize]
27
+ public async Task<ActionResult<SubscriptionDto>> GetMySubscription()
28
+ {
29
+ var userIdStr = User.FindFirstValue(ClaimTypes.NameIdentifier);
30
+ if (string.IsNullOrEmpty(userIdStr)) return Unauthorized();
31
+
32
+ var userId = int.Parse(userIdStr);
33
+ var subscription = await _subscriptionService.GetUserSubscriptionAsync(userId);
34
+
35
+ if (subscription == null) return NotFound(new { message = "No active subscription found." });
36
+
37
+ return Ok(subscription);
38
+ }
39
+
40
+ [HttpPost("subscriptions/subscribe")]
41
+ [Authorize]
42
+ public async Task<ActionResult<SubscriptionDto>> Subscribe([FromBody] SubscribeRequest request)
43
+ {
44
+ try
45
+ {
46
+ var userIdStr = User.FindFirstValue(ClaimTypes.NameIdentifier);
47
+ if (string.IsNullOrEmpty(userIdStr)) return Unauthorized();
48
+
49
+ var userId = int.Parse(userIdStr);
50
+ var subscription = await _subscriptionService.SubscribeUserAsync(userId, request.MembershipId);
51
+ return Ok(subscription);
52
+ }
53
+ catch (Exception ex)
54
+ {
55
+ return BadRequest(new { message = ex.Message });
56
+ }
57
+ }
58
+ }
59
+ }
LibraryManagement.Backend/Features/Subscriptions/SubscriptionModels.cs ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System;
2
+
3
+ namespace LibraryManagement.Backend.Features.Subscriptions
4
+ {
5
+ public class MembershipDto
6
+ {
7
+ public int Id { get; set; }
8
+ public string Type { get; set; } = string.Empty;
9
+ public int MaxBooks { get; set; }
10
+ public int BorrowingDays { get; set; }
11
+ public decimal Price { get; set; }
12
+ public string Currency { get; set; } = "MMK";
13
+ public int DurationMonths { get; set; }
14
+ }
15
+
16
+ public class SubscriptionDto
17
+ {
18
+ public int Id { get; set; }
19
+ public int UserId { get; set; }
20
+ public int MembershipId { get; set; }
21
+ public string MembershipType { get; set; } = string.Empty;
22
+ public DateTime StartDate { get; set; }
23
+ public DateTime ExpiryDate { get; set; }
24
+ public bool IsActive { get; set; }
25
+ public bool IsExpired => DateTime.UtcNow > ExpiryDate;
26
+ }
27
+
28
+ public class SubscribeRequest
29
+ {
30
+ public int MembershipId { get; set; }
31
+ }
32
+ }
LibraryManagement.Backend/Features/Subscriptions/SubscriptionService.cs ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using Database.Models;
2
+ using Microsoft.EntityFrameworkCore;
3
+
4
+ namespace LibraryManagement.Backend.Features.Subscriptions
5
+ {
6
+ public interface ISubscriptionService
7
+ {
8
+ Task<IEnumerable<MembershipDto>> GetMembershipsAsync();
9
+ Task<SubscriptionDto?> GetUserSubscriptionAsync(int userId);
10
+ Task<SubscriptionDto> SubscribeUserAsync(int userId, int membershipId);
11
+ }
12
+
13
+ public class SubscriptionService : ISubscriptionService
14
+ {
15
+ private readonly LibraryManagementContext _context;
16
+
17
+ public SubscriptionService(LibraryManagementContext context)
18
+ {
19
+ _context = context;
20
+ }
21
+
22
+ public async Task<IEnumerable<MembershipDto>> GetMembershipsAsync()
23
+ {
24
+ return await _context.Memberships
25
+ .Select(m => new MembershipDto
26
+ {
27
+ Id = m.Id,
28
+ Type = m.Type,
29
+ MaxBooks = m.MaxBooks,
30
+ BorrowingDays = m.BorrowingDays,
31
+ Price = m.Price,
32
+ DurationMonths = m.DurationMonths
33
+ }).ToListAsync();
34
+ }
35
+
36
+ public async Task<SubscriptionDto?> GetUserSubscriptionAsync(int userId)
37
+ {
38
+ var subscription = await _context.UserSubscriptions
39
+ .Include(s => s.Membership)
40
+ .Where(s => s.UserId == userId && s.IsActive)
41
+ .OrderByDescending(s => s.StartDate)
42
+ .FirstOrDefaultAsync();
43
+
44
+ if (subscription == null) return null;
45
+
46
+ return MapToDto(subscription);
47
+ }
48
+
49
+ public async Task<SubscriptionDto> SubscribeUserAsync(int userId, int membershipId)
50
+ {
51
+ var membership = await _context.Memberships.FindAsync(membershipId);
52
+ if (membership == null) throw new Exception("Membership plan not found.");
53
+
54
+ // Deactivate any existing active subscriptions for this user
55
+ var activeSubscriptions = await _context.UserSubscriptions
56
+ .Where(s => s.UserId == userId && s.IsActive)
57
+ .ToListAsync();
58
+
59
+ foreach (var s in activeSubscriptions)
60
+ {
61
+ s.IsActive = false;
62
+ }
63
+
64
+ var subscription = new UserSubscription
65
+ {
66
+ UserId = userId,
67
+ MembershipId = membershipId,
68
+ StartDate = DateTime.UtcNow,
69
+ ExpiryDate = DateTime.UtcNow.AddMonths(membership.DurationMonths),
70
+ IsActive = true
71
+ };
72
+
73
+ _context.UserSubscriptions.Add(subscription);
74
+ await _context.SaveChangesAsync();
75
+
76
+ // Explicitly load the membership data for the DTO mapping
77
+ await _context.Entry(subscription).Reference(s => s.Membership).LoadAsync();
78
+
79
+ return MapToDto(subscription);
80
+ }
81
+
82
+ private static SubscriptionDto MapToDto(UserSubscription subscription)
83
+ {
84
+ return new SubscriptionDto
85
+ {
86
+ Id = subscription.Id,
87
+ UserId = subscription.UserId,
88
+ MembershipId = subscription.MembershipId,
89
+ MembershipType = subscription.Membership.Type,
90
+ StartDate = subscription.StartDate,
91
+ ExpiryDate = subscription.ExpiryDate,
92
+ IsActive = subscription.IsActive
93
+ };
94
+ }
95
+ }
96
+ }
LibraryManagement.Backend/Features/Users/UserController.cs CHANGED
@@ -1,3 +1,4 @@
 
1
  using Microsoft.AspNetCore.Mvc;
2
 
3
  namespace LibraryManagement.Backend.Features.Users
@@ -13,11 +14,64 @@ namespace LibraryManagement.Backend.Features.Users
13
  _userService = userService;
14
  }
15
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  [HttpPost]
17
- public async Task<ActionResult<UserCreateResponse>> CreateUser([FromBody] UserCreateRequest request)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  {
19
- var response = await _userService.CreateUser(request);
20
- return Ok(response);
 
 
 
 
 
 
 
 
 
 
21
  }
22
  }
23
  }
 
 
1
+ using Microsoft.AspNetCore.Authorization;
2
  using Microsoft.AspNetCore.Mvc;
3
 
4
  namespace LibraryManagement.Backend.Features.Users
 
14
  _userService = userService;
15
  }
16
 
17
+ [HttpGet]
18
+ [Authorize(Roles = "Librarian")]
19
+ public async Task<ActionResult<IEnumerable<UserDto>>> GetUsers()
20
+ {
21
+ var users = await _userService.GetAllUsersAsync();
22
+ return Ok(users);
23
+ }
24
+
25
+ [HttpGet("{id}")]
26
+ [Authorize(Roles = "Librarian")]
27
+ public async Task<ActionResult<UserDto>> GetUser(int id)
28
+ {
29
+ var user = await _userService.GetUserByIdAsync(id);
30
+ if (user == null) return NotFound();
31
+ return Ok(user);
32
+ }
33
+
34
  [HttpPost]
35
+ [AllowAnonymous]
36
+ public async Task<ActionResult<UserDto>> CreateUser([FromBody] UserCreateRequest request)
37
+ {
38
+ try
39
+ {
40
+ var response = await _userService.CreateUserAsync(request);
41
+ return CreatedAtAction(nameof(GetUser), new { id = response.Id }, response);
42
+ }
43
+ catch (Exception ex)
44
+ {
45
+ return BadRequest(new { message = ex.Message });
46
+ }
47
+ }
48
+
49
+ [HttpPut("{id}")]
50
+ [Authorize(Roles = "Librarian")]
51
+ public async Task<ActionResult<UserDto>> UpdateUser(int id, [FromBody] UserUpdateRequest request)
52
+ {
53
+ var updatedUser = await _userService.UpdateUserAsync(id, request);
54
+ if (updatedUser == null) return NotFound();
55
+ return Ok(updatedUser);
56
+ }
57
+
58
+ [HttpPatch("{id}/role")]
59
+ [Authorize(Roles = "Librarian")]
60
+ public async Task<IActionResult> UpdateUserRole(int id, [FromBody] UserRoleUpdateRequest request)
61
  {
62
+ var success = await _userService.UpdateUserRoleAsync(id, request.Role);
63
+ if (!success) return NotFound();
64
+ return NoContent();
65
+ }
66
+
67
+ [HttpDelete("{id}")]
68
+ [Authorize(Roles = "Librarian")]
69
+ public async Task<IActionResult> DeleteUser(int id)
70
+ {
71
+ var success = await _userService.DeleteUserAsync(id);
72
+ if (!success) return NotFound();
73
+ return NoContent();
74
  }
75
  }
76
  }
77
+
LibraryManagement.Backend/Features/Users/UserService.cs CHANGED
@@ -1,30 +1,176 @@
1
- using Database.Models;
 
 
 
 
 
 
2
 
3
  namespace LibraryManagement.Backend.Features.Users
4
  {
5
  public interface IUserService
6
  {
7
- public Task<UserCreateResponse> CreateUser(UserCreateRequest request);
 
 
 
 
 
8
  }
9
 
10
  public class UserService : IUserService
11
  {
12
  private readonly LibraryManagementContext _context;
 
13
 
14
- public UserService(LibraryManagementContext context) {
 
15
  _context = context;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  }
17
 
18
- public Task<UserCreateResponse> CreateUser(UserCreateRequest request)
19
  {
20
- throw new NotImplementedException();
 
 
 
 
 
 
 
 
 
 
 
21
  }
22
  }
23
 
24
- public class UserCreateRequest {
25
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  }
27
 
28
- public class UserCreateResponse {
 
 
 
 
 
 
 
 
 
 
29
  }
30
  }
 
1
+ using Database.Models;
2
+ using Microsoft.EntityFrameworkCore;
3
+ using LibraryManagement.Backend.Features.Subscriptions;
4
+ using System;
5
+ using System.Collections.Generic;
6
+ using System.Linq;
7
+ using System.Threading.Tasks;
8
 
9
  namespace LibraryManagement.Backend.Features.Users
10
  {
11
  public interface IUserService
12
  {
13
+ Task<IEnumerable<UserDto>> GetAllUsersAsync();
14
+ Task<UserDto?> GetUserByIdAsync(int id);
15
+ Task<UserDto> CreateUserAsync(UserCreateRequest request);
16
+ Task<UserDto?> UpdateUserAsync(int id, UserUpdateRequest request);
17
+ Task<bool> UpdateUserRoleAsync(int id, string role);
18
+ Task<bool> DeleteUserAsync(int id);
19
  }
20
 
21
  public class UserService : IUserService
22
  {
23
  private readonly LibraryManagementContext _context;
24
+ private readonly ISubscriptionService _subscriptionService;
25
 
26
+ public UserService(LibraryManagementContext context, ISubscriptionService subscriptionService)
27
+ {
28
  _context = context;
29
+ _subscriptionService = subscriptionService;
30
+ }
31
+
32
+ public async Task<IEnumerable<UserDto>> GetAllUsersAsync()
33
+ {
34
+ var users = await _context.Users.ToListAsync();
35
+ return users.Select(MapToDto);
36
+ }
37
+
38
+ public async Task<UserDto?> GetUserByIdAsync(int id)
39
+ {
40
+ var user = await _context.Users.FindAsync(id);
41
+ return user == null ? null : MapToDto(user);
42
+ }
43
+
44
+ public async Task<UserDto> CreateUserAsync(UserCreateRequest request)
45
+ {
46
+ if (await _context.Users.AnyAsync(u => u.Email == request.Email))
47
+ {
48
+ throw new Exception("Email already registered.");
49
+ }
50
+
51
+ var user = new User
52
+ {
53
+ FullName = request.FullName,
54
+ Email = request.Email,
55
+ PasswordHash = BCrypt.Net.BCrypt.HashPassword(request.Password),
56
+ PhoneNumber = request.PhoneNumber,
57
+ Address = request.Address,
58
+ StudentId = request.StudentId,
59
+ Role = "Member", // Default role
60
+ IsActive = true,
61
+ CreatedAt = DateTime.UtcNow
62
+ };
63
+
64
+ _context.Users.Add(user);
65
+ await _context.SaveChangesAsync();
66
+
67
+ if (!string.IsNullOrEmpty(user.StudentId))
68
+ {
69
+ await _subscriptionService.SubscribeUserAsync(user.Id, 3); // 3 is "Basic Yearly"
70
+ }
71
+
72
+ return MapToDto(user);
73
+ }
74
+
75
+ public async Task<UserDto?> UpdateUserAsync(int id, UserUpdateRequest request)
76
+ {
77
+ var user = await _context.Users.FindAsync(id);
78
+ if (user == null) return null;
79
+
80
+ bool isNewlyStudent = string.IsNullOrEmpty(user.StudentId) && !string.IsNullOrEmpty(request.StudentId);
81
+
82
+ user.FullName = request.FullName;
83
+ user.PhoneNumber = request.PhoneNumber;
84
+ user.Address = request.Address;
85
+ user.StudentId = request.StudentId;
86
+ user.UpdatedAt = DateTime.UtcNow;
87
+
88
+ await _context.SaveChangesAsync();
89
+
90
+ if (isNewlyStudent)
91
+ {
92
+ await _subscriptionService.SubscribeUserAsync(user.Id, 3);
93
+ }
94
+
95
+ return MapToDto(user);
96
+ }
97
+
98
+ public async Task<bool> UpdateUserRoleAsync(int id, string role)
99
+ {
100
+ var user = await _context.Users.FindAsync(id);
101
+ if (user == null) return false;
102
+
103
+ user.Role = role;
104
+ user.UpdatedAt = DateTime.UtcNow;
105
+
106
+ await _context.SaveChangesAsync();
107
+ return true;
108
+ }
109
+
110
+ public async Task<bool> DeleteUserAsync(int id)
111
+ {
112
+ var user = await _context.Users.FindAsync(id);
113
+ if (user == null) return false;
114
+
115
+ // Soft delete
116
+ user.IsActive = false;
117
+ user.UpdatedAt = DateTime.UtcNow;
118
+
119
+ await _context.SaveChangesAsync();
120
+ return true;
121
  }
122
 
123
+ private static UserDto MapToDto(User user)
124
  {
125
+ return new UserDto
126
+ {
127
+ Id = user.Id,
128
+ FullName = user.FullName,
129
+ Email = user.Email,
130
+ PhoneNumber = user.PhoneNumber,
131
+ Role = user.Role,
132
+ IsActive = user.IsActive,
133
+ StudentId = user.StudentId,
134
+ Address = user.Address,
135
+ CreatedAt = user.CreatedAt
136
+ };
137
  }
138
  }
139
 
140
+ public class UserDto
141
+ {
142
+ public int Id { get; set; }
143
+ public string Name => FullName;
144
+ public string FullName { get; set; } = string.Empty;
145
+ public string Email { get; set; } = string.Empty;
146
+ public string? PhoneNumber { get; set; }
147
+ public string Role { get; set; } = string.Empty;
148
+ public bool IsActive { get; set; }
149
+ public string? StudentId { get; set; }
150
+ public string? Address { get; set; }
151
+ public DateTime CreatedAt { get; set; }
152
+ }
153
+
154
+ public class UserCreateRequest
155
+ {
156
+ public string FullName { get; set; } = string.Empty;
157
+ public string Email { get; set; } = string.Empty;
158
+ public string Password { get; set; } = string.Empty;
159
+ public string? PhoneNumber { get; set; }
160
+ public string? StudentId { get; set; }
161
+ public string? Address { get; set; }
162
  }
163
 
164
+ public class UserUpdateRequest
165
+ {
166
+ public string FullName { get; set; } = string.Empty;
167
+ public string? PhoneNumber { get; set; }
168
+ public string? StudentId { get; set; }
169
+ public string? Address { get; set; }
170
+ }
171
+
172
+ public class UserRoleUpdateRequest
173
+ {
174
+ public string Role { get; set; } = string.Empty;
175
  }
176
  }
LibraryManagement.Backend/Program.cs CHANGED
@@ -2,25 +2,31 @@ using System.Text;
2
  using Database.Models;
3
  using LibraryManagement.Backend.Features.Auth;
4
  using LibraryManagement.Backend.Features.Users;
 
5
  using Microsoft.AspNetCore.Authentication.JwtBearer;
6
  using Microsoft.EntityFrameworkCore;
7
  using Microsoft.IdentityModel.Tokens;
8
  using Microsoft.OpenApi.Models;
 
 
 
9
 
10
  var builder = WebApplication.CreateBuilder(args);
11
 
12
  builder.Services.AddControllers();
 
13
  builder.Services.AddEndpointsApiExplorer();
14
  builder.Services.AddSwaggerGen(c =>
15
  {
16
  c.SwaggerDoc("v1", new OpenApiInfo { Title = "Library Management API", Version = "v1" });
17
  c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
18
  {
19
- Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
20
  Name = "Authorization",
21
  In = ParameterLocation.Header,
22
- Type = SecuritySchemeType.ApiKey,
23
- Scheme = "Bearer"
 
24
  });
25
  c.AddSecurityRequirement(new OpenApiSecurityRequirement
26
  {
@@ -44,6 +50,10 @@ builder.Services.AddDbContext<LibraryManagementContext>(options =>
44
  // Register Services
45
  builder.Services.AddScoped<IAuthService, AuthService>();
46
  builder.Services.AddScoped<IUserService, UserService>();
 
 
 
 
47
 
48
  // Configure JWT Authentication
49
  var key = Encoding.ASCII.GetBytes(builder.Configuration["Jwt:Secret"] ?? "YourSuperSecretKeyForLibraryManagementSystem_AtLeast32CharsLong");
 
2
  using Database.Models;
3
  using LibraryManagement.Backend.Features.Auth;
4
  using LibraryManagement.Backend.Features.Users;
5
+ using LibraryManagement.Backend.Features.Books;
6
  using Microsoft.AspNetCore.Authentication.JwtBearer;
7
  using Microsoft.EntityFrameworkCore;
8
  using Microsoft.IdentityModel.Tokens;
9
  using Microsoft.OpenApi.Models;
10
+ using LibraryManagement.Backend.Features.Subscriptions;
11
+ using LibraryManagement.Backend.Features.Categories;
12
+ using LibraryManagement.Backend.Features.Borrowings;
13
 
14
  var builder = WebApplication.CreateBuilder(args);
15
 
16
  builder.Services.AddControllers();
17
+ builder.Services.AddAuthorization();
18
  builder.Services.AddEndpointsApiExplorer();
19
  builder.Services.AddSwaggerGen(c =>
20
  {
21
  c.SwaggerDoc("v1", new OpenApiInfo { Title = "Library Management API", Version = "v1" });
22
  c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
23
  {
24
+ Description = "JWT Authorization header using the Bearer scheme.",
25
  Name = "Authorization",
26
  In = ParameterLocation.Header,
27
+ Type = SecuritySchemeType.Http,
28
+ Scheme = "bearer",
29
+ BearerFormat = "JWT"
30
  });
31
  c.AddSecurityRequirement(new OpenApiSecurityRequirement
32
  {
 
50
  // Register Services
51
  builder.Services.AddScoped<IAuthService, AuthService>();
52
  builder.Services.AddScoped<IUserService, UserService>();
53
+ builder.Services.AddScoped<IBookService, BookService>();
54
+ builder.Services.AddScoped<ISubscriptionService, SubscriptionService>();
55
+ builder.Services.AddScoped<ICategoryService, CategoryService>();
56
+ builder.Services.AddScoped<IBorrowingService, BorrowingService>();
57
 
58
  // Configure JWT Authentication
59
  var key = Encoding.ASCII.GetBytes(builder.Configuration["Jwt:Secret"] ?? "YourSuperSecretKeyForLibraryManagementSystem_AtLeast32CharsLong");