Compare commits
2 Commits
6abafedfec
...
e84fa3ce3e
Author | SHA1 | Date | |
---|---|---|---|
|
e84fa3ce3e | ||
|
2211280078 |
@ -1,48 +1,50 @@
|
|||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm") version "1.9.25"
|
kotlin("jvm") version "1.9.25"
|
||||||
kotlin("plugin.spring") version "1.9.25"
|
kotlin("plugin.spring") version "1.9.25"
|
||||||
id("org.springframework.boot") version "3.4.1"
|
id("org.springframework.boot") version "3.4.1"
|
||||||
id("io.spring.dependency-management") version "1.1.7"
|
id("io.spring.dependency-management") version "1.1.7"
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "ru.vyatsu"
|
group = "ru.vyatsu"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
|
||||||
java {
|
java {
|
||||||
toolchain {
|
toolchain {
|
||||||
languageVersion = JavaLanguageVersion.of(17)
|
languageVersion = JavaLanguageVersion.of(17)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("org.springframework.boot:spring-boot-starter-data-jdbc")
|
implementation("org.springframework.boot:spring-boot-starter-data-jdbc")
|
||||||
implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server")
|
implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server")
|
||||||
implementation("org.springframework.boot:spring-boot-starter-security")
|
implementation("org.springframework.boot:spring-boot-starter-security")
|
||||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||||
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
|
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
|
||||||
implementation("org.jetbrains.kotlin:kotlin-reflect")
|
compileOnly("org.projectlombok:lombok")
|
||||||
implementation("org.liquibase:liquibase-core")
|
annotationProcessor("org.projectlombok:lombok")
|
||||||
implementation("ru.vyatsu:qr-access-hardware-contract:1.0.0")
|
implementation("org.jetbrains.kotlin:kotlin-reflect")
|
||||||
runtimeOnly("org.postgresql:postgresql")
|
implementation("org.liquibase:liquibase-core")
|
||||||
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
implementation("ru.vyatsu:qr-access-hardware-contract:1.0.0")
|
||||||
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
|
runtimeOnly("org.postgresql:postgresql")
|
||||||
testImplementation("org.springframework.security:spring-security-test")
|
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||||
testImplementation("org.testcontainers:postgresql")
|
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
|
||||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
testImplementation("org.springframework.security:spring-security-test")
|
||||||
implementation("org.yaml:snakeyaml")
|
testImplementation("org.testcontainers:postgresql")
|
||||||
|
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||||
|
implementation("org.yaml:snakeyaml")
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
compilerOptions {
|
compilerOptions {
|
||||||
freeCompilerArgs.addAll("-Xjsr305=strict")
|
freeCompilerArgs.addAll("-Xjsr305=strict")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType<Test> {
|
tasks.withType<Test> {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,10 @@ class SecurityConfig {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
fun defaultSecurityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
fun defaultSecurityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
return http.authorizeHttpRequests { it.anyRequest().authenticated() }
|
return http.authorizeHttpRequests {
|
||||||
|
it.requestMatchers("/public/**").permitAll()
|
||||||
|
.anyRequest().authenticated()
|
||||||
|
}
|
||||||
.oauth2ResourceServer { it.jwt(Customizer.withDefaults()) }
|
.oauth2ResourceServer { it.jwt(Customizer.withDefaults()) }
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
package ru.vyatsu.qr_access_api.slots.controller
|
||||||
|
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
import ru.vyatsu.qr_access_api.slots.response.SlotResponse
|
||||||
|
import ru.vyatsu.qr_access_api.slots.service.SlotService
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("public")
|
||||||
|
class PublicSlotsController(val service: SlotService) {
|
||||||
|
|
||||||
|
@GetMapping("/slots/{partnerId}")
|
||||||
|
fun getSlots(@PathVariable partnerId: String): SlotResponse {
|
||||||
|
return service.getAllSlotsByPartner(partnerId)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
package ru.vyatsu.qr_access_api.slots.repository
|
||||||
|
|
||||||
|
import org.springframework.jdbc.core.JdbcTemplate
|
||||||
|
import org.springframework.stereotype.Repository
|
||||||
|
import ru.vyatsu.qr_access_api.slots.repository.entity.*
|
||||||
|
|
||||||
|
const val FIND_DOORS_SCHEDULE_BY_PARTNER_ID =
|
||||||
|
"select d.id, d.description, d.count, s.date, s.start_time, s.end_time from oauth2_registered_client u join doors d on (d.unit_id = u.client_id) join schedule s on (d.id = s.door_id) where u.partner_id = ?"
|
||||||
|
const val FIND_RENT_DOORS_BY_DOOR_IDS =
|
||||||
|
"select d.id, r.date, r.start_time, r.end_time from oauth2_registered_client u join doors d on (d.unit_id = u.client_id) join rent r on (r.door_id = d.id) where u.partner_id = ? order by r.start_time asc"
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
class SlotRepository(private val jdbc: JdbcTemplate) {
|
||||||
|
|
||||||
|
fun findAllDoorsWithScheduleByPartnerId(partnerId: String): Collection<DoorWithSchedule> {
|
||||||
|
val doors: MutableMap<String, DoorWithSchedule> = mutableMapOf()
|
||||||
|
jdbc.query({
|
||||||
|
val stmt = it.prepareStatement(FIND_DOORS_SCHEDULE_BY_PARTNER_ID)
|
||||||
|
stmt.setString(1, partnerId)
|
||||||
|
stmt
|
||||||
|
}, { rs ->
|
||||||
|
val id = rs.getString("id")
|
||||||
|
doors[id]?.also {
|
||||||
|
it.schedule.add(
|
||||||
|
ScheduleEntry(
|
||||||
|
rs.getTime("start_time").toLocalTime(),
|
||||||
|
rs.getTime("end_time").toLocalTime(),
|
||||||
|
rs.getDate("date").toLocalDate()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} ?: run {
|
||||||
|
doors[id] = DoorWithSchedule(
|
||||||
|
id, rs.getString("description"), rs.getInt("count"), mutableListOf(
|
||||||
|
ScheduleEntry(
|
||||||
|
rs.getTime("start_time").toLocalTime(),
|
||||||
|
rs.getTime("end_time").toLocalTime(),
|
||||||
|
rs.getDate("date").toLocalDate()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return doors.values
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findRentDateTimesByDoorsId(partnerId: String): DoorRent {
|
||||||
|
val doors = DoorRent(mutableMapOf())
|
||||||
|
jdbc.query({
|
||||||
|
val stmt = it.prepareStatement(FIND_RENT_DOORS_BY_DOOR_IDS)
|
||||||
|
stmt.setString(1, partnerId)
|
||||||
|
stmt
|
||||||
|
}, { rs ->
|
||||||
|
val id = rs.getString("id")
|
||||||
|
val date = rs.getDate("date").toLocalDate()
|
||||||
|
doors.dates[id]?.also { d ->
|
||||||
|
d.rentDate[date]?.also { times ->
|
||||||
|
times.add(RentTime(rs.getTime("start_time").toLocalTime(), rs.getTime("end_time").toLocalTime()))
|
||||||
|
} ?: run {
|
||||||
|
d.rentDate[date] = mutableListOf(
|
||||||
|
RentTime(
|
||||||
|
rs.getTime("start_time").toLocalTime(),
|
||||||
|
rs.getTime("end_time").toLocalTime()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} ?: run {
|
||||||
|
doors.dates[id] = RentDate(
|
||||||
|
mutableMapOf(
|
||||||
|
date to mutableListOf(
|
||||||
|
RentTime(
|
||||||
|
rs.getTime("start_time").toLocalTime(),
|
||||||
|
rs.getTime("end_time").toLocalTime()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return doors
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
package ru.vyatsu.qr_access_api.slots.repository.entity
|
||||||
|
|
||||||
|
data class DoorRent(val dates: MutableMap<String, RentDate>)
|
@ -0,0 +1,8 @@
|
|||||||
|
package ru.vyatsu.qr_access_api.slots.repository.entity
|
||||||
|
|
||||||
|
data class DoorWithSchedule(
|
||||||
|
val id: String,
|
||||||
|
val description: String,
|
||||||
|
val count: Int,
|
||||||
|
val schedule: MutableList<ScheduleEntry>
|
||||||
|
)
|
@ -0,0 +1,5 @@
|
|||||||
|
package ru.vyatsu.qr_access_api.slots.repository.entity
|
||||||
|
|
||||||
|
import java.time.LocalDate
|
||||||
|
|
||||||
|
data class RentDate(val rentDate: MutableMap<LocalDate, MutableList<RentTime>>)
|
@ -0,0 +1,5 @@
|
|||||||
|
package ru.vyatsu.qr_access_api.slots.repository.entity
|
||||||
|
|
||||||
|
import java.time.LocalTime
|
||||||
|
|
||||||
|
data class RentTime(val startTime: LocalTime, val endTime: LocalTime)
|
@ -0,0 +1,6 @@
|
|||||||
|
package ru.vyatsu.qr_access_api.slots.repository.entity
|
||||||
|
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.time.LocalTime
|
||||||
|
|
||||||
|
data class ScheduleEntry(val startTime: LocalTime, val endTime: LocalTime, val date: LocalDate)
|
@ -0,0 +1,3 @@
|
|||||||
|
package ru.vyatsu.qr_access_api.slots.response
|
||||||
|
|
||||||
|
data class Door(val id: String, val description: String, val slots: List<Slot>)
|
@ -0,0 +1,6 @@
|
|||||||
|
package ru.vyatsu.qr_access_api.slots.response
|
||||||
|
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.time.LocalTime
|
||||||
|
|
||||||
|
data class Slot(val startTime: LocalTime, val endTime: LocalTime, val date: LocalDate, val status: SlotStatus)
|
@ -0,0 +1,3 @@
|
|||||||
|
package ru.vyatsu.qr_access_api.slots.response
|
||||||
|
|
||||||
|
data class SlotResponse(val doors: List<Door>)
|
@ -0,0 +1,5 @@
|
|||||||
|
package ru.vyatsu.qr_access_api.slots.response
|
||||||
|
|
||||||
|
enum class SlotStatus {
|
||||||
|
FREE, BOOKED, OUT_OF_WORKING_TIME
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
package ru.vyatsu.qr_access_api.slots.service
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import ru.vyatsu.qr_access_api.slots.repository.SlotRepository
|
||||||
|
import ru.vyatsu.qr_access_api.slots.response.Door
|
||||||
|
import ru.vyatsu.qr_access_api.slots.response.Slot
|
||||||
|
import ru.vyatsu.qr_access_api.slots.response.SlotResponse
|
||||||
|
import ru.vyatsu.qr_access_api.slots.response.SlotStatus
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.time.LocalTime
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class SlotService(private val repository: SlotRepository) {
|
||||||
|
fun getAllSlotsByPartner(partnerId: String): SlotResponse {
|
||||||
|
// TODO: Учитывать количество мест и переписать логику
|
||||||
|
val doorsWithSchedule = repository.findAllDoorsWithScheduleByPartnerId(partnerId)
|
||||||
|
val doorRents = repository.findRentDateTimesByDoorsId(partnerId)
|
||||||
|
val doors: MutableList<Door> = mutableListOf()
|
||||||
|
doorsWithSchedule.forEach { d ->
|
||||||
|
val slots: MutableList<Slot> = mutableListOf()
|
||||||
|
d.schedule.forEach { sch ->
|
||||||
|
val rentTimes = doorRents.dates[d.id]?.rentDate?.get(sch.date) ?: listOf()
|
||||||
|
createIntermediateSlot(LocalTime.MIN, sch.startTime, sch.date, SlotStatus.OUT_OF_WORKING_TIME)
|
||||||
|
?.also { slots.add(it) }
|
||||||
|
rentTimes.forEach { rt ->
|
||||||
|
val lastSlot: Slot? = if (slots.lastIndex == -1) null else slots.last()
|
||||||
|
createIntermediateSlot(lastSlot?.endTime, rt.startTime, sch.date, SlotStatus.FREE)
|
||||||
|
?.also { slots.add(it) }
|
||||||
|
slots.add(Slot(rt.startTime, rt.endTime, sch.date, SlotStatus.BOOKED))
|
||||||
|
}
|
||||||
|
var lastSlot: Slot? = if (slots.lastIndex == -1) null else slots.last()
|
||||||
|
createIntermediateSlot(lastSlot?.endTime, sch.endTime, sch.date, SlotStatus.FREE)
|
||||||
|
?.also { slots.add(it) }
|
||||||
|
lastSlot = if (slots.lastIndex == -1) null else slots.last()
|
||||||
|
createIntermediateSlot(lastSlot?.endTime, LocalTime.MAX, sch.date, SlotStatus.OUT_OF_WORKING_TIME)
|
||||||
|
?.also { slots.add(it) }
|
||||||
|
}
|
||||||
|
doors.add(Door(d.id, d.description, slots))
|
||||||
|
}
|
||||||
|
return SlotResponse(doors)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createIntermediateSlot(
|
||||||
|
startTime: LocalTime?,
|
||||||
|
endTime: LocalTime,
|
||||||
|
date: LocalDate,
|
||||||
|
status: SlotStatus
|
||||||
|
): Slot? {
|
||||||
|
return if (startTime != null && startTime != endTime) Slot(startTime, endTime, date, status) else null
|
||||||
|
}
|
||||||
|
}
|
@ -128,12 +128,30 @@ databaseChangeLog:
|
|||||||
constraints:
|
constraints:
|
||||||
nullable: false
|
nullable: false
|
||||||
- column:
|
- column:
|
||||||
name: parent_door_id
|
name: parent_door_ids
|
||||||
type: TEXT
|
type: TEXT
|
||||||
constraints:
|
constraints:
|
||||||
nullable: true
|
nullable: true
|
||||||
foreignKeyName: FK_parent_doors
|
- createTable:
|
||||||
references: doors(id)
|
tableName: clients
|
||||||
|
columns:
|
||||||
|
- column:
|
||||||
|
constraints:
|
||||||
|
nullable: false
|
||||||
|
primaryKey: true
|
||||||
|
primaryKeyName: PK_clients
|
||||||
|
name: id
|
||||||
|
type: TEXT
|
||||||
|
- column:
|
||||||
|
constraints:
|
||||||
|
nullable: false
|
||||||
|
name: email
|
||||||
|
type: TEXT
|
||||||
|
- column:
|
||||||
|
constraints:
|
||||||
|
nullable: false
|
||||||
|
name: email_is_confirmed
|
||||||
|
type: BOOLEAN
|
||||||
- createTable:
|
- createTable:
|
||||||
tableName: rent
|
tableName: rent
|
||||||
columns:
|
columns:
|
||||||
@ -160,7 +178,7 @@ databaseChangeLog:
|
|||||||
constraints:
|
constraints:
|
||||||
nullable: false
|
nullable: false
|
||||||
foreignKeyName: FK_rent_clients
|
foreignKeyName: FK_rent_clients
|
||||||
references: rent(id)
|
references: clients(id)
|
||||||
- column:
|
- column:
|
||||||
name: date
|
name: date
|
||||||
type: DATE
|
type: DATE
|
||||||
@ -188,26 +206,6 @@ databaseChangeLog:
|
|||||||
type: TIMESTAMP WITH TIME ZONE
|
type: TIMESTAMP WITH TIME ZONE
|
||||||
constraints:
|
constraints:
|
||||||
nullable: false
|
nullable: false
|
||||||
- createTable:
|
|
||||||
tableName: clients
|
|
||||||
columns:
|
|
||||||
- column:
|
|
||||||
constraints:
|
|
||||||
nullable: false
|
|
||||||
primaryKey: true
|
|
||||||
primaryKeyName: PK_clients
|
|
||||||
name: id
|
|
||||||
type: TEXT
|
|
||||||
- column:
|
|
||||||
constraints:
|
|
||||||
nullable: false
|
|
||||||
name: email
|
|
||||||
type: TEXT
|
|
||||||
- column:
|
|
||||||
constraints:
|
|
||||||
nullable: false
|
|
||||||
name: email_is_confirmed
|
|
||||||
type: BOOLEAN
|
|
||||||
- createTable:
|
- createTable:
|
||||||
tableName: schedule
|
tableName: schedule
|
||||||
columns:
|
columns:
|
||||||
|
Loading…
Reference in New Issue
Block a user