1. Trang chủ
  2. » Kỹ Thuật - Công Nghệ

Báo cáo thực tập cơ sở PTIT A+

36 172 3

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Ứng Dụng ToDo-List
Người hướng dẫn Đỗ Thị Bích Ngọc
Trường học Học viện Công nghệ Bưu chính Viễn thông
Chuyên ngành Công nghệ Thông tin
Thể loại báo cáo
Năm xuất bản 2023
Thành phố Hà Nội
Định dạng
Số trang 36
Dung lượng 0,91 MB

Cấu trúc

  • I. Giới thiệu bài toán (3)
  • II. Bản kế hoặc từng tuần (4)
  • III. Tìm hiểu công nghệ (5)
    • 1. Tìm hiểu flutter framework (5)
    • 2. Tìm hiểu spring boot (6)
  • IV. Kết quả đã làm được (12)
  • V. Những việc chưa làm được (12)
  • VI. Đặc tả (13)
    • 2. Mô tả chức năng (14)
  • VII. Class diagram (24)
  • VIII. Thiết kế cơ sở dữ liệu (25)
  • IX. Kiến trúc code (26)
    • 1. Kiến trúc code backend (26)
  • X. Triển khai (28)
    • 1. Backend (28)

Nội dung

Báo cáo môn thực tập cơ sở cô Ngọc PTIT đã được A+. Báo cáo ứng dụng to do list gồm 2 phần app và be ........................................................................................................................................................................................................................................................................................

Giới thiệu bài toán

Ứng dụng To-Do List giúp người dùng quản lý và theo dõi hiệu quả các dự án, nhiệm vụ và ghi chú của mình.

Ứng dụng này áp dụng phương pháp Pomodoro, một kỹ thuật quản lý thời gian hiệu quả, giúp người dùng tăng cường sự tập trung và nâng cao năng suất làm việc.

− Tính năng của ứng dụng cho phép người dùng thêm, sửa đổi và xóa các dự án, nhiệm vụ và ghi chú.

− Mỗi dự án có thể chứa nhiều nhiệm vụ và nhiều ghi chú.

Mỗi nhiệm vụ đều được gán một số lượng Pomodoro, đại diện cho đơn vị thời gian cần thiết để hoàn thành nhiệm vụ đó.

− Khi người dùng bắt đầu một nhiệm vụ, họ có thể chọn phương pháp luận

Phương pháp Pomodoro giúp theo dõi thời gian làm việc hiệu quả Mỗi nhiệm vụ chỉ nên sử dụng tối đa 5 Pomodoro để đạt hiệu suất cao nhất Nếu cần nhiều thời gian hơn, hãy chia nhỏ công việc thành các nhiệm vụ ngắn hơn.

− Trong ứng dụng, hiển thị thời gian đã sử dụng cho mỗi Pomodoro và thời gian còn lại cho nhiệm vụ hiện tại.

Người dùng có thể dễ dàng tạm dừng và tiếp tục nhiệm vụ bất kỳ lúc nào, trong khi ứng dụng tự động cập nhật thời gian sử dụng.

− Ứng dụng cũng cung cấp các chức năng nhắc nhở giúp người dùng không bỏ sót bất kỳ nhiệm vụ nào trong danh sách To-Do List.

Người dùng có thể tạo thông báo nhắc nhở cho các nhiệm vụ trong danh sách To-Do List, giúp họ không bỏ lỡ việc hoàn thành những nhiệm vụ quan trọng.

Ứng dụng cho phép người dùng dễ dàng tùy chỉnh và điều chỉnh thông tin liên quan đến dự án, nhiệm vụ và ghi chú.

− Tính năng này giúp người dùng linh hoạt trong việc quản lý và sắp xếp các công việc của mình.

− Tính năng đặc biệt của ứng dụng là khả năng tính toán số lượng Pomodoro cần thiết để hoàn thành nhiệm vụ.

− Điều này giúp người dùng lên kế hoạch và quản lý thời gian một cách hiệu quả hơn, từ đó nâng cao hiệu suất làm việc.

Ứng dụng quản lý công việc To-Do List kết hợp với phương pháp Pomodoro là một công cụ hiệu quả giúp người dùng dễ dàng quản lý, theo dõi các dự án và nhiệm vụ, cũng như ghi chú một cách có tổ chức.

Sự kết hợp giữa phương pháp Pomodoro và tính năng nhắc nhở giúp người dùng tăng cường khả năng tập trung và nâng cao năng suất làm việc, từ đó cải thiện hiệu quả công việc một cách rõ rệt.

Bản kế hoặc từng tuần

4 thêm kếhoạch làm lại tuần 3, lý do làm chưađủ, làm sai

10 vì xácđịnh nhầm kế hoạch không thểdùng firebase trước khi viết api nếu làm trước sẽ mất công làm lại

Update 19/03 thayđổi kếhoạch vìước tính thời gian khôngđủ

Tìm hiểu Figma để thiết kế giao diện

Xác định các thực thể Quan hệ vẽ ERD

4 Thiết kế giao diện thêm làm lại tuần 3

Code giao diện và fake data. Đăng kí đăng nhập, thêm sửa xoá project, thêm sửa xoá note,

In week 6, we will focus on integrating the features for adding, editing, and deleting projects, notes, and tasks, alongside the development of a PostgreSQL database to manage these tasks effectively.

