wiki:AdvancedApplicationDevelopment

Version 5 (modified by 211012, 2 weeks ago) ( diff )

--

Напреден апликативен развој

Во последната верзија апликацијата имплементирани се следните сценарија (сите од прототипот и дополнителни):

ID UseCase
1 Прегледува избирачки список
2 Ажурира избирачки список
3 Прегледува изборни единици
4 Ажурира изборни единици
5 Прегледува гласачки места
6 Aжурира гласачки места
7 Креира инстанца од избори
8 Пријавува учество на партија и кандидати
9 Назначува членови на комисија
10 Гласа онлајн
11 Доделува идентификациски код за гласање со физичко присуство
12 Гласа со физичко присуство
13 Прегледува вкупна излезност
14 Прегледува статистики од излезност по критериуми
15 Прегледува вкунпи резултати од избори со кандидат
16 Прегледува вкунпи резултати од избори со кандидатски листи
16 Прегледува статистики од резултати од избори по критериуми
17 Поднесува приговор
18 Одлучува по приговорот
19 Пријавува учество на кандидатски листи

Назначува членови на комисија

Администраторот пристапува на адреса /admin/committee каде му се отвора страница на која се прикажани сите комисии и формулар за внесување податоци за нова комисија. За ова е одговорен следниот контролер:

    @GetMapping("admin/committee")
    public String showCommittee(Model m) {
        m.addAttribute("realizations", electionRealizationService.findAll());
        m.addAttribute("committeeMembers", committeeMemberService.findAll());
        m.addAttribute("pollingStations", addressService.findAllPollingStations());
        m.addAttribute("committees", committeeService.findAll());
        m.addAttribute("committeeForm", new Committee());
        m.addAttribute("replaceTemplate", "add_comitee");
        return "admin";
    }

По внесување на податоците и клик на копчето „Запиши“ се праќа POST барање до контролерот кој ги обработува.

    @PostMapping("/admin/committee")
    public String addCommittee(
            @RequestParam(required = false) Long id,
            @RequestParam Long electionRealization,
            @RequestParam Long pollingStation,
            @RequestParam List<Long> membersId
    ) {
        committeeService.update(id, pollingStation, electionRealization, membersId);
        return "redirect:/admin/committee";
    }

Контролерот повикува функција од committeeService за додавање и ажурирање на комисии каде се креира нова инстанца од објектот Committee, и за истиот се полнат добиентите податоци од формуларот. Дополнително, се додаваат и членовите на комисијата кои се во many-to-many релација со комисијата. Оттаму се повикува функцијата save од committeeRepository кој наследува од JpaRepository. Таа служи за зачувување на новиот објект во базата. За да се обезбеди интегритет и конзистентност на податоците, методот е анотиран со @Transactional, односно функцијата успешно ќе заврши ако и само ако успешно се запишат и комисијата и членовите.

    @Transactional
    @Override
    public Committee update(Long id, Long pollingStationId, Long electionRealizationId, List<Long> membersId) {
        Committee committee;
        if (id != null){
            committee = findById(id);
        } else {
            committee = new Committee();
        }

        committee.setPollingStation(addressService.findPollingStationById(pollingStationId));
        committee.setElectionRealization(electionRealizationService.    findById(electionRealizationId));
        committeeRepository.save(committee);
        membersId.forEach(memberId -> addMemberToCommittee(committee.getId(), memberId));
        return committeeRepository.save(committee);
    }

Истава имплементација, преточена во SQL прашалникот кој се извршува позадински би изгледала вака:

DO $$
    DECLARE
        new_kom_id BIGINT;
    BEGIN
        INSERT INTO komisii (im_id, ri_id) VALUES (1, 1)
        RETURNING kom_id INTO new_kom_id;
    
        INSERT INTO se_clenovi_na (g_id, kom_id) VALUES (20, new_kom_id);
        INSERT INTO se_clenovi_na (g_id, kom_id) VALUES (21, new_kom_id);
        INSERT INTO se_clenovi_na (g_id, kom_id) VALUES (22, new_kom_id);
        INSERT INTO se_clenovi_na (g_id, kom_id) VALUES (23, new_kom_id);
        INSERT INTO se_clenovi_na (g_id, kom_id) VALUES (24, new_kom_id);
    
        COMMIT;
    END $$;

Доделува идентификациски код за гласање со физичко присуство

