esoe
7 months ago
9 changed files with 494 additions and 1 deletions
@ -0,0 +1,153 @@ |
|||||||
|
package ru.molokoin.clientserviceteachers.controllers; |
||||||
|
|
||||||
|
|
||||||
|
import java.time.Duration; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.core.ParameterizedTypeReference; |
||||||
|
import org.springframework.http.HttpMethod; |
||||||
|
import org.springframework.http.MediaType; |
||||||
|
import org.springframework.stereotype.Controller; |
||||||
|
import org.springframework.ui.Model; |
||||||
|
import org.springframework.validation.annotation.Validated; |
||||||
|
import org.springframework.web.bind.annotation.GetMapping; |
||||||
|
import org.springframework.web.bind.annotation.ModelAttribute; |
||||||
|
import org.springframework.web.bind.annotation.PathVariable; |
||||||
|
import org.springframework.web.bind.annotation.PostMapping; |
||||||
|
import org.springframework.web.bind.annotation.RequestMapping; |
||||||
|
import org.springframework.web.reactive.function.client.WebClient; |
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException; |
||||||
|
import com.fasterxml.jackson.databind.JsonMappingException; |
||||||
|
|
||||||
|
import reactor.core.publisher.Mono; |
||||||
|
import ru.molokoin.clientserviceteachers.entities.Program; |
||||||
|
import ru.molokoin.clientserviceteachers.entities.ProgramCretarea; |
||||||
|
|
||||||
|
@Controller |
||||||
|
@RequestMapping(path = "/") |
||||||
|
public class ProgramController { |
||||||
|
@Autowired |
||||||
|
private WebClient client; |
||||||
|
|
||||||
|
@GetMapping("/programs") |
||||||
|
public String teacherList(Model model) throws JsonMappingException, JsonProcessingException{ |
||||||
|
List<Program> programs = client.method(HttpMethod.GET) |
||||||
|
.uri("/program/list") |
||||||
|
.retrieve() |
||||||
|
.bodyToMono(new ParameterizedTypeReference <List<Program>>(){}) |
||||||
|
.block(); |
||||||
|
|
||||||
|
model.addAttribute("programs", programs); |
||||||
|
model.addAttribute("p", new Program()); |
||||||
|
System.out.println("PROGRAMS:" + programs.toString()); |
||||||
|
return "programs"; |
||||||
|
} |
||||||
|
|
||||||
|
@GetMapping("/program-edit/{id}") |
||||||
|
public String teacherById(Model model, @PathVariable Long id) { |
||||||
|
/** |
||||||
|
* получение критериев из ресурсов |
||||||
|
*/ |
||||||
|
List<ProgramCretarea> pcl = client.get() |
||||||
|
.uri("/cretarea/list") |
||||||
|
.retrieve() |
||||||
|
.bodyToMono(new ParameterizedTypeReference <List<ProgramCretarea>>(){}) |
||||||
|
.timeout(Duration.ofSeconds(1)) |
||||||
|
.block(); |
||||||
|
model.addAttribute("pcl", pcl); |
||||||
|
|
||||||
|
/** |
||||||
|
* Получение программы из ресурсов |
||||||
|
*/ |
||||||
|
Program p = client.get() |
||||||
|
.uri("/program/" + id) |
||||||
|
.retrieve() |
||||||
|
.bodyToMono(Program.class) |
||||||
|
.timeout(Duration.ofSeconds(1)) |
||||||
|
.block(); |
||||||
|
model.addAttribute("program", p); |
||||||
|
return "program-edit"; |
||||||
|
} |
||||||
|
|
||||||
|
@PostMapping( |
||||||
|
path = "/program/add", |
||||||
|
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, |
||||||
|
produces = { |
||||||
|
MediaType.APPLICATION_JSON_VALUE |
||||||
|
}) |
||||||
|
public String addProgram(Model model, @ModelAttribute("program") @Validated Program p) throws JsonMappingException, JsonProcessingException{ |
||||||
|
/** |
||||||
|
* получение критериев из ресурсов |
||||||
|
*/ |
||||||
|
List<ProgramCretarea> pcl = client.get() |
||||||
|
.uri("/cretarea/list") |
||||||
|
.retrieve() |
||||||
|
.bodyToMono(new ParameterizedTypeReference <List<ProgramCretarea>>(){}) |
||||||
|
.timeout(Duration.ofSeconds(1)) |
||||||
|
.block(); |
||||||
|
model.addAttribute("pcl", pcl); |
||||||
|
System.out.println("#######PCL#######"); |
||||||
|
System.out.println("pcl: " + pcl.toString()); |
||||||
|
|
||||||
|
client.post() |
||||||
|
.uri("/program/create") |
||||||
|
.body(Mono.just(p), Program.class) |
||||||
|
.retrieve() |
||||||
|
.toBodilessEntity() |
||||||
|
.timeout(Duration.ofSeconds(1)) |
||||||
|
.block(); |
||||||
|
return "redirect:/programs"; |
||||||
|
} |
||||||
|
|
||||||
|
@GetMapping(path = "/program/add") |
||||||
|
public String addTeacherFrame(Model model){ |
||||||
|
/** |
||||||
|
* получение критериев из ресурсов |
||||||
|
*/ |
||||||
|
List<ProgramCretarea> pcl = client.get() |
||||||
|
.uri("/cretarea/list") |
||||||
|
.retrieve() |
||||||
|
.bodyToMono(new ParameterizedTypeReference <List<ProgramCretarea>>(){}) |
||||||
|
.timeout(Duration.ofSeconds(1)) |
||||||
|
.block(); |
||||||
|
model.addAttribute("pcl", pcl); |
||||||
|
|
||||||
|
Program program = new Program(); |
||||||
|
model.addAttribute("newProgram", program); |
||||||
|
return "program-add"; |
||||||
|
} |
||||||
|
|
||||||
|
@GetMapping(path = "/program/delete/{id}") |
||||||
|
public String deleteTeacher(@PathVariable Long id) throws JsonMappingException, JsonProcessingException{ |
||||||
|
client.delete() |
||||||
|
.uri("/program/delete/" + id) |
||||||
|
.retrieve() |
||||||
|
.bodyToMono(String.class) |
||||||
|
.timeout(Duration.ofSeconds(1)) |
||||||
|
.block(); |
||||||
|
return "redirect:/programs"; |
||||||
|
} |
||||||
|
|
||||||
|
@PostMapping( |
||||||
|
path = "/program/update/{id}", |
||||||
|
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, |
||||||
|
produces = { |
||||||
|
MediaType.APPLICATION_JSON_VALUE |
||||||
|
}) |
||||||
|
public String updateTeacher(@PathVariable Long id |
||||||
|
, @ModelAttribute("p") @Validated Program program) throws JsonMappingException, JsonProcessingException{ |
||||||
|
client.post() |
||||||
|
.uri("/teacher/update/" + program.getId()) |
||||||
|
.body(Mono.just(program), Program.class) |
||||||
|
.retrieve() |
||||||
|
.toBodilessEntity() |
||||||
|
.timeout(Duration.ofSeconds(1)) |
||||||
|
.block(); |
||||||
|
return "redirect:/programs"; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,39 @@ |
|||||||
|
package ru.molokoin.clientserviceteachers.entities; |
||||||
|
|
||||||
|
import jakarta.annotation.Nullable; |
||||||
|
import lombok.AllArgsConstructor; |
||||||
|
import lombok.Data; |
||||||
|
import lombok.NoArgsConstructor; |
||||||
|
|
||||||
|
@NoArgsConstructor |
||||||
|
@AllArgsConstructor |
||||||
|
@Data |
||||||
|
public class Program { |
||||||
|
private long id; |
||||||
|
private String name;//Наименование
|
||||||
|
private Integer lenght;//длительность программы
|
||||||
|
private String study_direction;//направление обучения: обязательное-производственное (учесть в ограничениях)
|
||||||
|
private Integer price; |
||||||
|
// @Nullable
|
||||||
|
private ProgramCretarea cretarea; |
||||||
|
|
||||||
|
/** |
||||||
|
* конструктор со всеми полями сущности, за исключением id |
||||||
|
* Подготовить все варианты конструкторов |
||||||
|
* @param name |
||||||
|
* @param lenght |
||||||
|
* @param study_direction |
||||||
|
* @param price |
||||||
|
* @param cretarea |
||||||
|
*/ |
||||||
|
public Program(String name, Integer lenght, String study_direction, Integer price, ProgramCretarea cretarea) { |
||||||
|
this.name = name; |
||||||
|
this.lenght = lenght; |
||||||
|
this.study_direction = study_direction; |
||||||
|
this.price = price; |
||||||
|
this.cretarea = cretarea; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,57 @@ |
|||||||
|
<!DOCTYPE HTML> |
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" |
||||||
|
xmlns:th="http://www.thymeleaf.org"> |
||||||
|
<head> |
||||||
|
<meta charset="utf-8"> |
||||||
|
<title>Program-add</title> |
||||||
|
<script src="https://cdn.jsdelivr.net/npm/@webcomponents/webcomponentsjs@2/webcomponents-loader.min.js"></script> |
||||||
|
<script type="module" src="https://cdn.jsdelivr.net/gh/zerodevx/zero-md@1/src/zero-md.min.js"></script> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<header> |
||||||
|
add : program |
||||||
|
</header> |
||||||
|
<main> |
||||||
|
<h2>Добавление программы обучения:</h2> |
||||||
|
<form th:action="@{/program/add}" method="post" th:object="${newProgram}"> |
||||||
|
<div> |
||||||
|
<label>id: </label> |
||||||
|
<input type="text" th:field="${newProgram.id}" placeholder="" readonly /> |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
<label>Наименование: </label> |
||||||
|
<input type="text" th:field="${newProgram.name}" placeholder="" /> |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
<label>Продолжительность, часов: </label> |
||||||
|
<input type="text" th:field="${newProgram.lenght}" placeholder="" /> |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
<label>Направление обучения (обязательное/производственное): </label> |
||||||
|
<input type="text" th:field="${newProgram.study_direction}" placeholder="" /> |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
<label>Стоимость обчения: </label> |
||||||
|
<input type="text" th:field="${newProgram.price}" placeholder="" /> |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
<label>Критерий</label> |
||||||
|
<!-- Предусмотреть возможность: --> |
||||||
|
<!-- - выбора критерия из доступных --> |
||||||
|
<!-- - выбора критерия по умлчанию (DEFAULT), когда критерий не предусмотрен --> |
||||||
|
<!-- - создания нового критерия --> |
||||||
|
<!-- <input type="text" th:field="${newProgram.cretarea.id}" placeholder="" /> --> |
||||||
|
<select th:field="${newProgram.cretarea.id}"> |
||||||
|
<option value="0">выберите критерий</option> |
||||||
|
<option th:each="option : ${pcl}" th:value="${option.id}" th:text="${option.name_short}"></option> |
||||||
|
</select> |
||||||
|
</div> |
||||||
|
<br> |
||||||
|
<input type="submit" value="СОХРАНИТЬ И ВЕРНУТЬСЯ"/> |
||||||
|
</form> |
||||||
|
<br> |
||||||
|
<form th:action="@{/programs}" th:method="get"> |
||||||
|
<input type="submit" value="ВЕРНУТЬСЯ"/> |
||||||
|
</form> |
||||||
|
</main> |
||||||
|
</body> |
@ -0,0 +1,56 @@ |
|||||||
|
<!DOCTYPE HTML> |
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" |
||||||
|
xmlns:th="http://www.thymeleaf.org"> |
||||||
|
<head> |
||||||
|
<meta charset="utf-8"> |
||||||
|
<title>Program-edit</title> |
||||||
|
<script src="https://cdn.jsdelivr.net/npm/@webcomponents/webcomponentsjs@2/webcomponents-loader.min.js"></script> |
||||||
|
<script type="module" src="https://cdn.jsdelivr.net/gh/zerodevx/zero-md@1/src/zero-md.min.js"></script> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<header> |
||||||
|
edit : program |
||||||
|
</header> |
||||||
|
<main> |
||||||
|
<h2>Редактирование сведений о программе обучения:</h2> |
||||||
|
<form th:action="@{/program/add}" method="post" th:object="${program}"> |
||||||
|
<div> |
||||||
|
<label>id: </label> |
||||||
|
<input type="text" th:field="${program.id}" placeholder="" readonly /> |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
<label>Наименование: </label> |
||||||
|
<input type="text" th:field="${program.name}" placeholder="" /> |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
<label>Продолжительность, часов: </label> |
||||||
|
<input type="text" th:field="${program.lenght}" placeholder="" /> |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
<label>Направление обучения (обязательное/производственное): </label> |
||||||
|
<input type="text" th:field="${program.study_direction}" placeholder="" /> |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
<label>Стоимость обчения: </label> |
||||||
|
<input type="text" th:field="${program.price}" placeholder="" /> |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
<label>Критерий</label> |
||||||
|
<!-- Предусмотреть возможность: --> |
||||||
|
<!-- - выбора критерия из доступных --> |
||||||
|
<!-- - выбора критерия по умлчанию (DEFAULT), когда критерий не предусмотрен --> |
||||||
|
<!-- - создания нового критерия --> |
||||||
|
<select th:field="${newProgram.cretarea.id}"> |
||||||
|
<option value="0">выберите критерий</option> |
||||||
|
<option th:each="option : ${pcl}" th:value="${option.id}" th:text="${option.name_short}"></option> |
||||||
|
</select> |
||||||
|
</div> |
||||||
|
<br> |
||||||
|
<input type="submit" value="СОХРАНИТЬ И ВЕРНУТЬСЯ"/> |
||||||
|
</form> |
||||||
|
<br> |
||||||
|
<form th:action="@{/programs}" th:method="get"> |
||||||
|
<input type="submit" value="ВЕРНУТЬСЯ"/> |
||||||
|
</form> |
||||||
|
</main> |
||||||
|
</body> |
@ -0,0 +1,74 @@ |
|||||||
|
<!DOCTYPE HTML> |
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" |
||||||
|
xmlns:th="http://www.thymeleaf.org"> |
||||||
|
<head> |
||||||
|
<meta charset="utf-8"> |
||||||
|
<title>Programs</title> |
||||||
|
<script src="https://cdn.jsdelivr.net/npm/@webcomponents/webcomponentsjs@2/webcomponents-loader.min.js"></script> |
||||||
|
<script type="module" src="https://cdn.jsdelivr.net/gh/zerodevx/zero-md@1/src/zero-md.min.js"></script> |
||||||
|
</head> |
||||||
|
|
||||||
|
<body> |
||||||
|
<header> |
||||||
|
<h1>PROGRAMS</h1> |
||||||
|
menu : auth |
||||||
|
<br> |
||||||
|
</header> |
||||||
|
<main> |
||||||
|
<div class="main-wraper"> |
||||||
|
<h2>Перечень программ обучения:</h2> |
||||||
|
<form th:action="@{/programs}" method="get" th:object="${p}"></form> |
||||||
|
<table> |
||||||
|
<thead> |
||||||
|
<tr> |
||||||
|
<th>id</th> |
||||||
|
<th>name</th> |
||||||
|
<th>lenght</th> |
||||||
|
<th>study_direction</th> |
||||||
|
<th>price</th> |
||||||
|
<th>cretarea</th> |
||||||
|
<th> |
||||||
|
<!-- Переход на страницу добавления новой программы обучения --> |
||||||
|
<form th:action="@{/program/add}" th:method="get"> |
||||||
|
<input type="submit" value="ADD --|>"/> |
||||||
|
</form> |
||||||
|
</th> |
||||||
|
</tr> |
||||||
|
</thead> |
||||||
|
<tbody> |
||||||
|
<tr th:each="p: ${programs}"> |
||||||
|
<td> |
||||||
|
<input type="text" id="id" name="id" th:value="${p.id}" readonly /> |
||||||
|
</td> |
||||||
|
<td> |
||||||
|
<input type="text" id="name" name="name" th:value="${p.name}" readonly /> |
||||||
|
</td> |
||||||
|
<td> |
||||||
|
<input type="text" id="lenght" name="lenght" th:value="${p.lenght}" readonly /> |
||||||
|
</td> |
||||||
|
<td> |
||||||
|
<input type="text" id="study_direction" name="study_direction" th:value="${p.study_direction}" readonly /> |
||||||
|
</td> |
||||||
|
<td> |
||||||
|
<input type="text" id="price" name="price" th:value="${p.price}" readonly /> |
||||||
|
</td> |
||||||
|
<td> |
||||||
|
<input type="text" id="cretarea" name="cretarea" th:value="${p.cretarea.name_short}" readonly /> |
||||||
|
</td> |
||||||
|
<td> |
||||||
|
<form th:action="@{program-edit/{id}(id=${p.id})}" th:method="get"> |
||||||
|
<input type="submit" value="--|>"/> |
||||||
|
</form> |
||||||
|
<form th:action="@{/program/delete/{id}(id=${p.id})}" th:method="get"> |
||||||
|
<input type="submit" value="X"/> |
||||||
|
</form> |
||||||
|
</td> |
||||||
|
</tr> |
||||||
|
</tbody> |
||||||
|
</table> |
||||||
|
</table> |
||||||
|
</div> |
||||||
|
</main> |
||||||
|
|
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,68 @@ |
|||||||
|
package ru.molokoin.resourceserviceapi.controllers; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.http.HttpStatus; |
||||||
|
import org.springframework.http.MediaType; |
||||||
|
import org.springframework.http.ResponseEntity; |
||||||
|
import org.springframework.web.bind.annotation.DeleteMapping; |
||||||
|
import org.springframework.web.bind.annotation.GetMapping; |
||||||
|
import org.springframework.web.bind.annotation.PathVariable; |
||||||
|
import org.springframework.web.bind.annotation.PostMapping; |
||||||
|
import org.springframework.web.bind.annotation.PutMapping; |
||||||
|
import org.springframework.web.bind.annotation.RequestBody; |
||||||
|
import org.springframework.web.bind.annotation.RequestMapping; |
||||||
|
import org.springframework.web.bind.annotation.RestController; |
||||||
|
|
||||||
|
import ru.molokoin.resourceserviceapi.entities.Program; |
||||||
|
import ru.molokoin.resourceserviceapi.repositories.ProgramRepositoryFace; |
||||||
|
|
||||||
|
@RestController |
||||||
|
@RequestMapping(path = "/", consumes = {"*/*"}) |
||||||
|
public class ProgramController { |
||||||
|
@Autowired |
||||||
|
private ProgramRepositoryFace repo; |
||||||
|
|
||||||
|
@GetMapping("/program/list") |
||||||
|
public ResponseEntity<List<Program>> getPrograms(){ |
||||||
|
return new ResponseEntity<>(repo.findAll(), HttpStatus.OK); |
||||||
|
} |
||||||
|
|
||||||
|
@GetMapping("/program/{id}") |
||||||
|
public ResponseEntity<?> getTeacherByID(@PathVariable Integer id){ |
||||||
|
return new ResponseEntity<>(repo.findProgramById(id), HttpStatus.OK); |
||||||
|
} |
||||||
|
|
||||||
|
@PostMapping(path = "/program/create", |
||||||
|
consumes = MediaType.APPLICATION_JSON_VALUE, |
||||||
|
produces = MediaType.APPLICATION_JSON_VALUE) |
||||||
|
public ResponseEntity<?> saveTeacher(@RequestBody Program program) { |
||||||
|
repo.save(program); |
||||||
|
return new ResponseEntity<>(program, HttpStatus.CREATED); |
||||||
|
} |
||||||
|
|
||||||
|
@PutMapping(path = "/program/update/{id}", |
||||||
|
consumes = MediaType.APPLICATION_JSON_VALUE, |
||||||
|
produces = MediaType.APPLICATION_JSON_VALUE) |
||||||
|
public ResponseEntity<?> updateProgram(@PathVariable Integer id, @RequestBody Program program) { |
||||||
|
Program p = repo.findProgramById(id); |
||||||
|
p.setName(program.getName()); |
||||||
|
p.setLenght(program.getLenght()); |
||||||
|
p.setStudy_direction(program.getStudy_direction()); |
||||||
|
p.setPrice(program.getPrice()); |
||||||
|
p.setCretarea(program.getCretarea()); |
||||||
|
repo.save(p); |
||||||
|
return new ResponseEntity<>(repo.findProgramById(id), HttpStatus.CREATED); |
||||||
|
} |
||||||
|
|
||||||
|
@DeleteMapping("/program/delete/{id}") |
||||||
|
public ResponseEntity<String> deleteCretarea(@PathVariable Long id){ |
||||||
|
Program p = repo.findProgramById(id); |
||||||
|
System.out.println(p.toString()); |
||||||
|
repo.delete(p); |
||||||
|
return new ResponseEntity<>("Запись id#" + id + " удалена ... ", HttpStatus.OK); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,34 @@ |
|||||||
|
package ru.molokoin.resourceserviceapi.entities; |
||||||
|
|
||||||
|
import org.hibernate.annotations.OnDelete; |
||||||
|
import org.hibernate.annotations.OnDeleteAction; |
||||||
|
|
||||||
|
import jakarta.annotation.Nullable; |
||||||
|
import jakarta.persistence.Entity; |
||||||
|
import jakarta.persistence.FetchType; |
||||||
|
import jakarta.persistence.GeneratedValue; |
||||||
|
import jakarta.persistence.GenerationType; |
||||||
|
import jakarta.persistence.Id; |
||||||
|
import jakarta.persistence.ManyToOne; |
||||||
|
import lombok.AllArgsConstructor; |
||||||
|
import lombok.Data; |
||||||
|
import lombok.NoArgsConstructor; |
||||||
|
|
||||||
|
@NoArgsConstructor |
||||||
|
@AllArgsConstructor |
||||||
|
@Entity |
||||||
|
@Data |
||||||
|
public class Program { |
||||||
|
@Id |
||||||
|
@GeneratedValue(strategy=GenerationType.AUTO) |
||||||
|
private long id; |
||||||
|
private String name;//Наименование
|
||||||
|
private Integer lenght;//длительность программы
|
||||||
|
private String study_direction;//направление обучения: обязательное-производственное (учесть в ограничениях)
|
||||||
|
private Integer price; |
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.EAGER, optional = false) |
||||||
|
@OnDelete(action = OnDeleteAction.SET_NULL) |
||||||
|
// @Nullable
|
||||||
|
private ProgramCretarea cretarea; |
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
package ru.molokoin.resourceserviceapi.repositories; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import org.springframework.data.repository.ListCrudRepository; |
||||||
|
|
||||||
|
import ru.molokoin.resourceserviceapi.entities.Program; |
||||||
|
|
||||||
|
public interface ProgramRepositoryFace extends ListCrudRepository<Program, Long>{ |
||||||
|
List<Program> findAll(); |
||||||
|
Program findProgramById(long id); |
||||||
|
} |
Loading…
Reference in new issue