- Xử lý thông báo sử dụng firebase cloud messaging

Tìm hiểu và dựng db postgresql Thêm code giao diện

7 Tìm hiểu và dựng db postgresql Tìm hiểu spring, restful api.

Tìm hiểu spring, restful api.

Tìm hiểu authen spring phân quyền.

Tìm hiểu authen spring phân quyền.

- Xử lý thông báo sử dụng firebase cloud messaging Tuần

Tìm hiểu công nghệ

Tìm hiểu flutter framework

− Flutter là một framework dựa trên ngôn ngữ lập trình dart, hỗ trợ lập trình mobile app nhiều nền tảng (cross-platform) chỉ trên 1 code base.

Trong Flutter, tất cả các thành phần giao diện của ứng dụng được gọi là widget Để xây dựng một màn hình, chúng ta sử dụng widget Scaffold, trong đó chứa các thành phần cần thiết cho giao diện.

Các widget được chia thành hai loại chính: một loại để định nghĩa bố cục và một loại để hiển thị nội dung Các widget định nghĩa bố cục đóng vai trò quan trọng trong việc tổ chức giao diện của trang.

▪ Column: đặt các thành phần view trong Column theo chiều dọc.

▪ Row: đặt các thành phần view trong Row theo chiều ngang.

▪ Stack: cho phép các thành phần view nằm đè lên nhau. o Widget để hiển thị nội dung như:

▪ Text: hiển thị nội dung chữ.

▪ Button: cho phép hành động click.

Tìm hiểu spring boot

− Spring boot là một framework trong java framework.

− Là một framework mạnh mẽ và phổ biến, giúp rút ngắn thời gian phát triển, giảm sự rườm rà và tăng tính ổn định.

− Các annotation trong Spring Boot

Annotation @Component được sử dụng để đánh dấu một lớp trong Spring, giúp framework nhận diện nó như một Bean Khi ứng dụng Spring khởi động, nó sẽ quét tất cả các lớp trong cùng cấp hoặc trong các package con của lớp đã chỉ định Trong quá trình này, khi gặp lớp được đánh dấu, Spring sẽ tạo một instance và thêm nó vào ApplicationContext để quản lý Tất cả các Bean được quản lý trong ApplicationContext đều có kiểu Singleton.

The @ComponentScan annotation in Spring Boot enables the framework to scan and detect all classes within specified packages, such as "file1" and "file2," as well as their sub-packages, creating beans from the identified classes.

− @SpringBootApplication(scanBasePackages = {"file1", "file2"} ) o Ngoài ra ta có thể Config trực tiếp nó bên trong

