Bank gateway integration, rest for getting slots, email sending.
This commit is contained in:
parent
e84fa3ce3e
commit
46bf29a5eb
@ -24,7 +24,9 @@ dependencies {
|
||||
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-web")
|
||||
implementation("org.springframework.boot:spring-boot-starter-mail")
|
||||
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
|
||||
implementation("io.nayuki:qrcodegen:1.8.0")
|
||||
compileOnly("org.projectlombok:lombok")
|
||||
annotationProcessor("org.projectlombok:lombok")
|
||||
implementation("org.jetbrains.kotlin:kotlin-reflect")
|
||||
|
@ -2,8 +2,12 @@ package ru.vyatsu.qr_access_api
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||
import org.springframework.boot.runApplication
|
||||
import org.springframework.scheduling.annotation.EnableScheduling
|
||||
|
||||
//TODO: Сделать ретраи везде, где в блок-схемах они указаны
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
class QrAccessApiApplication
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
|
@ -0,0 +1,24 @@
|
||||
package ru.vyatsu.qr_access_api.booking.controller
|
||||
|
||||
import org.springframework.web.bind.annotation.PostMapping
|
||||
import org.springframework.web.bind.annotation.RequestBody
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
import ru.vyatsu.qr_access_api.booking.request.BookCallbackRequest
|
||||
import ru.vyatsu.qr_access_api.booking.service.BookingService
|
||||
import ru.vyatsu.qr_access_api.booking.request.BookRequest
|
||||
import ru.vyatsu.qr_access_api.booking.request.BookResponse
|
||||
|
||||
@RestController
|
||||
class BookingController(private val service: BookingService) {
|
||||
// TODO: Убрать /public, так как эти методы должны быть закрыты авторизацией client_credential и корсами
|
||||
// TODO: Для общего процесса бронирования нужно сделать альтернативный путь для шлюза с опросом.
|
||||
@PostMapping("/public/book")
|
||||
fun book(@RequestBody request: BookRequest): BookResponse {
|
||||
return BookResponse(service.book(request))
|
||||
}
|
||||
|
||||
@PostMapping("/public/book/callback")
|
||||
fun bookCallback(@RequestBody request: BookCallbackRequest) {
|
||||
return service.bookPayed(request.rentId)
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package ru.vyatsu.qr_access_api.booking.job
|
||||
|
||||
import org.springframework.scheduling.annotation.Scheduled
|
||||
import org.springframework.stereotype.Service
|
||||
import ru.vyatsu.qr_access_api.booking.repository.BookingRepository
|
||||
import ru.vyatsu.qr_access_api.common.logger
|
||||
import java.time.LocalDateTime
|
||||
|
||||
@Service
|
||||
class CleanExpiredNotPayedRentJob(private val repository: BookingRepository) {
|
||||
|
||||
@Scheduled(cron = "0 */5 * * * *")
|
||||
fun clean() {
|
||||
val deletedCount =
|
||||
repository.deleteRentByDateCreatedLessThen(LocalDateTime.now().minusMinutes(10))
|
||||
logger().info("CleanExpiredNotPayedRentJob deleted {} rents", deletedCount)
|
||||
}
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
package ru.vyatsu.qr_access_api.booking.repository
|
||||
|
||||
import org.springframework.dao.EmptyResultDataAccessException
|
||||
import org.springframework.jdbc.core.JdbcTemplate
|
||||
import org.springframework.jdbc.support.GeneratedKeyHolder
|
||||
import org.springframework.stereotype.Repository
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
import ru.vyatsu.apis.NotFoundException
|
||||
import ru.vyatsu.qr_access_api.booking.repository.entity.RentWithEmail
|
||||
import java.sql.Date
|
||||
import java.sql.Statement
|
||||
import java.sql.Time
|
||||
import java.sql.Timestamp
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalDateTime
|
||||
import java.time.LocalTime
|
||||
import java.util.*
|
||||
|
||||
private const val ADD_BOOKING_INFO_QUERY =
|
||||
"insert into rents(id, start_time, end_time, client_id, date, door_id, qr_code, payed, date_created) values (?, ? ,?, ?, ?, ?, ?, false, CURRENT_TIMESTAMP) RETURNING id"
|
||||
|
||||
private const val CREATE_NEW_CLIENT =
|
||||
"insert into clients(id, email, email_is_confirmed) values (?, ?, false) RETURNING id"
|
||||
|
||||
private const val FIND_CLIENT_BY_EMAIL = "select id from clients where email = ?"
|
||||
|
||||
private const val MARK_RENT_AS_PAYED_QUERY = "update rents set payed=true where id = ?"
|
||||
|
||||
@Repository
|
||||
class BookingRepository(val template: JdbcTemplate) {
|
||||
|
||||
@Transactional
|
||||
fun book(doorId: String, date: LocalDate, startTime: LocalTime, endTime: LocalTime, clientEmail: String): String {
|
||||
val clientIdHolder = GeneratedKeyHolder()
|
||||
val rentIdHolder = GeneratedKeyHolder()
|
||||
var clientId: String? = null
|
||||
try {
|
||||
clientId = template.queryForObject(
|
||||
FIND_CLIENT_BY_EMAIL,
|
||||
{ rs, _ -> rs.getString("id") },
|
||||
clientEmail
|
||||
)
|
||||
} catch (_: EmptyResultDataAccessException) {
|
||||
}
|
||||
|
||||
if (clientId == null) {
|
||||
template.update({
|
||||
val stmt = it.prepareStatement(CREATE_NEW_CLIENT, Statement.RETURN_GENERATED_KEYS)
|
||||
stmt.setString(1, UUID.randomUUID().toString())
|
||||
stmt.setString(2, clientEmail)
|
||||
stmt
|
||||
}, clientIdHolder)
|
||||
clientId = clientIdHolder.getKeyAs(String::class.java)
|
||||
}
|
||||
|
||||
if (clientId == null) throw RuntimeException("clientId is null even after insert, booking cannot be continued")
|
||||
|
||||
val insertedRows = template.update({
|
||||
val stmt = it.prepareStatement(ADD_BOOKING_INFO_QUERY, Statement.RETURN_GENERATED_KEYS)
|
||||
stmt.setString(1, UUID.randomUUID().toString())
|
||||
stmt.setTime(2, Time.valueOf(startTime))
|
||||
stmt.setTime(3, Time.valueOf(endTime))
|
||||
stmt.setString(4, clientId)
|
||||
stmt.setDate(5, Date.valueOf(date))
|
||||
stmt.setString(6, doorId)
|
||||
stmt.setString(7, UUID.randomUUID().toString())
|
||||
stmt
|
||||
}, rentIdHolder)
|
||||
|
||||
val insertedRentId = rentIdHolder.getKeyAs(String::class.java)
|
||||
if (insertedRows <= 0 || insertedRentId == null) throw RuntimeException(
|
||||
"Inserted rows number is invalid: %d. Have to be more then 0".format(
|
||||
insertedRows
|
||||
)
|
||||
)
|
||||
|
||||
return insertedRentId
|
||||
}
|
||||
|
||||
fun findRentWithClientById(id: String): RentWithEmail {
|
||||
return template.queryForObject(
|
||||
"select r.qr_code, c.email from rents r join clients c on (c.id = r.client_id) where r.id = ?",
|
||||
{ rs, _ -> RentWithEmail(rs.getString("qr_code"), rs.getString("email")) },
|
||||
id
|
||||
) ?: throw NotFoundException("Cannot find rent with id %s".format(id))
|
||||
}
|
||||
|
||||
fun markBookingPayed(rentId: String) {
|
||||
val updatedAmount = template.update {
|
||||
val stmt = it.prepareStatement(MARK_RENT_AS_PAYED_QUERY)
|
||||
stmt.setString(1, rentId)
|
||||
stmt
|
||||
}
|
||||
|
||||
if (updatedAmount == 0)
|
||||
throw NotFoundException("Cannot find rent with id %s".format(rentId))
|
||||
}
|
||||
|
||||
fun deleteRentByDateCreatedLessThen(dateTime: LocalDateTime): Int {
|
||||
return template.update {
|
||||
val stmt = it.prepareStatement("delete from rents where date_created <= ? and payed = false")
|
||||
stmt.setTimestamp(1, Timestamp.valueOf(dateTime))
|
||||
stmt
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
package ru.vyatsu.qr_access_api.booking.repository.entity
|
||||
|
||||
data class RentWithEmail(val qrCode: String, val email: String)
|
@ -0,0 +1,3 @@
|
||||
package ru.vyatsu.qr_access_api.booking.request
|
||||
|
||||
data class BookCallbackRequest(val rentId: String)
|
@ -0,0 +1,10 @@
|
||||
package ru.vyatsu.qr_access_api.booking.request
|
||||
|
||||
import java.time.LocalDateTime
|
||||
|
||||
data class BookRequest(
|
||||
val startDateTime: LocalDateTime,
|
||||
val endDateTime: LocalDateTime,
|
||||
val doorId: String,
|
||||
val clientEmail: String
|
||||
)
|
@ -0,0 +1,3 @@
|
||||
package ru.vyatsu.qr_access_api.booking.request
|
||||
|
||||
data class BookResponse(val rentId: String)
|
@ -0,0 +1,37 @@
|
||||
package ru.vyatsu.qr_access_api.booking.service
|
||||
|
||||
import com.fasterxml.jackson.databind.node.JsonNodeFactory
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode
|
||||
import org.springframework.stereotype.Service
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
import ru.vyatsu.qr_access_api.booking.repository.BookingRepository
|
||||
import ru.vyatsu.qr_access_api.booking.request.BookRequest
|
||||
import ru.vyatsu.qr_access_api.common.exception.ValidationException
|
||||
import ru.vyatsu.qr_access_api.email.repository.EmailRepository
|
||||
|
||||
@Service
|
||||
class BookingService(val bookingRepository: BookingRepository, val emailRepository: EmailRepository) {
|
||||
fun book(request: BookRequest): String {
|
||||
// TODO: Произвести еще валидации, если нужны
|
||||
val date = request.startDateTime.toLocalDate()
|
||||
if (date != request.endDateTime.toLocalDate()) {
|
||||
throw ValidationException(
|
||||
"startDateTime, endDateTime",
|
||||
"startDateTime and endDateTime have to have the same day"
|
||||
)
|
||||
}
|
||||
val startTime = request.startDateTime.toLocalTime()
|
||||
val endTime = request.endDateTime.toLocalTime()
|
||||
|
||||
return bookingRepository.book(request.doorId, date, startTime, endTime, request.clientEmail)
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun bookPayed(bookId: String) {
|
||||
bookingRepository.markBookingPayed(bookId)
|
||||
val (qrCode, email) = bookingRepository.findRentWithClientById(bookId)
|
||||
val additionalData = JsonNodeFactory.instance.objectNode()
|
||||
additionalData.set<ObjectNode>("qr_code", JsonNodeFactory.instance.textNode(qrCode))
|
||||
emailRepository.createEmailOutboxRecord(email, "qr_code", additionalData)
|
||||
}
|
||||
}
|
8
src/main/kotlin/ru/vyatsu/qr_access_api/common/Log.kt
Normal file
8
src/main/kotlin/ru/vyatsu/qr_access_api/common/Log.kt
Normal file
@ -0,0 +1,8 @@
|
||||
package ru.vyatsu.qr_access_api.common
|
||||
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
inline fun <reified T> T.logger(): Logger {
|
||||
return LoggerFactory.getLogger(T::class.java)
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package ru.vyatsu.qr_access_api.common.config
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.mail.javamail.JavaMailSender
|
||||
import org.springframework.mail.javamail.JavaMailSenderImpl
|
||||
|
||||
|
||||
@Configuration
|
||||
class EmailConfig {
|
||||
@Bean
|
||||
fun mailSender(): JavaMailSender {
|
||||
val mailSender = JavaMailSenderImpl()
|
||||
mailSender.host = "smtp.yandex.ru"
|
||||
mailSender.port = 587
|
||||
mailSender.username = "kashiuno@yandex.ru"
|
||||
mailSender.password = "qmpEMP262049!!!?EEWChaosMeteor"
|
||||
|
||||
val props = mailSender.javaMailProperties
|
||||
props["mail.transport.protocol"] = "smtp"
|
||||
props["mail.smtp.auth"] = "true"
|
||||
props["mail.smtp.starttls.enable"] = "true"
|
||||
props["mail.debug"] = "true"
|
||||
|
||||
return mailSender
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package ru.vyatsu.qr_access_api.common.exception
|
||||
|
||||
class ValidationException(val fieldName: String, override val message: String) :
|
||||
RuntimeException("fieldName: %s -- message: %s".format(fieldName, message))
|
@ -2,22 +2,40 @@ package ru.vyatsu.qr_access_api.config
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.http.HttpHeaders
|
||||
import org.springframework.http.HttpMethod
|
||||
import org.springframework.security.config.Customizer
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||
import org.springframework.security.web.SecurityFilterChain
|
||||
import org.springframework.web.cors.CorsConfiguration
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableWebSecurity(debug = true)
|
||||
class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
fun defaultSecurityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||
return http.authorizeHttpRequests {
|
||||
it.requestMatchers("/public/**").permitAll()
|
||||
it.requestMatchers("/public/**", "/error").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
}
|
||||
.oauth2ResourceServer { it.jwt(Customizer.withDefaults()) }
|
||||
.cors { c ->
|
||||
c.configurationSource {
|
||||
val config = CorsConfiguration()
|
||||
config.addAllowedOrigin("http://localhost:3000")
|
||||
config.allowedMethods = listOf(
|
||||
HttpMethod.GET.name(),
|
||||
HttpMethod.POST.name()
|
||||
)
|
||||
config.allowedHeaders = listOf(
|
||||
HttpHeaders.CONTENT_TYPE
|
||||
)
|
||||
config
|
||||
}
|
||||
}
|
||||
.csrf { c -> c.disable() }
|
||||
.build()
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
package ru.vyatsu.qr_access_api.email.job
|
||||
|
||||
import io.nayuki.qrcodegen.QrCode
|
||||
import org.springframework.mail.javamail.JavaMailSender
|
||||
import org.springframework.mail.javamail.MimeMessageHelper
|
||||
import org.springframework.scheduling.annotation.Scheduled
|
||||
import org.springframework.stereotype.Service
|
||||
import ru.vyatsu.qr_access_api.common.logger
|
||||
import ru.vyatsu.qr_access_api.email.repository.EmailRepository
|
||||
import java.awt.image.BufferedImage
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.util.*
|
||||
import javax.imageio.ImageIO
|
||||
|
||||
|
||||
@Service
|
||||
class EmailOutboxSendJob(val repository: EmailRepository, val mailSender: JavaMailSender) {
|
||||
|
||||
@Scheduled(cron = "*/5 * * * * *")
|
||||
fun sendEmails() {
|
||||
val messagesToSend = repository.findRecordsToSendWithBlock()
|
||||
|
||||
messagesToSend.forEach {
|
||||
val msg = mailSender.createMimeMessage()
|
||||
|
||||
val helper = MimeMessageHelper(msg, true)
|
||||
|
||||
helper.setFrom("kashiuno@yandex.ru")
|
||||
helper.setTo(it.email)
|
||||
helper.setSubject("Приобретение qr-кода")
|
||||
helper.setText("Вы приобрели проход в коворкинг на нашем сайте. qr-код во вложении. QR-код нужно приложить к сканеру соответствующей двери и она откроется")
|
||||
|
||||
val qrCode = it.additionalData.get("qr_code")
|
||||
val code = QrCode.encodeText(qrCode.asText(), QrCode.Ecc.MEDIUM)
|
||||
val image = toImage(code, 3, 2)
|
||||
val os = ByteArrayOutputStream()
|
||||
ImageIO.write(image, "png", os)
|
||||
val imageIS = ByteArrayInputStream(os.toByteArray())
|
||||
helper.addAttachment("qr_code.png") { imageIS }
|
||||
}
|
||||
|
||||
val recordsWasDeletedCount = repository.deleteRecordsToSend(messagesToSend.map { it.id })
|
||||
logger().info("Emails sent {}", recordsWasDeletedCount)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun toImage(qr: QrCode, scale: Int, border: Int, lightColor: Int, darkColor: Int): BufferedImage {
|
||||
Objects.requireNonNull(qr)
|
||||
require(!(scale <= 0 || border < 0)) { "Value out of range" }
|
||||
require(!(border > Int.MAX_VALUE / 2 || qr.size + border * 2L > Int.MAX_VALUE / scale)) { "Scale or border too large" }
|
||||
|
||||
val result = BufferedImage(
|
||||
(qr.size + border * 2) * scale,
|
||||
(qr.size + border * 2) * scale,
|
||||
BufferedImage.TYPE_INT_RGB
|
||||
)
|
||||
for (y in 0 until result.height) {
|
||||
for (x in 0 until result.width) {
|
||||
val color = qr.getModule(x / scale - border, y / scale - border)
|
||||
result.setRGB(x, y, if (color) darkColor else lightColor)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun toImage(qr: QrCode?, scale: Int, border: Int): BufferedImage {
|
||||
return toImage(qr!!, scale, border, 0xFFFFFF, 0x000000)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package ru.vyatsu.qr_access_api.email.repository
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import org.springframework.jdbc.core.BatchPreparedStatementSetter
|
||||
import org.springframework.jdbc.core.JdbcTemplate
|
||||
import org.springframework.stereotype.Repository
|
||||
import ru.vyatsu.qr_access_api.email.repository.entity.EmailOutbox
|
||||
import java.sql.PreparedStatement
|
||||
import java.util.*
|
||||
|
||||
private const val INSERT_EMAIL_OUTBOX_RECORD =
|
||||
"insert into email_outbox(id, email, template, additional_info) values (?, ?, ?, (to_json(?::json)))"
|
||||
|
||||
@Repository
|
||||
class EmailRepository(private val template: JdbcTemplate, private val om: ObjectMapper) {
|
||||
fun createEmailOutboxRecord(email: String, msgTemplate: String, additionalInfo: JsonNode) {
|
||||
val insertedCount = template.update {
|
||||
val stmt = it.prepareStatement(INSERT_EMAIL_OUTBOX_RECORD)
|
||||
stmt.setString(1, UUID.randomUUID().toString())
|
||||
stmt.setString(2, email)
|
||||
stmt.setString(3, msgTemplate)
|
||||
|
||||
stmt.setObject(4, om.writeValueAsString(additionalInfo))
|
||||
stmt
|
||||
}
|
||||
|
||||
if (insertedCount != 1)
|
||||
throw RuntimeException("Inserted rows should be equals to 1")
|
||||
}
|
||||
|
||||
fun findRecordsToSendWithBlock(): List<EmailOutbox> {
|
||||
return template.query("select id, email, template, additional_info from email_outbox for update", { rs, _ ->
|
||||
EmailOutbox(
|
||||
rs.getString("id"),
|
||||
rs.getString("email"),
|
||||
rs.getString("template"),
|
||||
om.readTree(rs.getString("additional_info"))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fun deleteRecordsToSend(ids: List<String>): Int {
|
||||
val query = "delete from email_outbox where id = ?"
|
||||
return template.batchUpdate(query, object : BatchPreparedStatementSetter {
|
||||
override fun setValues(ps: PreparedStatement, i: Int) {
|
||||
ps.setString(1, ids[i])
|
||||
}
|
||||
|
||||
override fun getBatchSize(): Int = ids.size
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package ru.vyatsu.qr_access_api.email.repository.entity
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode
|
||||
|
||||
data class EmailOutbox(val id: String, val email: String, val template: String, val additionalData: JsonNode)
|
@ -1,10 +1,10 @@
|
||||
package ru.vyatsu.qr_access_api.controller
|
||||
package ru.vyatsu.qr_access_api.qr.controller
|
||||
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
import ru.vyatsu.apis.QrApi
|
||||
import ru.vyatsu.models.QrCodesResponse
|
||||
import ru.vyatsu.qr_access_api.service.QrSyncService
|
||||
import ru.vyatsu.qr_access_api.qr.service.QrSyncService
|
||||
|
||||
@RestController
|
||||
class QrSyncController(val syncService: QrSyncService) : QrApi {
|
@ -1,4 +1,4 @@
|
||||
package ru.vyatsu.qr_access_api.controller
|
||||
package ru.vyatsu.qr_access_api.qr.controller
|
||||
|
||||
import org.springframework.http.HttpStatusCode
|
||||
import org.springframework.http.ResponseEntity
|
@ -1,4 +1,4 @@
|
||||
package ru.vyatsu.qr_access_api.repository
|
||||
package ru.vyatsu.qr_access_api.qr.repository
|
||||
|
||||
import org.springframework.jdbc.core.JdbcTemplate
|
||||
import org.springframework.stereotype.Repository
|
@ -1,9 +1,9 @@
|
||||
package ru.vyatsu.qr_access_api.service
|
||||
package ru.vyatsu.qr_access_api.qr.service
|
||||
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
import org.springframework.stereotype.Service
|
||||
import ru.vyatsu.models.QrCode
|
||||
import ru.vyatsu.qr_access_api.repository.QrRepository
|
||||
import ru.vyatsu.qr_access_api.qr.repository.QrRepository
|
||||
|
||||
@Service
|
||||
class QrSyncService(val qrRepository: QrRepository) {
|
@ -1,15 +1,12 @@
|
||||
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 org.springframework.web.bind.annotation.*
|
||||
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) {
|
||||
class SlotsController(val service: SlotService) {
|
||||
|
||||
@GetMapping("/slots/{partnerId}")
|
||||
fun getSlots(@PathVariable partnerId: String): SlotResponse {
|
@ -6,12 +6,11 @@ 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"
|
||||
const val FIND_RENT_DOORS_BY_PARTNER_ID =
|
||||
"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 rents 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({
|
||||
@ -43,10 +42,10 @@ class SlotRepository(private val jdbc: JdbcTemplate) {
|
||||
return doors.values
|
||||
}
|
||||
|
||||
fun findRentDateTimesByDoorsId(partnerId: String): DoorRent {
|
||||
fun findRentDateTimesByPartnerId(partnerId: String): DoorRent {
|
||||
val doors = DoorRent(mutableMapOf())
|
||||
jdbc.query({
|
||||
val stmt = it.prepareStatement(FIND_RENT_DOORS_BY_DOOR_IDS)
|
||||
val stmt = it.prepareStatement(FIND_RENT_DOORS_BY_PARTNER_ID)
|
||||
stmt.setString(1, partnerId)
|
||||
stmt
|
||||
}, { rs ->
|
||||
|
@ -14,7 +14,7 @@ class SlotService(private val repository: SlotRepository) {
|
||||
fun getAllSlotsByPartner(partnerId: String): SlotResponse {
|
||||
// TODO: Учитывать количество мест и переписать логику
|
||||
val doorsWithSchedule = repository.findAllDoorsWithScheduleByPartnerId(partnerId)
|
||||
val doorRents = repository.findRentDateTimesByDoorsId(partnerId)
|
||||
val doorRents = repository.findRentDateTimesByPartnerId(partnerId)
|
||||
val doors: MutableList<Door> = mutableListOf()
|
||||
doorsWithSchedule.forEach { d ->
|
||||
val slots: MutableList<Slot> = mutableListOf()
|
||||
|
@ -132,6 +132,11 @@ databaseChangeLog:
|
||||
type: TEXT
|
||||
constraints:
|
||||
nullable: true
|
||||
- column:
|
||||
name: price
|
||||
type: DECIMAL(12, 2)
|
||||
constraints:
|
||||
nullable: false
|
||||
- createTable:
|
||||
tableName: clients
|
||||
columns:
|
||||
@ -145,6 +150,7 @@ databaseChangeLog:
|
||||
- column:
|
||||
constraints:
|
||||
nullable: false
|
||||
unique: true
|
||||
name: email
|
||||
type: TEXT
|
||||
- column:
|
||||
@ -153,7 +159,7 @@ databaseChangeLog:
|
||||
name: email_is_confirmed
|
||||
type: BOOLEAN
|
||||
- createTable:
|
||||
tableName: rent
|
||||
tableName: rents
|
||||
columns:
|
||||
- column:
|
||||
constraints:
|
||||
|
@ -7,6 +7,7 @@ import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Import
|
||||
import org.springframework.jdbc.core.JdbcTemplate
|
||||
import ru.vyatsu.qr_access_api.database.utils.InsertDatabaseHelper
|
||||
import ru.vyatsu.qr_access_api.qr.repository.QrRepository
|
||||
|
||||
@JdbcTest
|
||||
@Import(RepositoryTest.Configuration::class)
|
||||
|
Loading…
Reference in New Issue
Block a user