
- •3 Курса группы 12002108
- •Обзор и анализ предметной области
- •Проектирование и разработка базы данных информационной системы
- •Инфологическая модель базы данных
- •Логическая модель базы данных
- •Физическая модель базы данных
- •Разработка программного приложения аис магазина спортивных товаров
- •Тестирование разработанного программного приложения
Тестирование разработанного программного приложения
В данном разделе представлен процесс тестирования программного приложения, разработанного для управления данными в магазине спортивных товаров. Тестирование играет ключевую роль в обеспечении качества и надежности приложения перед его внедрением. В ходе тестирования будут оценены функциональные возможности, производительность, безопасность и стабильность приложения.
После запуска приложения следует открыть вкладку браузера с указанным в свойствах приложения портом localhost (при тестировании используется адрес http://localhost:8099/sportline). Сначала пользователю приложения можно открыть главную (домашнюю) страницу, представленную на рисунке 7 (рисунок 7).
Рисунок 7 – Домашняя страница
В «шапке» веб-страницы можно перейти на страницы товаров, блога, специальных предложений и брендов по нажатию на соответствующую кнопку. При переходе на страницу товаров в её верху находится форма добавления товара (рисунок 8). При пролистывании вниз доступен список всех товаров с возможностью перехода к каждому конкретному (рисунок 9).
Рисунок 8 – Форма добавления товара
Рисунок 9 – Список товаров
При переходе на страницу товара доступна детальная информация о нём, а также есть возможность перехода ко всем товарам бренда (рисунок 10). При пролистывании вниз доступны формы для редактирования и удаления товара, представленного на странице (рисунок 11).
Рисунок 10 – Детальная информация о товаре
Рисунок 11 – Формы изменения и удаления товара
На страницу с детальной информацией нужного бренда (рисунок 12) можно попасть как со страницы товара (рисунок 10), так и со страницы добавления и списка брендов (рисунок 14). При пролистывании страницы бренда вниз доступны формы для редактирования и удаления бренда, а также список товаров этого бренда (рисунок 13).
Рисунок 12 – Детальная информация о бренде
Рисунок 13 – Формы изменения и удаления товара, список товаров бренда
Рисунок 14 – Страница добавления и списка брендов
Аналогично (страница с формой добавления и списком хранимых сущностей, страница детального просмотра, редактирования и удаления) устроено взаимодействие с записями блога и специальными предложениями. Советующие страницы представлены на рисунках 15-19.
Рисунок 15 – Страница добавления и списка специальных предложений
Рисунок 16 – Список специальных предложений
Рисунок 17 – Информация и форма изменения специального предложения
Рисунок 18 – Страница добавления и списка постов блога
Рисунок 19 – Информация и форма изменения поста блога
В результате проведенного тестирования приложения было установлено, что оно соответствует заявленным требованиям и функциональности. Тестирование приложения играет важную роль в обеспечении его надежности и эффективности перед выпуском в эксплуатацию.
Заключение
В результате курсовой работы была разработана автоматизированная информационная система под названием для магазина спортивного инвентаря, предназначенная для управления учета товаров в магазине спортивных товаров. Основанное на Spring Web-технологиях приложение предоставляет удобный интерфейс для администраторов и менеджеров по продажам, позволяя им эффективно управлять каталогом товаров, брендами, постами в блоге и специальными предложениями.
Система включает в себя функционал для создания, просмотра, редактирования и удаления товаров, брендов, постов в блоге и специальных предложений. Благодаря интеграции с платежными системами, клиенты могут безопасно и удобно оформлять заказы на сайте магазина. Кроме того, приложение предоставляет возможность проведения акций и специальных предложений, что способствует увеличению продаж и привлечению новых клиентов.
Интерфейс приложения разработан с учетом удобства пользователя, что обеспечивает простоту и эффективность в работе. В ходе выполнения работы были использованы знания, полученные как на лекциях, так и на практических занятиях, что подчеркивает важность усвоения теоретического материала для успешного решения практических задач. Курсовая работа оформлена согласно всем методическим рекомендациям.
Список использованных источников
Spring Framework Documenation. – Режим доступа: https://docs.spring.io/spring-framework/reference/index.html
Documentation - Thymeleaf. – Режим доступа: https://www.thymeleaf.org/documentation.html
Overview (Lombok). – Режим доступа: https://projectlombok.org/api/
PostgreSQL: Documentation – Режим доступа: https://www.postgresql.org/docs/current/index.html
Java Documentation – Режим доступа: https://docs.oracle.com/en/java/
Приложение А
Контроллеры
package com.sportline.controller; import lombok.AllArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @Controller @AllArgsConstructor @RequestMapping("sportline") public class BasicController { @GetMapping("") public String getHomePage() { return "sportline/basic/home"; } }
package com.sportline.controller; import com.sportline.model.entity.BlogPost; import com.sportline.service.BlogService; import lombok.AllArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; @Controller @AllArgsConstructor @RequestMapping("/sportline/blog") public class BlogController { private static final String REDIRECT_LIST = "redirect:/sportline/blog"; private static final String REDIRECT_ITEM = "redirect:/sportline/blog/{id}"; private BlogService blogService; @PostMapping("") public String postBlogPost(@ModelAttribute("blogPost") BlogPost blogPost) { blogService.create(blogPost); return REDIRECT_LIST; } @PutMapping("/{id}") public String putBlogPost(@PathVariable("id") long id, @ModelAttribute("blogPost") BlogPost blogPost) { blogService.updateById(blogPost, id); return REDIRECT_ITEM; } @DeleteMapping("/{id}") public String deleteBlogPost(@PathVariable("id") long id) { blogService.deleteById(id); return REDIRECT_LIST; } @GetMapping("") public String getBlogPosts(Model model, @ModelAttribute("blogPost") BlogPost blogPost, @RequestParam(name = "pageNum", required = false, defaultValue = "0") int pageNum, @RequestParam(name = "pageSize", required = false, defaultValue = "4") int pageSize) { model.addAttribute("blogPostsPage", blogService.findAllByPage(pageNum, pageSize)); model.addAttribute("blogPostsList", blogService.findAllByPage(pageNum, pageSize).getContent()); return "sportline/blog/list"; } @GetMapping("/{id}") public String getBlogPost(Model model, @PathVariable("id") long id) { model.addAttribute("blogPostItem", blogService.getById(id)); return "sportline/blog/item"; } }
package com.sportline.controller; import com.sportline.model.entity.Brand; import com.sportline.service.BrandService; import lombok.AllArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; @Controller @AllArgsConstructor @RequestMapping("/sportline/brands") public class BrandController { private static final String REDIRECT_LIST = "redirect:/sportline/brands"; private static final String REDIRECT_ITEM = "redirect:/sportline/brands/{id}"; private BrandService brandService; @PostMapping("") public String postBrand(@ModelAttribute("brand") Brand brand) { brandService.create(brand); return REDIRECT_LIST; } @PutMapping("/{id}") public String putBrand(@PathVariable("id") long id, @ModelAttribute("brand") Brand brand) { brandService.updateById(brand, id); return REDIRECT_ITEM; } @DeleteMapping("/{id}") public String deleteBrand(@PathVariable("id") long id) { brandService.deleteById(id); return REDIRECT_LIST; } @GetMapping("") public String getBrands(Model model, @ModelAttribute("brand") Brand brand, @RequestParam(name = "pageNum", required = false, defaultValue = "0") int pageNum, @RequestParam(name = "pageSize", required = false, defaultValue = "4") int pageSize) { model.addAttribute("brandsPage", brandService.findAllByPage(pageNum, pageSize)); model.addAttribute("brandsList", brandService.findAllByPage(pageNum, pageSize).getContent()); return "sportline/brand/list"; } @GetMapping("/{id}") public String getBrand(Model model, @PathVariable("id") long id) { model.addAttribute("brandItem", brandService.getById(id)); return "sportline/brand/item"; } }
package com.sportline.controller; import com.sportline.model.entity.Product; import com.sportline.model.entity.Status; import com.sportline.service.BrandService; import com.sportline.service.ProductService; import lombok.AllArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; @Controller @AllArgsConstructor @RequestMapping("/sportline/products") public class ProductController { private static final String REDIRECT_LIST = "redirect:/sportline/products"; private static final String REDIRECT_ITEM = "redirect:/sportline/products/{id}"; private ProductService productService; private BrandService brandService; @PostMapping("") public String postProduct(@ModelAttribute("product") Product product) { productService.create(product); return REDIRECT_LIST; } @PutMapping("/{id}") public String putProduct(@PathVariable("id") long id, @ModelAttribute("product") Product product) { productService.updateById(product, id); return REDIRECT_ITEM; } @DeleteMapping("/{id}") public String deleteProduct(@PathVariable("id") long id) { productService.deleteById(id); return REDIRECT_LIST; } @GetMapping("") public String getProducts(Model model, @ModelAttribute("product") Product product, @RequestParam(name = "pageNum", required = false, defaultValue = "0") int pageNum, @RequestParam(name = "pageSize", required = false, defaultValue = "4") int pageSize) { model.addAttribute("productsPage", productService.findAllByPage(pageNum, pageSize)); model.addAttribute("brandsList", brandService.findAll()); model.addAttribute("statusesArray", Status.values()); model.addAttribute("productsList", productService.findAllByPage(pageNum, pageSize).getContent()); return "sportline/product/list"; } @GetMapping("/{id}") public String getProduct(Model model, @PathVariable("id") long id) { model.addAttribute("productItem", productService.getById(id)); model.addAttribute("brandsList", brandService.findAll()); model.addAttribute("statusesArray", Status.values()); return "sportline/product/item"; } }
package com.sportline.controller; import com.sportline.model.entity.SpecialOffer; import com.sportline.service.SpecialOfferService; import lombok.AllArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; @Controller @AllArgsConstructor @RequestMapping("/sportline/offers") public class SpecialOfferController { private static final String REDIRECT_LIST = "redirect:/sportline/offers"; private static final String REDIRECT_ITEM = "redirect:/sportline/offers/{id}"; private SpecialOfferService specialOfferService; @PostMapping("") public String postSpecialOffer(@ModelAttribute("offer") SpecialOffer specialOffer) { specialOfferService.create(specialOffer); return REDIRECT_LIST; } @PutMapping("/{id}") public String putSpecialOffer(@PathVariable("id") long id, @ModelAttribute("offer") SpecialOffer specialOffer) { specialOfferService.updateById(specialOffer, id); return REDIRECT_ITEM; } @DeleteMapping("/{id}") public String deleteSpecialOffer(@PathVariable("id") long id) { specialOfferService.deleteById(id); return REDIRECT_LIST; } @GetMapping("") public String getSpecialOffer(Model model, @ModelAttribute("offer") SpecialOffer specialOffer, @RequestParam(name = "pageNum", required = false, defaultValue = "0") int pageNum, @RequestParam(name = "pageSize", required = false, defaultValue = "4") int pageSize) { model.addAttribute("offersPage", specialOfferService.findAllByPage(pageNum, pageSize)); model.addAttribute("offersList", specialOfferService.findAllByPage(pageNum, pageSize).getContent()); return "sportline/offer/list"; } @GetMapping("/{id}") public String getSpecialOffer(Model model, @PathVariable("id") long id) { model.addAttribute("offerItem", specialOfferService.getById(id)); return "sportline/offer/item"; } }
Приложение Б
Сервисы
package com.sportline.service.impl; import com.sportline.exc.CustomNotFoundException; import com.sportline.model.entity.BlogPost; import com.sportline.repository.BlogRepository; import com.sportline.service.BlogService; import lombok.AllArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; import java.util.List; @Service @AllArgsConstructor @Transactional(readOnly = true) public class BlogServiceImpl implements BlogService { private final BlogRepository blogRepository; @Transactional @Override public void create(BlogPost blogPost) { blogPost.setPublicationDate(LocalDateTime.now()); blogRepository.save(blogPost); } @Override public List<BlogPost> findAll() { return blogRepository.findAll(); } @Override public Page<BlogPost> findAllByPage(int pageNum, int pageSize) { return blogRepository.findAll( PageRequest.of(pageNum, pageSize, Sort.by(Sort.Direction.DESC, "publicationDate") )); } @Override public BlogPost getById(Long id) { return blogRepository .findById(id) .orElseThrow(() -> new CustomNotFoundException("Запись в блоге", id)); } @Transactional @Override public void updateById(BlogPost blogPost, long id) { BlogPost blogPostBefore = getById(id); blogPostBefore.setHeading(blogPost.getHeading()); blogPostBefore.setContent(blogPost.getContent()); blogRepository.save(blogPostBefore); } @Transactional @Override public void deleteById(Long id) { blogRepository.deleteById(id); } }
package com.sportline.service.impl; import com.sportline.exc.CustomNotFoundException; import com.sportline.model.entity.Brand; import com.sportline.repository.BrandRepository; import com.sportline.service.BrandService; import lombok.AllArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; @Service @AllArgsConstructor @Transactional(readOnly = true) public class BrandServiceImpl implements BrandService { private final BrandRepository brandRepository; @Transactional @Override public void create(Brand brand) { brandRepository.save(brand); } @Override public List<Brand> findAll() { return brandRepository.findAll(); } @Override public Page<Brand> findAllByPage(int pageNum, int pageSize) { return brandRepository.findAll(PageRequest.of(pageNum, pageSize)); } @Override public Brand getById(Long id) { return brandRepository .findByIdWithItems(id) .orElseThrow(() -> new CustomNotFoundException("Бренд", id)); } @Transactional @Override public void updateById(Brand brand, long id) { brand.setId(id); brandRepository.save(brand); } @Transactional @Override public void deleteById(Long id) { brandRepository.deleteById(id); } }
package com.sportline.service.impl; import com.sportline.exc.CustomNotFoundException; import com.sportline.model.entity.Product; import com.sportline.repository.ProductRepository; import com.sportline.service.ProductService; import lombok.AllArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; @Service @AllArgsConstructor @Transactional(readOnly = true) public class ProductServiceImpl implements ProductService { private final ProductRepository productRepository; @Transactional @Override public void create(Product product) { productRepository.save(product); } @Override public List<Product> findAll() { return productRepository.findAll(); } @Override public Page<Product> findAllByPage(int pageNum, int pageSize) { return productRepository.findAll(PageRequest.of(pageNum, pageSize)); } @Override public Product getById(Long id) { return productRepository .findById(id) .orElseThrow(() -> new CustomNotFoundException("Товар", id)); } @Transactional @Override public void updateById(Product product, long id) { product.setId(id); productRepository.save(product); } @Transactional @Override public void deleteById(Long id) { productRepository.deleteById(id); } }
package com.sportline.service.impl; import com.sportline.exc.CustomNotFoundException; import com.sportline.model.entity.SpecialOffer; import com.sportline.repository.SpecialOfferRepository; import com.sportline.service.SpecialOfferService; import lombok.AllArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; @Service @AllArgsConstructor @Transactional(readOnly = true) public class SpecialOfferServiceImpl implements SpecialOfferService { private final SpecialOfferRepository specialOfferRepository; @Transactional @Override public void create(SpecialOffer specialOffer) { specialOfferRepository.save(specialOffer); } @Override public List<SpecialOffer> findAll() { return specialOfferRepository.findAll(); } @Override public Page<SpecialOffer> findAllByPage(int pageNum, int pageSize) { return specialOfferRepository.findAll(PageRequest.of(pageNum, pageSize)); } @Override public SpecialOffer getById(Long id) { return specialOfferRepository .findById(id) .orElseThrow(() -> new CustomNotFoundException("Специальное предложение", id)); } @Transactional @Override public void updateById(SpecialOffer specialOffer, long id) { SpecialOffer specialOfferBefore = getById(id); specialOffer.setId(id); if (specialOffer.getStartDate() == null) { specialOffer.setStartDate(specialOfferBefore.getStartDate()); } if (specialOffer.getEndDate() == null) { specialOffer.setEndDate(specialOfferBefore.getEndDate()); } specialOfferRepository.save(specialOffer); } @Transactional @Override public void deleteById(Long id) { specialOfferRepository.deleteById(id); } }
Приложение В
Сущности
package com.sportline.model.entity; import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDateTime; @Data @AllArgsConstructor @NoArgsConstructor @Entity @Table(name = "blog_posts") public class BlogPost { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String heading; private String content; @DateTimeFormat(pattern = "dd-MM-yyyy HH:mm:ss") @Temporal(TemporalType.TIMESTAMP) private LocalDateTime publicationDate; }
package com.sportline.model.entity; import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; import java.util.List; @Data @AllArgsConstructor @NoArgsConstructor @Entity @Table(name = "brands") public class Brand { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String country; private String description; private String website; @ToString.Exclude @OneToMany(mappedBy = "brand") private List<Product> products; }
package com.sportline.model.entity; import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; @Data @AllArgsConstructor @NoArgsConstructor @Entity @Table(name = "products") public class Product { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String description; private String imgSrc; private Double price; @Enumerated(EnumType.STRING) private Status status; @ToString.Exclude @ManyToOne @JoinColumn(name = "brand_id") private Brand brand; }
package com.sportline.model.entity; import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDate; @Data @AllArgsConstructor @NoArgsConstructor @Entity @Table(name = "special_offers") public class SpecialOffer { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String description; @DateTimeFormat(pattern = "dd/MM/yyyy") @Temporal(TemporalType.DATE) private LocalDate startDate; @DateTimeFormat(pattern = "dd/MM/yyyy") @Temporal(TemporalType.DATE) private LocalDate endDate; }
package com.sportline.model.entity; public enum Status { IN_STOCK, OUT_OF_STOCK, PRE_ORDER, WILL_BE_SOON; public String getTranslated() { if (this.equals(IN_STOCK)) { return "В наличии"; } else if (this.equals(OUT_OF_STOCK)) { return "Нет в наличии"; } else if (this.equals(PRE_ORDER)) { return "По предзаказу"; } else if (this.equals(WILL_BE_SOON)) { return "Скоро в продаже"; } else { return ""; } } }