Корисниците со улога членови на комисија, можат да се најават на системот и пристапат на адресата /admin/electionRealizationInterface, при што добиваат преглед на избирачкиот список од реализацијата којашто се одржува на деновите на најава, за гласачкото место во кое се одговорни. Дополнително, на крајот на редот достапна е акцијата „Гласај“ со што можат да генерираат идентификациски код за гласање доколку гласачот сака да го оствари правото на глас со физичко присуство. Контролерот кој ја опслужува оваа акција е следен:

    @GetMapping("/admin/electionRealizationInterface")
    public String showElectionRealization(Model m, Principal p, Authentication authentication) {
        UserProfile userProfile = (UserProfile) authentication.getPrincipal();
        m.addAttribute("realization", committeeService.findElectionRealizationByCitizen(userProfile.getCitizen().getId()));
        m.addAttribute("citizens", committeeService.getCitizens(userProfile.getCitizen().getId()));
        m.addAttribute("replaceTemplate","election_realization_interface");
        return "index";
    }

За да се избегнат било какви манипулации и приказ на информации кои не треба да се достапни за најавениот корисник, неговиот идентитет се утврдува директно на серверска страна, од најавениот корисник. Дополнително во сервисите се процесираат реализацијата и избирачкиот список кој треба да го гледа корисникот.

    @Override
    public List<Citizen> getCitizens(Long committeeId) {
        CommitteeMember committeeMember = committeeMemberRepository.findById(committeeId).orElseThrow(RuntimeException::new);
        Committee committee = committeeRepository.findCommitteeByMembersContainsAndElectionRealization_DateIsAfter(committeeMember, LocalDate.now().minusDays(1));
        PollingStation pollingStation = committee.getPollingStation();
        return citizenRepository.findAllByAddress_PollingStation(pollingStation.getId()).stream().filter(x -> x instanceof Citizen).toList();
    }

Во committeeService-от се пронаѓа членот на комисија кој го претставува корисничкиот профил, комисијата во која е член, како и избирачкиот список за гласачкото место за кое е одговорна. Во програмската имплементација, ова се прави со повик на соодветни методи од Spring Data JPA, а во SQL би изгледало вака:

    select *
    from komisii k
    join se_clenovi_na ck on k.kom_id = ck.kom_id
    join realizacii_na_izbori ri on ri.ri_id = k.ri_id
    where ck.g_id = 1 and ri.ri_id = 1 and ri.ri_datum = CURRENT_DATE
    limit 1

Приказот кој го добива членот на комисијата е прикажан на следната слика. Во делот акции, достапно е времето на гласање за оние корисници кои правото на глас го оствариле онлајн или со физичко присуство, а за остантите достапно е копче за генерирање код за гласање. При клик се повикува следниот контролер:

    @GetMapping("/admin/electionRealizationInterface/{id}")
    public String showElectionRealization(Model m, @PathVariable Long id, Principal p, Authentication authentication) {
        UserProfile userProfile = (UserProfile) authentication.getPrincipal();
        Long realizationId = committeeService.findElectionRealizationByCitizen(userProfile.getCitizen().getId()).getId();
        CommitteeMember committeeMember = committeeMemberService.findById(userProfile.getCitizen().getId());
        if(!committeeService.getSamePollingStation(committeeMember.getId(), id)){
            return "redirect:/admin/electionRealizationInterface";
        }
        return "redirect:/admin/voteCode/" + realizationId + "/" + id;
    }

Како што е и претходно споменато, за да се избегнат манипулации, идентитетот повторно се проверува на серверската страна, односно се проверува дали корисникот кој го испратил барањето и гласачот за кој се генерира код се од исто гласачко место и потоа се пренасочува кон контролерот кој повикува сервис за генерирање на кодот доколку тој не постои или го прикажува веќе генерираниот. Кодот за гласање е од тип UUID, односно шансите за негово повторување или случајно погодување се практично еднакви на нула. Приказот кој го добива членот на комисијата по генерирање код изгледа вака.

Гласа со физичко присуство

Граѓанин кој сака да го оствари правото на глас со физичко присуство на потенцијално поставена машина во гласачко место може да го направи тоа користејќи го кодот добиен од избирачката комисија на страницата /codeVote, при што го гледа следниот интерфејс. Потоа, процесот на гласање продолжува исто како што тоа е опишано во делот од прототипот на апликацијата.

Attachments (9)

Download all attachments as: .zip

Note: See TracWiki for help on using the wiki.