− @DeleteMapping("{id}") o Response delete(@PathVariable("id") Long id){

The example demonstrates the use of the @GetMapping annotation in a Spring application to define a RESTful API endpoint for retrieving user information by ID The method getById(@PathVariable String id) utilizes @PathVariable to capture the ID from the URI, while the method get(@PathVariable(required = false) String id) shows that the ID is optional, allowing the endpoint to function even if the ID is not provided, adhering to the previously defined path.

The @RequestBody and @ResponseBody annotations are essential for mapping data between the client and server in a Spring application The @RequestBody annotation indicates that a method parameter must be bound to the body of the HTTP request, facilitating the conversion of the request body into domain objects (POJOs) that match the data types of the domain class Meanwhile, the @ResponseBody annotation informs the controller that the Java object returned to the client should be automatically converted to JSON and included in the HTTP response, and it should be placed on the method that returns the response.

The @Autowired annotation signals to Spring that it should automatically inject the corresponding Bean into the marked location Once a class with the @Component annotation is identified, the Bean injection process is initiated.

● Nếu Class đó KHÔNG có: Constructor + Setter, sử dụng Java Reflection để đưa đối tượng vào thuộc tính có đánh dấu

● Nếu có hàm ConstructorL: sẽ Inject Bean vào bởi tham số của hàm

When a Setter method is present, a Bean will be injected through the method's parameter All Beans managed within the ApplicationContext are created only once, and when a Class requests an @Autowired dependency, the existing object from the ApplicationContext is used for injection.

Annotation @Repository giúp đánh dấu các tầng trong ứng dụng, và bản chất của @Service, @Repository, và @Component là tương đương nhau Mặc dù có thể thay đổi cho nhau, nhưng nên sử dụng tên gọi đúng với từng tầng để dễ dàng bảo trì và làm cho mã nguồn trở nên rõ ràng hơn.

− @RestController o Là sự kết hợp của @Controller + @ResponseBody ( tức ta sẽ không cần thêm @ResponseBody lên trên của method )

Annotation @Configuration đánh dấu một lớp, cho phép Spring Boot (SB) nhận biết đây là nơi định nghĩa các Bean Nó cũng là một @Component, nhưng có ý nghĩa sử dụng khác Bên cạnh việc tìm kiếm các lớp được đánh dấu bằng @Component, SB còn tìm các lớp được đánh dấu @Configuration để tạo ra đối tượng Sau đó, SB sẽ tìm các phương thức được đánh dấu @Bean trong đối tượng vừa tạo ra, thực hiện gọi các phương thức đó để lấy ra các Bean và đưa vào Context.

Annotation @Bean trên các phương thức cho phép Spring Boot nhận diện và đưa đối tượng vào Context Nó thường được sử dụng trong các lớp được đánh dấu bằng @Configuration Nếu phương thức có tham số, Spring Boot sẽ tự động quản lý các tham số này khi tạo Bean.

SB tự động Inject các Bean đã có trong Context vào làm tham số.

The Controller layer is responsible for processing incoming HTTP requests and generating responses It utilizes two annotations to designate a class for the purpose of serving as the Controller Layer.

− Phần khoanh là phần xử lý controller. o B1: Request được nhận bởi DispatcherServlet o DispatcherServlet chịu trách nghiệm xử lí mọi Request tới + map Request tới Controller method

Annotation @RequestBody giúp tự động ánh xạ nội dung của HTTP Request Body thành đối tượng Java Đối tượng đi kèm với annotation này sẽ nhận giá trị sau khi quá trình ánh xạ từ Body sang đối tượng Java hoàn tất Tuy nhiên, cần đảm bảo rằng kiểu dữ liệu của đối tượng được gán với annotation này phải tương thích với định dạng JSON được gửi kèm trong HTTP Request.

− @ResponseBody o @Annotation nói với Controller rằng object được trả về tự động bằng cách map Java object -> JSON/ HttpResponse object

The @RestController annotation combines the functionalities of @Controller and @RequestMapping, enabling it to handle requests at both the class and method levels This annotation maps incoming requests to specific controller methods, processing the path, headers, and HTTP methods involved It is commonly used in conjunction with other annotations to enhance its capabilities.

Để xây dựng một web server, chúng ta có thể thực hiện toàn bộ mã trong lớp Controller Tuy nhiên, để giảm độ phức tạp và dễ dàng bảo trì hoặc nâng cấp, thông thường, kiến trúc sẽ được chia thành ba lớp.

− Controller layer (đã nói ở trên)

The service layer is responsible for processing business logic and managing data flow between external clients and internal databases It receives Input Data Transfer Objects (DTOs) from the controller via method parameters, maps them to entities, and performs operations such as create, update, delete, and retrieve from the database After processing, it returns Output DTOs to the controller, which then sends the response back to the client A class mapper facilitates the conversion between DTOs and entities, ensuring seamless data handling.

Repository layer là phần giao tiếp trực tiếp với cơ sở dữ liệu, nhận các yêu cầu từ service layer, truy xuất dữ liệu và trả lại kết quả cho service layer để xử lý.

− ORM o ORM ( Object relational Mapping ) là 1 quá trình mapping dữ liệu từ Java object CSDL và ngược lại o ORM giúp mình ánh xạ các Tables, Columns, DataType, Table

Kết quả đã làm được

Sử dụng Java Spring Boot làm backend cho ứng dụng, bao gồm các chức năng như đăng nhập, đăng ký và cập nhật thông tin người dùng Ngoài ra, ứng dụng còn hỗ trợ các thao tác CRUD cho dự án, nhiệm vụ và ghi chú, giúp quản lý thông tin một cách hiệu quả và dễ dàng.

Sử dụng Flutter framework để phát triển ứng dụng di động tích hợp các API từ backend, cho phép giao tiếp hiệu quả với cơ sở dữ liệu Ứng dụng cung cấp các tính năng như đăng nhập, đăng xuất, đăng ký, xem và cập nhật thông tin người dùng Ngoài ra, ứng dụng còn hỗ trợ các chức năng CRUD cho Project, Task và Note, cùng với việc áp dụng phương pháp luận Pomodoro để quản lý nhiệm vụ hiệu quả.

Những việc chưa làm được

Triển khai thông báo đẩy gần đến hạn công việc là một giải pháp hiệu quả để nhắc nhở người dùng Tuy nhiên, Firebase Cloud Messaging không hỗ trợ tính năng này theo lịch đã định sẵn Để thực hiện, cần sử dụng cronjob, nhưng do thời gian hạn chế và độ phức tạp của công việc, việc triển khai không thể thực hiện được.

Đặc tả

Mô tả chức năng

Tên chức năng Xem danh sách project.

Tác nhân User Điều kiện trước Người dùng được phân quyền và đăng nhập vào ứng dụng thành công.

1 Người dùng đăng nhập vào ứng dụng.

2 Người dùng chuyển sang tab project.

3 Ứng dụng hiển thị danh sách tất cả các project hiện có của người dùng.

Tên chức năng Xem chi tiết project.

Tác nhân User Điều kiện trước Người dùng được phân quyền và đăng nhập vào ứng dụng thành công.

1 Người dùng đăng nhập vào ứng dụng.

2 Người dùng chuyển sang tab project.

3 Ứng dụng hiển thị danh sách tất cả các project hiện có của người dùng.

4 Người dùng chọn một project muốn xem.

5 Ứng dụng hiển thị thông tin chi tiết của project kèm các task của project.

Tên chức năng Tạo project.

Tác nhân User Điều kiện trước Người dùng được phân quyền và đăng nhập vào ứng dụng thành công.

1 Người dùng đăng nhập vào ứng dụng.

2 Người dùng ấn vào floating action button.

3 Ứng dụng hiện lên 3 lựa chọn tạo project, tạo task, tạo note.

4 Người dùng chọn nút tạo project.

5 Ứng dụng chuyển đến màn hình tạo project mới.

6 Người dùng nhập thông tin project.

7 Người dùng ấn nút lưu.

8 Ứng dụng kiểm tra dữ liệu người dùng nhập vào và lưu lại.

Tên chức năng Chỉnh sửa project.

Tác nhân User Điều kiện trước Người dùng được phân quyền và đăng nhập vào ứng dụng thành công.

1 Người dùng đăng nhập vào ứng dụng.

2 Người dùng chuyển sang tab project.

3 Ứng dụng hiển thị danh sách các project.

4 Người dùng chọn nút show more trên góc phải trên cùng của một item project.

6 Ứng dụng chuyển đến màn hình chỉnh sửa thông tin project.

7 Người dùng chỉnh sửa thông tin project.

8 Người dùng ấn nút lưu.

9 Ứng dụng kiểm tra dữ liệu người dùng nhập vào và lưu lại.

Luồng thay thế 4a Người dùng ấn vào project muốn sửa.

4a.1 Ứng dụng chuyển đến màn hình thông tin chi tiết project.

4a.2 Người dùng chọn nút show more ở góc trái trên cùng màn hình

Use case tiếp tục bước 5.

Tên chức năng Xóa project.

Tác nhân User Điều kiện trước Người dùng được phân quyền và đăng nhập vào ứng dụng thành công.

1 Người dùng đăng nhập vào ứng dụng.

2 Người dùng chuyển sang tab project.

3 Ứng dụng hiển thị danh sách các project.

4 Người dùng chọn nút show more trên góc phải trên cùng của một item project.

6 Ứng dụng xóa project khỏi cơ sở dữ liệu

Tên chức năng Xem danh sách task.

Tác nhân User Điều kiện trước Người dùng được phân quyền và đăng nhập vào ứng dụng thành công.

1 Người dùng đăng nhập vào ứng dụng.

2 Ứng dụng hiển thị danh sách task có trong ngày hôm nay, tối đa 5 task.

3 Người dùng ấn vào see all để xem tất cả.

4 Ứng dụng hiển thị tất cả các task có trong ngày hôm nay.

Luồng thay thế 3a Người dùng chọn tab project.

3a.1 Ứng dụng hiển thị danh sách các project.

3a.2 Người dùng chọn một project.

3a.3 Ứng dụng hiển thị danh sách task trong project. 3b Người dùng chọn tab calendar.

3b.1 Ứng dụng hiển thị danh sách task có trong ngày đang chọn.

3b.3 Ứng dụng cập nhật danh sách task theo ngày chọn.

Tên chức năng Xem chi tiết task.

Tác nhân User Điều kiện trước Người dùng được phân quyền và đăng nhập vào ứng dụng thành công.

1 Người dùng đăng nhập vào ứng dụng.

2 Người dùng chọn task cần xem chi tiết.

3 Ứng dụng chuyển đến màn hình thông tin chi tiết task.

Tên chức năng Tạo task.

Tác nhân User Điều kiện trước Người dùng được phân quyền và đăng nhập vào ứng dụng thành công.

1 Người dùng đăng nhập vào ứng dụng.

2 Người dùng ấn vào floating action button.

3 Ứng dụng hiện lên 3 lựa chọn tạo project, tạo task, tạo note.

4 Người dùng chọn nút tạo task.

5 Ứng dụng chuyển đến màn hình tạo task mới.

6 Người dùng nhập thông tin task.

7 Người dùng ấn nút lưu.

8 Ứng dụng kiểm tra dữ liệu người dùng nhập vào và lưu lại.

Tên chức năng Chỉnh sửa task.

Tác nhân User Điều kiện trước Người dùng được phân quyền và đăng nhập vào ứng dụng thành công.

1 Người dùng đăng nhập vào ứng dụng.

2 Người dùng tìm task cần chỉnh sửa.

3 Người dùng chọn nút show more trên góc phải trên cùng của một item task.

5 Ứng dụng chuyển đến màn hình chỉnh sửa thông tin task.

6 Người dùng chỉnh sửa thông tin task.

7 Người dùng ấn nút lưu.

8 Ứng dụng kiểm tra dữ liệu người dùng nhập vào và lưu lại.

Luồng thay thế 3a Người dùng ấn vào task muốn sửa.

3a.1 Ứng dụng chuyển đến màn hình thông tin chi tiết task.

3a.2 Người dùng chọn nút show more ở góc trái trên cùng màn hình

Use case tiếp tục bước 4.

Tên chức năng Xóa task.

Tác nhân User Điều kiện trước

Người dùng được phân quyền và đăng nhập vào ứng dụng thành công.

1 Người dùng đăng nhập vào ứng dụng.

2 Người dùng tìm đến task cần xóa.

3 Người dùng chọn nút show more trên góc phải trên cùng của một item task.

5 Ứng dụng xóa task khỏi cơ sở dữ liệu

Tên chức năng Xem danh sách note.

Tác nhân User Điều kiện trước Người dùng được phân quyền và đăng nhập vào ứng dụng thành công.

5 Người dùng đăng nhập vào ứng dụng.

6 Ứng dụng hiển thị danh sách tất cả các note, tối đa 5 task.

7 Người dùng ấn vào see all để xem tất cả.

8 Ứng dụng hiển thị tất cả các note của người dùng.

Tên chức năng Xem chi tiết note.

Tác nhân User Điều kiện trước Người dùng được phân quyền và đăng nhập vào ứng dụng thành công.

1 Người dùng đăng nhập vào ứng dụng.

2 Người dùng chọn note cần xem chi tiết.

3 Ứng dụng chuyển đến màn hình thông tin chi tiết note.

Tên chức năng Tạo note.

Tác nhân User Điều kiện trước Người dùng được phân quyền và đăng nhập vào ứng dụng thành công.

1 Người dùng đăng nhập vào ứng dụng.

2 Người dùng ấn vào floating action button.

3 Ứng dụng hiện lên 3 lựa chọn tạo project, tạo task, tạo note.

4 Người dùng chọn nút tạo note.

5 Ứng dụng chuyển đến màn hình tạo note mới.

6 Người dùng nhập thông tin note.

7 Người dùng ấn nút lưu.

8 Ứng dụng kiểm tra dữ liệu người dùng nhập vào và lưu lại.

Tên chức năng Chỉnh sửa note.

Tác nhân User Điều kiện trước Người dùng được phân quyền và đăng nhập vào ứng dụng thành công.

1 Người dùng đăng nhập vào ứng dụng.

2 Người dùng tìm note cần chỉnh sửa.

3 Người dùng ấn vào note muốn sửa.

4 Ứng dụng chuyển đến màn thông tin chi tiết note.

5 Người dùng chọn nút show more trên góc phải trên cùng của màn hình.

7 Ứng dụng chuyển đến màn hình chỉnh sửa thông tin note.

8 Người dùng chỉnh sửa thông tin note.

9 Người dùng ấn nút lưu.

10 Ứng dụng kiểm tra dữ liệu người dùng nhập vào và lưu lại.

Tên chức năng Xóa note.

Tác nhân User Điều kiện trước

Người dùng được phân quyền và đăng nhập vào ứng dụng thành công.

1 Người dùng đăng nhập vào ứng dụng.

2 Người dùng tìm đến note cần xóa.

3 Người dùng ấn vào note muốn xóa.

4 Ứng dụng chuyển đến màn thông tin chi tiết note.

5 Người dùng chọn nút show more trên góc phải trên cùng của màn hình.

7 Ứng dụng xóa note khỏi cơ sở dữ liệu.

Kiến trúc code

Kiến trúc code backend

Kiến trúc 3 layer trong ứng dụng Spring Boot bao gồm ba lớp chính: lớp giao diện (Presentation layer), lớp xử lý logic nghiệp vụ (Business layer), và lớp truy cập dữ liệu (Data access layer) Lớp giao diện, hay controller, nhận các yêu cầu từ client và trả về phản hồi Lớp xử lý logic nghiệp vụ nhận yêu cầu từ lớp giao diện, thực hiện các xử lý cần thiết và có thể giao tiếp với lớp truy cập dữ liệu để lấy thông tin Lớp truy cập dữ liệu tương tác với cơ sở dữ liệu hoặc các nguồn dữ liệu khác, thực hiện truy vấn và trả dữ liệu về cho lớp xử lý nghiệp vụ Mỗi lớp chỉ nên giao tiếp với lớp liền kề, ví dụ, lớp giao diện chỉ giao tiếp với lớp xử lý nghiệp vụ, không trực tiếp với lớp truy cập dữ liệu Việc tuân thủ kiến trúc này giúp tách biệt các phần của ứng dụng, dễ bảo trì, giảm sự phụ thuộc và tăng khả năng mở rộng Cấu trúc thư mục trong ứng dụng Spring Boot theo kiến trúc 3 layer có thể được tổ chức như sau: com.example.myapp.

Kiến trúc Clean Architecture trong ứng dụng Flutter, do Uncle Bob phát minh, nhằm tạo ra hệ thống dễ bảo trì, tái sử dụng và kiểm thử Kiến trúc này tách biệt rõ ràng các thành phần của hệ thống, giúp giảm sự phụ thuộc và tăng tính linh hoạt Clean Architecture được chia thành ba lớp chính: presentation, domain và data Lớp presentation triển khai các thành phần view và giao tiếp với dữ liệu qua lớp domain Lớp domain xử lý logic nghiệp vụ của ứng dụng mà không phụ thuộc vào framework, định nghĩa các phương thức abstract để truy vấn dữ liệu Lớp data triển khai các phương thức truy vấn dữ liệu đã được định nghĩa ở lớp domain, giúp ứng dụng dễ kiểm thử và bảo trì Nếu nguồn dữ liệu thay đổi, chỉ cần sửa ở lớp data mà không ảnh hưởng đến domain hay presentation Tương tự, bất kỳ thay đổi nào ở lớp presentation cũng chỉ cần sửa tại đó mà không cần điều chỉnh hai lớp còn lại Cấu trúc thư mục của kiến trúc Clean Architecture sẽ được tổ chức trong thư mục lib.

│ ├── pages // Các trang UI và widget

│ │ │── view_model // model sử dụng để hiển thị lên UI

│ └── routes // Quản lý điều hướng giữa các trang domain

├── entities // Các đối tượng cốt lõi của ứng dụng

├── repositories // Các lớp định nghĩa ra các phương thức thao tác với dữ liệu

└── usecases // Các trường hợp sử dụng chứa logic nghiệp vụ data

├── repositories // Triển khai các phương thức lưu trữ dữ liệu

├── data_sources // Các nguồn dữ liệu như API, cơ sở dữ liệu cục bộ

└── dto // Mô hình dữ liệu sử dụng trong ứng dụng

Triển khai

Backend

@Override public Pagination getAllByUserId(Long userId, Pageable pageable, SearchProjectInputDto searchProject) {

Pagination pageOutputDto = new Pagination();

The code snippet retrieves a paginated list of project entities based on a user's ID and optional search parameters, including a keyword and status It processes the retrieved projects into a format suitable for output by mapping each project entity to a corresponding output DTO Finally, it sets the total number of elements found in the results and returns the compiled output DTO.

@Override public ProjectOutputDto getProjectById(Long projectId, Long userId) { if (!isProjectExist(projectId, userId)) { throw Errors.PROJECT_NOT_FOUND;

ProjectEntity entity = projectRepository.findById(projectId).get(); return getProjectOutputDtoFromProjectEntity(entity);

@Override public ProjectOutputDto createProject(ProjectInputDto projectInputDto, Long userId) {

ProjectEntity projectEntity = projectMapper.getProjectEntityFromProjectInputDto(projectInputDto); projectEntity.setUserId(userId); if (projectEntity.getDeadline().isBefore(OffsetDateTime.now())) { throw Errors.PROJECT_DEADLINE_IS_BEFORE_NOW;

} return getProjectOutputDtoFromProjectEntity(projectRepository.save(projectEntity)); }

@Override public ProjectOutputDto updateProject(ProjectInputDto projectInputDto, Long projectId, Long userId) { if (!isProjectExist(projectId, userId)) { throw Errors.PROJECT_NOT_FOUND;

ProjectEntity projectEntity = projectMapper.getProjectEntityFromProjectInputDto(projectInputDto); if (projectEntity.getDeadline().isBefore(OffsetDateTime.now())) { throw Errors.PROJECT_DEADLINE_IS_BEFORE_NOW;

} projectEntity.setUserId(userId); projectEntity.setId(projectId); return getProjectOutputDtoFromProjectEntity(projectRepository.save(projectEntity)); }

@Override public void deleteProject(Long projectId, Long userId) { if (!isProjectExist(projectId, userId)) { throw Errors.PROJECT_NOT_FOUND;

} taskRepository.deleteByProjectId(projectId); noteRepository.deleteByProjectId(projectId); projectRepository.deleteById(projectId);

@Override public ProjectOutputDto getProjectOutputDtoFromProjectEntity(ProjectEntity projectEntity) {

ProjectOutputDto projectOutputDto = projectMapper.getProjectOutputDtoFromProjectEntity(projectEntity); projectOutputDto.setCountAllTask(taskRepository.countAllByProjectId(projectE ntity.getId())); projectOutputDto.setCountDoneTask(taskRepository.countAllByProjectIdAndStatu s(projectEntity.getId(), Status.DONE)); projectOutputDto.setProgress((double)

(projectOutputDto.getCountDoneTask() * 100) / projectOutputDto.getCountAllTask())); return projectOutputDto;

@Override public boolean isProjectExist(Long projectId, Long userId) { return projectRepository.existsByIdAndUserId(projectId, userId);

@Override public TaskDetailOutputDto getTaskDetail(Long taskId, Long userId) { if (!isTaskExist(taskId, userId)) throw Errors.TASK_NOT_FOUND;

TaskEntity taskEntity = taskRepository.findById(taskId).orElseThrow(() -> Errors.TASK_NOT_FOUND); return getTaskDetailOutputDtoFromTaskEntity(taskEntity);

@Override public Pagination getTasksByUserId(Long userId, Pageable pageable, SearchTaskInputDto searchTaskInputDto) {

Pagination pagination = new Pagination();

The `taskRepository` retrieves task entities based on the user ID and various search criteria, including a keyword, deadline, and status If the keyword is not provided, an empty string is used The deadline is converted to UTC format if specified The results are paginated, with the items mapped to a specific output format, and the total number of elements is recorded Finally, the pagination object is returned with the filtered task data.

@Override public TaskDetailOutputDto createTask(TaskInputDto taskInputDto, Long userId) { TaskEntity taskEntity = taskMapper.getTaskEntityFromTaskInputDto(taskInputDto); validate(taskEntity, userId); return getTaskDetailOutputDtoFromTaskEntity(taskRepository.save(taskEntity));

@Override public TaskDetailOutputDto updateTask(TaskInputDto taskInputDto, Long taskId, Long userId) {

TaskEntity taskEntity = taskMapper.getTaskEntityFromTaskInputDto(taskInputDto); taskEntity.setId(taskId); validate(taskEntity, userId); return getTaskDetailOutputDtoFromTaskEntity(taskRepository.save(taskEntity));

@Override public void deleteTask(Long taskId, Long userId) { if (!isTaskExist(taskId, userId)) throw Errors.TASK_NOT_FOUND; taskRepository.deleteById(taskId);

@Override public TaskDetailOutputDto getTaskDetailOutputDtoFromTaskEntity(TaskEntity taskEntity) {

The `taskDetailOutputDto` is created by mapping the `taskEntity`, and the project is retrieved using the project ID If the `currentDoingTime` of the task is null, the method returns the `taskDetailOutputDto` The total time allocated for the task is calculated based on the number of Pomodoros, each lasting 25 minutes The current minutes spent on the task are compared to the total minutes, ensuring the current time does not exceed the total Finally, the progress percentage is calculated and set in the `taskDetailOutputDto` before returning it.

@Override public TaskOutputDto getTaskOutputDtoFromTaskEntity(TaskEntity taskEntity) {

The taskOutputDto is created by mapping the taskEntity, which calculates the total time based on the number of Pomodoros, each lasting 25 minutes If the current doing time is not set, the taskOutputDto is returned without progress However, if current doing time is available, the current minutes are compared to the total minutes, ensuring it does not exceed the total Finally, the progress percentage is calculated and set in the taskOutputDto before returning it.

} private void validate(TaskEntity taskEntity, Long userId) { if (taskEntity.getId() != null && !isTaskExist(taskEntity.getId(), userId)) throw Errors.TASK_NOT_FOUND; if (taskEntity.getDeadline().isBefore(OffsetDateTime.now())) throw

Errors.TASK_DEADLINE_IS_BEFORE_NOW; if (!projectRepository.existsByIdAndUserId(taskEntity.getProjectId(), userId)) throw Errors.PROJECT_NOT_FOUND;

ProjectEntity projectEntity = projectRepository.findById(taskEntity.getProjectId()).orElseThrow(() ->

Errors.PROJECT_NOT_FOUND); if (taskEntity.getDeadline().isAfter(projectEntity.getDeadline())) throw Errors.PROJECT_DEADLINE_IS_BEFORE_TASK_DEADLINE; if (projectEntity.getStatus() == Status.DONE) { throw Errors.PROJECT_STATUS_IS_DONE;

} else if (projectEntity.getStatus() == Status.TODO && taskEntity.getStatus() != Status.TODO && taskEntity.getStatus() != null) { throw Errors.PROJECT_STATUS_IS_TODO;

@Override public boolean isTaskExist(Long taskId, Long userId) { return taskRepository.existsByIdAndUserId(taskId, userId);

@Override public boolean isTaskExist(Long taskId) { return taskRepository.existsById(taskId);

@Override public Pagination getTaskByProjectId(Long userId, Pageable pageable, SearchTaskInputDto search, Long projectId) {

Pagination pagination = new Pagination();

The code snippet demonstrates how to retrieve task entities from a project using a search repository It utilizes the project ID, keyword, user ID, deadline, and status to filter the tasks The results are then mapped to a DTO format and stored in a pagination object, which also includes the total number of elements retrieved This approach ensures efficient data handling and organization for displaying tasks based on user-defined criteria.

@Override public void doTask(Long id, Long userId, DoTaskInputDto doTaskInputDto) {

TaskEntity taskEntity = taskRepository.findById(id).orElseThrow(() ->

Errors.TASK_NOT_FOUND); if (taskEntity.getStatus() == Status.DONE) throw

To update the status of a task, first check if it is in the 'TODO' state; if so, change it to 'IN_PROGRESS' Next, set the current time spent on the task based on the input data If the total time spent reaches or exceeds the calculated Pomodoro duration, mark the task as 'DONE' Finally, save the updated task entity in the repository.

} private ProjectOutputDto getProjectById(Long id) {

ProjectEntity projectEntity = projectRepository.findById(id).orElseThrow(() -> Errors.PROJECT_NOT_FOUND); return projectMapper.getProjectOutputDtoFromProjectEntity(projectEntity); }

@Override public NoteDetailOutputDto getNoteDetail(Long noteId, Long userId) { if (!isNoteExist(noteId, userId)) { throw Errors.NOTE_NOT_FOUND;

NoteEntity noteEntity = noteRepository.findById(noteId).orElseThrow(() -> Errors.NOTE_NOT_FOUND); return getNoteDetailOutputDtoFromNoteEntity(noteEntity);

@Override public Pagination getNotesByUserId(Long userId, Pageable pageable, SearchNoteInputDto searchNoteInputDto) {

Pagination pagination = new Pagination();

Page noteEntities = noteRepository.searchInUser(userId, searchNoteInputDto.getKeyword() != null ? searchNoteInputDto.getKeyword() : "", pageable); pagination.setItems(noteEntities.stream().map(noteMapper::getNoteOutputDtoFromN oteEntity).collect(Collectors.toList())); pagination.setTotals(noteEntities.getTotalElements()); return pagination;

@Override public NoteDetailOutputDto createNote(NoteInputDto noteInputDto, Long userId) { NoteEntity noteEntity = noteMapper.getNoteEntityFromNoteInputDto(noteInputDto); validate(noteEntity, userId); return getNoteDetailOutputDtoFromNoteEntity(noteRepository.save(noteEntity));

@Override public NoteDetailOutputDto updateNote(NoteInputDto noteInputDto, Long noteId, Long userId) {

NoteEntity noteEntity = noteMapper.getNoteEntityFromNoteInputDto(noteInputDto); noteEntity.setId(noteId); validate(noteEntity, userId); return getNoteDetailOutputDtoFromNoteEntity(noteRepository.save(noteEntity));

@Override public void deleteNote(Long noteId, Long userId) { if (!isNoteExist(noteId, userId)) { throw Errors.NOTE_NOT_FOUND;

@Override public boolean isNoteExist(Long noteId, Long userId) { return noteRepository.existsByIdAndUserId(noteId, userId);

@Override public boolean isNoteExist(Long noteId) { return noteRepository.existsById(noteId);

@Override public Pagination getNotesByProjectId(Long userId, Pageable pageable, SearchNoteInputDto search, Long projectId) {

Pagination pagination = new Pagination();

The note repository retrieves note entities based on the project ID and user ID, utilizing a keyword from the search criteria if available The results are then mapped to output DTOs for display Finally, pagination is updated with the total number of elements retrieved.

@Override public NoteDetailOutputDto getNoteDetailOutputDtoFromNoteEntity(NoteEntity noteEntity) {

NoteDetailOutputDto noteDetailOutputDto = noteMapper.getNoteDetailOutputDtoFromNoteEntity(noteEntity);

ProjectEntity projectEntity = projectRepository.findById(noteEntity.getProjectId()).orElseThrow(() ->

ProjectOutputDto projectOutputDto = projectMapper.getProjectOutputDtoFromProjectEntity(projectEntity); noteDetailOutputDto.setProject(projectOutputDto); return noteDetailOutputDto;

} private void validate(NoteEntity noteEntity, Long userId) { if(noteEntity.getId() != null && !isNoteExist(noteEntity.getId(), userId)) { throw Errors.NOTE_NOT_FOUND;

} if(!projectRepository.existsByIdAndUserId(noteEntity.getProjectId(), userId)) { throw Errors.PROJECT_NOT_FOUND;

@Override public UserDetailOutputDto createUser(UserInputDto userInputDto) { if(userRepository.existsUserEntitiesByUsername(userInputDto.getUsername())) { throw Errors.USERNAME_EXISTED;

UserEntity userEntity = userMapper.getUserEntityFromUserInputDto(userInputDto); return userMapper.getUserDetailOutputDtoFromUserEntity(userRepository.save(userEntity) );

@Override public UserOutputDto getUserByUserName(Long userId) { return userMapper.getUserOutputDtoFromUserEntity(userRepository.getByUserId(userId)); }

@Override public UserDetailOutputDto getUserDetail(Long userId) { return userMapper.getUserDetailOutputDtoFromUserEntity(userRepository.getByUserId(user Id));

@Override public UserDetailOutputDto updateUser(Long userId, UserInputDto userInputDto) { UserEntity userEntity = userMapper.getUserEntityFromUserInputDto(userInputDto); userEntity.setUserId(userId); return userMapper.getUserDetailOutputDtoFromUserEntity(userRepository.save(userEntity) );

- Xử lý đăng nhập đăng ký.

@Override public LoginOutputDto login(LoginInputDto loginInputDto) { if(accountService.isAccountExist(loginInputDto.getUsername())) {

String encodePassword = accountService.getAccount(loginInputDto.getUsername()).getPassword(); if(!passwordEncoder.matches(loginInputDto.getPassword(), encodePassword)) throw Errors.USERNAME_OR_PASSWORD_INCORRECT;

Authentication authentication = authenticationManager.authenticate( new UsernamePasswordAuthenticationToken( loginInputDto.getUsername(),loginInputDto.getPassword()

SecurityContextHolder.getContext().setAuthentication(authentication); String jwt = jwtService.generateToken((UserDetailEntity) authentication.getPrincipal());

String refreshJwt = jwtService.generateRefreshToken((UserDetailEntity) authentication.getPrincipal()); return new LoginOutputDto(jwt, refreshJwt);

} else { throw Errors.USERNAME_OR_PASSWORD_INCORRECT;

@Override public RegisterOutputDto register(RegisterInputDto registerInputDto) {

AccountInputDto accountInputDto = authMapper.getAccountInputDtoFromRegisterInputDto(registerInputDto);

UserInputDto userInputDto = authMapper.getUserInputDtoFromRegisterInputDto(registerInputDto);

UserDetailOutputDto userDetailOutputDto = userService.createUser(userInputDto); accountInputDto.setUserId(userDetailOutputDto.getUserId()); accountInputDto.setPassword(passwordEncoder.encode(registerInputDto.getPassword ()));

System.out.println(accountInputDto.getPassword()); accountService.createAccount(accountInputDto); accountMapper.getEntityFromInput(accountInputDto); return authMapper.getRegisterOutputDtoFromUserDetailOutputDto(userDetailOutputDto); }

@Override public LoginOutputDto refreshToken(String refreshToken) {

UserDetailEntity userDetail = (UserDetailEntity) securityService.loadUserByUsername(jwtService.getUserNameFromToken(refreshToken ));

String jwt = jwtService.generateToken(userDetail);

String refreshJwt = jwtService.generateRefreshToken(userDetail); return new LoginOutputDto(jwt, refreshJwt);

- Xử lý xác thực, phân quyền.

@Override protected void doFilterInternal(HttpServletRequest request,

HttpServletResponse response, FilterChain filterChain) { try { if(request.getRequestURI().contains("/api/v1/auth/")) { filterChain.doFilter(request, response); return;

String jwt = getJwtFromRequest(request); if (StringUtils.hasText(jwt) && jwtService.validateToken(jwt)) {

Ngày đăng: 11/11/2023, 22:46

TỪ KHÓA LIÊN QUAN

w